View Javadoc

1   //
2   // $Revision: 5 $
3   // $LastChangedBy: mhanns $
4   // $Date: 2010-04-01 10:10:45 +0200 (Do, 01 Apr 2010) $
5   // $HeadURL:
6   // svn://localhost/winf-ps/trunk/repository/src/main/java/de/uni_leipzig/wifa/iwi/mr3/service/impl/ModelRepositoryServiceImpl.java
7   // $
8   //
9   
10  package de.uni_leipzig.wifa.iwi.mr3.service.impl;
11  
12  import java.io.BufferedInputStream;
13  import java.io.BufferedWriter;
14  import java.io.ByteArrayInputStream;
15  import java.io.IOException;
16  import java.io.InputStream;
17  import java.io.StringReader;
18  import java.io.StringWriter;
19  import java.io.Writer;
20  import java.util.Arrays;
21  import java.util.HashMap;
22  import java.util.Map;
23  
24  import javax.xml.stream.XMLInputFactory;
25  import javax.xml.stream.XMLStreamException;
26  import javax.xml.stream.XMLStreamReader;
27  
28  import org.apache.axiom.om.OMElement;
29  import org.apache.axiom.om.impl.builder.StAXOMBuilder;
30  import org.apache.axis2.AxisFault;
31  import org.apache.axis2.context.ConfigurationContext;
32  import org.apache.axis2.context.ServiceContext;
33  import org.apache.axis2.description.AxisService;
34  import org.apache.axis2.description.Parameter;
35  import org.apache.axis2.engine.ServiceLifeCycle;
36  import org.apache.axis2.service.Lifecycle;
37  import org.apache.log4j.Logger;
38  import org.eclipse.emf.common.util.URI;
39  import org.eclipse.emf.ecore.EObject;
40  import org.eclipse.emf.ecore.EPackage;
41  import org.eclipse.emf.ecore.resource.ResourceSet;
42  import org.eclipse.emf.ecore.resource.Resource.Factory;
43  import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
44  import org.eclipse.emf.ecore.xmi.PackageNotFoundException;
45  import org.eclipse.emf.ecore.xmi.XMIResource;
46  import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
47  
48  import de.uni_leipzig.wifa.iwi.mr3.common.Comparison;
49  import de.uni_leipzig.wifa.iwi.mr3.common.Match;
50  import de.uni_leipzig.wifa.iwi.mr3.dao.ModelRepositoryDao;
51  import de.uni_leipzig.wifa.iwi.mr3.dao.neo4j.impl.ModelRepositoryDaoNeo4jImpl;
52  import de.uni_leipzig.wifa.iwi.mr3.service.CompareProcessor;
53  import de.uni_leipzig.wifa.iwi.mr3.service.MRException;
54  import de.uni_leipzig.wifa.iwi.mr3.service.ModelRepositorySkeletonInterface;
55  import de.uni_leipzig.wifa.iwi.mr3.service.ServiceHelper;
56  
57  /**
58   * Service implementation for model repository.
59   * <p>
60   * This class asks the global configuration (for axis2: axis2.xml) for a
61   * configuration parameter "ModelRepository.DatabasePath". Use this parameter to
62   * configure the persistence folder. If there is no such folder, we use a static
63   * one.
64   */
65  public class ModelRepositoryServiceImpl implements ModelRepositorySkeletonInterface, ServiceLifeCycle, Lifecycle
66  {
67    /** Logger. */
68    private static final Logger LOG = Logger.getLogger(ModelRepositoryServiceImpl.class);
69  
70    /** Byte array buffer size. */
71    private static final int BUFFER_SIZE = 1024 * 1024;
72  
73    /** Default content type literal. */
74    private static final String DEFAULT_CONTENT_TYPE = "*";
75  
76    /** Dummy URI for in-memory resources. */
77    private static final String DUMMY_URI = "";
78  
79    /** Parameter name for database path parameter. */
80    private static final String PARAMETER_DB_PATH = "ModelRepository.DatabasePath";
81  
82    /** Value of System.getProperty("os.name") in case of Windows. */
83    private static final String OS_NAME_WINDOWS = "windows";
84  
85    /** Parameter name for System.getProperty(..). */
86    private static final String SYSTEM_PARAMETER_OS_NAME = "os.name";
87  
88    /** Static folder for persistent data. */
89    private static final String WINDOWS_DB_PATH = "/modelrepository/database";
90    private static final String UNIX_DB_PATH = "/var/lib" + WINDOWS_DB_PATH;
91  
92    /** Parameter name for DAO. */
93    private static final String PARAMETER_DAO = "dao";
94  
95    /** Parameter name for compare processor. */
96    private static final String PARAMETER_COMPARE_PROCESSOR = "compare.processor";
97  
98    /** Parameter name for service helper. */
99    private static final String PARAMETER_HELPER = "service.helper";
100 
101   /** XMI resource factory. */
102   private static final Factory RESOURCE_FACTORY = new XMIResourceFactoryImpl();
103   // private static final Factory RESOURCE_FACTORY = new
104   // EcoreResourceFactoryImpl();
105 
106   /** Service context. */
107   private ServiceContext serviceCtx;
108 
109   /**
110    * Service live cycle method.
111    * <p>
112    * This (callback) method is called by axis2 when the engine starts, before it
113    * is initially used. Therefore the first request need not wait until
114    * initialization has finished.
115    *
116    * @param configctx
117    *          Service context
118    * @param service
119    *          Service
120    * @see org.apache.axis2.engine.ServiceLifeCycle#startUp(org.apache.axis2.context.ConfigurationContext,
121    *      org.apache.axis2.description.AxisService)
122    */
123   @Override
124   public void startUp(final ConfigurationContext configctx, final AxisService service)
125   {
126     LOG.debug("starting");
127 
128     // build up and wire objects.
129 
130     // construct DAO and its components
131     final ModelRepositoryDaoNeo4jImpl dao = new ModelRepositoryDaoNeo4jImpl();
132     dao.setDatabasePath(getDatabasePath(service));
133     dao.startUp();
134 
135     // constructs helper
136     final ServiceHelper helper = new ServiceHelperImpl();
137 
138     // construct service's components
139     final CompareProcessorImpl compareProcessor = new CompareProcessorImpl();
140     compareProcessor.setDao(dao);
141     compareProcessor.setHelper(helper);
142 
143     try
144     {
145       service.addParameter(new Parameter(PARAMETER_DAO, dao));
146       service.addParameter(new Parameter(PARAMETER_HELPER, helper));
147       service.addParameter(new Parameter(PARAMETER_COMPARE_PROCESSOR, compareProcessor));
148       LOG.info("started");
149     }
150     catch (final AxisFault e)
151     {
152       LOG.error("error while starting", e);
153     }
154   }
155 
156   /**
157    * Service live cycle method.
158    *
159    * @param configctx
160    *          Service context
161    * @param service
162    *          Service
163    * @see org.apache.axis2.engine.ServiceLifeCycle#shutDown(org.apache.axis2.context.ConfigurationContext,
164    *      org.apache.axis2.description.AxisService)
165    */
166   @Override
167   public void shutDown(final ConfigurationContext configctx, final AxisService service)
168   {
169     LOG.debug("shutting down");
170     ((ModelRepositoryDao) service.getParameter(PARAMETER_DAO).getValue()).shutDown();
171     LOG.info("shutted down");
172   }
173 
174   /**
175    * Init of service context.
176    * <p>
177    * Each service class may more then once instantiated for some reasons. Each
178    * instance get it's own service context. At time the instance is constructed
179    * this method get called. We keep the service context in instance variable
180    * for further usage.
181    *
182    * @param context
183    *          Service context
184    * @throws AxisFault
185    *           Never happens
186    * @see org.apache.axis2.service.Lifecycle#init(org.apache.axis2.context.ServiceContext)
187    */
188   @Override
189   public void init(final ServiceContext context) throws AxisFault
190   {
191     LOG.debug("init");
192     serviceCtx = context;
193   }
194 
195   /**
196    * On destroy the service context.
197    *
198    * @param context
199    *          Service context
200    * @see org.apache.axis2.service.Lifecycle#destroy(org.apache.axis2.context.ServiceContext)
201    */
202   @Override
203   public void destroy(final ServiceContext context)
204   {
205     LOG.debug("destroy");
206   }
207 
208   /**
209    * Determine the persistence folder.
210    * <p>
211    * First we try to get the service specific folder. If there is no such one,
212    * we try to get the global one. If there is no such one, too, we use os
213    * depending static folder.
214    *
215    * @param service
216    *          Axis service
217    * @return Folder for persitence data
218    */
219   private String getDatabasePath(final AxisService service)
220   {
221     final Parameter serviceDbPath = service.getParameter(PARAMETER_DB_PATH);
222     if (serviceDbPath != null)
223     {
224       return serviceDbPath.getValue().toString();
225     }
226     final Parameter globalDbPath = service.getAxisConfiguration().getParameter(PARAMETER_DB_PATH);
227     if (globalDbPath != null)
228     {
229       return globalDbPath.getValue().toString();
230     }
231     return System.getProperty(SYSTEM_PARAMETER_OS_NAME).toLowerCase().indexOf(OS_NAME_WINDOWS) > -1 ? WINDOWS_DB_PATH
232         : UNIX_DB_PATH;
233   }
234 
235   /**
236    * Store model into model repository.
237    * <p>
238    * If model exists, the parameter <code>override</code> must be
239    * <code>true</code> to override the model, otherwise storing fails.
240    *
241    * @param model
242    *          Model to store into model repository
243    * @param uri
244    *          uri of the model resource, used as name to tag models without
245    *          nsURI
246    * @throws MRException
247    *           Service level exception
248    * @see de.uni_leipzig.wifa.iwi.mr3.service.ModelRepositorySkeletonInterface#save(org.apache.axiom.om.OMElement,
249    *      java.lang.String)
250    */
251   @Override
252   public void save(final OMElement model, final String uri) throws MRException
253   {
254     if (LOG.isDebugEnabled())
255     {
256       final String nl = System.getProperty("line.separator");
257       final String ln = "----------------------------------";
258       LOG.debug(new StringBuilder(nl).append(ln).append(nl).append(model).append(nl).append(ln));
259     }
260     final ResourceSet rset = new ResourceSetImpl();
261     rset.getResourceFactoryRegistry().getContentTypeToFactoryMap().put(DEFAULT_CONTENT_TYPE, RESOURCE_FACTORY);
262 
263     final long startTime = System.currentTimeMillis();
264 
265     // lift up API level.
266     final XMIResource res = (XMIResource) rset.createResource(URI.createURI(DUMMY_URI));
267     while (!res.isLoaded())
268     {
269       final InputStream stream = new BufferedInputStream(new ByteArrayInputStream(model.toString().getBytes()), BUFFER_SIZE);
270       try
271       {
272         res.load(stream, res.getDefaultLoadOptions());
273         res.setURI(URI.createURI(uri));
274 
275         // EcoreUtil.
276 
277         // check existence
278         final EObject m = res.getContents().get(0);
279 
280         // save
281         if (!modelExists(m))
282         {
283           getDao().save(res);
284         }
285 
286         // time measure
287         if (LOG.isInfoEnabled())
288         {
289           LOG.info("Model [" + uri + "] saved after " + (System.currentTimeMillis() - startTime) + " ms");
290         }
291       }
292       catch (final IOException e)
293       {
294         // try to load neccessary packages and fail if those couldn't be loaded
295         final Throwable cause = e.getCause();
296         if (cause instanceof PackageNotFoundException)
297         {
298           final String nsUri = ((PackageNotFoundException) cause).uri();
299           final EObject pkg = getDao().load(nsUri, new HashMap<Object, EObject>());
300           if (null == pkg)
301           {
302             final MRException ex = new MRException("First save the meta model with nsURI: " + nsUri);
303             LOG.warn(ex);
304             throw ex;
305           }
306 
307           getHelper().registerAllSubpackages((EPackage) pkg, rset);
308 
309           res.unload();
310         }
311         else
312         {
313           final MRException ex =
314               new MRException("Unable to process the model data. For further details, please use the EMF validator!");
315           LOG.warn(ex);
316           throw ex;
317         }
318       }
319       finally
320       {
321         try
322         {
323           if (stream != null)
324           {
325             stream.close();
326           }
327         }
328         catch (final IOException e)
329         {
330           LOG.error(e);
331         }
332       }
333     }
334   }
335 
336   /**
337    * Checks if the given model exists.
338    *
339    * @param model
340    *          the model
341    * @return true, if model exists
342    * @throws MRException
343    *           Service level exception
344    */
345   private boolean modelExists(final EObject model) throws MRException
346   {
347     if (model instanceof EPackage)
348     {
349       final EPackage pkg = (EPackage) model;
350       if (getDao().modelExists(pkg.getNsURI()))
351       {
352         final MRException ex =
353             new MRException("Package or subpackage with namespace uri [" + pkg.getNsURI()
354                 + "] still exists. Please delete it before save.");
355         LOG.warn(ex);
356         throw ex;
357       }
358       for (final EPackage subPkg : pkg.getESubpackages())
359       {
360         return modelExists(subPkg);
361       }
362     }
363     else
364     {
365 
366       final String nsUri = model.eClass().getEPackage().getNsURI() + " [" + model.eResource().getURI() + "]";
367       if (getDao().modelExists(nsUri))
368       {
369         final MRException ex =
370             new MRException("Model with identifier " + nsUri + " still exists. Please delete it before save.");
371         LOG.warn(ex);
372         throw ex;
373       }
374     }
375     return false;
376   }
377 
378   /**
379    * Load a model by given namespace URI.
380    *
381    * @param nsUri
382    *          Namespace URI
383    * @return Model with the given namespace URI or <code>null</code> if no such
384    *         model exists in repository
385    * @throws MRException
386    *           Service level exception
387    * @see de.uni_leipzig.wifa.iwi.mr3.service.ModelRepositorySkeletonInterface#load(java.lang.String)
388    */
389   @Override
390   public OMElement load(final String nsUri) throws MRException
391   {
392     LOG.debug("loading model with nsURI [" + nsUri + "]");
393 
394     final long startTime = System.currentTimeMillis();
395 
396     final ResourceSet rset = new ResourceSetImpl();
397     rset.getResourceFactoryRegistry().getContentTypeToFactoryMap().put(DEFAULT_CONTENT_TYPE, RESOURCE_FACTORY);
398 
399     final EObject emfModel = getDao().load(nsUri, new HashMap<Object, EObject>());
400 
401     final XMIResource resource = (XMIResource) rset.createResource(URI.createURI(DUMMY_URI));
402 
403     try
404     {
405       resource.getContents().add(emfModel);
406 
407       final StringWriter writer = new StringWriter();
408       final Writer out = new BufferedWriter(writer);
409 
410       final Map<Object, Object> saveOptions = resource.getDefaultSaveOptions();
411 
412       // save model to string
413       resource.save(out, saveOptions);
414       out.flush();
415 
416       // hand over to string reader
417       final StringReader reader = new StringReader(writer.getBuffer().toString());
418 
419       // hand over to XML reader
420       final XMLStreamReader xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(reader);
421 
422       // hand over to AXIOM builder
423       final StAXOMBuilder builder = new StAXOMBuilder(xmlReader);
424       final OMElement axiomModel = builder.getDocumentElement();
425 
426       out.close();
427       writer.close();
428 
429       if (LOG.isInfoEnabled())
430       {
431         LOG.info("loaded model [" + nsUri + "] after " + (System.currentTimeMillis() - startTime) + " ms");
432       }
433       return axiomModel;
434     }
435     catch (final IllegalArgumentException e)
436     {
437       final MRException ex = new MRException("No model with nsURI [" + nsUri + "]", e);
438       LOG.warn(ex);
439       throw ex;
440     }
441     catch (final IOException e)
442     {
443       final MRException ex = new MRException("error while loading model [" + nsUri + "]", e);
444       LOG.warn(ex);
445       throw ex;
446     }
447     catch (final XMLStreamException e)
448     {
449       final MRException ex = new MRException("error while loading model [" + nsUri + "]", e);
450       LOG.warn(ex);
451       throw ex;
452     }
453   }
454 
455   /**
456    * Delete model.
457    *
458    * @param nsUri
459    *          Namespace URI of model to delete
460    * @param cascading
461    *          true, if all instance models should also be deleted
462    * @throws MRException
463    *           Service level exception
464    * @see de.uni_leipzig.wifa.iwi.mr3.service.ModelRepositorySkeletonInterface#delete(java.lang.String,
465    *      boolean)
466    */
467   @Override
468   public void delete(final String nsUri, final boolean cascading) throws MRException
469   {
470     LOG.debug("delete model [" + nsUri + "]");
471 
472     final long startTime = System.currentTimeMillis();
473 
474     getDao().delete(nsUri, cascading);
475 
476     if (LOG.isInfoEnabled())
477     {
478       LOG.info("deleted model [" + nsUri + "] after " + (System.currentTimeMillis() - startTime) + " ms.");
479     }
480   }
481 
482   /**
483    * Find all models.
484    * <p>
485    * Lookup the nsURIs of all model instances in repository beyound the model
486    * with the given nsURI. If the meta nsURI is unkonown (no such model in
487    * repository) or no model is found, the result is empty, not
488    * <code>null</code>.
489    * <p>
490    * If the argument is <code>null</code>, the method looks up all models
491    * explicitly without an assigned meta model. Usually these are meta models,
492    * forming the maximum model level in it's context.
493    *
494    * @param metaNsUri
495    *          Namespace URI of parent model.
496    * @return nsURIs of found models
497    * @see de.uni_leipzig.wifa.iwi.mr3.service.ModelRepositorySkeletonInterface#getInstanceModels(java.lang.String)
498    */
499   @Override
500   public String[] getInstanceModels(final String metaNsUri)
501   {
502     final long startTime = System.currentTimeMillis();
503 
504     final String[] models = getDao().getInstanceModels(metaNsUri);
505 
506     if (LOG.isInfoEnabled())
507     {
508       if (LOG.isDebugEnabled())
509       {
510         LOG.debug("metaNsUri: " + metaNsUri);
511         for (final String model : models)
512         {
513           LOG.debug("    +- " + model);
514         }
515       }
516       LOG.info("got instance models of [" + metaNsUri + "] after " + (System.currentTimeMillis() - startTime) + " ms.");
517     }
518     return models;
519   }
520 
521   /**
522    * Find model by conditions. y
523    *
524    * @param classifiers
525    *          the classifiers to search in
526    * @param expression
527    *          the string expression to search for
528    * @param isRegEx
529    *          RegExp search
530    * @param isCaseSensitive
531    *          Case sensitive search
532    * @return Array of found models, matching the given conditions
533    * @see de.uni_leipzig.wifa.iwi.mr3.service.ModelRepositorySkeletonInterface#find(java.lang.String,
534    *      java.lang.String[], boolean, boolean)
535    */
536   @Override
537   public Match[] find(final String[] classifiers, final String expression, final boolean isRegEx, final boolean isCaseSensitive)
538   {
539     if (LOG.isDebugEnabled())
540     {
541       LOG.debug("expression: " + expression);
542       LOG.debug("case sensitive: " + String.valueOf(isCaseSensitive));
543       LOG.debug("regexp: " + String.valueOf(isRegEx));
544       LOG.debug("classifiers: " + Arrays.toString(classifiers));
545     }
546     final long startTime = System.currentTimeMillis();
547 
548     final Match[] result = getDao().find(expression, classifiers, isRegEx, isCaseSensitive);
549 
550     if (LOG.isInfoEnabled())
551     {
552       LOG.info("search finished after " + (System.currentTimeMillis() - startTime) + " ms.");
553     }
554     return result;
555   }
556 
557   /**
558    * Compare two models.
559    *
560    * @param leftUri
561    *          Identificator of left model
562    * @param rightUri
563    *          Identificator of right model
564    * @return Commonalities s and differences of the two models
565    * @throws MRException
566    *           Service level exception
567    * @see de.uni_leipzig.wifa.iwi.mr3.service.ModelRepositorySkeletonInterface#compare(org.apache.axis2.databinding.types.URI,
568    *      org.apache.axis2.databinding.types.URI)
569    */
570   @Override
571   public Comparison compare(final String leftUri, final String rightUri) throws MRException
572   {
573     LOG.debug("comparing models with nsURI [" + leftUri + "] and [" + rightUri + "]");
574 
575     return getCompareProcessor().process(leftUri, rightUri);
576   }
577 
578   /**
579    * Get the DAO from service context.
580    * <p>
581    * Instead of hold the DAO in an instance variable, we get it from service
582    * context because of axis2 design policy.
583    *
584    * @return DAO
585    */
586   private ModelRepositoryDao getDao()
587   {
588     return (ModelRepositoryDao) serviceCtx.getAxisService().getParameter(PARAMETER_DAO).getValue();
589   }
590 
591   /**
592    * Get the compare processor from service context.
593    * <p>
594    * Instead of hold the compare processor in an instance variable, we get it
595    * from service context because of axis2 design policy.
596    *
597    * @return Compare processor
598    */
599   private CompareProcessor getCompareProcessor()
600   {
601     return (CompareProcessor) serviceCtx.getAxisService().getParameter(PARAMETER_COMPARE_PROCESSOR).getValue();
602   }
603 
604   /**
605    * Gets the service helper.
606    *
607    * @return Service helper
608    */
609   private ServiceHelper getHelper()
610   {
611     return (ServiceHelper) serviceCtx.getAxisService().getParameter(PARAMETER_HELPER).getValue();
612   }
613 }