embedded

Configuring Jersey Servlet Container on Embedded Tomcat

One of the ways of configuring a Jersey Servlet Container on Tomcat is via a web.xml file.
For example, typically the following web.xml file is used where a Servlet and its mappings are defined:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>  
<web-app xmlns="http://java.sun.com/xml/ns/javaee"  
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    <servlet>
        <servlet-name>API</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>

        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.app.v2.RestApplication</param-value>
        </init-param>

    </servlet>
    <servlet-mapping>
        <servlet-name>API</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
</web-app>  

When starting an embedded version of Tomcat the same Jersey Servlet that was defined in the XML file can be defined via code:

    RestApplication restApplication = new RestApplication();
    ServletContainer servletContainer = new ServletContainer(restApplication);
    Tomcat.addServlet(ctx, "api", servletContainer);
    ctx.addServletMapping("/api/*", "hubble-api");

There are three steps involved in configuring Jersey:

Tomcat Embedded Server Configuration

Tomcat initially started as a web server container, meaning that one Tomcat server could host several web applications. With the popularity of languages such as Ruby and Node growing and frameworks like rails and express becoming very popular for web applications the new pattern of embedding the web server as part of the application itself removing the need of any external dependencies became very common.
Even though this feature is not very well documented, Tomcat does support an embedded configuration.

Below is an example of converting a standard server.xml to an embedded server.

<?xml version='1.0' encoding='utf-8'?>

<Server port="8010" shutdown="SHUTDOWN">  
  <!--APR library loader. Documentation at /docs/apr.html -->
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

    <Service name="Catalina">

        <Connector port="8080"
                   protocol="org.apache.coyote.http11.Http11NioProtocol"
                   compression="on"
                   compressableMimeType="application/json"
                   connectionTimeout="5000"
                   maxThreads="50"
                   minSpareThreads="50"
                   redirectPort="9410" />

        <Engine name="Catalina" defaultHost="localhost">

            <Host name="localhost"  appBase="webapps"
                  unpackWARs="true" autoDeploy="false">

            </Host>
        </Engine>
    </Service>

</Server>  

The main components defined in the XML file are:

  • Server
  • Listeners
  • Service
  • Connector
  • Engine
  • Host

When configuring an embedded Tomcat, the xml file listed above can be converted to java code as follows:

Tomcat tomcat = new Tomcat();  
Path tempPath = Files.createTempDirectory("tomcat-base-dir");  
tomcat.setBaseDir(tempPath.toString());

// Configure connector
Connector connector = new Connector();  
connector.setPort(8080);  
connector.setProtocol(Http11NioProtocol.class.getName());  
connector.setProperty("maxThreads", "100");  
connector.setProperty("minSpareThreads", "100");  
connector.setProperty("compression", "on");  
connector.setProperty("compressableMimeType", "application/json");  
connector.setProperty("connectionTimeout", "8000");  
tomcat.getService().addConnector(connector);  
tomcat.setConnector(connector);

// Configure tomcat life cycle listeners
JreMemoryLeakPreventionListener jreMemoryLeakPreventionListener = new JreMemoryLeakPreventionListener();  
GlobalResourcesLifecycleListener globalResourcesLifecycleListener = new GlobalResourcesLifecycleListener();  
ThreadLocalLeakPreventionListener threadLocalLeakPreventionListener = new ThreadLocalLeakPreventionListener();  
VersionLoggerListener versionLoggerListener = new VersionLoggerListener();  
tomcat.getServer().addLifecycleListener(jreMemoryLeakPreventionListener);  
tomcat.getServer().addLifecycleListener(globalResourcesLifecycleListener);  
tomcat.getServer().addLifecycleListener(threadLocalLeakPreventionListener);  
tomcat.getServer().addLifecycleListener(versionLoggerListener);

// Create web context
File webContentFolder = Files.createTempDirectory("default-doc-base").toFile();  
StandardContext ctx = (StandardContext) tomcat.addWebapp("", webContentFolder.getAbsolutePath());

