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.pi.handle;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.Base64;
24  import java.util.Date;
25  import java.util.HashMap;
26  import java.util.Map;
27  import java.util.Optional;
28  import java.util.concurrent.ConcurrentHashMap;
29  import java.util.concurrent.locks.ReentrantLock;
30  import java.util.function.Predicate;
31  
32  import org.jdom2.Document;
33  import org.mycore.backend.jpa.MCREntityManagerProvider;
34  import org.mycore.common.config.annotation.MCRProperty;
35  import org.mycore.common.content.MCRContent;
36  import org.mycore.common.content.MCRJDOMContent;
37  import org.mycore.common.content.transformer.MCRContentTransformer;
38  import org.mycore.common.content.transformer.MCRContentTransformerFactory;
39  import org.mycore.datamodel.metadata.MCRBase;
40  import org.mycore.datamodel.metadata.MCRMetadataManager;
41  import org.mycore.datamodel.metadata.MCRObjectID;
42  import org.mycore.frontend.MCRFrontendUtil;
43  import org.mycore.pi.MCRPIJobService;
44  import org.mycore.pi.backend.MCRPI;
45  import org.mycore.pi.exceptions.MCRPersistentIdentifierException;
46  
47  public class MCREpicService extends MCRPIJobService<MCRHandle> {
48  
49      public static final String EPIC_KEY = "EPIC";
50  
51      public static final String OBJECT_ID_KEY = "ObjectID";
52  
53      /**
54       * The Username which will be used by the epic client
55       */
56      @MCRProperty(name = "Username")
57      public String username;
58  
59      /**
60       * The password which will be used by the epic client
61       */
62      @MCRProperty(name = "Password")
63      public String password;
64  
65      /**
66       * The url to the actual epic api endpoint e.g. https://epic.grnet.gr/api/v2/, http://pid.gwdg.de/
67       */
68      @MCRProperty(name = "Endpoint")
69      public String endpoint;
70  
71      /**
72       * This is a alternative to mcr.baseurl mostly for testing purposes
73       */
74      @MCRProperty(name = "BaseURL", required = false)
75      public String baseURL;
76  
77      /**
78       * This can be used to store metadata as a Handle Object. The Transformer will be used to convert the Object to an
79       * String.
80       */
81      @MCRProperty(name = "Transformer", required = false)
82      public String transformerID = null;
83  
84      /**
85       * The Type which should be used in the Handle Object.
86       */
87      @MCRProperty(name = "MetadataType", required = false)
88      public String metadataType = null;
89  
90      /**
91       * The Index which should be used in the handle object.
92       */
93      @MCRProperty(name = "MetadataIndex", required = false)
94      public String metadataIndex = null;
95  
96      public ConcurrentHashMap<String, ReentrantLock> idLockMap = new ConcurrentHashMap<>();
97  
98      public MCREpicService() {
99          super("handle");
100     }
101 
102     @Override
103     public MCRPI insertIdentifierToDatabase(MCRBase obj, String additional,
104         MCRHandle identifier) {
105         Date registrationStarted = null;
106         if (getRegistrationPredicate().test(obj)) {
107             registrationStarted = new Date();
108             startRegisterJob(obj, identifier);
109         }
110 
111         MCRPI databaseEntry = new MCRPI(identifier.asString(), getType(), obj.getId().toString(), additional,
112             this.getServiceID(), provideRegisterDate(obj, additional), registrationStarted);
113         MCREntityManagerProvider.getCurrentEntityManager().persist(databaseEntry);
114         return databaseEntry;
115     }
116 
117     @Override
118     protected void registerIdentifier(MCRBase obj, String additional, MCRHandle pi)
119         throws MCRPersistentIdentifierException {
120         if (!"".equals(additional)) {
121             String className = this.getClass().getName();
122             throw new MCRPersistentIdentifierException(
123                 className + " doesn't support additional information! (" + additional + ")");
124         }
125     }
126 
127     @Override
128     protected void delete(MCRHandle identifier, MCRBase obj, String additional)
129         throws MCRPersistentIdentifierException {
130         this.startDeleteJob(obj, identifier);
131     }
132 
133     @Override
134     protected void update(MCRHandle identifier, MCRBase obj, String additional)
135         throws MCRPersistentIdentifierException {
136         if (!this.hasRegistrationStarted(obj.getId(), additional)) {
137             Predicate<MCRBase> registrationCondition = this.getRegistrationPredicate();
138             if (registrationCondition.test(obj)) {
139                 this.updateStartRegistrationDate(obj.getId(), "", new Date());
140                 this.startRegisterJob(obj, identifier);
141             }
142         } else if (this.isRegistered(obj.getId(), "")) {
143             this.startUpdateJob(obj, identifier);
144         }
145     }
146 
147     @Override
148     protected void deleteJob(Map<String, String> parameters) throws MCRPersistentIdentifierException {
149         String epic = parameters.get(EPIC_KEY);
150 
151         try {
152             getClient().delete(new MCRHandle(epic));
153         } catch (IOException e) {
154             throw new MCRPersistentIdentifierException("Error while communicating with epic service", e);
155         }
156     }
157 
158     @Override
159     protected void updateJob(Map<String, String> parameters) throws MCRPersistentIdentifierException {
160         String epic = parameters.get(EPIC_KEY);
161         String objId = parameters.get(OBJECT_ID_KEY);
162 
163         createOrUpdate(epic, objId);
164     }
165 
166     private void createOrUpdate(String epic, String objId) throws MCRPersistentIdentifierException {
167         new ReentrantLock();
168 
169         final MCRObjectID objectID = MCRObjectID.getInstance(objId);
170         if (!MCRMetadataManager.exists(objectID)) {
171             return;
172         }
173 
174         validateJobUserRights(objectID);
175 
176         final MCRHandle mcrHandle = new MCRHandle(epic);
177         final String urlForObject = getURLForObject(objId);
178 
179         try {
180             final ArrayList<MCRHandleInfo> handleInfos = new ArrayList<>();
181             processMedataData(objectID, handleInfos);
182 
183             ReentrantLock reentrantLock = idLockMap.computeIfAbsent(epic, (l) -> new ReentrantLock());
184             try {
185                 reentrantLock.lock();
186                 getClient().create(urlForObject, mcrHandle, handleInfos);
187             } finally {
188                 reentrantLock.unlock();
189             }
190         } catch (IOException e) {
191             throw new MCRPersistentIdentifierException("Error while communicating with EPIC Service", e);
192         }
193 
194     }
195 
196     private void processMedataData(MCRObjectID objectID, ArrayList<MCRHandleInfo> handleInfos) throws IOException {
197         if (transformerID != null && metadataType != null && metadataIndex != null) {
198             final int index = Integer.parseInt(metadataIndex, 10);
199             final Document xml = MCRMetadataManager.retrieve(objectID).createXML();
200             final MCRContentTransformer transformer = MCRContentTransformerFactory.getTransformer(transformerID);
201             final MCRContent result = transformer.transform(new MCRJDOMContent(xml));
202             final byte[] bytes = result.asByteArray();
203             final String encodedData = Base64.getEncoder().encodeToString(bytes);
204 
205             final MCRHandleInfo metadataInfo = new MCRHandleInfo();
206             metadataInfo.setIdx(index);
207             metadataInfo.setData(encodedData);
208             metadataInfo.setType(metadataType);
209             handleInfos.add(metadataInfo);
210         }
211     }
212 
213     protected String getURLForObject(String objectId) {
214         String baseURL = this.baseURL != null ? this.baseURL : MCRFrontendUtil.getBaseURL();
215         return baseURL + "receive/" + objectId;
216 
217     }
218 
219     @Override
220     protected void registerJob(Map<String, String> parameters) throws MCRPersistentIdentifierException {
221         String epic = parameters.get(EPIC_KEY);
222         String objId = parameters.get(OBJECT_ID_KEY);
223 
224         createOrUpdate(epic, objId);
225     }
226 
227     @Override
228     protected Optional<String> getJobInformation(Map<String, String> contextParameters) {
229         return Optional.empty();
230     }
231 
232     private void startUpdateJob(MCRBase obj, MCRHandle epic) {
233         HashMap<String, String> contextParameters = new HashMap<String, String>();
234         contextParameters.put(EPIC_KEY, epic.asString());
235         contextParameters.put(OBJECT_ID_KEY, obj.getId().toString());
236         this.addUpdateJob(contextParameters);
237     }
238 
239     private void startRegisterJob(MCRBase obj, MCRHandle epic) {
240         HashMap<String, String> contextParameters = new HashMap<String, String>();
241         contextParameters.put(EPIC_KEY, epic.asString());
242         contextParameters.put(OBJECT_ID_KEY, obj.getId().toString());
243         this.addRegisterJob(contextParameters);
244     }
245 
246     private void startDeleteJob(MCRBase obj, MCRHandle epic) {
247         HashMap<String, String> contextParameters = new HashMap<String, String>();
248         contextParameters.put(EPIC_KEY, epic.asString());
249         contextParameters.put(OBJECT_ID_KEY, obj.getId().toString());
250         this.addDeleteJob(contextParameters);
251     }
252 
253     private MCREpicClient getClient() {
254         return new MCREpicClient(username, password, endpoint);
255     }
256 
257 }