Browsing all articles tagged with java
Feb
27

Building SOAP services on Google App Engine in Java: Part 1 – The Servlet

Background

Google App Engine (GAE) is an interesting platform that aims to allow for the development and hosting of Java, Python, and Go web applications.  The pricing scheme is particularly nice:  low-usage applications are hosted for free, pricing is solely dependent on resource usage (with the ability to set budgets and limits), and the minimum charge is only $2.10 per week.  No, I’m not affiliated with or being compensated by Google, but I do think this platform has some potential.

Google supplies a nice Eclipse plugin for use with the Google Web Toolkit and the App Engine, which allows for pretty easy construction of your standard web applications.  I’ve heard that it’s fairly easy to construct user interfaces, JSP pages, etc., but that’s not what we’re talking about here.

The Task

How can we build an application that uses the Google App Engine which uses SOAP for an API?  The primary problem (for me) is that one cannot use the Axis2 libraries (on the server side) due to permission issues, so things must be done a little more manually.

Google has an article covering the topic, but I wanted to do things slightly differently.  Specifically, I wanted to be able to create my service class in Java, generate the WSDL (with appropriately named fields in the XML types – not Parameter1,2,…), generate XML types which will be shared between the server and client projects, and to do it in such a way that I do not have to worry about keeping track of QNAMEs or other low-level details.

Requirements

For this project, I use:

• Eclipse for development, with the Eclipse plugin for GAE

• Axis2 for the client class (also for the java2wsdl and wsdl2java tools)

• JAXB for XML types (auto-generated by Axis2)

The Server

Overview of the Process

Here’s the logic flow for the server:

1. SOAP request arrives via Post into a HttpServlet.

2. HttpServlet builds a SOAPMessage for the request, and passes it to the SoapHandler class.

3. SoapHandler unmarshalls the XML message object, detects its type, and passes a specific application request to the ServiceAdapter class.

4. ServiceAdapter translates from the XML request message (in a safe fashion) to the Service method call.

5. Service method is run, business logic is performed, all the magic happens.

6. ServiceAdapter translates Service method response to XML response message.

7. SoapHandler builds SOAPMessage response (including catching thrown exceptions and turning them into SOAP Fault messages).

8. HttpServlet writes response to client.

All of that seems like a lot of work, but it’s pretty straightforward.  The best part?  I’ll give you the skeleton for all of this; there’s not much application-specific content in all of this.  The process for adding a method to your service class is: implement service class method, run Axis2 generators, modify/fix Axis2 client (the same every time, some auto-gen code must be changed), add 2 lines to SoapHandler, add an adaption method to SoapAdapter, and add a method to the client class.  It’s easier than it sounds.

Part 1 – The HttpServlet

Good news!  There’s no service specific code here.  I’ll just post the relevant parts of what I use:

public class SOAPServerServlet extends HttpServlet {
	public static final String URL = "http://ms4models.appspot.com/service";

	private static final Logger log = Logger
		.getLogger(SOAPServerServlet.class.getName());
	private static MessageFactory messageFactory;
	private static ServiceSOAPHandler soapHandler;

	static {
		try {
			messageFactory = MessageFactory.newInstance();
			soapHandler = new ServiceSOAPHandler();
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}
	}

	@Override
	public void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws IOException {
		log.info("SOAPAction: " + req.getHeader("SOAPAction"));
		log.entering("SOAPServerServlet", "doPost", new Object[] {
				req, resp });
		try {
			// Get all the headers from the HTTP request
			MimeHeaders headers = getHeaders(req);

			// Construct a SOAPMessage from the XML in the request body
			InputStream is = req.getInputStream();
			SOAPMessage soapRequest = messageFactory.createMessage(headers, is);

			ByteArrayOutputStream out = new ByteArrayOutputStream();
			soapRequest.writeTo(out);
			String strMsg = new String(out.toByteArray());

			log.finer("SOAP request: " + strMsg);

			// Handle soapReqest
			SOAPMessage soapResponse = soapHandler
					.handleSOAPRequest(soapRequest);

			// Write to HttpServeltResponse
			resp.setStatus(HttpServletResponse.SC_OK);
			resp.setContentType("text/xml;charset=\"utf-8\"");
			OutputStream os = resp.getOutputStream();
			soapResponse.writeTo(os);
			os.flush();
		} catch (SOAPException e) {
			throw new IOException("Exception while creating SOAP message.", e);
		}
		log.exiting("SOAPServerServlet", "doPost");
	}

