How should I design this program?

  • A+
Category:Languages

I am writing a program in Java. The picture is self-explanatory -

How should I design this program? The main method spawns three threads. The SAX processor processes the input XML file, generates JAXB objects and puts them in guava cache. Guava cache is handled by another thread. Whenever any object comes into the cache, this thread notifies the third thread which is the MDL generator (it relates the similar JAXB objects, interconnects them and generates another XML file, called MDL). I have coded the following for the main class -

package test;  import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;  public class MainClass {      public static void main(String args[]) {          ExecutorService xMLService = Executors.newFixedThreadPool(1);         xMLService.execute(new XMLProcessor());          ExecutorService cacheService = Executors.newFixedThreadPool(1);         cacheService.execute(new CacheProcessor());          ExecutorService mdlService = Executors.newFixedThreadPool(1);         mdlService.execute(new MDLProcessor());          xMLService.shutdown();         cacheService.shutdown();         mDLService.shutdown();     } } 

But now I have doubts regarding how to pass objects between the threads and how to notify the MDL generator whenever a new object comes to the cache. In Java old threading model we could use notify(), but I want to use the current ExecutorService. And there are asynchronous callbacks. So I am wondering how to design this framework. How to pass objects and notify the threads? We are keeping the cache objects in HashMap and the CacheService thread needs to pass the key to the MDLService. So which pattern should I use?

 


Here is an example implementation for the above described case. Please note that the implementation could have been possible even without Guava cache, as mentioned by some of the others who have replied; nevertheless I presume there may have been a valid reason for Nirmalaya to ask for it. One such reason that I could think about is spilling over of the cache to storage devices or databases, to save upon the runtime memory.

employee-records.xml

<?xml version="1.0" encoding="UTF-8"?> <Employees>     <Employee id="1">         <name>Thomas</name>     </Employee>     <Employee id="2">         <name>Lisa</name>     </Employee>     <Employee id="3">         <name>Ronald</name>     </Employee>     <Employee id="4">         <name>Erica</name>     </Employee> </Employees> 

Employee.java

package com.technoroy.examples.guava;  /**  * A value holder POJO implementation for Employee records  * @author Rahul R  *  */ class Employee {     private Integer id = null;     private String name = null;      public Employee() {         super();     }      public Employee(Integer id, String name) {         super();         this.id = id;         this.name = name;     }      public Integer getId() {         return id;     }      public void setId(Integer id) {         this.id = id;     }      public String getName() {         return name;     }      public void setName(String name) {         this.name = name;     }      @Override     public String toString() {         return "Employee [id=" + id + ", name=" + name + "]";     } } 

GuavaCacheProcessor.java

package com.technoroy.examples.guava;  import java.io.IOException; import java.io.InputStream; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue;  import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory;  import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler;  import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder;  /**  * The primary executable class  *   * @author Rahul R  *  */ public class GuavaCacheProcessor {     private final static BlockingQueue<Integer> notificationQueue = new LinkedBlockingQueue<>();      public static void main(String... arguments) {         Runnable xmlProcessor = new Runnable() {             public void run() {                 parseDataFile();             }         };          Runnable mdlGenerator = new Runnable() {             public void run() {                 try {                     while (true) {                         Integer id = notificationQueue.take();                         Employee record = ApplicationCacheUtil.getRecord(id);                         generateContent(record);                     }                 } catch (InterruptedException e) {                     Thread.currentThread().interrupt();                 }             }         };          ExecutorService executorService = Executors.newFixedThreadPool(2);         executorService.submit(xmlProcessor);         executorService.submit(mdlGenerator);     }      public static void generateContent(Employee employee) {         System.out.println(employee);     }      public static void parseDataFile() {         SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();         InputStream dataInputStream = GuavaCacheProcessor.class.getResourceAsStream("employee-records.xml");          try {             SAXParser saxParser = saxParserFactory.newSAXParser();             saxParser.parse(dataInputStream, new DefaultHandler() {                 private Employee employee = null;                 private StringBuilder elementValue = null;                  @Override                 public void startElement(String uri, String localName, String qName, Attributes attributes)                         throws SAXException {                     if (qName.equalsIgnoreCase("Employee")) {                         employee = new Employee();                          String id = attributes.getValue("id");                         if (id.matches("-?//d+(//.//d+)?")) {                             employee.setId(Integer.valueOf(id));                         }                     }                      elementValue = new StringBuilder();                 }                  @Override                 public void characters(char ch[], int start, int length) throws SAXException {                     if (elementValue != null) {                         elementValue.append(new String(ch, start, length));                     }                 }                  @Override                 public void endElement(String uri, String localName, String qName) throws SAXException {                     if (qName.equalsIgnoreCase("name")) {                         if (employee != null && elementValue != null) {                             employee.setName(elementValue.toString());                         }                     } else if (qName.equalsIgnoreCase("Employee")) {                         ApplicationCacheUtil.putRecord(employee.getId(), employee);                         try {                             notificationQueue.put(employee.getId());                         } catch (InterruptedException e) {                             e.printStackTrace();                         }                     }                      elementValue = null;                 }             });         } catch (ParserConfigurationException | SAXException | IOException e) {             e.printStackTrace();         }     } }  /**  * The Cache utilities class, that initializes and returns a handle to the  * cache.  *   * @author Rahul R  *  */ class ApplicationCacheUtil {     private static Cache<Integer, Employee> cache = CacheBuilder.newBuilder().build();      public static Cache<Integer, Employee> getCache() {         return cache;     }      public static void putRecord(Integer key, Employee value) {         cache.put(key, value);     }      public static Employee getRecord(Integer key) {         return cache.getIfPresent(key);     } } 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: