Friday, April 20, 2012

Alfresco 3.4: Links to Tasks in an Email

Something that keeps cropping up in my dealing with Alfresco is the ability to generate a link to either a node in the repository or a task in a workflow. There are some example scripts on how this can be done (using ${url.serverPath} in a template for instance). However, from my experience, the server name has always been 'localhost' or the template would produce an error saying it couldn't find 'url.serverPath'. So as with all problems I encountered, I went to Google first to see who else has come across this problem and how they fixed it... fortunately, many other people have had this problem; unfortunately, they were largely unsolved solutions or solutions I didn't like.

https://forums.alfresco.com/en/viewtopic.php?f=34&t=22202
https://forums.alfresco.com/en/viewtopic.php?f=34&t=14595&start=0&st=0&sk=t&sd=a
https://forums.alfresco.com/en/viewtopic.php?f=53&t=42238
https://forums.alfresco.com/en/viewtopic.php?t=8111
https://forums.alfresco.com/en/viewtopic.php?f=9&t=24759
https://forums.alfresco.com/en/viewtopic.php?f=3&t=42011

And finally this comment which was key for me:

https://forums.alfresco.com/en/viewtopic.php?f=48&t=40028#p117384

It essentially says to extend or create your own email action because the model in the default one is rather limited.

We have a need to use templates that are outside the context of the repository, which the default email action (in the enterprise version) does not handle. Since we already have rolled our own email handler (based off the community version with my own tweaks here and there), I thought I would customize it a little further to enable link generation.

My first thought was to use Java's InetAddress to get everything I needed; however, that would've proved difficult for obtaining the port; I would have had to scan each port to figure out which one the application is using. So I opted for the config route. Alfresco actually already provides us with the properties needed in alfresco-global.properties. The properties are:

...
#
# URL Generation Parameters (The ${localname} token is replaced by the local server name)
#-------------
#alfresco.context=alfresco
#alfresco.host=${localname}
#alfresco.port=8080
#alfresco.protocol=http
#
#share.context=share
#share.host=alfrescodevsvr
#share.port=8080
#share.protocol=http
...

Depending on your setup, you may have need to use both the alfresco.* properties and share.* properties. You'll probably want to account for both if you have the Alfresco repository and Share on two separate servers and/or need to generate links for both Alfresco Explorer and Alfresco Share interfaces. We only want to provide a link to the Share interface and both Alfresco and Share will reside on the same server, so we will only use one set of these properties to generate one link.

So in our mail action, I added these properties and setters:
...
 private String serverProtocol;
 private String serverName;
 private String serverPort;

 ...

 public void setServerProtocol( String serverProtocol ) {
  
  this.serverProtocol = serverProtocol;
 }
 
 public void setServerName( String serverName ) {
  
  this.serverName = serverName;
 }
 
 public void setServerPort( String serverPort ) {
  
  this.serverPort = serverPort;
 }
 ...