	@SuppressWarnings("rawtypes")
	static MimeHeaders getHeaders(HttpServletRequest req) {
		Enumeration headerNames = req.getHeaderNames();
		MimeHeaders headers = new MimeHeaders();
		while (headerNames.hasMoreElements()) {
			String headerName = (String) headerNames.nextElement();
			String headerValue = req.getHeader(headerName);
			StringTokenizer values = new StringTokenizer(headerValue, ",");
			while (values.hasMoreTokens()) {
				headers.addHeader(headerName, values.nextToken().trim());
			}
		}
		return headers;
	}
}

Leave a comment if you have a question about what’s going on, I’ll not explain it for brevity’s sake (also: because I am lazy).

Coming Soon – Part 2

Jun
3

Easily load Xtext files and objects in Eclipse plugin or RCP projects using adapters

Motivation

I’m currently working on a fairly involved Eclipse RCP project which makes heavy use of Xtext for grammar parsing.  One task that’s come up fairly often is that we need to be able to perform some action on a model object contained in an Xtext file.  For example, we want to provide context menu commands to open up a view to display the structure of a model; to do this we need to load the file, parse it with Xtext, and pass the model object off to the view.

The Xtext FAQ provides instructions for doing this in a standalone Java here, but it involves calling the StandaloneSetup method to create and obtain an injector, which isn’t the best for performance.  If we’re already in a RCP application that includes our Xtext language’s UI plugin, we can efficiently obtain the injector from the activator.  In addition to this, we can wrap the whole thing in a class that ties into Eclipse’s adapter manager framework so that other plugins can load our Xtext models without needing a direct dependency on the UI plugin (though of course they still need to depend on the plugin that  defines the model classes).

The Eclipse adapter framework: IAdapterFactory, IAdaptable, and IAdapterManager

In a nutshell, the Eclipse adapter framework works by letting plugins register ‘adapter factory’ classes (that implement IAdapterFactory).  These classes are designed to attempt the singular task of taking and object of some source class and figuring out how to transform it into an object of another target class.  If the transformation is possible, the AdapterFactory must return an object that can be cast as an instance of the target class.  If it is not possible, the AdapterFactory must return null.  In addition, AdapterFactory classes implement a method that returns a list of all target classes supported by the factory, and their defining plugin must provide an extension to org.eclipse.core.runtime.adapters specifying what transformations can be performed.  Traditionally, objects that support being adapted implement the IAdaptable interface, but this is not strictly required.

Once an adapter has been registered that can perform a transformation, any plugin can use this with a simple call:

final TargetObjType target = (TargetObjType)Platform.getAdapterManager().getAdapter(sourceObject, TargetObjType.class);
if (target==null) { /* Adaptation failed */ }

The caller is guaranteed that getAdapter will either return null or an object that can be cast as TargetObjType.

The ModelLoadingAdapter

To create a ModelLoadingAdapter, we must implement the code to load domain models from files and register this in plugin.xml.  As an easy extension we will also let the same adapter handle selections and structured selections (allowing us to directly adapt a list selection to a domain model, if the item selected is a file).  After this is done, any plugin can easily load Xtext DSL models from files with a minimum performance hit.

For this example we’re going to assume the projects are called “org.xtext.example.mydsl” and “org.xtext.example.mydsl.ui”, the language is called “org.xtext.example.mydsl.MyDsl”,  it uses files with extension “mydsl”, and the root model of a document is a DslModel.

The IAdapterFactory

Our actual adapter must do three things:  Obtain the Injector for our DSL, use the injector to obtain a properly initialized XtextResourceSet, and use the resource set to load the file.

Obtaining the domain-specific language injector

Update: From the comments below, the easy/correct way to use injection in Eclipse RCP/plugin projects is to make use of the ExecutableExtensionFactory class. If you specify, in plugin.xml, the name of your AdapterFactory class as “ExecutableExtensionFactory:AdapterFactory” then you do not need to manually get the injector. You can just specify a private class variable using “@Inject
private XtextResourceSetProvider resourceSetProvider;”

When Xtext generates the .ui project, it creates an activator for this plugin that is responsible for creating and storing the Injector for this language.  We can retrieve the injector from this class using the getInjector method, which takes the name of the language as an argument and either returns the injector or null.  The code for this would be

final private static Injector injector = MyDslActivator.getInstance().getInjector("org.xtext.example.mydsl.MyDsl");

Loading the model

This is very similar to the FAQ’s instructions, we just omit the creation of the injector and use the one that we retrieve from the activator.

XtextResourceSet resourceSet = (XtextResourceSet) injector
	.getInstance(XtextResourceSetProvider.class)
	.get(file.getProject());
resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE);
Resource resource = resourceSet.getResource(URI.createURI(file.getLocationURI().toString()),true);
DslModel model = (DslModel) resource.getContents().get(0);

Putting it all together

The only details we’re missing are relatively minor: error checking, transforming selections to files, and the format required by IAdapterFactory.  Our complete class ends up looking like this:

