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.restapi.v2;
20  
21  import java.net.URI;
22  import java.net.URISyntaxException;
23  import java.nio.ByteBuffer;
24  import java.nio.file.Path;
25  import java.nio.file.attribute.BasicFileAttributes;
26  import java.time.Instant;
27  import java.util.Base64;
28  import java.util.Date;
29  import java.util.Locale;
30  import java.util.Optional;
31  import java.util.concurrent.TimeUnit;
32  import java.util.function.Function;
33  
34  import org.apache.logging.log4j.LogManager;
35  import org.mycore.common.MCRException;
36  import org.mycore.common.MCRSessionMgr;
37  import org.mycore.common.events.MCREvent;
38  import org.mycore.common.events.MCREventHandlerBase;
39  import org.mycore.datamodel.metadata.MCRBase;
40  import org.mycore.datamodel.metadata.MCRDerivate;
41  import org.mycore.datamodel.metadata.MCRMetadataManager;
42  import org.mycore.datamodel.metadata.MCRObject;
43  import org.mycore.datamodel.metadata.MCRObjectID;
44  import org.mycore.datamodel.niofs.MCRFileAttributes;
45  import org.mycore.datamodel.niofs.MCRPath;
46  
47  import com.google.gson.JsonArray;
48  import com.google.gson.JsonObject;
49  import com.google.gson.JsonParser;
50  
51  import jakarta.servlet.ServletContext;
52  import jakarta.ws.rs.core.MediaType;
53  import jakarta.ws.rs.sse.OutboundSseEvent;
54  import jakarta.ws.rs.sse.Sse;
55  import jakarta.ws.rs.sse.SseBroadcaster;
56  
57  class MCREventHandler {
58  
59      private static String getName(MCREvent evt) {
60          return evt.getObjectType() + "." + evt.getEventType();
61      }
62  
63      private static URI getPathURI(String path) {
64          try {
65              return new URI(null, null, path, null);
66          } catch (URISyntaxException e) {
67              throw new MCRException(e);
68          }
69      }
70  
71      private static String getId(MCREvent evt) {
72          ByteBuffer byteBuffer = ByteBuffer.allocate(Long.BYTES + Integer.BYTES);
73          byte[] bytes = byteBuffer.putLong(System.currentTimeMillis())
74              .putInt(evt.hashCode())
75              .array();
76          Base64.Encoder b64 = Base64.getEncoder();
77          return b64.encodeToString(bytes);
78      }
79  
80      private static void addUserInfo(JsonObject jEvent) {
81          if (!MCRSessionMgr.hasCurrentSession()) {
82              return;
83          }
84          String userID = MCRSessionMgr.getCurrentSession().getUserInformation().getUserID();
85          jEvent.addProperty("user", userID);
86      }
87  
88      private static void copyServiceDateToProperty(MCRBase obj, JsonObject jsonObj, String dateType,
89          String propertyName) {
90          Optional.ofNullable(obj.getService().getDate(dateType))
91              .map(Date::toInstant)
92              .map(Instant::toString)
93              .ifPresent(d -> jsonObj.addProperty(propertyName, d));
94      }
95  
96      private static void copyFlagToProperty(MCRBase obj, JsonObject json, String flagName, String propertyName) {
97          obj.getService()
98              .getFlags(flagName)
99              .stream()
100             .findFirst()
101             .ifPresent(c -> {
102                 json.addProperty(propertyName, c);
103             });
104     }
105 
106     public static class MCRObjectHandler implements org.mycore.common.events.MCREventHandler {
107         private final SseBroadcaster sseBroadcaster;
108 
109         private final Sse sse;
110 
111         private final Function<URI, URI> uriResolver;
112 
113         MCRObjectHandler(SseBroadcaster sseBroadcaster, Sse sse,
114             Function<URI, URI> uriResolver) {
115             this.sseBroadcaster = sseBroadcaster;
116             this.sse = sse;
117             this.uriResolver = uriResolver;
118         }
119 
120         @Override
121         public void doHandleEvent(MCREvent evt) throws MCRException {
122             if (evt.getObjectType() != MCREvent.ObjectType.OBJECT) {
123                 return;
124             }
125             MCRObject obj = (MCRObject) evt.get(MCREvent.OBJECT_KEY);
126             JsonObject jEvent = new JsonObject();
127             JsonObject newData = getData(obj);
128             addUserInfo(jEvent);
129             jEvent.add("current", newData);
130             MCRObject oldObj = (MCRObject) evt.get(MCREvent.OBJECT_OLD_KEY);
131             if (oldObj != null) {
132                 JsonObject oldData = getData(oldObj);
133                 jEvent.add("old", oldData);
134             }
135             OutboundSseEvent event = sse.newEventBuilder()
136                 .mediaType(MediaType.APPLICATION_JSON_TYPE)
137                 .id(getId(evt))
138                 .name(getName(evt))
139                 .data(jEvent.toString())
140                 .build();
141             sseBroadcaster.broadcast(event);
142         }
143 
144         private JsonObject getData(MCRObject obj) {
145             JsonObject event = new JsonObject();
146             event.addProperty("id", obj.getId().toString());
147             event.addProperty("uri", uriResolver.apply(getPathURI("objects/" + obj.getId())).toString());
148             Optional.ofNullable(obj.getService().getState())
149                 .ifPresent(s -> event.addProperty("state", s.getID()));
150             copyFlagToProperty(obj, event, "createdby", "createdBy");
151             copyServiceDateToProperty(obj, event, "createdate", "created");
152             copyFlagToProperty(obj, event, "modifiedby", "modifiedBy");
153             copyServiceDateToProperty(obj, event, "modifydate", "modified");
154             JsonArray pi = new JsonArray();
155             obj.getService().getFlags("MyCoRe-PI").stream()
156                 .map(JsonParser::parseString)
157                 .forEach(pi::add);
158             event.add("pi", pi);
159             return event;
160         }
161 
162         @Override
163         public void undoHandleEvent(MCREvent evt) throws MCRException {
164             //do nothing
165         }
166     }
167 
168     public static class MCRDerivateHandler implements org.mycore.common.events.MCREventHandler {
169         private final SseBroadcaster sseBroadcaster;
170 
171         private final Sse sse;
172 
173         private final Function<URI, URI> uriResolver;
174 
175         MCRDerivateHandler(SseBroadcaster sseBroadcaster, Sse sse, Function<URI, URI> uriResolver) {
176             this.sseBroadcaster = sseBroadcaster;
177             this.sse = sse;
178             this.uriResolver = uriResolver;
179         }
180 
181         @Override
182         public void doHandleEvent(MCREvent evt) throws MCRException {
183             if (evt.getObjectType() != MCREvent.ObjectType.DERIVATE) {
184                 return;
185             }
186             MCRDerivate der = (MCRDerivate) evt.get(MCREvent.DERIVATE_KEY);
187             JsonObject jEvent = new JsonObject();
188             addUserInfo(jEvent);
189             JsonObject newData = getData(der);
190             jEvent.add("current", newData);
191             MCRDerivate oldDer = (MCRDerivate) evt.get(MCREvent.DERIVATE_OLD_KEY);
192             if (oldDer != null) {
193                 JsonObject oldData = getData(oldDer);
194                 jEvent.add("old", oldData);
195             }
196             OutboundSseEvent event = sse.newEventBuilder()
197                 .mediaType(MediaType.APPLICATION_JSON_TYPE)
198                 .id(getId(evt))
199                 .name(getName(evt))
200                 .data(jEvent.toString())
201                 .build();
202             sseBroadcaster.broadcast(event);
203         }
204 
205         private JsonObject getData(MCRDerivate der) {
206             JsonObject event = new JsonObject();
207             event.addProperty("id", der.getId().toString());
208             event.addProperty("uri",
209                 uriResolver.apply(getPathURI("objects/" + der.getOwnerID())) + "/derivates/" + der.getId());
210             event.addProperty("object", der.getOwnerID().toString());
211             event.addProperty("objectUri", uriResolver.apply(getPathURI("objects/" + der.getOwnerID())).toString());
212             copyFlagToProperty(der, event, "createdby", "createdBy");
213             copyServiceDateToProperty(der, event, "createdate", "created");
214             copyFlagToProperty(der, event, "modifiedby", "modifiedBy");
215             copyServiceDateToProperty(der, event, "modifydate", "modified");
216             return event;
217         }
218 
219         @Override
220         public void undoHandleEvent(MCREvent evt) throws MCRException {
221             //do nothing
222         }
223     }
224 
225     public static class MCRPathHandler extends MCREventHandlerBase {
226         private final SseBroadcaster sseBroadcaster;
227 
228         private final Sse sse;
229 
230         private final ServletContext context;
231 
232         private final Function<URI, URI> uriResolver;
233 
234         MCRPathHandler(SseBroadcaster sseBroadcaster, Sse sse, Function<URI, URI> uriResolver, ServletContext context) {
235             this.sseBroadcaster = sseBroadcaster;
236             this.sse = sse;
237             this.context = context;
238             this.uriResolver = uriResolver;
239         }
240 
241         @Override
242         public void doHandleEvent(MCREvent evt) throws MCRException {
243             if (evt.getObjectType() != MCREvent.ObjectType.PATH) {
244                 return;
245             }
246             super.doHandleEvent(evt);
247         }
248 
249         @Override
250         protected void handlePathUpdated(MCREvent evt, Path path, BasicFileAttributes attrs) {
251             sendEvent(evt, path, attrs);
252         }
253 
254         @Override
255         protected void handlePathDeleted(MCREvent evt, Path path, BasicFileAttributes attrs) {
256             sendEvent(evt, path, attrs);
257         }
258 
259         @Override
260         protected void handlePathRepaired(MCREvent evt, Path path, BasicFileAttributes attrs) {
261             sendEvent(evt, path, attrs);
262         }
263 
264         @Override
265         protected void updatePathIndex(MCREvent evt, Path path, BasicFileAttributes attrs) {
266             sendEvent(evt, path, attrs);
267         }
268 
269         @Override
270         protected void handlePathCreated(MCREvent evt, Path path, BasicFileAttributes attrs) {
271             sendEvent(evt, path, attrs);
272         }
273 
274         public void sendEvent(MCREvent evt, Path path, BasicFileAttributes attrs) {
275             if (!(path instanceof MCRPath) || !(attrs instanceof MCRFileAttributes)) {
276                 LogManager.getLogger().warn("Cannot handle {} {}", path.getClass(), attrs.getClass());
277                 return;
278             }
279             JsonObject file = new JsonObject();
280             addUserInfo(file);
281             String derId = ((MCRPath) path).getOwner();
282             String fPath = ((MCRPath) path).getOwnerRelativePath();
283             String objId = MCRMetadataManager.getObjectId(MCRObjectID.getInstance(derId), 1, TimeUnit.MINUTES)
284                 .toString();
285             String relPath = String.format(Locale.ROOT, "objects/%s/derivates/%s/contents/%s", objId, derId, fPath);
286             String uri = uriResolver.apply(getPathURI(relPath)).toString();
287             file.addProperty("uri", uri);
288             file.addProperty("derivate", derId);
289             file.addProperty("object", objId);
290             file.addProperty("size", attrs.size());
291             file.addProperty("modified", attrs.lastModifiedTime().toInstant().toString());
292             file.addProperty("md5", ((MCRFileAttributes) attrs).md5sum());
293             file.addProperty("mimeType", context.getMimeType(path.getFileName().toString()));
294             OutboundSseEvent event = sse.newEventBuilder()
295                 .mediaType(MediaType.APPLICATION_JSON_TYPE)
296                 .id(getId(evt))
297                 .name(getName(evt))
298                 .data(file.toString())
299                 .build();
300             sseBroadcaster.broadcast(event);
301         }
302 
303         @Override
304         public void undoHandleEvent(MCREvent evt) throws MCRException {
305             //do nothing
306         }
307     }
308 
309 }