Friday, February 27, 2009

Alfresco Web Forms Integration - Mock JSF Faces Context

On my current project, we are using Alfresco and working on an integration with JBoss Portal. In particular, we were creating our own version of Alfresco WebForms editor that is built into their web client. We had built all the Alfresco Web Scripts to fetch the appropriate WebForm for a given content item and a college of mine had built all the Portal magic to render and save the form in a fashion similar to Chiba. The last component I needed to build was the Webscript to generate the renditions of the web forms within Alfresco. I found the magic component with Alfresco that did this in AVMEditBean. But in order to utilize it, I had to cut and paste the following lines:

if (services.getAVMService().hasAspect(nodeRef.getVersion(), nodeRef.getPath(), WCMAppModel.ASPECT_FORM_INSTANCE_DATA)) {
this.regenerateRenditions(nodeRef);
}

private void regenerateRenditions(AVMNode node)
throws FormNotFoundException {
final String avmPath = node.getPath();
final FormInstanceData fid = formsService.getFormInstanceData(-1, avmPath);
final List result = fid.regenerateRenditions();
for (FormInstanceData.RegenerateResult rr : result) {
if (rr.getException() != null) {
Utils.addErrorMessage(
"error regenerating rendition using "
+ rr.getRenderingEngineTemplate().getName() + ": "
+ rr.getException().getMessage(), rr.getException());
}
}
}

The trouble, I ran into was all the utility classes that were called by this blurb of code required a static instances of the current Alfresco FacesContext. My first instinct was to start pulling apart each of these utility classes that were called and removing their dependence upon the FacesContext. I had done this effectively in the past when I was mimicking the Submit All functionality, but this time it was not going as well. I seem to keep digging myself a bigger and bigger hole of code that following the Cut And Paste Anti-Pattern. I was talking with my colleague Phil Kedy and he suggested that we create a Mock version of the FacesContext and just initialize it with the things that the Alfresco utility classes needed, mainly the Spring WebContext. He got to work on building the Mock class and I figured out to wire in Spring context. Wiring in the Spring context was fairly easy because all Alfresco Webscripts are declared in a Spring context file so I just had my Webscript implement the Spring interface ApplicationContextAware and we would be set. Mr. Kedy figured out how to create the mock class and now all I had to add in was the following:

MockFacesContext faces = new MockFacesContext((WebApplicationContext) ctx);

Now in conjunction with the MockFacesContext, he had to create 2 supporting classes a MockApplication class that implemented the method createValueBinding. This was necessary in order to process the el syntax that Alfresco used to locate the Alfresco Services. And in conjunction with that he had to implement a MockValueBinding object to handle the location of the services. And finally, we had to load in the message bundles to handle the word substitution.

Once all of this was done, we had a working version of the Web Forms upload and rendition generation that worked within the JBoss Portal.

Sunday, February 22, 2009

Posting JSON with Commons HTTPClient and XStream

I recently had an occasion where I had to perform an HTTP POST with JSON data from a Java service class as oppose to Javascript. No amount of Google searches turned up the answer I was after. Here are the steps I took to do so:

STEP 1 - Handle HTTP Post
The project I am working we were already using Commons HTTPClient which has a PostMethod class that peforms an HTTP Post. Here is the code to setup the post:

HttpClient clientService = new HttpClient();
PostMethod post = new PostMethod();
post.setURI(new URI("http://yoururl" false));

Step 2 - Find JSON Converter
The best tool kit I found for handling JSON is a combination of XStream and Jettison. Following the XStream tutorial, I did the following:

// This ensure that we drop the root element
XStream xstream = new XStream(new JsonHierarchicalStreamDriver() {
public HierarchicalStreamWriter createWriter(Writer writer) {
return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE);
}
});

xstream.setMode(XStream.NO_REFERENCES);

// Stream the class I want converted into JSON
xstream.alias("site", ProjectBean.class);

Step 3 - Post the JSON Stream
Next up, is putting the two together and posting the JSON.

// Model bean to stream
ProjectBean site = new ProjectBean();

post.setRequestHeader("Content-Type", "application/json");

// apply content-length here if known (i.e. from proxied req)
// if this is not set, then the content will be buffered in memory
post.setRequestEntity(new StringRequestEntity(xstream.toXML(site), "application/json", null));

// execute the POST
int status = clientService.executeMethod(post);

// Check response code
if (status != HttpStatus.SC_OK) {
throw new Exception("Received error status " + status);
}

Conclusion
That is all there. I hope this helps.