package org.xtext.example.mydsl.util;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IAdapterFactory;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.resource.XtextResourceSet;
import com.google.inject.Injector;
import org.xtext.example.mydsl.myDsl.DslModel;
import org.xtext.example.mydsl.ui.internal.MyDslActivator;
/* Author: Robert Coop
* See: http://coopology.com/2011/06/easily-load-xtext-files-and-objects-in-eclipse-plugin-or-rcp-projects-using-adapters/
*/
@SuppressWarnings("rawtypes")
public class ModelLoadingAdapter implements IAdapterFactory {
	private static org.apache.log4j.Logger log = org.apache.log4j.Logger
		.getLogger(ModelLoadingAdapter.class);
	final private static Injector injector = MyDslActivator
		.getInstance().getInjector("org.xtext.example.mydsl.MyDsl");

	@Override
	public Object getAdapter(Object adaptableObject, Class adapterType) {
		if (adapterType == DslModel.class) {
			if (injector==null) {
				log.error("Could not obtain injector for MyDsl");
				return null;
			}

			if (adaptableObject instanceof ISelection) {
				final ISelection sel = (ISelection)adaptableObject;
				if (!(sel instanceof IStructuredSelection)) return null;
				final IStructuredSelection selection = (IStructuredSelection) sel;
				if (!(selection.getFirstElement() instanceof IFile))
					return null;
				adaptableObject = (IFile)selection.getFirstElement();
			}
			if (adaptableObject instanceof IFile) {
				final IFile file = (IFile)adaptableObject;
				if (!file.getFileExtension().toLowerCase().equals("mydsl")) return null;

				XtextResourceSet resourceSet = (XtextResourceSet) injector
					.getInstance(XtextResourceSetProvider.class)
					.get(file.getProject());
				resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE);
				Resource resource = resourceSet.getResource(URI.createURI(file.getLocationURI().toString()),true);
				DslModel model = (DslModel) resource.getContents().get(0);
				return model;
			}
		}

		return null;
	}

	@Override
	public Class[] getAdapterList() {
		return new Class[] { DslModel.class };
	}
}

Finishing Up

To tie everything together, we just need to modify plugin.xml to tell Eclipse about the adapter, then we can use it to load models easily.  Add the following to plugin.xml

<extension point="org.eclipse.core.runtime.adapters">
      <factory adaptableType="org.eclipse.core.resources.IFile" class="org.xtext.example.mydsl.util.ModelLoadingAdapter">
         <adapter type="org.xtext.example.mydsl.myDsl.DslModel" />
      </factory>
      <factory adaptableType="org.eclipse.jface.viewers.ISelection" class="org.xtext.example.mydsl.util.ModelLoadingAdapter">
         <adapter type="org.xtext.example.mydsl.myDsl.DslModel" />
      </factory>
</extension>

Using our new adapter

Using the adapter from any plugin is very simple.  Given any IFile or ISelection object representing a mydsl file, we can obtain a DslModel using this code

DslModel target = (DslModel)Platform.getAdapterManager().getAdapter(sourceObject, DslModel.class);
if (target==null) { /* Adaptation failed */ }

Final Thoughts

This technique can be applied to any adapter with the advantage of not needing direct dependencies on the plugin providing the adapter factory.  Hopefully this will prove useful to you either because it demonstrates a good way to load Xtext files or it demonstrates some of the adapter framework uses.  Enjoy!

Apr
19

Using Log4J in Eclipse RCP (and forcing all other plugins to use it too!)

There are many resources out there that describe the process (and headache!) of using Apache’s Log4J http://logging.apache.org/log4j/ logging framework within an Eclipse rich client platform (RCP) project.  After digesting all the resources that the internet has to offer (my favorite links at the end) and adding log4j to the RCP project that I am currently working on, the most common problems seem to be:

  • Getting log4j into the environment
  • Classloader errors caused by different versions of log4j classes being loaded by different plugins
  • Problems accessing/locating the log4j.properties file

I also had a problem where I wanted to use only Log4J for logging, so I wanted to be sure that all log messages from all plugins in my RCP project were being picked up by the logging framework.