I originally was going to inject the entire properties object (alfresco-global: https://forums.alfresco.com/en/viewtopic.php?f=4&t=41823) but later found out I can inject the individual properties I want via config. This is more flexible in our case as it allows us to choose whether we want to use either the alfresco.* properties, the share.* properties, or some other value. Here is what the spring bean config looks like to inject values into these variables:

...


...

  
   ${share.protocol}
  
  
  
   ${share.host}
  
  
  
   ${share.port}
  

...



...

Be sure to uncomment whatever properties you decide to use in the alfresco-global.properties file. As you see above, I chose to use the Share properties, as that is what we are going to generate a link for.

Next, be sure your class implements InitializingBean, which provides us with the function afterPropertiesSet().

public class MyCompanyMailAction extends ActionExecuterAbstractBase implements InitializingBean {

We generate the server path in this function once to save on performance, and because the config can only be updated on a restart. Here is how I implemented it:

...
 private String serverPath = null;
 
 ...
 
  @Override
 public void afterPropertiesSet() throws Exception {
  
  ...

  StringBuilder serverPathBuilder = new StringBuilder();
  serverPathBuilder.append( ( serverProtocol != null && !serverProtocol.startsWith( "${" ) && !serverProtocol.endsWith( "}" ) ) ? serverProtocol : "http" );
  serverPathBuilder.append( "://" );
  
  if ( serverName != null && !serverName.startsWith( "${" ) && !serverName.endsWith( "}" ) ) {
   
   serverPathBuilder.append( serverName );
  }
  else {
   
   InetAddress localHost = InetAddress.getLocalHost();
   serverPathBuilder.append( localHost.getCanonicalHostName() );
  }
  
  serverPathBuilder.append( ':' );
  serverPathBuilder.append( ( serverPort != null && !serverPort.startsWith( "${" ) && !serverPort.endsWith( "}" ) ) ? serverPort : "8080" );
  serverPath = serverPathBuilder.toString();
  
  if ( log.isDebugEnabled() ) log.debug( "Server path: " + serverPath );
 }
 
 ...

I had an issue where one of the properties did not get transposed with the actual value. This occurred when the properties were not uncommented in the alfresco-global.properties file, so the injected text was literally ${share.port} instead of the port number for example. I solved that by checking for the start and end curly braces for each property:

!serverPort.startsWith( "${" ) && !serverPort.endsWith( "}" )

I wanted a default configuration in case none was provided (ie commented out), so in the code I attempt to get the host name via an InetAddress if the config is not available. The other two properties, serverProtocol and serverPort, I default those to http and 8080 as those are the most common and those values are a little harder to detect efficiently and accurately.

Next, we need to inject the serverPath into the model to be use in the template. To do this, I made a separate function to generate the model and insert whatever default values I need into it.

...
 private Map<String, Object> createEmailTemplateModel( NodeRef ref, Map<String, Object> suppliedModel, NodeRef fromPerson ) {
  
  Map<String, Object> model = new HashMap<String, Object>( 8, 1.0f );
  
  ...
  
  if ( serverPath != null ) {
   
   model.put( "serverPath", serverPath );
  }
  
  ...
  
  return model;
 }
 
 ...

Now the variable is ready to be used within a template for example:

...

${serverPath}/share/page/task-edit?taskId=${taskId}

...

As you can see above, this generates a link to a task through the Share interface using the property taskId that I also put into the model (this is actually done through a parameter on the action where I supply a custom model to be merged with the default model). If you wish to see the full source let me know, I will see what I can do.

Wednesday, August 3, 2011

Using a Template in the Classpath for an Email on Alfresco 3.4

When trying to use a template on the classpath with the MailActionExecutor but without much success.  I was under the impression that all one had to do was provide the relative path to the ftl file via the PARAM_TEMPLATE parameter (can't remember what I read that made me think this... it also doesn't help that there is not a whole lot of Java examples/documentation other than the skimpy javadocs).  I tried this, and was met with the following exception:

org.springframework.mail.MailPreparationException: Could not prepare mail; nested exception is java.lang.ClassCastException: java.lang.String cannot be cast to org.alfresco.service.cmr.repository.NodeRef

A quick look at the source code revealed why:

...
// See if an email template has been specified
String text = null;
NodeRef templateRef = (NodeRef)ruleAction.getParameterValue(PARAM_TEMPLATE);
if (templateRef != null)
...

It doesn't even attempt to differentiate between a template in the repository vs one in the classpath.  It automatically assumes it is in the repo (hence the cast to a NodeRef before anything else is done).  To get around this, I had to manually process the template to generate the text to be used in the email.

To do this, a model needs to be generated for the template to use.  I used the code that generates the model in the MailActionExecutor.java minus a few objects I deemed unnecessary for my use case.  Then I passed in the path to the ftl and the model to the template service and it generated the output string.  I then set it to the PARAM_TEXT parameter for the email.

Here is the code:
private static final String EXPIRE_EMAIL_TEMPLATE = "alfresco/extension/burris/common/templates/expiration-email-template.ftl";
...
// Process the template - the mail action executor expects it to be in the repository which is why this has to be done
Map model = new HashMap( 8, 1.0f );
NodeRef person = personService.getPerson( authenticationService.getCurrentUserName() );
model.put( "person", new TemplateNode( person, serviceRegistry, null ) );
model.put( "document", new TemplateNode( nodeRef, serviceRegistry, null ) );
NodeRef parent = nodeService.getPrimaryParent( nodeRef ).getParentRef();
model.put( "space", new TemplateNode( parent, serviceRegistry, null ) );
model.put( "message", new I18NMessageMethod() );

String text = templateService.processTemplate( "freemarker", EXPIRE_EMAIL_TEMPLATE, model );

// Send email notice detailing the state change
Action emailAction = actionService.createAction( MailActionExecuter.NAME );

emailAction.setParameterValue( MailActionExecuter.PARAM_SUBJECT, subject.toString() );
emailAction.setParameterValue( MailActionExecuter.PARAM_TO_MANY, emailList );
emailAction.setParameterValue( MailActionExecuter.PARAM_TEXT, text );

if ( log.isDebugEnabled() ) log.debug( "Sending email..." );
actionService.executeAction( emailAction, nodeRef );
...

Monday, August 1, 2011

Send an Email to an Entire Alfresco Share Site

I had the need to send the users of an Alfresco Share site an email; however, doing so is not readily apparent.  It is pretty easy once you know the convention:

GROUP_site_siteId


This differs from the Alfresco Explorer groups which follow the pattern:

GROUP_groupId

Here is an example of what a site group identifier should look like if the site id is transportation:

GROUP_site_transportation

If you're working in the javascript world or just want to hardcode things together, you can construct the group identifier using the above as a template.  However, in the java world, Alfresco provides us with the SiteService. If you know the short name of your site, you can retrieve the string identifier for the site group. In my case, I retrieved the short name of the site from the nodeRef passed into a behavior.

Here is the code I used to do this:

// To
 SiteInfo siteInfo = siteService.getSite( nodeRef );
 String siteShortName = siteInfo.getShortName();
 String siteGroup = siteService.getSiteGroup( siteShortName );
   
 // Send email notice detailing the state change
 Action emailAction = actionService.createAction( MailActionExecuter.NAME );
 
 emailAction.setParameterValue( MailActionExecuter.PARAM_SUBJECT, subject );
 emailAction.setParameterValue( MailActionExecuter.PARAM_TO_MANY, siteGroup );
 emailAction.setParameterValue( MailActionExecuter.PARAM_TEXT, "Hello world." );
 
 actionService.executeAction( emailAction, nodeRef );

Friday, June 10, 2011

Alfresco and a Custom TaskControllerHandler

My work has required me to create an advance workflow in Alfresco to facilitate the process of how the users insert documents and apply the corresponding metadata.  The outline of what needed to be done is this:
  1. Upload a document
  2. Start a new workflow for that document
  3. Require the user to select a document type from a list of subtypes of the current document type
Under the covers, Alfresco uses jBPM.  It actually has been integrated quite nicely, but there are certain features that are not readily available after their marriage.  It does not provide a way to display a list of options out of the box, unless it is static.

There are hacks that allow one to do this with a ListConstraint, but a hack isn't always the best way to go (think fitting a square peg in a round hole).  A ListConstraint almost fits the need here for selecting a document type, except its missing one crucial element: access to the node in the workflow (or any node for that matter).  In order to get a list of subtypes, I need to check the node's current type.  There are ways around this, but they aren't exactly best practice.  So this option was a non starter.

I looked into using actions before and after the task to populate the list and change the document type, but this seemed to be a bunch of extra work and also a bit of a kludge.

Another option was to use BeanShell script or Javascript directly in the workflow.  I can't say I'm a big fan of putting code directly in the xml of the workflow, nor am I a fan of server side Javascript (too hard to debug).

Some searching on jBPM revealed that it is possible to map properties to a task from the process context (or whatever source you want) via a TaskControllerHandler.  The task controller excels at allowing the programmer provide data to the task that is not a 1 to 1 mapping of what is in the process context (which is why configuring the default task controller in the workflow was out of the question as it only does 1 to 1).  This seemed to be the option to go with, however, there are several pain points I came across implementing this.  The rest of this blog post will address those points.

The process I outlined of what the controller needed to do:
  • Retrieve the document node and determine its child types
  • Present that to the user via the task instance
  • User is able to select one of the options
  • Upon submission, the document node is changed to the new document subtype
First thing I needed to do was obtain references to the Alfresco services.  At first I wasn't sure how to do this, since the TaskControllerHandler is managed by jBPM and not Spring so dependency injection was out.  I've done some work using workflow actions in Alfresco, which has access to the services via a BeanFactory.  So to the source code I went and figured out how they set that up (javadoc and source for 
JBPMSpringActionHandler).  With this knowledge, I created the following abstract class to extend all my Alfresco task controllers from:

package com.burris.common.bpm.taskcontrollers;

import org.jbpm.taskmgmt.def.TaskControllerHandler;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;

@SuppressWarnings("serial")
public abstract class AlfrescoTaskControllerHandler implements TaskControllerHandler {

 public AlfrescoTaskControllerHandler() {
  
  BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
  BeanFactoryReference factoryReference = factoryLocator.useBeanFactory( null );
  BeanFactory factory = factoryReference.getFactory();
  initializeHandler( factory );
 }
 
 protected abstract void initializeHandler( BeanFactory factory );
}

The initializeHandler function is intended to grab and store references to the services required. Now I am ready to create my actual task handler, called DocumentSubtypeTaskController:

package com.burris.common.bpm.taskcontrollers;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.alfresco.repo.workflow.jbpm.JBPMNode;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.log4j.Logger;
import org.jbpm.context.exe.ContextInstance;
import org.jbpm.graph.exe.Token;
import org.jbpm.taskmgmt.exe.TaskInstance;
import org.springframework.beans.factory.BeanFactory;

@SuppressWarnings( "serial" )
public class DocumentSubtypeTaskController extends AlfrescoTaskControllerHandler {

 public DocumentSubtypeTaskController() {}
 
 @Override
 protected void initializeHandler( BeanFactory factory ) {
  // TODO Auto-generated method stub
  
 }
 
 @Override
 public void initializeTaskVariables( TaskInstance taskInstance, ContextInstance contextInstance, Token token ) {
  // TODO Auto-generated method stub
  
 }
 
 @Override
 public void submitTaskVariables( TaskInstance taskInstance, ContextInstance contextInstance, Token token ) {
  // TODO Auto-generated method stub
  
 }
}

I first implemented my initializeHandler method and added the services to the class:

...
 
 private NodeService nodeService;
 private DictionaryService dictionaryService;
 private NamespaceService namespaceService;

 ...

 @Override
 protected void initializeHandler( BeanFactory factory ) {
  
  this.nodeService = (NodeService) factory.getBean( "nodeService" );
  this.dictionaryService = (DictionaryService) factory.getBean( "dictionaryService" );
  this.namespaceService = (NamespaceService) factory.getBean( "namespaceService" );
 }

Next I implemented the two methods from the TaskControllerHandler interface: initializeTaskVariables and submitTaskVariables. The purpose of these methods is to setup the variables of the task and to retrieve their values to process them, respectively. The values can come from just about anywhere, but usually from the process context. In this case, I need to retrieve the type of the document node in the bpm_package, which is in the the process context.

...
 
 private static final String DOCUMENT_TYPE = "blndwf_documentType";
 
 ...
 
 @Override
 public void initializeTaskVariables( TaskInstance taskInstance, ContextInstance contextInstance, Token token ) {
  
  // Get the document from the workflow package
  Object object = contextInstance.getVariable( "bpm_package" );
  if ( object == null ) return;
  
  NodeRef bpmPackageNodeRef = ((JBPMNode) object).getNodeRef();
  if ( bpmPackageNodeRef == null ) return;
  
  List children = nodeService.getChildAssocs( bpmPackageNodeRef );
  ChildAssociationRef childNodeRef = children.get( 0 );
  if ( childNodeRef == null ) return;
  
  NodeRef documentNodeRef = childNodeRef.getChildRef();
  if ( documentNodeRef == null ) return;
  
  // Get the document's subtypes 
  QName documentQName = nodeService.getType( documentNodeRef );
  Collection subTypes = dictionaryService.getSubTypes( documentQName, false );
  
  List options = new ArrayList();
  for ( QName type : subTypes ) {
   
   ClassDefinition classDef = dictionaryService.getClass( type );
   options.add( type.getPrefixString() + "|" + classDef.getTitle() );
  }
  taskInstance.setVariable( DOCUMENT_TYPE, options );

A couple things you need to note here. The constant DOCUMENT_TYPE value is using an underscore (_). The actual property (which is coming from the workflow task model definition) looks like this: blndwf:documentType. The reason for this is that jBPM does not allow colons (:) in the variable names. I'm not sure which piece does this, but using the underscore automatically maps it to the correct property in the task model.

The other thing to note is the for loop. I setup a list of strings containing the type string and and the title separated by the pipe symbol (|). At the end of the day this list is converted to a string before it gets to the UI. I have created a custom form control that will split these strings (splits on "," and then "|") to use in a select box. The type gets mapped to the value of an option while the title is what is actually displayed. There was no other easy way to do this without string manipulation in the UI.

Lastly, I completed the submitTaskVariables method:

@Override
 public void submitTaskVariables( TaskInstance taskInstance, ContextInstance contextInstance, Token token ) {
  
  // Get the document from the workflow package
  Object object = contextInstance.getVariable( "bpm_package" );
  if ( object == null ) return;
  
  NodeRef bpmPackageNodeRef = ((JBPMNode) object).getNodeRef();
  if ( bpmPackageNodeRef == null ) return;
  
  List children = nodeService.getChildAssocs( bpmPackageNodeRef );
  ChildAssociationRef childNodeRef = children.get( 0 );
  if ( childNodeRef == null ) return;
  
  NodeRef documentNodeRef = childNodeRef.getChildRef();
  if ( documentNodeRef == null ) return;
  
  String selectedType = (String) taskInstance.getVariable( DOCUMENT_TYPE );
  if ( log.isDebugEnabled() ) log.debug( selectedType );
  
  // TODO: Validate user input
  
  // Set the document type if valid
  QName newType = QName.createQName( selectedType, namespaceService );
  nodeService.setType( documentNodeRef, newType );
  
  // TODO: Signal only when data is valid
  token.signal();
 }

After this is done processing the task variables (often just get mapped to the context instance, except not in this case - I actually change the document type) token.signal() needs to be called. This tells jBPM that the task controller is done and the workflow is ready to transition to the next task in the workflow.

That's it for the Java code! This can be jar'd up and put into the lib directory (alfresco.war/WEB-INF/lib). Here is the final class in it's entirety:

package com.burris.common.bpm.taskcontrollers;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.alfresco.repo.workflow.jbpm.JBPMNode;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.log4j.Logger;
import org.jbpm.context.exe.ContextInstance;
import org.jbpm.graph.exe.Token;
import org.jbpm.taskmgmt.exe.TaskInstance;
import org.springframework.beans.factory.BeanFactory;

@SuppressWarnings( "serial" )
public class DocumentSubtypeTaskController extends AlfrescoTaskControllerHandler {

 private static final String DOCUMENT_TYPE = "blndwf_documentType";
 
 private NodeService nodeService;
 private DictionaryService dictionaryService;
 private NamespaceService namespaceService;
 
 public DocumentSubtypeTaskController() {}
 
 @Override
 protected void initializeHandler( BeanFactory factory ) {
  
  this.nodeService = (NodeService) factory.getBean( "nodeService" );
  this.dictionaryService = (DictionaryService) factory.getBean( "dictionaryService" );
  this.namespaceService = (NamespaceService) factory.getBean( "namespaceService" );
 }
 
 @Override
 public void initializeTaskVariables( TaskInstance taskInstance, ContextInstance contextInstance, Token token ) {
  
  // Get the document from the workflow package
  Object object = contextInstance.getVariable( "bpm_package" );
  if ( object == null ) return;
  
  NodeRef bpmPackageNodeRef = ((JBPMNode) object).getNodeRef();
  if ( bpmPackageNodeRef == null ) return;
  
  List children = nodeService.getChildAssocs( bpmPackageNodeRef );
  ChildAssociationRef childNodeRef = children.get( 0 );
  if ( childNodeRef == null ) return;
  
  NodeRef documentNodeRef = childNodeRef.getChildRef();
  if ( documentNodeRef == null ) return;
  
  // Get the document's subtypes 
  QName documentQName = nodeService.getType( documentNodeRef );
  Collection subTypes = dictionaryService.getSubTypes( documentQName, false );
  
  List options = new ArrayList();
  for ( QName type : subTypes ) {
   
   ClassDefinition classDef = dictionaryService.getClass( type );
   options.add( type.getPrefixString() + "|" + classDef.getTitle() );
  }
  taskInstance.setVariable( DOCUMENT_TYPE, options );
 }

 @Override
 public void submitTaskVariables( TaskInstance taskInstance, ContextInstance contextInstance, Token token ) {
  
  // Get the document from the workflow package
  Object object = contextInstance.getVariable( "bpm_package" );
  if ( object == null ) return;
  }
  
  NodeRef bpmPackageNodeRef = ((JBPMNode) object).getNodeRef();
  if ( bpmPackageNodeRef == null ) {
   
   log.fatal( "nodeRef is null." );
   return;
  }
  
  List children = nodeService.getChildAssocs( bpmPackageNodeRef );
  ChildAssociationRef childNodeRef = children.get( 0 );
  if ( childNodeRef == null ) return;
  
  NodeRef documentNodeRef = childNodeRef.getChildRef();
  if ( documentNodeRef == null ) return;
  
  String selectedType = (String) taskInstance.getVariable( DOCUMENT_TYPE );
  if ( log.isDebugEnabled() ) log.debug( selectedType );
  
  // Validate user input
  
  // Set the document type if valid
  QName newType = QName.createQName( selectedType, namespaceService );
  nodeService.setType( documentNodeRef, newType );
  
  // Signal only when data is valid?
  token.signal();
 }
}

Wednesday, May 25, 2011

Design Patterns

All programmers need to be aware of common design patterns.  What leads me to this all inclusive conclusion?  Because using design patterns buys you many things when writing code and will help to prevent it from showing up on sites like this.

It is so easy when starting a new project to spend little to no time on the design phase and go straight into coding.  This is the fast track to sending your code to the fan.  Take your time and choose your design carefully.  You'll thank yourself later.

What is a design pattern?
Design patterns help us to write code that is reusable, extensible, and maintainable.  It helps programmers to think about code in terms of objects.  While design patterns are not code, they are general solutions that can be applied in many cases.  They can help to save time by giving an approach to common problems.

Reusability
Thinking in terms of a designing a car, it is determined that it needs a wheel.  Therefor a wheel is invented and attached the car.  But low and behold it is decided that a car needs more than just one wheel, so do we reinvent the wheel each time we need one? No, because that would be a waste of time.  We only need to design the wheel once, and create 4 instances of it to put on the car.  But it is important to note that the wheel doesn't, and shouldn't, do everything.  It serves it purpose (in terms of transportation) to facilitate movement on the ground.  But water, air, or space travel do not always make use of wheels for movement.  While it may be possible in some cases to shoe-horn a wheel into a vehicle's design to make it traverse these other mediums, it is not the most efficient, practical, time saving, or easy thing to do.

Extensibility
There is only one constant in any project, and that is change.  Requirements change, members come and go, technical roadblocks can come out of nowhere, etc.  Design patterns may help your code to be extensible so when the customer asks, "Can it do this?" it will be easier to add in that extra functionality. It is good to think about extensibility when designing, but it can be easy to fall into the trap where you try to make everything and its brother extensible.  When working on this aspect of the design, you need to stop and ask, does this need to be extendible or how easy would be to make it extendible down the road?  Otherwise you may end up creating an entire computer just to use it as a simple calculator.

Maintainability
Last but not least (by far).  Ever inherited a project full of spaghetti code or written code that you have no idea what it does a week later?  I have on occasion, and let me tell you, it is a nightmare.  Using established design patterns makes it easier to fix bugs, add functionality, or take functionality out.  They provide inherent organization and make code easier to understand and follow.

Good Resources

Friday, January 7, 2011

Google Docs API: Retrieving Contents of Root Folder

I have been working on a program that interacts with Google Docs, and the need came up to display the contents of the root folder. After some searching around, I found these queries that do what I want them to do (sort of):

Contents of root:
http://docs.google.com/feeds/default/private/full/folder%3Aroot/contents/

Only folders in root:
http://docs.google.com/feeds/default/private/full/folder%3Aroot/contents/-/folder

Everything but folders:
http://docs.google.com/feeds/default/private/full/folder%3Aroot/contents/-/-folder

The most useful one for me in my case was the first one. However, it did not work as I initially thought. I executed the query and I found items in the feed that were in folders. So after a bit of head scratching and searching I found the answer: both the root folder and the other misc folder(s) had an edit link to the file, so naturally it would show up in both feeds. In order to remove these from the root folder, you have to delete the edit link to it in that location.

Saturday, January 30, 2010

Concurrent Programming Concepts

Introduction
Writing linear programs, while being a lot easier than concurrent programming, is a thing of the past. Linear programming was effective while Moore's Law held; however, as raw processing speed has currently hit a brick wall we have had to look at executing code in parallel. With more and more systems being built with multiple processors with multiple cores each, not taking advantage of them is poor programming. This limits the scalability, responsiveness, and performance of any program. To make things worse, many that code using Java still operate linearly even though threads permeate Java's system. Servlets operate using threads and can be concurrently accessed by multiple requests from users, GUI frameworks use threads underneath to retain responsiveness, the garbage collector operates in separate threads of it own, and etc. They are everywhere. If a program is being created without concurrency in mind, you run the great risk of inadvertently creating errant code because even if you yourself don't use multiple threads, that doesn't mean there aren't any there to access it. If there are multiple threads accessing the same mutable (changeable) variables, and those variables aren't properly synchronized, your program is broken. End of story. It may look like your code works initially, and it may work correctly for years to come, but it all amounts to lucky/unlucky timing. The worst time to see that the code is broken is when it fails in production under heavy load (and no doubt that is when it will happen because of the system having so much going on).

State
Concurrent programming is not defined by using threads and locks; rather, it is all about maintaining state. Threads and locks are the building blocks that we use to maintain state with concurrent access. An object's state is encompassed by the instance variables, static variables, or other dependent objects. When we talk about thread-safety, it may sound like we are talking about code, but what we are really saying is that we are trying to protect data by controlling concurrent access to the data.

Ways to Manage It
There are three primary ways in Java to maintain a class's state:

  1. Don't share state across threads (encapsulation).

  2. Make state variables immutable (unchangeable).

  3. Use synchronization techniques whenever accessing state variables.


It is important to remember to encapsulate synchronization inside an object so that when anything uses it, it doesn't have to worry about implementing its own synchronization outside of the object for the object. Stateless objects are always thread-safe, because there is nothing it has to remember or keep track of during its execution. If a single element of state is added to a stateless class, then it will be thread-safe if and only if the state is fully managed by a thread-safe object. When there are multiple variables that comprise an object's state, it is important to know which ones are dependent upon each other, and which ones that aren't. If there are 2 or more variables that are related in some fashion are accessed by more than one thread could result in the 2 variables getting out of sync (meaning the data contained within them doesn't match up). If the variables are independent of each other, the synchronization may be exclusive to each variable. This means that each one can be read, modified, or written to without worrying what the others are.

Final Thought
Concurrent programming is difficult at first, but it also not going away... ever. The only way to get better at is to read about it, read it again, and do it until my head and fingers hurt. Repetition is the mother of knowledge. That is the only way this will get any easier.