1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
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
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
306 }
307 }
308
309 }