A log4j bundle is available in the Eclipse Orbit builds (http://download.eclipse.org/tools/orbit/downloads/drops/R20100519200754/); so, actually getting log4j into the RCP environment just involves downloading that bundle and requiring it in the RCP project.  However, there is still the potential for classloader problems and dependency issues when multiple plugins are developed as part of a project and each brings its own log4j package to the table.  The solution is somewhat simple:  designate one plugin project to initialize and supply the logging bundle, and have all the other plugins rely on this.  The choice of the master plugin is usually fairly simple;  for my project it is the RCP project itself, which actually initializes the application environment and has a dependency on the feature plugins.  That master plugin should have the log4j as a bundle dependency (Required Plug-ins), and the other plugins should simply list org.apache.log4j as an imported package.

To configure the logger using a .properties file, a bit of additional work is needed.  Many resources seem to imply there is a way to accomplish this that results in Log4j automatically loading the properties (because it will search for the log4j.properties file at startup if no config is provided), but I wasn’t able to get that to work and didn’t want to spend that much time one it.  My solution was to include log4j.properties in the project directory of the RCP plugin, make sure to add this file to the binary build under build configuration, and initialize the logger using this properties file when the application starts.  This initialization is done by adding the following to your Activator class in the master plugin:

import java.net.URL;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.eclipse.core.runtime.FileLocator;

final private static Logger log = Logger.getLogger(Activator.class);

public void start(final BundleContext context) throws Exception {
	// [...]
	// Setup logging
	URL confURL = getBundle().getEntry("log4j.properties");
	PropertyConfigurator.configure( FileLocator.toFileURL(confURL).getFile());
	log.info("Logging using log4j and configuration " + FileLocator.toFileURL(confURL).getFile());
	hookPluginLoggers(context); // You need to add this method to hook other plugins, described later...
	// [...]
}

That will load and configure the logger using the properties file. The properties file can be modified without needing a re-compile.  To actually log from any class in any plugin now, just add the following:


public class MyClassInAnyPlugin {
	private static org.apache.log4j.Logger log = org.apache.log4j.Logger
			.getLogger(MyClassInAnyPlugin.class);
	void someMethod() {
		log.debug("A debug message");
		log.warn("A warning message");
	}
}

A description of all the log methods and levels can be found in the log4j docs at the official site.  At this point, you should have logging up and running in your project, but we have yet to ensure that _all_ log messages will be sent through log4j.  In particular, other Eclipse plugins use the Eclipse logging framework; without special hooking, these messages will be missed by log4j.  To accomplish that, I used the PluginLogListener class described at http://www.ibm.com/developerworks/library/os-eclog/ in order to add a log listener to each loaded plugin.  To add a listener for each plugin, we need to add the following method (and field):

import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.Platform;

final private List<PluginLogListener> pluginLogHooks = new ArrayList<PluginLogListener>();

// Hook all loaded bundles into the log4j framework
private void hookPluginLoggers(final BundleContext context) {
	for (Bundle bundle : context.getBundles()){
		ILog pluginLogger = Platform.getLog(bundle);
		pluginLogHooks.add(new PluginLogListener(pluginLogger,
				Logger.getLogger(bundle.getSymbolicName())));
		log.trace("Added logging hook for bundle: " + bundle.getSymbolicName());
	}
}

Even though we don’t ever need to read the contents of the pluginLogHooks list, it’s very important to store the PluginLogListener instances we create; without persistent storage they will be reclaimed by the garbage collection process and their logging hooks will be removed.  After adding this method, when your application starts up, you should see something like this:

0    [main] INFO  com.rtsync.devs.rcp.Activator  (Activator.java:76): Logging using log4j and configuration [...]/my.project.rcp/log4j.properties
3    [main] TRACE com.rtsync.devs.rcp.Activator  (Activator.java:103): Added logging hook for bundle: org.eclipse.osgi
4    [main] TRACE com.rtsync.devs.rcp.Activator  (Activator.java:103): Added logging hook for bundle: com.ibm.icu
5    [main] TRACE com.rtsync.devs.rcp.Activator  (Activator.java:103): Added logging hook for bundle: org.apache.commons.logging
5    [main] TRACE com.rtsync.devs.rcp.Activator  (Activator.java:103): Added logging hook for bundle: org.eclipse.ant.core
6    [main] TRACE com.rtsync.devs.rcp.Activator  (Activator.java:103): Added logging hook for bundle: org.eclipse.compare.core
6    [main] TRACE com.rtsync.devs.rcp.Activator  (Activator.java:103): Added logging hook for bundle: org.eclipse.core.commands[...]

Now, any regular log message created by any plugin in your RCP project will be caught and interpreted by the log4j framework, which looks like this:

!ENTRY org.eclipse.core.resources 2 10035 2011-04-19 10:02:03.710!MESSAGE The workspace exited with unsaved changes in the previous session; refreshing workspace to recover changes.
819  [main] WARN  org.eclipse.core.resources  (PluginLogListener.java:91): org.eclipse.core.resources - 10035 - The workspace exited with unsaved changes in the previous session; refreshing workspace to recover changes.

Note that as long as the -consoleLog option is used with Eclipse (which is added by default to your run/debug configurations) you’ll see the original plugin log message and then the Log4J log message.  Now you have the entire environment using the same logging framework, whether it wants to or not!

Good in-depth explanations of logging in Eclipse RCP:

 

%d bloggers like this: