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/dao/impl/neo4j/ModelRepositoryDaoNeo4jImpl.java
7   // $
8   //
9   
10  package de.uni_leipzig.wifa.iwi.mr3.dao.neo4j.impl;
11  
12  import java.io.File;
13  import java.util.ArrayList;
14  import java.util.Arrays;
15  import java.util.HashMap;
16  import java.util.List;
17  import java.util.Map;
18  
19  import org.apache.log4j.Logger;
20  import org.eclipse.emf.common.util.URI;
21  import org.eclipse.emf.ecore.EObject;
22  import org.eclipse.emf.ecore.EPackage;
23  import org.eclipse.emf.ecore.EcorePackage;
24  import org.eclipse.emf.ecore.resource.Resource;
25  import org.eclipse.emf.ecore.resource.ResourceSet;
26  import org.eclipse.emf.ecore.resource.Resource.Factory;
27  import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
28  import org.eclipse.emf.ecore.xmi.XMIResource;
29  import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
30  import org.neo4j.api.core.Direction;
31  import org.neo4j.api.core.EmbeddedNeo;
32  import org.neo4j.api.core.NeoService;
33  import org.neo4j.api.core.Node;
34  import org.neo4j.api.core.Relationship;
35  import org.neo4j.api.core.ReturnableEvaluator;
36  import org.neo4j.api.core.StopEvaluator;
37  import org.neo4j.api.core.Transaction;
38  import org.neo4j.api.core.Traverser.Order;
39  
40  import de.uni_leipzig.wifa.iwi.mr3.common.Match;
41  import de.uni_leipzig.wifa.iwi.mr3.dao.Constants;
42  import de.uni_leipzig.wifa.iwi.mr3.dao.LoadProcessor;
43  import de.uni_leipzig.wifa.iwi.mr3.dao.ModelRepositoryDao;
44  import de.uni_leipzig.wifa.iwi.mr3.dao.SaveProcessor;
45  import de.uni_leipzig.wifa.iwi.mr3.dao.SearchProcessor;
46  import de.uni_leipzig.wifa.iwi.mr3.dao.neo4j.EcoreRelationshipType;
47  import de.uni_leipzig.wifa.iwi.mr3.service.MRException;
48  
49  /**
50   * Implementation of DAO for access to neo4j network database.
51   */
52  public class ModelRepositoryDaoNeo4jImpl implements ModelRepositoryDao
53  {
54    /** Logger. */
55    private static final Logger LOG = Logger.getLogger(ModelRepositoryDaoNeo4jImpl.class);
56  
57    /** Neo instance. */
58    private NeoService neo;
59  
60    /** Neo4j database path. */
61    private String databasePath;
62  
63    /** Neo4j helper. */
64    private NeoHelper helper;
65  
66    /** Search processor. */
67    private SearchProcessor searchProcessor;
68  
69    /** Load processor. */
70    private LoadProcessor loadProcessor;
71  
72    /** Save processor for phase one. */
73    private SaveProcessor saveProcessorPhaseOne;
74  
75    /** Save processor for phase two. */
76    private SaveProcessor saveProcessorPhaseTwo;
77  
78    /** Default content type literal. */
79    private static final String DEFAULT_CONTENT_TYPE = "*";
80  
81    /** XMI resource factory. */
82    private static final Factory RESOURCE_FACTORY = new XMIResourceFactoryImpl();
83  
84    /**
85     * Setter.
86     *
87     * @param databasePath
88     *          the databasePath to set
89     */
90    public void setDatabasePath(final String databasePath)
91    {
92      this.databasePath = databasePath;
93    }
94  
95    /**
96     * Getter.
97     *
98     * @return the databasePath
99     */
100   public String getDatabasePath()
101   {
102     return databasePath;
103   }
104 
105   /**
106    * Init the neo node space.
107    */
108   @Override
109   public void startUp()
110   {
111     LOG.debug("Initialize database.");
112     if (neo != null)
113     {
114       shutDown();
115     }
116     if (LOG.isInfoEnabled())
117     {
118       LOG.info("Using neo4j database location at " + new File(databasePath).getAbsolutePath());
119     }
120     neo = new EmbeddedNeo(databasePath);
121 
122     helper = new NeoHelper();
123     helper.setNeo(neo);
124 
125     final PhaseOneSaveProcessor spp1 = new PhaseOneSaveProcessor();
126     spp1.setHelper(helper);
127     saveProcessorPhaseOne = spp1;
128 
129     final PhaseTwoSaveProcessor spp2 = new PhaseTwoSaveProcessor();
130     spp2.setHelper(helper);
131     saveProcessorPhaseTwo = spp2;
132 
133     final LoadProcessorImpl lp = new LoadProcessorImpl();
134     loadProcessor = lp;
135 
136     final SearchProcessorImpl sp = new SearchProcessorImpl();
137     sp.setHelper(helper);
138     searchProcessor = sp;
139   }
140 
141   /**
142    * @see de.uni_leipzig.wifa.iwi.mr3.dao.ModelRepositoryDao#shutDown()
143    */
144   @Override
145   public void shutDown()
146   {
147     LOG.debug("Shutdown neo4j DAO.");
148     neo.shutdown();
149     neo = null;
150   }
151 
152   /**
153    * Setter.
154    *
155    * @param neo
156    *          the neo to set
157    */
158   public void setNeo(final NeoService neo)
159   {
160     this.neo = neo;
161   }
162 
163   /**
164    * Getter.
165    *
166    * @return the neo
167    */
168   public NeoService getNeo()
169   {
170     return neo;
171   }
172 
173   /**
174    * <<<<<<< .mine Setter.
175    *
176    * @param helper
177    *          the helper to set
178    */
179   public void setHelper(final NeoHelper helper)
180   {
181     this.helper = helper;
182   }
183 
184   /**
185    * Getter.
186    *
187    * @return the helper
188    */
189   public NeoHelper getHelper()
190   {
191     return helper;
192   }
193 
194   /**
195    * Setter.
196    *
197    * @param saveProcessorPhaseOne
198    *          the saveProcessorPhaseOne to set
199    */
200   public void setSaveProcessorPhaseOne(final SaveProcessor saveProcessorPhaseOne)
201   {
202     this.saveProcessorPhaseOne = saveProcessorPhaseOne;
203   }
204 
205   /**
206    * Getter.
207    *
208    * @return the saveProcessorPhaseOne
209    */
210   public SaveProcessor getSaveProcessorPhaseOne()
211   {
212     return saveProcessorPhaseOne;
213   }
214 
215   /**
216    * Setter.
217    *
218    * @param saveProcessorPhaseTwo
219    *          the saveProcessorPhaseTwo to set
220    */
221   public void setSaveProcessorPhaseTwo(final SaveProcessor saveProcessorPhaseTwo)
222   {
223     this.saveProcessorPhaseTwo = saveProcessorPhaseTwo;
224   }
225 
226   /**
227    * Getter.
228    *
229    * @return the saveProcessorPhaseTwo
230    */
231   public SaveProcessor getSaveProcessorPhaseTwo()
232   {
233     return saveProcessorPhaseTwo;
234   }
235 
236   /**
237    * Setter.
238    *
239    * @param loadProcessor
240    *          the loadProcessor to set
241    */
242   public void setLoadProcessor(final LoadProcessor loadProcessor)
243   {
244     this.loadProcessor = loadProcessor;
245   }
246 
247   /**
248    * Getter.
249    *
250    * @return the loadProcessor
251    */
252   public LoadProcessor getLoadProcessor()
253   {
254     return loadProcessor;
255   }
256 
257   /**
258    * Setter.
259    *
260    * @param searchProcessor
261    *          the searchProcessor to set
262    */
263   public void setSearchProcessor(final SearchProcessor searchProcessor)
264   {
265     this.searchProcessor = searchProcessor;
266   }
267 
268   /**
269    * Getter.
270    *
271    * @return the searchProcessor
272    */
273   public SearchProcessor getSearchProcessor()
274   {
275     return searchProcessor;
276   }
277 
278   /**
279    * Find all instance models of the model with specified nsURI.
280    *
281    * @param metaNsUri
282    *          Meta model nsURI
283    * @return Array of all models namespace URIs having xmlns equals given nsURI.
284    * @see de.uni_leipzig.wifa.iwi.mr3.dao.ModelRepositoryDao#getInstanceModels(java.lang.String)
285    */
286   @Override
287   public String[] getInstanceModels(final String metaNsUri)
288   {
289     final Transaction tx = neo.beginTx();
290     try
291     {
292       final List<String> instanceModels = new ArrayList<String>();
293       // get all availablemodels
294       final Iterable<Node> models = helper.getSubrefNodeChildren(EcoreRelationshipType.RESOURCES);
295 
296       for (final Node model : models)
297       {
298         // get top level metametamodels
299         if (null == metaNsUri || "".equals(metaNsUri))
300         {
301           // top level metametamodels must not have an incoming INSTANCE MODEL
302           // relationship
303           if (!model.getRelationships(EcoreRelationshipType.INSTANCE_MODEL, Direction.INCOMING).iterator().hasNext())
304           {
305             instanceModels.add((String) model.getProperty(Constants.PROPERTY_NS_URI));
306           }
307         }
308         // get any other models
309         else
310         {
311           if (metaNsUri.equals(model.getProperty(Constants.PROPERTY_NS_URI)))
312           {
313             // get all models having an incoming INSTANCE relationship from this
314             // model
315             for (final Relationship relationship : model.getRelationships(EcoreRelationshipType.INSTANCE_MODEL,
316                 Direction.OUTGOING))
317             {
318               instanceModels.add((String) relationship.getEndNode().getProperty(Constants.PROPERTY_NS_URI));
319             }
320           }
321         }
322       }
323       tx.success();
324 
325       final String[] response = instanceModels.toArray(new String[instanceModels.size()]);
326       Arrays.sort(response);
327       return response;
328     }
329     finally
330     {
331       tx.finish();
332     }
333   }
334 
335   /**
336    * Checks the existance of a model with the given namespace uri.
337    *
338    * @see de.uni_leipzig.wifa.iwi.mr3.dao.ModelRepositoryDao#modelExists(java.lang.String)
339    * @param nsUri
340    *          a namespace uri
341    * @return true, if the given namespace uri exists
342    */
343   @Override
344   public boolean modelExists(final String nsUri)
345   {
346     LOG.debug("DAO modelExists()");
347 
348     final Transaction tx = neo.beginTx();
349     try
350     {
351       // search in models (also m1)
352       for (final Node modelNode : helper.getSubrefNodeChildren(EcoreRelationshipType.RESOURCES))
353       {
354         if (((String) modelNode.getProperty(Constants.PROPERTY_NS_URI)).equals(nsUri))
355         {
356           return true;
357         }
358         // search in (sub-)packages
359         final Node ePackageNode = helper.determineEcoreClassifierNode(EcorePackage.Literals.EPACKAGE.getName());
360         if (null != ePackageNode)
361         {
362           for (final Node packageNode : ePackageNode.traverse(Order.BREADTH_FIRST, StopEvaluator.DEPTH_ONE,
363               ReturnableEvaluator.ALL_BUT_START_NODE, EcoreRelationshipType.INSTANCE, Direction.OUTGOING))
364           {
365             if (nsUri.equals(packageNode.getProperty(Constants.PROPERTY_NS_URI)))
366             {
367               return true;
368             }
369           }
370         }
371       }
372     }
373     finally
374     {
375       tx.finish();
376     }
377     return false;
378   }
379 
380   /**
381    * Save {@link Resource}.
382    * <p>
383    * The resource will be saved under it's nsURI. Any existing model can not be
384    * overwritten. In case of existence it must be deleted before saved.
385    * <p>
386    *
387    * @param resource
388    *          EMF {@link Resource} to save
389    * @throws MRException
390    *           Application logic exception
391    * @see de.uni_leipzig.wifa.iwi.mr3.dao.ModelRepositoryDao#save(Resource)
392    */
393   @Override
394   public void save(final Resource resource) throws MRException
395   {
396     LOG.debug("DAO save()");
397 
398     final Transaction tx = neo.beginTx();
399     try
400     {
401       // local registry/node cache
402       final Map<EObject, Object> registry = new HashMap<EObject, Object>();
403 
404       /*
405        * Phase (1): While this phase all nodes and its containment relationships
406        * will be generated.
407        */
408       saveProcessorPhaseOne.process(resource, registry);
409 
410       /*
411        * Phase (2): While this phase all relationships to the meta model will be
412        * generated. Those are any meta-, type- and instance relationships.
413        */
414       saveProcessorPhaseTwo.process(resource, registry);
415 
416       tx.success();
417     }
418     catch (final IllegalStateException e)
419     {
420       throw new MRException("Could not save model:" + e.getMessage(), e);
421     }
422     finally
423     {
424       tx.finish();
425     }
426   }
427 
428   /**
429    * Loads the model object with the given namespace uri into the given
430    * resource.
431    * 
432    * @param nsUri
433    *          a namespace uri
434    * @param registry
435    *          the registry for loaded objects
436    * @return the model to load
437    * @see de.uni_leipzig.wifa.iwi.mr3.dao.ModelRepositoryDao#load(java.lang.String)
438    */
439   @Override
440   public EObject load(final String nsUri, final Map<Object, EObject> registry)
441   {
442     LOG.debug("DAO load(String)");
443     final ResourceSet rset = new ResourceSetImpl();
444     rset.getResourceFactoryRegistry().getContentTypeToFactoryMap().put(DEFAULT_CONTENT_TYPE, RESOURCE_FACTORY);
445 
446     final Transaction tx = neo.beginTx();
447     try
448     {
449       final Node modelNode = helper.getModelNode(nsUri);
450       if (null == modelNode)
451       {
452         return findTopLevelPackage(nsUri, registry);
453       }
454       else
455       {
456         for (final Node referenced : modelNode.traverse(Order.BREADTH_FIRST, StopEvaluator.END_OF_GRAPH,
457             ReturnableEvaluator.ALL_BUT_START_NODE, EcoreRelationshipType.DEPENDS, Direction.OUTGOING,
458             EcoreRelationshipType.INSTANCE_MODEL, Direction.INCOMING))
459         {
460           final String uri = (String) referenced.getProperty(Constants.PROPERTY_NS_URI);
461           final XMIResource resource = (XMIResource) rset.createResource(URI.createURI(uri));
462           resource.getContents().add(load(uri, registry));
463         }
464         return loadProcessor.process(modelNode, registry);
465       }
466     }
467     finally
468     {
469       tx.finish();
470     }
471   }
472 
473   /**
474    * Finds an instance of EPackage with given nsUri and returns its top level
475    * package.
476    *
477    * @param nsUri
478    *          the namespace uri
479    * @param registry
480    *          the registry of the load process
481    * @return the top level package
482    */
483   private EPackage findTopLevelPackage(final String nsUri, final Map<Object, EObject> registry)
484   {
485     final Node ePackageNode = helper.determineEcoreClassifierNode(EcorePackage.Literals.EPACKAGE.getName());
486     for (final Node pkgNode : ePackageNode.traverse(Order.BREADTH_FIRST, StopEvaluator.DEPTH_ONE,
487         ReturnableEvaluator.ALL_BUT_START_NODE, EcoreRelationshipType.INSTANCE, Direction.OUTGOING))
488     {
489       if (nsUri.equals(pkgNode.getProperty(Constants.PROPERTY_NS_URI)))
490       {
491         Node container = pkgNode;
492         while (container.hasRelationship(EcoreRelationshipType.CONTAINS, Direction.INCOMING))
493         {
494           container = container.getSingleRelationship(EcoreRelationshipType.CONTAINS, Direction.INCOMING).getStartNode();
495         }
496         return (EPackage) load((String) container.getProperty(Constants.PROPERTY_NS_URI), registry);
497       }
498     }
499     return null;
500   }
501 
502   /**
503    * Deletes the model with the given namespace uri.
504    *
505    * @see de.uni_leipzig.wifa.iwi.mr3.dao.ModelRepositoryDao#delete(String,
506    *      boolean)
507    * @param nsUri
508    *          a namespace uri
509    * @param cascading
510    *          whether cascading delete model instances of current model.
511    * @throws MRException
512    *           Application logic exception
513    */
514   @Override
515   public void delete(final String nsUri, final boolean cascading) throws MRException
516   {
517     LOG.debug("DAO delete()");
518 
519     final Transaction tx = neo.beginTx();
520     try
521     {
522       for (final Node modelNode : helper.getSubrefNodeChildren(EcoreRelationshipType.RESOURCES))
523       {
524         if (nsUri.equals(modelNode.getProperty(Constants.PROPERTY_NS_URI)))
525         {
526           deleteContent(modelNode, cascading);
527           tx.success();
528           return;
529         }
530       }
531 
532       throw new MRException("Could not find model with nsUri:" + nsUri);
533     }
534     finally
535     {
536       tx.finish();
537     }
538   }
539 
540   /**
541    * Deletes the given node and its content recursively.
542    *
543    * @param modelNode
544    *          the node to delete
545    * @param cascading
546    *          whether cascading delete model instances of current model.
547    * @throws MRException
548    *           Application logic exception
549    */
550   private void deleteContent(final Node modelNode, final boolean cascading) throws MRException
551   {
552     // All nodes this node directly and indirectly contains
553     final Iterable<Node> contents =
554         modelNode.traverse(Order.DEPTH_FIRST, StopEvaluator.END_OF_GRAPH, ReturnableEvaluator.ALL,
555             EcoreRelationshipType.CONTAINS, Direction.OUTGOING);
556 
557     if (modelNode.hasRelationship(EcoreRelationshipType.DEPENDS, Direction.INCOMING))
558     {
559       final StringBuilder message = new StringBuilder("Could not delete model. Please first delete models that depend on it: ");
560       final List<Node> dependentModels = new ArrayList<Node>(modelNode.traverse(Order.BREADTH_FIRST, StopEvaluator.DEPTH_ONE,
561           ReturnableEvaluator.ALL_BUT_START_NODE, EcoreRelationshipType.DEPENDS, Direction.INCOMING).getAllNodes());
562       for (int i = 0; i < dependentModels.size(); i++)
563       {
564          message.append((String) dependentModels.get(i).getProperty(Constants.PROPERTY_NS_URI))
565             .append(
566             i < dependentModels.size() - 1 ? ", " : ".");
567       }
568       throw new MRException(message.toString());
569     }
570     if (modelNode.hasRelationship(EcoreRelationshipType.INSTANCE_MODEL, Direction.OUTGOING))
571     {
572       if (cascading)
573       {
574         for (final Relationship relationship : modelNode.getRelationships(EcoreRelationshipType.INSTANCE_MODEL,
575             Direction.OUTGOING))
576         {
577           deleteContent(relationship.getEndNode(), cascading);
578         }
579         // if this model has instance models and no cascading delete is allowed,
580         // reject proceeding of delete
581       }
582       else
583       {
584         throw new MRException("You have to delete instance models of this model first!");
585       }
586     }
587 
588     // delete this model and all of its contents
589     for (final Node node : contents)
590     {
591       if (node.hasRelationship())
592       {
593         for (final Relationship relationship : node.getRelationships())
594         {
595           relationship.delete();
596         }
597       }
598       node.delete();
599     }
600   }
601 
602   /**
603    * Finds all instances of given classifiers that contain given expression.
604    *
605    * @param expression
606    *          the expression to search for
607    * @param classifiers
608    *          the classifiers to search in
609    * @param isRegEx
610    *          RegExp search
611    * @param isCaseSensitive
612    *          Case sensitive search
613    * @return the array of found models matching the given expression
614    * @see de.uni_leipzig.wifa.iwi.mr3.dao.ModelRepositoryDao#find(java.lang.String,
615    *      java.lang.String[])
616    */
617   @Override
618   public Match[] find(final String expression, final String[] classifiers, final boolean isRegEx, final boolean isCaseSensitive)
619   {
620     LOG.debug("DAO find()");
621 
622     Arrays.sort(classifiers);
623 
624     final int eObjectPos = Arrays.binarySearch(classifiers, EcorePackage.Literals.EOBJECT.getName());
625     final boolean containsEObject = eObjectPos >= 0;
626     final Transaction tx = neo.beginTx();
627     try
628     {
629       if (containsEObject)
630       {
631         return searchProcessor.processCompleteSearch(expression, classifiers, isCaseSensitive, isRegEx);
632       }
633       return searchProcessor.processPartialSearch(expression, classifiers, isCaseSensitive, isRegEx);
634     }
635     finally
636     {
637       tx.finish();
638     }
639   }
640 }