From: jsimomaa Date: Fri, 15 Dec 2017 12:21:43 +0000 (+0200) Subject: Generic HTTP REST Client/Server AuditLogging framework X-Git-Tag: v1.43.0~136^2~648 X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=commitdiff_plain;h=da4210cb095e4acd25ddba55a86aa6fe0b18301d Generic HTTP REST Client/Server AuditLogging framework refs #7684 Change-Id: Ia861758f0e23a4bc55edeffd946030fff4e52121 --- diff --git a/bundles/org.simantics.auditlogging/.classpath b/bundles/org.simantics.auditlogging/.classpath new file mode 100644 index 000000000..eca7bdba8 --- /dev/null +++ b/bundles/org.simantics.auditlogging/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.simantics.auditlogging/.project b/bundles/org.simantics.auditlogging/.project new file mode 100644 index 000000000..058132d96 --- /dev/null +++ b/bundles/org.simantics.auditlogging/.project @@ -0,0 +1,28 @@ + + + org.simantics.auditlogging + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/bundles/org.simantics.auditlogging/.settings/org.eclipse.jdt.core.prefs b/bundles/org.simantics.auditlogging/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..0c68a61dc --- /dev/null +++ b/bundles/org.simantics.auditlogging/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/bundles/org.simantics.auditlogging/META-INF/MANIFEST.MF b/bundles/org.simantics.auditlogging/META-INF/MANIFEST.MF new file mode 100644 index 000000000..61adfd1f8 --- /dev/null +++ b/bundles/org.simantics.auditlogging/META-INF/MANIFEST.MF @@ -0,0 +1,22 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Simantics Audit Logging +Bundle-SymbolicName: org.simantics.auditlogging +Bundle-Version: 1.0.0.qualifier +Bundle-Activator: org.simantics.audit.Activator +Require-Bundle: org.eclipse.core.runtime, + com.fasterxml.jackson.core.jackson-databind, + com.fasterxml.jackson.core.jackson-core, + org.slf4j.api, + javax.servlet-api, + javax.ws.rs-api, + org.eclipse.jetty.server, + org.eclipse.jetty.servlet, + org.eclipse.jetty.util, + org.glassfish.jersey.core.jersey-server, + org.glassfish.jersey.media.jersey-media-json-jackson, + org.glassfish.jersey.containers.jersey-container-servlet-core, + org.glassfish.jersey.core.jersey-client, + org.glassfish.jersey.core.jersey-common +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ActivationPolicy: lazy diff --git a/bundles/org.simantics.auditlogging/build.properties b/bundles/org.simantics.auditlogging/build.properties new file mode 100644 index 000000000..a4fd10d42 --- /dev/null +++ b/bundles/org.simantics.auditlogging/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + scl/ diff --git a/bundles/org.simantics.auditlogging/scl/Simantics/AuditLogging/Client.scl b/bundles/org.simantics.auditlogging/scl/Simantics/AuditLogging/Client.scl new file mode 100644 index 000000000..f60474aa4 --- /dev/null +++ b/bundles/org.simantics.auditlogging/scl/Simantics/AuditLogging/Client.scl @@ -0,0 +1,15 @@ +import "Map" as Map + +importJava "org.simantics.audit.client.AuditLoggingClient" where + @JavaName sendLog + sendLogM :: Map.T String a -> () + sendLog :: [a] -> () + + @JavaName sendError + sendErrorM :: Map.T String a -> () + sendError :: [a] -> () + + @JavaName sendTrace + sendTraceM :: Map.T String a -> () + sendTrace :: [a] -> () + \ No newline at end of file diff --git a/bundles/org.simantics.auditlogging/scl/Simantics/AuditLogging/Server.scl b/bundles/org.simantics.auditlogging/scl/Simantics/AuditLogging/Server.scl new file mode 100644 index 000000000..396f5f561 --- /dev/null +++ b/bundles/org.simantics.auditlogging/scl/Simantics/AuditLogging/Server.scl @@ -0,0 +1,11 @@ +import "Map" as Map + +importJava "org.simantics.audit.AuditLogging" where + register :: String -> String + log :: String -> Map.T String a -> () + error :: String -> Map.T String a -> () + trace :: String -> Map.T String a -> () + getLogEvents :: String -> String -> String -> String -> [String] + +importJava "org.simantics.audit.server.AuditLoggingServer" where + start :: String -> Integer -> () \ No newline at end of file diff --git a/bundles/org.simantics.auditlogging/src/org/simantics/audit/Activator.java b/bundles/org.simantics.auditlogging/src/org/simantics/audit/Activator.java new file mode 100644 index 000000000..7451ffd76 --- /dev/null +++ b/bundles/org.simantics.auditlogging/src/org/simantics/audit/Activator.java @@ -0,0 +1,42 @@ +package org.simantics.audit; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Platform; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +public class Activator implements BundleActivator { + + private static BundleContext context; + private static Path logLocation; + + static BundleContext getContext() { + return context; + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext bundleContext) throws Exception { + Activator.context = bundleContext; + IPath ipath = Platform.getStateLocation(getContext().getBundle()); + logLocation = Paths.get(ipath.toOSString()); + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext bundleContext) throws Exception { + Activator.context = null; + } + + public static Path getLogLocation() { + return logLocation; + } + +} diff --git a/bundles/org.simantics.auditlogging/src/org/simantics/audit/AuditLogging.java b/bundles/org.simantics.auditlogging/src/org/simantics/audit/AuditLogging.java new file mode 100644 index 000000000..cbbf14209 --- /dev/null +++ b/bundles/org.simantics.auditlogging/src/org/simantics/audit/AuditLogging.java @@ -0,0 +1,131 @@ +package org.simantics.audit; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystemException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class AuditLogging { + + private static final Logger LOGGER = LoggerFactory.getLogger(AuditLogging.class); + + private static ObjectMapper mapper = new ObjectMapper(); + + public enum Level { + INFO, + ERROR, + TRACE + } + + public static String register(String id) throws AuditLoggingException { + try { + String entryRoot = id + "_" + UUID.randomUUID().toString(); + Files.createDirectories(getEntryRoot(entryRoot)); + return entryRoot; + } catch (Exception e) { + throw new AuditLoggingException("Could not register service with id " + id, e); + } + } + + public static List getLogEvents(String uuid, String level, String startDate, String endDate) throws AuditLoggingException { + Path entryRoot = getEntryRoot(uuid); + try { + LocalDate localStartDate = LocalDate.parse(startDate); + LocalDate localEndDate = LocalDate.parse(endDate).plusDays(1); + List allLines = new ArrayList<>(); + while (localStartDate.isBefore(localEndDate)) { + String fileName = resolveLogFileName(uuid, Level.valueOf(level.toUpperCase()), localStartDate); + try { + List lines = Files.readAllLines(entryRoot.resolve(fileName)); + allLines.addAll(lines); + } catch (FileSystemException e) { + // presumably file not found but lets not throw this cause forward, log here + LOGGER.error("Could not read file {}", fileName, e); + } finally { + localStartDate = localStartDate.plusDays(1); + } + } + return allLines; + } catch (Exception e) { + throw new AuditLoggingException(e); + } + } + + + public static Path getEntryRoot(String uuid) { + return Activator.getLogLocation().resolve(uuid); + } + + public static Path getLogFile(String uuid, Level level) { + Path root = getEntryRoot(uuid); + String fileName = resolveLogFileName(uuid, level, LocalDate.now()); + return root.resolve(fileName); + } + + private static String resolveLogFileName(String uuid, Level level, LocalDate date) { + return date.toString() + "_" + uuid + "." + level.toString().toLowerCase(); + } + + public static void log(String uuid, Map json) throws AuditLoggingException { + write(uuid, Level.INFO, json); + } + + public static void error(String uuid, Map json) throws AuditLoggingException { + write(uuid, Level.ERROR, json); + } + + public static void trace(String uuid, Map json) throws AuditLoggingException { + write(uuid, Level.TRACE, json); + } + + private static void write(String uuid, Level level, Map json) throws AuditLoggingException { + Map wrappedAuditEvent = wrapAndAddAuditMetadata(uuid, level, json); + try { + String jsonLine = mapper.writeValueAsString(wrappedAuditEvent); + Path logFile = getLogFile(uuid, level); + if (!Files.exists(logFile)) + Files.createFile(logFile); + String lineWithNewline = jsonLine + "\n"; + Files.write(logFile, lineWithNewline.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE, StandardOpenOption.APPEND); + } catch (JsonProcessingException e) { + throw new AuditLoggingException("Could not serialize input", e); + } catch (IOException e) { + throw new AuditLoggingException("Could not write line to log", e); + } + } + + private static final String timestamp = "timestamp"; + + private static Map wrapAndAddAuditMetadata(String uuid, Level level, Map original) { + Map wrapped = new HashMap<>(); + long newValue = System.currentTimeMillis(); + Object possibleExisting = wrapped.put(timestamp, newValue); + if (possibleExisting != null) { + LOGGER.warn("Replacing existing value {} for key {} - new value is {}", possibleExisting, timestamp, newValue); + } + possibleExisting = wrapped.put("uuid", uuid); + if (possibleExisting != null) { + LOGGER.warn("Replacing existing value {} for key {} - new value is {}", possibleExisting, "uuid", uuid); + } + possibleExisting = wrapped.put("level", level.toString()); + if (possibleExisting != null) { + LOGGER.warn("Replacing existing value {} for key {} - new value is {}", possibleExisting, "level", level.toString()); + } + wrapped.put("original", original); + return wrapped; + } +} diff --git a/bundles/org.simantics.auditlogging/src/org/simantics/audit/AuditLoggingException.java b/bundles/org.simantics.auditlogging/src/org/simantics/audit/AuditLoggingException.java new file mode 100644 index 000000000..a9ab1be90 --- /dev/null +++ b/bundles/org.simantics.auditlogging/src/org/simantics/audit/AuditLoggingException.java @@ -0,0 +1,23 @@ +package org.simantics.audit; + +public class AuditLoggingException extends Exception { + + private static final long serialVersionUID = -5798538535211386651L; + + public AuditLoggingException() { + super(); + } + + public AuditLoggingException(String message, Throwable cause) { + super(message, cause); + } + + public AuditLoggingException(String message) { + super(message); + } + + public AuditLoggingException(Throwable cause) { + super(cause); + } + +} diff --git a/bundles/org.simantics.auditlogging/src/org/simantics/audit/client/AuditLoggingAPIClient.java b/bundles/org.simantics.auditlogging/src/org/simantics/audit/client/AuditLoggingAPIClient.java new file mode 100644 index 000000000..a7fc6022a --- /dev/null +++ b/bundles/org.simantics.auditlogging/src/org/simantics/audit/client/AuditLoggingAPIClient.java @@ -0,0 +1,116 @@ +package org.simantics.audit.client; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Collections; +import java.util.Map; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.jackson.JacksonFeature; +import org.simantics.audit.Activator; +import org.simantics.audit.AuditLoggingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AuditLoggingAPIClient { + + private static final Logger LOGGER = LoggerFactory.getLogger(AuditLoggingAPIClient.class); + + private Client httpClient; + private WebTarget base; + private String uuid; + + public AuditLoggingAPIClient(String id, String serverAddress) throws AuditLoggingException { + ClientConfig configuration = new ClientConfig(); + configuration.register(JacksonFeature.class); + httpClient = ClientBuilder.newClient(configuration); + if (!serverAddress.startsWith("http://")) { + serverAddress = "http://" + serverAddress; + } + base = httpClient.target(serverAddress); + + // see if registered already + uuid = possibleUUIDFromFile(); + if (uuid == null) { + // register + register(id); + } + } + + private void register(String id) throws AuditLoggingException { + try { + Response response = base.path("register").request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(Collections.singletonMap("id", id))); + Map payload = response.readEntity(Map.class); + String possibleUUID = payload.get("uuid"); + if (possibleUUID != null && !possibleUUID.isEmpty()) { + persistUUID(possibleUUID); + } else { + LOGGER.warn("Invalid response received from {} for register with response payload {}", base.getUri(), payload); + } + } catch (Exception e) { + throw new AuditLoggingException(e); + } + } + + private static Path auditLoggingFile() { + return Activator.getLogLocation().resolve(".auditlogging"); + } + + private static String possibleUUIDFromFile() { + Path auditLoggingFile = auditLoggingFile(); + if (Files.exists(auditLoggingFile)) { + try { + String possibleUUID = new String(Files.readAllBytes(auditLoggingFile)); + if (possibleUUID != null && !possibleUUID.isEmpty()) { + return possibleUUID; + } else { + LOGGER.warn(".auditlogging file exists but is somehow corrupted"); + } + } catch (IOException e) { + LOGGER.error("Could not read .auditlogging file and related information", e); + } + } + return null; + } + + private void persistUUID(String possibleUUID) throws IOException { + Path auditLoggingFile = auditLoggingFile(); + Files.write(auditLoggingFile, possibleUUID.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + uuid = possibleUUID; + } + + public void log(Map message) throws AuditLoggingException { + try { + Response response = base.path(uuid).path("log").request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(message)); + } catch (Exception e) { + throw new AuditLoggingException(e); + } + } + + public void error(Map message) throws AuditLoggingException { + try { + Response response = base.path(uuid).path("error").request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(message)); + } catch (Exception e) { + throw new AuditLoggingException(e); + } + } + + public void trace(Map message) throws AuditLoggingException { + try { + Response response = base.path(uuid).path("trace").request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(message)); + } catch (Exception e) { + throw new AuditLoggingException(e); + } + } + +} diff --git a/bundles/org.simantics.auditlogging/src/org/simantics/audit/client/AuditLoggingClient.java b/bundles/org.simantics.auditlogging/src/org/simantics/audit/client/AuditLoggingClient.java new file mode 100644 index 000000000..e31a6ab21 --- /dev/null +++ b/bundles/org.simantics.auditlogging/src/org/simantics/audit/client/AuditLoggingClient.java @@ -0,0 +1,115 @@ +package org.simantics.audit.client; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.simantics.audit.AuditLogging; +import org.simantics.audit.AuditLogging.Level; +import org.simantics.audit.AuditLoggingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AuditLoggingClient { + + private static final String AUDIT_SERVER_ADDRESS = "org.simantics.audit.serverAddress"; + + private static final Logger LOGGER = LoggerFactory.getLogger(AuditLoggingClient.class); + + private static AuditLoggingClient INSTANCE; + + private AuditLoggingAPIClient apiClient; + + private AuditLoggingClient() throws AuditLoggingException { + // Read config from sysargs + System.out.println("asd"); + String serverAddress = System.getProperty(AUDIT_SERVER_ADDRESS); + if (serverAddress != null && !serverAddress.isEmpty()) { + apiClient = new AuditLoggingAPIClient("testlog", serverAddress); + + } else { + LOGGER.warn("No {} system property defined so client not configured", AUDIT_SERVER_ADDRESS); + } + } + + private static AuditLoggingClient instance() throws AuditLoggingException { + if (INSTANCE == null) { + synchronized (AuditLoggingClient.class) { + if (INSTANCE == null) { + INSTANCE = new AuditLoggingClient(); + } + } + } + return INSTANCE; + } + + public static void sendLog(List keyValues) throws AuditLoggingException { + commit(Level.INFO, toMap(keyValues.toArray())); + } + + private static Map toMap(Object... keyValues) { + if ((keyValues.length % 2) != 0) + throw new IllegalArgumentException("Invalid amount of arguments! " + Arrays.toString(keyValues)); + Map results = new HashMap<>(keyValues.length / 2); + for (int i = 0; i < keyValues.length; i += 2) { + Object key = keyValues[i]; + Object value = keyValues[i + 1]; + if (!(key instanceof String)) + throw new IllegalArgumentException("Key with index " + i + " is not String"); + results.put((String) key, value); + } + return results; + } + + public static void sendLog(Map event) throws AuditLoggingException { + commit(Level.INFO, event); + } + + public static void sendError(Map event) throws AuditLoggingException { + commit(Level.ERROR, event); + } + + public static void sendError(List keyValues) throws AuditLoggingException { + commit(Level.ERROR, toMap(keyValues.toArray())); + } + + public static void sendTrace(Map event) throws AuditLoggingException { + commit(Level.TRACE, event); + } + + public static void sendTrace(List keyValues) throws AuditLoggingException { + commit(Level.TRACE, toMap(keyValues.toArray())); + } + + private static void commit(Level level, Map message) throws AuditLoggingException { + try { + AuditLoggingAPIClient client = instance().apiClient; + if (client == null) { + // No can do - at least log to file + LOGGER.warn("Audit logging server not configured - printing event to log"); + LOGGER.info(message.toString()); + } else { + switch (level) { + case INFO: + client.log(message); + break; + case ERROR: + client.error(message); + break; + case TRACE: + client.trace(message); + break; + default: + break; + } + } + } catch (AuditLoggingException e) { + // Just for debugging purposes + LOGGER.error("Could not send audit event {} with level {}", message, level, e); + // log this locally to a file just in case + AuditLogging.log("local", message); + throw e; + } + } +} diff --git a/bundles/org.simantics.auditlogging/src/org/simantics/audit/server/AuditLoggingAPI.java b/bundles/org.simantics.auditlogging/src/org/simantics/audit/server/AuditLoggingAPI.java new file mode 100644 index 000000000..980ea4c98 --- /dev/null +++ b/bundles/org.simantics.auditlogging/src/org/simantics/audit/server/AuditLoggingAPI.java @@ -0,0 +1,62 @@ +package org.simantics.audit.server; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Response; + +import org.simantics.audit.AuditLogging; +import org.simantics.audit.AuditLoggingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Path("audit") +public class AuditLoggingAPI { + + private static final Logger LOGGER = LoggerFactory.getLogger(AuditLoggingAPI.class); + + private static Map buildJSONResponse(Object... keyValues) { + if ((keyValues.length % 2) != 0) + throw new IllegalArgumentException("Invalid amount of arguments! " + Arrays.toString(keyValues)); + Map results = new HashMap<>(keyValues.length / 2); + for (int i = 0; i < keyValues.length; i += 2) { + Object key = keyValues[i]; + Object value = keyValues[i + 1]; + if (!(key instanceof String)) + throw new IllegalArgumentException("Key with index " + i + " is not String"); + results.put((String) key, value); + } + return results; + } + + @Path("register") + @POST + public Response register(Map payload) { + String id = payload.get("id"); + + try { + String uuid = AuditLogging.register(id); + return Response.ok(buildJSONResponse("uuid", uuid)).build(); + } catch (AuditLoggingException e) { + LOGGER.error("Could not register audit with id {}", id, e); + return Response.serverError().entity(buildJSONResponse("message", e.getMessage())).build(); + } + } + + @Path("{uuid}/log") + @POST + public Response log(@PathParam("uuid") String uuid, Map payload) { + + try { + AuditLogging.log(uuid, payload); + return Response.ok().build(); + } catch (AuditLoggingException e) { + LOGGER.error("Could not log audit with id {}", uuid, e); + return Response.serverError().entity(buildJSONResponse("message", e.getMessage())).build(); + } + } +} diff --git a/bundles/org.simantics.auditlogging/src/org/simantics/audit/server/AuditLoggingServer.java b/bundles/org.simantics.auditlogging/src/org/simantics/audit/server/AuditLoggingServer.java new file mode 100644 index 000000000..5bb305d4f --- /dev/null +++ b/bundles/org.simantics.auditlogging/src/org/simantics/audit/server/AuditLoggingServer.java @@ -0,0 +1,81 @@ +package org.simantics.audit.server; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.glassfish.jersey.jackson.JacksonFeature; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.servlet.ServletContainer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AuditLoggingServer { + + private static final Logger LOGGER = LoggerFactory.getLogger(AuditLoggingServer.class); + + private static AuditLoggingServer INSTANCE = null; + private static Server server; + private static ServiceServerThread serverThread; + + private AuditLoggingServer(String token, int preferablePort) { + ResourceConfig config = new ResourceConfig(); + // JSON serialization/deserialization + config.register(JacksonFeature.class); + // Actual API + config.register(AuditLoggingAPI.class); + // Authorization +// config.register(new AuthorizationFilter(token)); + + ServletHolder holder = new ServletHolder(new ServletContainer(config)); + + server = new Server(); + ServerConnector connector = new ServerConnector(server); + connector.setPort(preferablePort); + + server.setConnectors(new Connector[] { connector }); + + ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS); + context.addServlet(holder, "/*"); + } + + private static class ServiceServerThread extends Thread { + + @Override + public void run() { + try { + server.start(); + server.join(); + } catch (Exception e) { + LOGGER.error("Could not start server ", e); + } + } + } + + private static synchronized AuditLoggingServer getInstance(String token, int port) { + try { + if (INSTANCE == null) { + INSTANCE = new AuditLoggingServer(token, port); + } + } catch (Exception e) { + LOGGER.error("Could not initialize SCL REST server", e); + } + return INSTANCE; + } + + public static synchronized void start(String token, int port) throws Exception { + // Ensure that an instance is created + getInstance(token, port); + if (serverThread == null && server != null) { + serverThread = new ServiceServerThread(); + serverThread.start(); + } + } + + public static synchronized void stop() throws Exception { + if (server != null) + server.stop(); + serverThread = null; + } +} diff --git a/bundles/pom.xml b/bundles/pom.xml index 6631fd69e..5f42fcdad 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -58,6 +58,7 @@ org.simantics.annotation.ontology org.simantics.annotation.ui org.simantics.application + org.simantics.auditlogging org.simantics.backup org.simantics.backup.db org.simantics.backup.ontology diff --git a/features/org.simantics.sdk.feature/feature.xml b/features/org.simantics.sdk.feature/feature.xml index 3c952a4d8..e6bc9771d 100644 --- a/features/org.simantics.sdk.feature/feature.xml +++ b/features/org.simantics.sdk.feature/feature.xml @@ -316,4 +316,11 @@ version="0.0.0" unpack="false"/> + +