Wednesday, March 21, 2018

Tomcat Customization

Introduction

Recently I had a business requirement to intercept the raw request to a servlet container.  The normal entry point to a servlet through the ServletContext was inadequate.  Having worked with Tomcat for a long time, since its earlier version 3.0, I downloaded the source code, and the stepped through the code.  While stepping through the code I noticed that the method service(org.apache.coyote.Request req, org.apache.coyote.Response resof class org.apache.catalina.connector.CoyoteAdapter initiates the request processing and the raw request could be intercepted in this method, and hard coded and is instantiated in the class org.apache.catalina.connector.Connector.  While tomcat server configuration file conf/server.xml provides xml attributes for customization of Connector, could not figure out a way to customize CoyoteAdapter.  With these observations, I added a new parameter to the Connector class to dynamically instantiate the adapter class as against the hard coded CoyoteAdpater class.  With this dynamic instantiation of the AdapterClass I was able to achieve the required customization of Tomcat.  Details of the code changes are detailed in this blog.

Server Configuration


conf/server.xml is the configuration file for Tomcat.  The connector element as shown below is the one which provided the configuration parameters for Tomcat. 

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

For dynamically instantiating the adapter, I added a new parameter called adapterClassName.  With the addition of the new parameter the Connector element changed to:


<Connector port="8080" protocol="HTTP/1.1"
               adapterClassName="<my custom adapter full class name>"
               connectionTimeout="20000"
               redirectPort="8443" />

Modifications to Connector

To read adapterClassName from the server configuration file added a new attribute 
adapterClassName to the Connector class, org.apache.catalina.connector.Connector and provided accessor and mutator methods.
To provide CoyoteAdpater as the default adapter and to dynamically instantiate the adapter based on the attribute specified in the server configuration, code related to adapter instantiation was changed from:
        adapter = new CoyoteAdapter(this);
to:
if(adapterClassName == null) {
adapter = new CoyoteAdapter(this);
} else {
        try {
Class<?> clazz = Class.forName(adapterClassName);
Constructor<Adapter> constructor = (Constructor<Adapter>) clazz.getConstructor(this.getClass());
adapter = (Adapter) constructor.newInstance(this);
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
As can be seen dynamic instantiation of custom adapter was no different than the existing instantiation except that reflection was used to instantiate the adapter.

Extending CoyoteAdpater

The class CoyoteAdpater is about 300 lines of code.  Instead of trying to rewrite the code, I went with the option of extending class and override the service method.

Compiling Tomcat

Since the custom adapter class is dynamically invoked the Tomcat's libraries are independent of the custom adapter and hence existing ant scripts in Tomcat's source code did not require any changes to compile the code.

Compiling Custom Adpater

Custom adpater class is dependent on several Tomcat libraries.  As such a separate Java project was created, with Tomcat libraries as dependencies.  A jar file containing the custom container was created, added to Tomcat lib folder.  Probably the jar file can be added to Tomcat's endorsed directory.  With these few changes I was able to see that custom adapter was instantiated and was working similar to CayoteAdapter.
The CustomAdpater can potentially be used for customizing Tomcat, and there could be several uses.  A few of them would be a proxy server for Microservices where microservice internal location could be determined from a variety of resources.

Summary

Intercepting raw http request in Tomcat requires Tomcat customization.  A simple solution using dynamic invocation was provided to intercept http raw request, and Tomcat customization was presented.  The minimal code changes required for Tomcat customization are presented.



Sunday, March 18, 2018

Integrating Microsoft Exchange Server with Web Applications in Java

Introduction

Recently in one of the projects I have been working I had a requirement to provide email address search where the backend email server was a Microsoft Exchange Server.  After trying several options, came across the open source java api, ews-java-api.  While getting started guide was pretty good, did not provide examples for searching the email addresses, which was my primary requirement.  Having searched the web for several hours then came across the commercial product, Jwebservices for Exchange.  This product provided a trial license for 30 days, and is relatively inexpensive compared with the hourly rates billed by developers.  The product has nice organized examples.  While trying the example code, it was observed that the class names in the commercial products were very similar to the open source API.  Based on the examples I was able to make some simple changes and use it in my web application for searching the email addresses.  Presented here is the sample code I was able to use within my application.

Sample Code

Following is the sample code I used:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import microsoft.exchange.webservices.data.core.ExchangeService;
import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
import microsoft.exchange.webservices.data.credential.ExchangeCredentials;
import microsoft.exchange.webservices.data.credential.WebCredentials;
import microsoft.exchange.webservices.data.misc.NameResolution;
import microsoft.exchange.webservices.data.misc.NameResolutionCollection;
import microsoft.exchange.webservices.data.property.complex.EmailAddress;


public class MyExchangeService {

private ExchangeService service;

public MyExchangeService(String emailAddress, String password) {
service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
ExchangeCredentials credentials = new WebCredentials(emailAddress, password);
service.setCredentials(credentials);

try {
service.autodiscoverUrl(emailAddress);
} catch (Exception e) {
e.printStackTrace();
}
}

public List<EmailAddress> getEmailsAddress(String searchString) {
List<EmailAddress> emailAddresses = new ArrayList<>();
try {
NameResolutionCollection response = service.resolveName(searchString);
if(searchString.startsWith(". ")) {
Iterator<NameResolution> iterator = response.iterator();
while(iterator.hasNext()) {
NameResolution nameResolution = iterator.next();
emailAddresses.add(nameResolution.getMailbox());
}

} else {
Iterator<NameResolution> iterator = response.iterator();
while(iterator.hasNext()) {
NameResolution nameResolution = iterator.next();
emailAddresses.add(nameResolution.getMailbox());
}
}
} catch (Exception e) {
e.printStackTrace();
}

return emailAddresses;
}

public static void main(String[] args) {
final String EMAILADDRESS = "nonone@nowhere.none";
final String PASSWORD = "secret";

MyExchangeService myExchangeService = new MyExchangeService(EMAILADDRESS, PASSWORD);

String searchString = ". AC";
searchString = "somoone@somewhere.com";
List<EmailAddress> emailAddresses = myExchangeService.getEmailsAddress(searchString);

for(EmailAddress emailAddress : emailAddresses) {
System.out.println(emailAddress);
}
}
}

Performance

Compared with the commercial product the open source version is a little bit slower.  But for requirement the open source code worked.  I did not have to goto my project sponsor for $500/-