This is a pattern that is very easy to configure and use in Spring. It allows you to map object or any arbitrary key in a map to a service that will process objects of any types.
The Engine delegates calls to each one of the configured processors. Although the example is very simple and shows no real functionality I have used this same pattern to create services that generate thumbnails, process images, extract text from files based on content types, etc…
Once the engine is configured you can smply call engine.process(object) and based on the configuration the engine will delegate to the right processor.
This same pattern with slight modifications can be reused to implement filters and chain of commands/responsibility.
You can base the keys on the map on any other attribute besides the class.
This tutorial is available for download with svn:
svn checkout http://raulrajatutorials.googlecode.com/svn/trunk/ raulrajatutorials-read-only
Comments in the code itself should be self-explanatory if you already have some java + spring experience.
And now to the point.
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" default-autowire="byName"> <!-- A processor that know about Person objects --> <bean id="personProcessor" class="com.raulraja.processor.impl.PersonProcessorImpl"/> <!-- A processor that knows about Place objects --> <bean id="placeProcessor" class="com.raulraja.processor.impl.PlaceProcessorImpl"/> <!-- A configured processing engine that maps object types to their processors and delegates calls to each one of them --> <bean id="processingEngine" class="com.raulraja.processor.impl.DefaultProcessingEngineImpl"> <property name="processors"> <map> <entry key="com.raulraja.domain.Person" value-ref="personProcessor"/> <entry key="com.raulraja.domain.Place" value-ref="placeProcessor"/> </map> </property> </bean> <!-- This handler is actually not required but used for demonstrating the tutorial by invoking http://localhost:port/tutorialName --> <bean id="serviceInvokerHandler" class="com.raulraja.util.handler.impl.ServiceInvokerHandlerImpl" autowire="autodetect" /> </beans>
ProcessingEngine.java
package com.raulraja.processor; /** * A processing engine that you can feed objects to and processes them */ public interface ProcessingEngine<Target> { /** * Processes something * @param target the target object to be processed */ void process(Target target); }
DefaultProcessingEngineImpl.java
package com.raulraja.processor.impl; import com.raulraja.processor.ProcessingEngine; import com.raulraja.processor.Processor; import org.apache.log4j.Logger; import java.util.Map; /** * Default impl of the processing engine based on a spring configured service. * Delegates processing calls to the right processor */ public class DefaultProcessingEngineImpl<Target> implements ProcessingEngine<Target> { private final static Logger log = Logger.getLogger(DefaultProcessingEngineImpl.class); /** * A map of processor that maps object types to a processor that know how to process it */ private Map<Class<Target>, Processor<Target>> processors; public void setProcessors(Map<Class<Target>, Processor<Target>> processors) { this.processors = processors; } /** * Processes something * * @param target the target object to be processed */ public void process(Target target) { log.debug("started processing " + target); Processor<Target> processor = processors.get(target.getClass()); if (processor != null) { processor.process(target); } else { log.debug("processor not found for " + target); } log.debug("finished processing " + target); } }
Processor.java
package com.raulraja.processor; /** * A service that know how to process a type of object */ public interface Processor<Target> { /** * Processes something * @param target the target object to be processed */ void process(Target target); }
PersonProcessorImpl.java
package com.raulraja.processor.impl; import com.raulraja.domain.Person; import com.raulraja.processor.Processor; import org.apache.log4j.Logger; /** * Demonstrates how a Person can be processed */ public class PersonProcessorImpl implements Processor<Person> { private final static Logger log = Logger.getLogger(PersonProcessorImpl.class); /** * Processes something * * @param person the target object to be processed */ public void process(Person person) { log.debug(person + " something is being done to a person object"); } }
PlaceProcessorImpl.java
package com.raulraja.processor.impl; import com.raulraja.domain.Place; import com.raulraja.processor.Processor; import org.apache.log4j.Logger; /** * Demonstrates how a Person can be processed */ public class PlaceProcessorImpl implements Processor<Place> { private final static Logger log = Logger.getLogger(PlaceProcessorImpl.class); /** * Processes something * * @param place the target object to be processed */ public void process(Place place) { log.debug(place + " something is being done to a place object"); } }
Person.java
package com.raulraja.domain; /** * Represents a person */ public class Person { }
Place.java
package com.raulraja.domain; /** * Represents a place */ public class Place { }
ServiceInvokerHandlerImpl.java
package com.raulraja.util.handler.impl; import com.raulraja.processor.ProcessingEngine; import com.raulraja.domain.Person; import com.raulraja.domain.Place; import org.springframework.web.HttpRequestHandler; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Frontend entry point for the demo */ public class ServiceInvokerHandlerImpl implements HttpRequestHandler { /** * The engine that processes object types */ private ProcessingEngine<Object> processingEngine; /** * sets the engine that processes object types * @param processingEngine the engine */ public void setProcessingEngine(ProcessingEngine<Object> processingEngine) { this.processingEngine = processingEngine; } /** * Process the given request, generating a response. * * @param request current HTTP request * @param response current HTTP response * @throws javax.servlet.ServletException in case of general errors * @throws java.io.IOException in case of I/O errors */ public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Person person = new Person(); Place place = new Place(); processingEngine.process(person); processingEngine.process(place); } }
Output
07/01 08:53:59 DEBUG com.raulraja.processor.impl.DefaultProcessingEngineImpl – started processing com.raulraja.domain.Person@53fb687a
07/01 08:53:59 DEBUG com.raulraja.processor.impl.PersonProcessorImpl – com.raulraja.domain.Person@53fb687a something is being done to a person object
07/01 08:53:59 DEBUG com.raulraja.processor.impl.DefaultProcessingEngineImpl – finished processing com.raulraja.domain.Person@53fb687a
07/01 08:53:59 DEBUG com.raulraja.processor.impl.DefaultProcessingEngineImpl – started processing com.raulraja.domain.Place@a427a0b
07/01 08:53:59 DEBUG com.raulraja.processor.impl.PlaceProcessorImpl – com.raulraja.domain.Place@a427a0b something is being done to a place object
07/01 08:53:59 DEBUG com.raulraja.processor.impl.DefaultProcessingEngineImpl – finished processing com.raulraja.domain.Place@a427a0b