View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.datamodel.common;
20  
21  import java.util.Map;
22  import java.util.concurrent.ConcurrentHashMap;
23  
24  import org.mycore.common.events.MCREventHandler;
25  import org.mycore.datamodel.metadata.MCRBase;
26  import org.mycore.datamodel.metadata.MCRDerivate;
27  import org.mycore.datamodel.metadata.MCRObjectID;
28  
29  /**
30   * Experimental class to improve performance on delete and import operations.
31   * You can mark object's as "will be deleted" or "will be imported". You can
32   * use this information on {@link MCREventHandler}'s to exclude those
33   * marked objects from operations which makes no sense.
34   *
35   * <h2>
36   * Current delete behavior:
37   * </h2>
38   * <ol>
39   *   <li>An user delete's a parent object with 500 children.</li>
40   *   <li>MyCoRe tries to delete the parent, but first, it has to delete all children.</li>
41   *   <li>MyCoRe runs through each child and deletes it.</li>
42   *   <li><b>BUT</b> after the deletion of <b>ONE</b> child, the parent object will be updated.</li>
43   *   <li>This results in updating the parent 500 times, before its actually deleted.</li>
44   * </ol>
45   *
46   * <p>
47   * What this class tries to solve:<br>
48   * We mark the parent as "will be deleted". When a child is deleted, and the EventHandler tries
49   * to removed the child from its parent, the parent will not be updated because its "marked as
50   * deleted".
51   * </p>
52   *
53   * <h2>
54   * Current import behavior:
55   * </h2>
56   *
57   * <ol>
58   *   <li>An import is started with a bunch of hierarchic objects.</li>
59   *   <li>MyCoRe imports all the objects and does a "create" update.</li>
60   *   <li><b>BUT</b> for each object created the parent is updated again (because a child was added)!</li>
61   *   <li>This results in unnecessary updates.</li>
62   * </ol>
63   *
64   * <p>
65   * What this class tries to solve:<br>
66   * We mark all objects as "will be imported". On import, we ignore all solr index call for
67   * those objects. After the import, we delete all marks and do an solr import for all
68   * objects at once.
69   * </p>
70   *
71   * TODO: check side effects
72   *
73   * @author Matthias Eichner
74   */
75  public class MCRMarkManager {
76  
77      private static MCRMarkManager INSTANCE = null;
78  
79      public enum Operation {
80          DELETE, IMPORT
81      }
82  
83      private Map<MCRObjectID, Operation> marks;
84  
85      private MCRMarkManager() {
86          this.marks = new ConcurrentHashMap<>();
87      }
88  
89      /**
90       * Returns the instance to the singleton {@link MCRMarkManager}.
91       *
92       * @return instance of {@link MCRMarkManager}
93       */
94      public static MCRMarkManager instance() {
95          if (INSTANCE == null) {
96              // make it thread safe
97              synchronized (MCRMarkManager.class) {
98                  if (INSTANCE == null) {
99                      INSTANCE = new MCRMarkManager();
100                 }
101             }
102         }
103         return INSTANCE;
104     }
105 
106     /**
107      * Marks a single object with the given operation.
108      *
109      * @param mcrId the mycore object identifier
110      * @param operation the operation
111      * @return the previous Operation associated with the mycore identifier or null
112      */
113     public Operation mark(MCRObjectID mcrId, Operation operation) {
114         return this.marks.put(mcrId, operation);
115     }
116 
117     /**
118      * Removes the current mark for the given mycore identifier.
119      *
120      * @param mcrId the object where the mark should be removed
121      */
122     public void remove(MCRObjectID mcrId) {
123         this.marks.remove(mcrId);
124     }
125 
126     /**
127      * Checks if the object is marked.
128      *
129      * @param mcrId the mcr identifier
130      * @return true if its marked
131      */
132     public boolean isMarked(MCRObjectID mcrId) {
133         return this.marks.containsKey(mcrId);
134     }
135 
136     /**
137      * Checks if the given base object is marked. If base is an instance of MCRDerivate, this method checks also if the
138      * linked object is marked.
139      *
140      * @param base the mycore object
141      * @return true if its marked
142      */
143     public boolean isMarked(MCRBase base) {
144         if (base instanceof MCRDerivate) {
145             MCRDerivate derivate = (MCRDerivate) base;
146             return isMarked(derivate.getId()) || isMarked(derivate.getOwnerID());
147         }
148         return isMarked(base.getId());
149     }
150 
151     /**
152      * Checks if the object is marked for deletion.
153      *
154      * @param mcrId the mcr identifier
155      * @return true if its marked for deletion
156      */
157     public boolean isMarkedForDeletion(MCRObjectID mcrId) {
158         return Operation.DELETE.equals(this.marks.get(mcrId));
159     }
160 
161     /**
162      * Checks if the derivate or the corresponding mycore object is
163      * marked for deletion.
164      *
165      * @return true if one of them is marked for deletion
166      */
167     public boolean isMarkedForDeletion(MCRDerivate derivate) {
168         return isMarkedForDeletion(derivate.getId()) || isMarkedForDeletion(derivate.getOwnerID());
169     }
170 
171     /**
172      * Checks if the object is marked for import.
173      *
174      * @param mcrId the mcr identifier
175      * @return true if its marked for import
176      */
177     public boolean isMarkedForImport(MCRObjectID mcrId) {
178         return Operation.IMPORT.equals(this.marks.get(mcrId));
179     }
180 
181 }