SlideShare a Scribd company logo
OpenDMS
I couldnt think of anything to call it, but as Darren said it could be open sourced and it is a DMS this
seemed logical.
While I am working (at the moment) on eBay functionality, I have been mindful to do things is such a
way as to allow this to serve as a framework of sorts for the rest of the DMS proper. In this document I
hope to take you through the salient points of the design and how they would be extensible to
additional functionality.
Introduction
While I have split this project into a loose Model-View-Presenter architecture, it is not tied to any
paricular MVP framework or library, more a guiding principal than anything else. Still it serves well
enough to divide up the functionality
model
Database (appendix A)
As it stands currently, the core of the Model is the JDBC database integration, this begins with the
conection manager. I have used the C3PO connection pooling library to implement a pooled datasource
object which allows client objects request connections from (current max of 20 connections but that
can be changed easily).
Individual database queries are encapsulated as concrete commands in a command pattern with the
invoker being a factory. As it stands the invoker/factory keeps copies of the commands in a map for
easy access, but should there turn out to be a significant number of concrete database commands it
would make sense to have a specific factory class for each concrete command to avoid having to store
all of them in each instance of the invoker.
As a side point each concrete command has a 'List<String[]> execute(String[])' where the individual
query functionality is implemented. In the return type ach element of the String[] represents a column,
and each element of the list is a seperate row.
Obviously insert/update/delete queries only return a result code so there is a choice here. Seperate the
whole thing into two command patterns or treat every query equally and just use the ouput appropiately
and consistantly.
Services (appendix B)
This is the second part of the model and encompasses everything else that needs to go on in the
background. I can see two types of services that might need to be implemented: Scheduled services and
triggered services. i.e. a service that runs at set intervals and services that are expressly triggered by the
user.
I have implemented the Scheduled Services part but not the Triggered Services part as yet. The
Scheduled services are implemented using a class called ServiceScheduler, which creates a
ScheduledThreadPoolExecutor (from the java.util.concurrent lib) and stores AbstractService derived
types (AbstractService is an empty interface that implements runnable) in a List. It has an
add(AbstractService, delay, rate) method so that each service can run at its own pre-specified interval,
and delay; as well as a start method which iterates through the list calling the .get() method on each
services ScheduledFuture<?>, fairly self explanatory.
As I said I have not implemented the triggered services yet as I dont know what the best way of doing
this would be.
View/presenter (appendix C)
The View is Jetty based (embedded http server and servlet container). Currently the HTTP server has a
fairly basic configuration (no SSL, no sessions, no auth) but I figured that could be configured at a later
stage (jetty has built in database auth functionality, as well as support for SSL and sessions, and a
bunch of other fun stuff).
The HTTP Server is responsible for one servlet at the moment, cunningly named UIServlet. This
UIServlet uses a View/Presenter Abstract factory to select an appropriate pair of View and Presenter
based on the URL parameters passed to the servlets doGet method from individual HTTP requests.
The concrete View objects are responsible for reading .html files located in a resources directory and
providing a 'make' method for the presenter to use when putting a particular view together i.e. the
make method returns a string representing the particular part of a view that has been requested
header/footer/navbar/etc.
As a result the presenters responsibility is two-fold, firstly to assemble the static part of the view (the
static html from the concrete views) and to insert dynamic content from the database(using the JDBC
implementation described earlier). They do all of this within their String present(AbstractView)
methods.
The Abstract factory class brings this all together so that the UIServlet can do its job of selecting the
appropriate view based on the URL query strings from an http request.
I hope that this has been at least relatively clear. I know its a lot trying to get an overview of the system
as is in a couple of pages. I have included appendices after this if it helps.
jpchansondev@gmail.com
jpchanson@tomoparts.co.uk
07796311906
Appendix A
Connection Manager
/**
* This class provides access to a pool of database connections. It is a
threadsafe singleton
* so always use the instance method to get an instance of this class, NEVER STORE
THE INSTANCE.
* @author Jan P.C. Hanson
*
*/
public class ConnectionManager
{
/**Singleton instance with eager instanciation**/
private static ConnectionManager test = new ConnectionManager();
/**C3P0 Data pool**/
private ComboPooledDataSource cpds;
/**
* singleton contstructor, initialises the connection pool
*/
private ConnectionManager()
{
cpds = new ComboPooledDataSource();
try
{cpds.setDriverClass("com.mysql.jdbc.Driver");}
catch (PropertyVetoException e)
{e.printStackTrace();}
cpds.setJdbcUrl("jdbc:mysql://192.168.0.202:3306/ebay");
cpds.setUser("openDMS");
cpds.setPassword("0p3nDMS/*-");
cpds.setMinPoolSize(3);
cpds.setAcquireIncrement(5);
cpds.setMaxPoolSize(20);
cpds.setMaxStatements(180);
}
/**
* instance method returns an instance of the ConnectionManager class
* @return ConnectionManager instance
*/
public static ConnectionManager instance()
{return test;}
/**
* retrieves a database connection from the pool of available connections.
When
* Connection.close is called the connection is returned to the pool.
* @return
* @throws SQLException
*/
public Connection getConnection() throws SQLException
{return this.cpds.getConnection();}
}
Database Query (invoker)
public class DatabaseQuery
{
/**map of commands**/
private Map<QueryType, AbstractDBQuery> commandMap_M;
/**defensive enum for map**/
public enum QueryType
{
SELECT_EBAY_ORDERS, INSERT_EBAY_ORDERS
}
/**
* constructor, initialises the internal map.
*/
public DatabaseQuery()
{
super();
this.commandMap_M = new HashMap<QueryType, AbstractDBQuery>();
this.populateMap();
}
/**
*
* @param command enum value taken from the internal enum 'QueryType'
represents the type
* of query that you wish to execute.
* @param parameters a string array containing the parameters for the query
in question.
* Not all queries require parameters whereas others do. Check the concrete
query type
* documentation for more information
* @return List<String[]> the position within the list defines the row, and
the position
* withing the string array defines the column so list index 0, array index
2 would be the
* 3rd column of the 1st row.
* @throws SQLException
*/
public List<String[]> execute(QueryType command, String[] parameters) throws
SQLException
{return this.commandMap_M.get(command).execute(parameters);}
/**
* populates the map with the concrete AbstractDBQuery Types.
*/
private void populateMap()
{
this.commandMap_M.put(DatabaseQuery.QueryType.SELECT_EBAY_ORDERS, new
SelectEbayOrders());
this.commandMap_M.put(DatabaseQuery.QueryType.INSERT_EBAY_ORDERS, new
InsertEbayOrders());
}
Abstract Command interface
/**
* Interface that all database queries must adhere to. this is the Abstract
command class for
* this command pattern.
* @author Jan P.C. Hanson
*
*/
public interface AbstractDBQuery
{
/**
* executes the query.
* @param Parameter(s) to use in the query, see concrete type's class
documentation for more
* information
* @return String representing the output of a particular query.
*/
public List<String[]> execute(String[] Parameter) throws SQLException;
}
Concrete Ebay Command Select Example
public class SelectEbayOrders implements AbstractDBQuery
{
/**reference to the JDBC Statement**/
private PreparedStatement statement_M = null;
/**reference to the JDBC Database connection**/
private Connection connection_M = null;
/**SQL query string**/
private static final String query = "SELECT * FROM ebay.ebay_buyers;";
/**
* default constructor
*/
public SelectEbayOrders()
{super();}
/**
* execute the query
* @param parameter NOT USED for this query.
* @return String representing the results of the query.
* @throws SQLException
*/
public List<String[]> execute(String[] parameter) throws SQLException
{
this.initQuery();
ResultSet resultset = statement_M.executeQuery(query);
List<String[]> result = this.formatResults(resultset);
this.connection_M.commit();
this.cleanup();
return result;
}
/**
* formats the ResultSet (returned from the executed query) as a string
* @param results the ResultSet (post query execution)
* @return String containing the formatted results.
* @throws SQLException
*/
private List<String[]> formatResults(ResultSet results) throws SQLException
{
List<String[]> rows = new ArrayList<String[]>();
while (results.next())
{
String[] cols = new String[4];
cols[0] = results.getString("buyerID");
cols[1] = results.getString("name");
cols[2] = results.getString("shippingAddress");
cols[3] = results.getString("invoiceAddress");
rows.add(cols);
}
return rows;
}
/**
* initialise the connection and statement and set transaction variables.
* @throws SQLException
*/
private void initQuery() throws SQLException
{
this.connection_M = ConnectionManager.instance().getConnection();
this.connection_M.setAutoCommit(false);
statement_M = connection_M.prepareStatement(query);
}
/**
* do cleanup after the query has been executed
* @throws SQLException
*/
private void cleanup() throws SQLException
{
if (statement_M != null)
{statement_M.close();System.out.println("closing statement");}
if (connection_M != null)
{connection_M.close();System.out.println("closing connection");}
}
}
Concrete Command Insert Example
public class InsertEbayOrders implements AbstractDBQuery
{
/**reference to the JDBC Statement**/
private PreparedStatement statement_M = null;
/**reference to the JDBC Database connection**/
private Connection connection_M = null;
/**SQL query string**/
private String query ="INSERT IGNORE INTO ebay.ebay_orders (orderID,
transactionID, buyerID, salesRecNo, shippingType, createdTime)"
+ "VALUES (?,?,?,?,?,?);";
/**
* default constructor
*/
public InsertEbayOrders()
{super();}
/**
* execute the query
* @param parameter an array of strings where the 0th element is the
parameter for the
* first column, the 1st element is the parameter for the 2nd column and so
on. The Ebay
* Orders Table only has 6 columns so any element above the 3rd element will
be ignored.
* col1 =orderID:int(30), col2=transactionID:int(20),
col3=buyerID:varchar(40),
* col4=salesRecNo:int(10), col5=shippingType:varchar(200),
col6=createdTine:datetime
* @return List<String> representing the results of the query.
* @throws SQLException
*/
public List<String[]> execute(String[] parameter) throws SQLException
{
this.initQuery();
this.connection_M.prepareStatement(query);
this.statement_M.setInt(1, 0000);
this.statement_M.setInt(2, 1111);
this.statement_M.setString(3, "kirk");
this.statement_M.setInt(4, 42);
this.statement_M.setString(5, "carrier pidgeon");
java.util.Date date = new Date();
java.sql.Date sqlDate = new java.sql.Date(date.getTime());
this.statement_M.setTimestamp(6, new
java.sql.Timestamp(date.getTime()));
// this.statement_M.setInt(6, 20151013);
int resultCode = statement_M.executeUpdate();
this.connection_M.commit();
this.cleanup();
List<String[]> res = new ArrayList<String[]>();
res.add(new String[] {resultCode + ""});
return res;
}
/**
* initialise the connection and statement and set transaction variables.
* @throws SQLException
*/
private void initQuery() throws SQLException
{
this.connection_M = ConnectionManager.instance().getConnection();
this.connection_M.setAutoCommit(false);
statement_M = connection_M.prepareStatement(query);
}
/**
* do cleanup after the query has been executed
* @throws SQLException
*/
private void cleanup() throws SQLException
{
if (statement_M != null)
{statement_M.close();System.out.println("closing statement");}
if (connection_M != null)
{connection_M.close();System.out.println("closing connection");}
Appendix B
Service Scheduler
public class ServicesScheduler
{
/****/
ScheduledThreadPoolExecutor serviceScheduler_M;
/****/
List<ScheduledFuture<?>> services_M;
/**
*
* @param noOfThreads
*/
public ServicesScheduler(int noOfThreads)
{
this.serviceScheduler_M = new
ScheduledThreadPoolExecutor(noOfThreads);
this.serviceScheduler_M.setMaximumPoolSize(noOfThreads+2);
this.services_M = new ArrayList<ScheduledFuture<?>>();
}
/**
*
* @param service
*/
public void add(AbstractService service, long delay, long rate)
{
this.services_M.add(this.serviceScheduler_M.scheduleWithFixedDelay(service,
delay, rate, TimeUnit.MINUTES));
}
/**
*
*/
public void start()
{
try
{
for(int i = 0 ; i < this.services_M.size() ; ++i)
{
this.services_M.get(i).get();
}
}
catch(InterruptedException | ExecutionException e)
{e.printStackTrace();}
serviceScheduler_M.shutdown();
}
}
Abstract Service Interface
/**
* Convienience interface, allows the system to only accept runnable's that
implement this
* interface rather than all runnables.
* @author Jan P.C. Hanson
*
*/
public interface AbstractService extends Runnable
{
public void run();
}
eBay Service example
/**
* This class represents the start of the execution flow for the eBay Service, The
service
* quereies the eBay API for Order and Item information and
* @author Jan P.C. Hanson
*
*/
public class EbayService implements AbstractService
{
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run()
{
//index 0 = blank, index 1 = sandbox server string, index 2 = sandbox
user token
//index 3 = production server string, index 4 = production user token.
String[] credentials = ConfigReader.read("./config/", "ebay.cfg");
OrdersCall oCall = new OrdersCall(credentials[4], credentials[3]);
try
{
// OrderType[] orders = oCall.call(1);
this.insertOrders();
this.insertTransactions();
this.insertBuyers();
this.insertItems();
}
// catch (ApiException e)
// {e.printStackTrace();}
// catch (SdkException e)
// {e.printStackTrace();}
catch (Exception e)
{e.printStackTrace();}
}
private void insertOrders() throws SQLException
{
InsertEbayOrders iOrd = new InsertEbayOrders();
iOrd.execute(new String[] {});
}
private void insertTransactions()
{
}
private void insertBuyers()
{
}
private void insertItems()
{
}
}
Appendix C
Servlet
public class UIServlet extends HttpServlet
{
/**needed to avoid warnings**/
private static final long serialVersionUID = -417534770555839323L;
/**
* instantiates a servlet using the views provided.
* @param views
*/
public UIServlet()
{super();}
/**
* service method, this controls how the servlet responds to particular URL
query strings
* @param request the http request to the servlet
* @param response the http response to the servlet
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
PrintWriter out = response.getWriter();
// ViewPresenterFactory viewPresenterFactory =
ViewPresenterFactory.instance();
AbstractViewPresenterFactory factory;
String viewParam = request.getParameter("view");
if (ViewPresenterFactory.instance().hasFactory(viewParam)==true)
{
factory = ViewPresenterFactory.instance().getFactory(viewParam);
out.print(factory.makePresenter().present(factory.makeView()));
}
else
{
factory = ViewPresenterFactory.instance().getFactory("ROOT");
out.print(factory.makePresenter().present(factory.makeView()));
}
out.close();
}
}
Abstract Factory interface
/**
* the interface that all concrete factories should subscribe to.
* @author Jan P.C. Hanson
*
*/
public interface AbstractViewPresenterFactory
{
/**
* factory method that creates a view
* @return AbstractView the view that has been requested
*/
public AbstractView makeView();
/**
* factory method that creates a presenter
* @return AbstractPresenter the presenter than has been requested.
*/
public AbstractPresenter makePresenter();
}
concrete Factory for eBay View/Presenter
/**
* EbayAbstractFactory creates the presenter and view for the eBay view.
* @author Jan P.C. Hanson
*
*/
public class EbayFactory implements AbstractViewPresenterFactory
{
/* (non-Javadoc)
* @see
openDMS.helpers.viewPresenterFactory.AbstractViewPresenterFactory#makeView()
*/
@Override
public AbstractView makeView()
{return new EbayView();}
/* (non-Javadoc)
* @see
openDMS.helpers.viewPresenterFactory.AbstractViewPresenterFactory#makePresenter()
*/
@Override
public AbstractPresenter makePresenter()
{return new EbayPresenter();}
}
concrete Factory for root view/presenter
/**
* RootAbstractFactory creates the presenter and view for the root view.
* @author Jan P.C. Hanson
*
*/
public class RootFactory implements AbstractViewPresenterFactory
{
/* (non-Javadoc)
* @see
openDMS.helpers.viewPresenterFactory.AbstractViewPresenterFactory#makeView()
*/
@Override
public AbstractView makeView()
{return new RootView();}
/* (non-Javadoc)
* @see
openDMS.helpers.viewPresenterFactory.AbstractViewPresenterFactory#makePresenter()
*/
@Override
public AbstractPresenter makePresenter()
{return new RootPresenter();}
}
Abstract Factory implementation
public class ViewPresenterFactory
{
/**Singleton instance variable**/
private static final ViewPresenterFactory instance_M = new
ViewPresenterFactory();
/**enum to use for factory creation options**/
public enum Select {ROOT, EBAY}
/**map of enum constants to concrete factories**/
Map<Select, AbstractViewPresenterFactory> factory_M;
/**
* default constructor
*/
private ViewPresenterFactory()
{
super();
factory_M = new HashMap<Select, AbstractViewPresenterFactory>();
this.populateMap();
}
/**
* singleton instance method, returns an instance of this object.
* @return ViewPresenterFactory the singleton instance.
*/
public static ViewPresenterFactory instance()
{return ViewPresenterFactory.instance_M;}
/**
* creates a specific factory based on the string provided.
* @param factory string that is compared to enum values.
* @return AbstractViewPresenterFactory a concrete viewPresenterFactory
*/
public AbstractViewPresenterFactory getFactory(String factory)
{return this.factory_M.get(ViewPresenterFactory.Select.valueOf(factory));}
/**
* check to see if a factory type exists by comparing the string provided to
the
* internal enum 'Select'
* @param factory the string to compare to the internal enum
* @return boolean true if the factory is a valid type, false otherwise.
*/
public boolean hasFactory(String factory)
{
if (factory == null) {return false;}
else if (Select.valueOf(factory) != null) {return true;}
else {return false;}
}
/**
* populate the internal map with viewPresenterFactories.
*/
private void populateMap()
{
this.factory_M.put(ViewPresenterFactory.Select.ROOT, new
RootFactory());
this.factory_M.put(ViewPresenterFactory.Select.EBAY, new
EbayFactory());
}
}
public interface AbstractView
{
/**
* This method constructs the part of the view that is requested in the
parameters.
* @param part should be taken from the internal Enum defined within this
class. Each enum
* constant refers to a specific part of the view.
* @return String containing the HTML for the part of the view that has been
requested.
*/
public String make(AbstractViewPart part);
}
Views interface
/**
* This interface provides a root for all enums needed for the AbstractView
derived objects
* @author Jan P.C. Hanson
*
*/
public interface AbstractViewPart
{
/**root enum, to be implemented by the enums inside the Abstract view
derived classes**/
public enum Part {};
}
concrete view 1
public class EbayView implements AbstractView
{
/**mapping of enum constant keys to html strings**/
private Map<Part, String> viewPart_M;
/**enum containing constants relating to the view parts**/
public enum Part implements AbstractViewPart{HEADER, FOOTER};
/**
* default constructor
*/
public EbayView()
{
super();
viewPart_M = new HashMap<Part, String>();
this.populateMap();
}
/* (non-Javadoc)
* @see openDMS.view.AbstractView#make(openDMS.view.AbstractView.PART)
*/
@Override
public String make(AbstractViewPart part)
{return this.viewPart_M.get(part);}
/**
* populates the map with the
*/
protected void populateMap()
{
this.viewPart_M.put(EbayView.Part.HEADER,
FileToString.convert("./res/ebay/", "eBayHeader.html", "utf-8"));
this.viewPart_M.put(EbayView.Part.FOOTER,
FileToString.convert("./res/ebay/", "eBayFooter.html", "utf-8"));
}
}
concrete view 2
public class RootView implements AbstractView
{
/**mapping of enum constant keys to html strings**/
private Map<Part, String> viewPart_M;
/**enum containing constants relating to the view parts**/
public enum Part implements AbstractViewPart{INDEX}
public RootView()
{
super();
viewPart_M = new HashMap<Part, String>();
this.populateMap();
}
/* (non-Javadoc)
* @see openDMS.view.AbstractView#make(openDMS.view.AbstractViewPart)
*/
@Override
public String make(AbstractViewPart part)
{return this.viewPart_M.get(part);}
private void populateMap()
{
this.viewPart_M.put(RootView.Part.INDEX,
FileToString.convert("./res/", "index.html", "utf-8"));
}
}
presenter interface
public interface AbstractPresenter
{
/**
* this method is called when you actually want to present the information
contained in the
* view/presenter to the servlet
* @param view the view to be rpesented
* @return String representing the html contained in the view/presenter
*/
public String present(AbstractView view);
}
cocnrete presenter 2
public class RootPresenter implements AbstractPresenter
{
/**
* default constructor
*/
public RootPresenter()
{super();}
/* (non-Javadoc)
* @see
openDMS.presenters.AbstractPresenter#present(openDMS.view.views.AbstractView)
*/
@Override
public String present(AbstractView view)
{
String output = "";
output += view.make(RootView.Part.INDEX);
System.out.println("Root page loaded");
return output;
}
}
concrete presenter 1
public class EbayPresenter implements AbstractPresenter
{
/**
* default constructor
*/
public EbayPresenter()
{super();}
/* (non-Javadoc)
* @see
openDMS.presenters.AbstractPresenter#present(openDMS.view.views.AbstractView)
*/
@Override
public String present(AbstractView view)
{
String output = "";
output += view.make(EbayView.Part.HEADER);
output += this.doStuff();
output += view.make(EbayView.Part.FOOTER);
return output;
}
private String doStuff()
{
String result = "";
try
{
DatabaseQuery query = new DatabaseQuery();
query.execute(DatabaseQuery.QueryType.INSERT_EBAY_ORDERS, new
String[] {});
List<String[]> rows =
query.execute(DatabaseQuery.QueryType.SELECT_EBAY_ORDERS,new String[] {""});
result+= "<table class='table table-bordered'> n";
result+= "<thead>n<tr>n"
+ "<th>ID</th>n"
+ "<th>name</th>n"
+ "<th>Shipping Address</th>n"
+ "<th>Invoice Address</th>n"
+ "</tr>n</thead>n <tbody>";
for (String[] cols : rows)
{
result+="<tr>n";
result+="<td>"+ cols[0].trim() + "</td>n";
result+="<td>"+ cols[1].trim() + "</td>n";
result+="<td>"+ cols[2].trim() + "</td>n";
result+="<td>"+ cols[3].trim() + "</td>n";
result+="</tr>n";
}
result+="</tbody>n</table>";
}
catch(SQLException e)
{e.printStackTrace();}
return "<h1> eBay </h1> n <a class='btn btn-default'
href='/'>Root</a><br/>n" + result;
}
}

More Related Content

OpenDMS - the first 2 weeks

  • 1. OpenDMS I couldnt think of anything to call it, but as Darren said it could be open sourced and it is a DMS this seemed logical. While I am working (at the moment) on eBay functionality, I have been mindful to do things is such a way as to allow this to serve as a framework of sorts for the rest of the DMS proper. In this document I hope to take you through the salient points of the design and how they would be extensible to additional functionality. Introduction While I have split this project into a loose Model-View-Presenter architecture, it is not tied to any paricular MVP framework or library, more a guiding principal than anything else. Still it serves well enough to divide up the functionality model Database (appendix A) As it stands currently, the core of the Model is the JDBC database integration, this begins with the conection manager. I have used the C3PO connection pooling library to implement a pooled datasource object which allows client objects request connections from (current max of 20 connections but that can be changed easily). Individual database queries are encapsulated as concrete commands in a command pattern with the invoker being a factory. As it stands the invoker/factory keeps copies of the commands in a map for easy access, but should there turn out to be a significant number of concrete database commands it would make sense to have a specific factory class for each concrete command to avoid having to store all of them in each instance of the invoker. As a side point each concrete command has a 'List<String[]> execute(String[])' where the individual query functionality is implemented. In the return type ach element of the String[] represents a column, and each element of the list is a seperate row. Obviously insert/update/delete queries only return a result code so there is a choice here. Seperate the whole thing into two command patterns or treat every query equally and just use the ouput appropiately and consistantly. Services (appendix B) This is the second part of the model and encompasses everything else that needs to go on in the background. I can see two types of services that might need to be implemented: Scheduled services and triggered services. i.e. a service that runs at set intervals and services that are expressly triggered by the user.
  • 2. I have implemented the Scheduled Services part but not the Triggered Services part as yet. The Scheduled services are implemented using a class called ServiceScheduler, which creates a ScheduledThreadPoolExecutor (from the java.util.concurrent lib) and stores AbstractService derived types (AbstractService is an empty interface that implements runnable) in a List. It has an add(AbstractService, delay, rate) method so that each service can run at its own pre-specified interval, and delay; as well as a start method which iterates through the list calling the .get() method on each services ScheduledFuture<?>, fairly self explanatory. As I said I have not implemented the triggered services yet as I dont know what the best way of doing this would be. View/presenter (appendix C) The View is Jetty based (embedded http server and servlet container). Currently the HTTP server has a fairly basic configuration (no SSL, no sessions, no auth) but I figured that could be configured at a later stage (jetty has built in database auth functionality, as well as support for SSL and sessions, and a bunch of other fun stuff). The HTTP Server is responsible for one servlet at the moment, cunningly named UIServlet. This UIServlet uses a View/Presenter Abstract factory to select an appropriate pair of View and Presenter based on the URL parameters passed to the servlets doGet method from individual HTTP requests. The concrete View objects are responsible for reading .html files located in a resources directory and providing a 'make' method for the presenter to use when putting a particular view together i.e. the make method returns a string representing the particular part of a view that has been requested header/footer/navbar/etc. As a result the presenters responsibility is two-fold, firstly to assemble the static part of the view (the static html from the concrete views) and to insert dynamic content from the database(using the JDBC implementation described earlier). They do all of this within their String present(AbstractView) methods. The Abstract factory class brings this all together so that the UIServlet can do its job of selecting the appropriate view based on the URL query strings from an http request. I hope that this has been at least relatively clear. I know its a lot trying to get an overview of the system as is in a couple of pages. I have included appendices after this if it helps. jpchansondev@gmail.com jpchanson@tomoparts.co.uk 07796311906
  • 3. Appendix A Connection Manager /** * This class provides access to a pool of database connections. It is a threadsafe singleton * so always use the instance method to get an instance of this class, NEVER STORE THE INSTANCE. * @author Jan P.C. Hanson * */ public class ConnectionManager { /**Singleton instance with eager instanciation**/ private static ConnectionManager test = new ConnectionManager(); /**C3P0 Data pool**/ private ComboPooledDataSource cpds; /** * singleton contstructor, initialises the connection pool */ private ConnectionManager() { cpds = new ComboPooledDataSource(); try {cpds.setDriverClass("com.mysql.jdbc.Driver");} catch (PropertyVetoException e) {e.printStackTrace();} cpds.setJdbcUrl("jdbc:mysql://192.168.0.202:3306/ebay"); cpds.setUser("openDMS"); cpds.setPassword("0p3nDMS/*-"); cpds.setMinPoolSize(3); cpds.setAcquireIncrement(5); cpds.setMaxPoolSize(20); cpds.setMaxStatements(180); } /** * instance method returns an instance of the ConnectionManager class * @return ConnectionManager instance */ public static ConnectionManager instance() {return test;} /** * retrieves a database connection from the pool of available connections. When * Connection.close is called the connection is returned to the pool. * @return * @throws SQLException */ public Connection getConnection() throws SQLException {return this.cpds.getConnection();} }
  • 4. Database Query (invoker) public class DatabaseQuery { /**map of commands**/ private Map<QueryType, AbstractDBQuery> commandMap_M; /**defensive enum for map**/ public enum QueryType { SELECT_EBAY_ORDERS, INSERT_EBAY_ORDERS } /** * constructor, initialises the internal map. */ public DatabaseQuery() { super(); this.commandMap_M = new HashMap<QueryType, AbstractDBQuery>(); this.populateMap(); } /** * * @param command enum value taken from the internal enum 'QueryType' represents the type * of query that you wish to execute. * @param parameters a string array containing the parameters for the query in question. * Not all queries require parameters whereas others do. Check the concrete query type * documentation for more information * @return List<String[]> the position within the list defines the row, and the position * withing the string array defines the column so list index 0, array index 2 would be the * 3rd column of the 1st row. * @throws SQLException */ public List<String[]> execute(QueryType command, String[] parameters) throws SQLException {return this.commandMap_M.get(command).execute(parameters);} /** * populates the map with the concrete AbstractDBQuery Types. */ private void populateMap() { this.commandMap_M.put(DatabaseQuery.QueryType.SELECT_EBAY_ORDERS, new SelectEbayOrders()); this.commandMap_M.put(DatabaseQuery.QueryType.INSERT_EBAY_ORDERS, new InsertEbayOrders()); } Abstract Command interface /**
  • 5. * Interface that all database queries must adhere to. this is the Abstract command class for * this command pattern. * @author Jan P.C. Hanson * */ public interface AbstractDBQuery { /** * executes the query. * @param Parameter(s) to use in the query, see concrete type's class documentation for more * information * @return String representing the output of a particular query. */ public List<String[]> execute(String[] Parameter) throws SQLException; } Concrete Ebay Command Select Example public class SelectEbayOrders implements AbstractDBQuery { /**reference to the JDBC Statement**/ private PreparedStatement statement_M = null; /**reference to the JDBC Database connection**/ private Connection connection_M = null; /**SQL query string**/ private static final String query = "SELECT * FROM ebay.ebay_buyers;"; /** * default constructor */ public SelectEbayOrders() {super();} /** * execute the query * @param parameter NOT USED for this query. * @return String representing the results of the query. * @throws SQLException */ public List<String[]> execute(String[] parameter) throws SQLException { this.initQuery(); ResultSet resultset = statement_M.executeQuery(query); List<String[]> result = this.formatResults(resultset); this.connection_M.commit(); this.cleanup(); return result; } /** * formats the ResultSet (returned from the executed query) as a string * @param results the ResultSet (post query execution)
  • 6. * @return String containing the formatted results. * @throws SQLException */ private List<String[]> formatResults(ResultSet results) throws SQLException { List<String[]> rows = new ArrayList<String[]>(); while (results.next()) { String[] cols = new String[4]; cols[0] = results.getString("buyerID"); cols[1] = results.getString("name"); cols[2] = results.getString("shippingAddress"); cols[3] = results.getString("invoiceAddress"); rows.add(cols); } return rows; } /** * initialise the connection and statement and set transaction variables. * @throws SQLException */ private void initQuery() throws SQLException { this.connection_M = ConnectionManager.instance().getConnection(); this.connection_M.setAutoCommit(false); statement_M = connection_M.prepareStatement(query); } /** * do cleanup after the query has been executed * @throws SQLException */ private void cleanup() throws SQLException { if (statement_M != null) {statement_M.close();System.out.println("closing statement");} if (connection_M != null) {connection_M.close();System.out.println("closing connection");} } } Concrete Command Insert Example public class InsertEbayOrders implements AbstractDBQuery { /**reference to the JDBC Statement**/ private PreparedStatement statement_M = null; /**reference to the JDBC Database connection**/ private Connection connection_M = null; /**SQL query string**/ private String query ="INSERT IGNORE INTO ebay.ebay_orders (orderID, transactionID, buyerID, salesRecNo, shippingType, createdTime)" + "VALUES (?,?,?,?,?,?);"; /** * default constructor */
  • 7. public InsertEbayOrders() {super();} /** * execute the query * @param parameter an array of strings where the 0th element is the parameter for the * first column, the 1st element is the parameter for the 2nd column and so on. The Ebay * Orders Table only has 6 columns so any element above the 3rd element will be ignored. * col1 =orderID:int(30), col2=transactionID:int(20), col3=buyerID:varchar(40), * col4=salesRecNo:int(10), col5=shippingType:varchar(200), col6=createdTine:datetime * @return List<String> representing the results of the query. * @throws SQLException */ public List<String[]> execute(String[] parameter) throws SQLException { this.initQuery(); this.connection_M.prepareStatement(query); this.statement_M.setInt(1, 0000); this.statement_M.setInt(2, 1111); this.statement_M.setString(3, "kirk"); this.statement_M.setInt(4, 42); this.statement_M.setString(5, "carrier pidgeon"); java.util.Date date = new Date(); java.sql.Date sqlDate = new java.sql.Date(date.getTime()); this.statement_M.setTimestamp(6, new java.sql.Timestamp(date.getTime())); // this.statement_M.setInt(6, 20151013); int resultCode = statement_M.executeUpdate(); this.connection_M.commit(); this.cleanup(); List<String[]> res = new ArrayList<String[]>(); res.add(new String[] {resultCode + ""}); return res; } /** * initialise the connection and statement and set transaction variables. * @throws SQLException */ private void initQuery() throws SQLException { this.connection_M = ConnectionManager.instance().getConnection(); this.connection_M.setAutoCommit(false); statement_M = connection_M.prepareStatement(query); } /** * do cleanup after the query has been executed * @throws SQLException */
  • 8. private void cleanup() throws SQLException { if (statement_M != null) {statement_M.close();System.out.println("closing statement");} if (connection_M != null) {connection_M.close();System.out.println("closing connection");} Appendix B Service Scheduler public class ServicesScheduler { /****/ ScheduledThreadPoolExecutor serviceScheduler_M; /****/ List<ScheduledFuture<?>> services_M; /** * * @param noOfThreads */ public ServicesScheduler(int noOfThreads) { this.serviceScheduler_M = new ScheduledThreadPoolExecutor(noOfThreads); this.serviceScheduler_M.setMaximumPoolSize(noOfThreads+2); this.services_M = new ArrayList<ScheduledFuture<?>>(); } /** * * @param service */ public void add(AbstractService service, long delay, long rate) { this.services_M.add(this.serviceScheduler_M.scheduleWithFixedDelay(service, delay, rate, TimeUnit.MINUTES)); } /** * */ public void start() { try { for(int i = 0 ; i < this.services_M.size() ; ++i) { this.services_M.get(i).get(); } } catch(InterruptedException | ExecutionException e) {e.printStackTrace();} serviceScheduler_M.shutdown();
  • 9. } } Abstract Service Interface /** * Convienience interface, allows the system to only accept runnable's that implement this * interface rather than all runnables. * @author Jan P.C. Hanson * */ public interface AbstractService extends Runnable { public void run(); } eBay Service example /** * This class represents the start of the execution flow for the eBay Service, The service * quereies the eBay API for Order and Item information and * @author Jan P.C. Hanson * */ public class EbayService implements AbstractService { /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { //index 0 = blank, index 1 = sandbox server string, index 2 = sandbox user token //index 3 = production server string, index 4 = production user token. String[] credentials = ConfigReader.read("./config/", "ebay.cfg"); OrdersCall oCall = new OrdersCall(credentials[4], credentials[3]); try { // OrderType[] orders = oCall.call(1); this.insertOrders(); this.insertTransactions(); this.insertBuyers(); this.insertItems(); } // catch (ApiException e) // {e.printStackTrace();} // catch (SdkException e) // {e.printStackTrace();} catch (Exception e) {e.printStackTrace();} } private void insertOrders() throws SQLException {
  • 10. InsertEbayOrders iOrd = new InsertEbayOrders(); iOrd.execute(new String[] {}); } private void insertTransactions() { } private void insertBuyers() { } private void insertItems() { } } Appendix C Servlet public class UIServlet extends HttpServlet { /**needed to avoid warnings**/ private static final long serialVersionUID = -417534770555839323L; /** * instantiates a servlet using the views provided. * @param views */ public UIServlet() {super();} /** * service method, this controls how the servlet responds to particular URL query strings * @param request the http request to the servlet * @param response the http response to the servlet */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); // ViewPresenterFactory viewPresenterFactory = ViewPresenterFactory.instance(); AbstractViewPresenterFactory factory; String viewParam = request.getParameter("view"); if (ViewPresenterFactory.instance().hasFactory(viewParam)==true) { factory = ViewPresenterFactory.instance().getFactory(viewParam);
  • 11. out.print(factory.makePresenter().present(factory.makeView())); } else { factory = ViewPresenterFactory.instance().getFactory("ROOT"); out.print(factory.makePresenter().present(factory.makeView())); } out.close(); } } Abstract Factory interface /** * the interface that all concrete factories should subscribe to. * @author Jan P.C. Hanson * */ public interface AbstractViewPresenterFactory { /** * factory method that creates a view * @return AbstractView the view that has been requested */ public AbstractView makeView(); /** * factory method that creates a presenter * @return AbstractPresenter the presenter than has been requested. */ public AbstractPresenter makePresenter(); } concrete Factory for eBay View/Presenter /** * EbayAbstractFactory creates the presenter and view for the eBay view. * @author Jan P.C. Hanson * */ public class EbayFactory implements AbstractViewPresenterFactory { /* (non-Javadoc) * @see openDMS.helpers.viewPresenterFactory.AbstractViewPresenterFactory#makeView() */ @Override public AbstractView makeView() {return new EbayView();} /* (non-Javadoc) * @see openDMS.helpers.viewPresenterFactory.AbstractViewPresenterFactory#makePresenter() */ @Override
  • 12. public AbstractPresenter makePresenter() {return new EbayPresenter();} } concrete Factory for root view/presenter /** * RootAbstractFactory creates the presenter and view for the root view. * @author Jan P.C. Hanson * */ public class RootFactory implements AbstractViewPresenterFactory { /* (non-Javadoc) * @see openDMS.helpers.viewPresenterFactory.AbstractViewPresenterFactory#makeView() */ @Override public AbstractView makeView() {return new RootView();} /* (non-Javadoc) * @see openDMS.helpers.viewPresenterFactory.AbstractViewPresenterFactory#makePresenter() */ @Override public AbstractPresenter makePresenter() {return new RootPresenter();} } Abstract Factory implementation public class ViewPresenterFactory { /**Singleton instance variable**/ private static final ViewPresenterFactory instance_M = new ViewPresenterFactory(); /**enum to use for factory creation options**/ public enum Select {ROOT, EBAY} /**map of enum constants to concrete factories**/ Map<Select, AbstractViewPresenterFactory> factory_M; /** * default constructor */ private ViewPresenterFactory() { super(); factory_M = new HashMap<Select, AbstractViewPresenterFactory>(); this.populateMap(); } /** * singleton instance method, returns an instance of this object.
  • 13. * @return ViewPresenterFactory the singleton instance. */ public static ViewPresenterFactory instance() {return ViewPresenterFactory.instance_M;} /** * creates a specific factory based on the string provided. * @param factory string that is compared to enum values. * @return AbstractViewPresenterFactory a concrete viewPresenterFactory */ public AbstractViewPresenterFactory getFactory(String factory) {return this.factory_M.get(ViewPresenterFactory.Select.valueOf(factory));} /** * check to see if a factory type exists by comparing the string provided to the * internal enum 'Select' * @param factory the string to compare to the internal enum * @return boolean true if the factory is a valid type, false otherwise. */ public boolean hasFactory(String factory) { if (factory == null) {return false;} else if (Select.valueOf(factory) != null) {return true;} else {return false;} } /** * populate the internal map with viewPresenterFactories. */ private void populateMap() { this.factory_M.put(ViewPresenterFactory.Select.ROOT, new RootFactory()); this.factory_M.put(ViewPresenterFactory.Select.EBAY, new EbayFactory()); } } public interface AbstractView { /** * This method constructs the part of the view that is requested in the parameters. * @param part should be taken from the internal Enum defined within this class. Each enum * constant refers to a specific part of the view. * @return String containing the HTML for the part of the view that has been requested. */ public String make(AbstractViewPart part); } Views interface /**
  • 14. * This interface provides a root for all enums needed for the AbstractView derived objects * @author Jan P.C. Hanson * */ public interface AbstractViewPart { /**root enum, to be implemented by the enums inside the Abstract view derived classes**/ public enum Part {}; } concrete view 1 public class EbayView implements AbstractView { /**mapping of enum constant keys to html strings**/ private Map<Part, String> viewPart_M; /**enum containing constants relating to the view parts**/ public enum Part implements AbstractViewPart{HEADER, FOOTER}; /** * default constructor */ public EbayView() { super(); viewPart_M = new HashMap<Part, String>(); this.populateMap(); } /* (non-Javadoc) * @see openDMS.view.AbstractView#make(openDMS.view.AbstractView.PART) */ @Override public String make(AbstractViewPart part) {return this.viewPart_M.get(part);} /** * populates the map with the */ protected void populateMap() { this.viewPart_M.put(EbayView.Part.HEADER, FileToString.convert("./res/ebay/", "eBayHeader.html", "utf-8")); this.viewPart_M.put(EbayView.Part.FOOTER, FileToString.convert("./res/ebay/", "eBayFooter.html", "utf-8")); } } concrete view 2 public class RootView implements AbstractView { /**mapping of enum constant keys to html strings**/ private Map<Part, String> viewPart_M;
  • 15. /**enum containing constants relating to the view parts**/ public enum Part implements AbstractViewPart{INDEX} public RootView() { super(); viewPart_M = new HashMap<Part, String>(); this.populateMap(); } /* (non-Javadoc) * @see openDMS.view.AbstractView#make(openDMS.view.AbstractViewPart) */ @Override public String make(AbstractViewPart part) {return this.viewPart_M.get(part);} private void populateMap() { this.viewPart_M.put(RootView.Part.INDEX, FileToString.convert("./res/", "index.html", "utf-8")); } } presenter interface public interface AbstractPresenter { /** * this method is called when you actually want to present the information contained in the * view/presenter to the servlet * @param view the view to be rpesented * @return String representing the html contained in the view/presenter */ public String present(AbstractView view); } cocnrete presenter 2 public class RootPresenter implements AbstractPresenter { /** * default constructor */ public RootPresenter() {super();} /* (non-Javadoc) * @see openDMS.presenters.AbstractPresenter#present(openDMS.view.views.AbstractView) */ @Override public String present(AbstractView view) { String output = "";
  • 16. output += view.make(RootView.Part.INDEX); System.out.println("Root page loaded"); return output; } } concrete presenter 1 public class EbayPresenter implements AbstractPresenter { /** * default constructor */ public EbayPresenter() {super();} /* (non-Javadoc) * @see openDMS.presenters.AbstractPresenter#present(openDMS.view.views.AbstractView) */ @Override public String present(AbstractView view) { String output = ""; output += view.make(EbayView.Part.HEADER); output += this.doStuff(); output += view.make(EbayView.Part.FOOTER); return output; } private String doStuff() { String result = ""; try { DatabaseQuery query = new DatabaseQuery(); query.execute(DatabaseQuery.QueryType.INSERT_EBAY_ORDERS, new String[] {}); List<String[]> rows = query.execute(DatabaseQuery.QueryType.SELECT_EBAY_ORDERS,new String[] {""}); result+= "<table class='table table-bordered'> n"; result+= "<thead>n<tr>n" + "<th>ID</th>n" + "<th>name</th>n" + "<th>Shipping Address</th>n" + "<th>Invoice Address</th>n" + "</tr>n</thead>n <tbody>"; for (String[] cols : rows) { result+="<tr>n";
  • 17. result+="<td>"+ cols[0].trim() + "</td>n"; result+="<td>"+ cols[1].trim() + "</td>n"; result+="<td>"+ cols[2].trim() + "</td>n"; result+="<td>"+ cols[3].trim() + "</td>n"; result+="</tr>n"; } result+="</tbody>n</table>"; } catch(SQLException e) {e.printStackTrace();} return "<h1> eBay </h1> n <a class='btn btn-default' href='/'>Root</a><br/>n" + result; } }