// Disable jar scanner for better start up performance
StandardJarScanFilter jarScanFilter = (StandardJarScanFilter) ctx.getJarScanner().getJarScanFilter();  
jarScanFilter.setTldSkip("*");

tomcat.start();  
tomcat.getServer().await();  

*It is important to set both the default Tomcat connector as well as the service connector the application will use. By default, Tomcat configures an HTTP/1.1 connector listening on port 8080, so if only the service connector is set Tomcat will try to load both connectors at once.

Update, ElementMatch and the $ Positional Operator on MongoDB/Mongoose

One of the biggest advantages of mongodb is the option of defining documents inside documents and by doing so creating powerful and at the same time flexible data structures.
In most cases, the deepest you will go is one level, even though mongo doesn’t set any limit on how many levels you can go inside a document, for example: A BlogPost can have an array of Comments, or an Image can have an array of Tags, and there is plenty of documentation online on how to manipulate single dimension arrays in documents. However, what if you have a multi dimensional array in a document?
Take for example the following model:

   
 /**  
 * Line Item schema  
 */  
 exports.EstimateLineItem = (function () {  
 schemas.lineItem = new Schema({  
 ‘name’ : String,  
 ‘description’ : String,  
 ‘quantity’ : Number,  
 ‘cost’ : Number  
 });

 return db.model(‘EstimateLineItem’, schemas.lineItem);  
 })();

/**  
 * Estimate schema  
 */  
 exports.Estimate = (function () {  
 schemas.estimate = new Schema({  
 ‘name’ : String,  
 ‘quoteID’ : Number,  
 ‘subTotal’ : Number,  
 ‘finalTotal’ : Number,  
 ‘creationDate’ : { type: Date, default: Date.now },  
 ‘status’ : { type: String, default: "Active" },  
 ‘lineItemSet’ : [schemas.lineItem]  
 });

 return db.model(‘Estimate’, schemas.estimate);  
 })();

/**  
 * Job schema  
 */  
 exports.Job = (function () {  
 schemas.job = new Schema({  
 ‘name’ : String,  
 ‘description’ : String,  
 ‘creationDate’ : { type: Date, default: Date.now },  
 ‘status’ : { type: String, default: "Active" },  
 ‘scheduledDates’ : [Date],  
 ‘customerID’ : ObjectId,  
 ‘estimateSet’ : [schemas.estimate]  
 });

 return db.model(‘Job’, schemas.job);  
 })();

So a Job has an array of Estimates and an Estimate has an array of LineItems.
The problem is, how to add a new LineItem to an Estimate?
One option would be to query for a job, then loop through its estimateSet and find the selected estimate, then push a new line item to the lineItemSet and finally save back the job. However, there is a simpler way:

   
 Job.update(  
 {estimateSet: {"$elemMatch": {_id: estimateID}}},  
 {$push:  
 {  
 "estimateSet.$.lineItemSet":  
 {  
 ‘name’ : lineItem.name,  
 ‘description’ : lineItem.description,  
 ‘quantity’ : parseInt(lineItem.quantity),  
 ‘cost’ : parseInt(lineItem.cost),  
 ‘_id': lineItem._id  
 }  
 }  
 },  
 {upsert:false,safe:true},  
 function (err) {  
 console.log("err: ", err);  
 if (err) {  
 res.send({  
 "err": true,  
 });  
 }  
 else {  
 res.send({  
 "err": false,  
 })  
 }  
 }  
 );

Breaking down the query:
It finds a job that has an Estimate with the specified ID:

{estimateSet: {"$elemMatch": {_id: estimateID}}}

Once the job is found, it pushes a new line item to the lineItemSet:

 
 {$push:  
 {  
 "estimateSet.$.lineItemSet":  
 {  
 ‘name’ : lineItem.name,  
 ‘description’ : lineItem.description,  
 ‘quantity’ : parseInt(lineItem.quantity),  
 ‘cost’ : parseInt(lineItem.cost),  
 ‘_id': lineItem._id  
 }  
 }  
 },  

For more info: