Repository: marmotta
Updated Branches:
  refs/heads/maintenance-3.3.x f22f4d476 -> 95d580f01


MARMOTTA-580: Implemented simple /ping service, that also checks the sesame 
repository / database connection.

To force the ping request to fail, set "marmotta.enabled = false" in the 
configuration.

(cherry picked from commit 0ddf4395245338e371d9a20c06b9719b7db75bf1)


Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo
Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/95d580f0
Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/95d580f0
Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/95d580f0

Branch: refs/heads/maintenance-3.3.x
Commit: 95d580f0187987f53833cd51623d88fdbcdb5142
Parents: f22f4d4
Author: Jakob Frank <[email protected]>
Authored: Fri Dec 12 12:40:15 2014 +0100
Committer: Jakob Frank <[email protected]>
Committed: Fri Dec 12 12:46:37 2014 +0100

----------------------------------------------------------------------
 .../webservices/status/StatusWebservice.java    | 327 +++++++++++++++++++
 .../src/main/resources/kiwi-module.properties   |   3 +-
 2 files changed, 329 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/marmotta/blob/95d580f0/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/status/StatusWebservice.java
----------------------------------------------------------------------
diff --git 
a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/status/StatusWebservice.java
 
b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/status/StatusWebservice.java
new file mode 100644
index 0000000..72435d9
--- /dev/null
+++ 
b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/status/StatusWebservice.java
@@ -0,0 +1,327 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.marmotta.platform.core.webservices.status;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.marmotta.platform.core.api.config.ConfigurationService;
+import org.apache.marmotta.platform.core.api.modules.ModuleService;
+import org.apache.marmotta.platform.core.api.triplestore.SesameService;
+import org.apache.marmotta.platform.core.api.user.UserService;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.openrdf.model.URI;
+import org.openrdf.repository.Repository;
+import org.openrdf.repository.RepositoryConnection;
+import org.openrdf.repository.RepositoryException;
+import org.openrdf.repository.RepositoryResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.*;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Webservice for checking the current state of the Marmotta platform.
+ */
+@ApplicationScoped
+@Path("/ping")
+public class StatusWebservice {
+
+    private static final String CONFIG_KEY_MARMOTTA_ENABLED = 
"marmotta.enabled";
+
+    private Logger log = LoggerFactory.getLogger(this.getClass());
+
+    @Inject
+    private UserService userService;
+
+    @Inject
+    private ConfigurationService configurationService;
+
+    @Inject
+    private SesameService sesameService;
+
+    @Inject
+    private ModuleService moduleService;
+
+    @PostConstruct
+    private void init() {
+        
ResteasyProviderFactory.getInstance().register(PingResponseMessageBodyWriter.class);
+    }
+
+    @GET
+    @Produces({"application/json", "text/plain"})
+    public Response ping(@QueryParam("extended") @DefaultValue("1") int 
detailLevel, @QueryParam("echo") String echoMessage) {
+
+        // Check if the server is out of order/down for mainenance
+        if 
(!configurationService.getBooleanConfiguration(CONFIG_KEY_MARMOTTA_ENABLED, 
true)) {
+            return Response.status(Response.Status.SERVICE_UNAVAILABLE)
+                    .entity("this instance is currently not serving requests.")
+                    .build();
+        } else {
+            try {
+                final PingResponse response = new 
PingResponse(StringUtils.defaultString(echoMessage,"pong"), detailLevel, 
userService);
+
+                // More checking of the ConfigurationService
+                if (StringUtils.isNoneBlank(
+                        configurationService.getHome(),
+                        configurationService.getBaseUri()
+                )) {
+                    response.setConfigStatus(configurationService);
+                } else {
+                    throw new Exception("configuration-service failed");
+                }
+
+                // Check the TripleStore:
+                try {
+                    final RepositoryConnection con = 
sesameService.getConnection();
+                    try {
+                        con.begin();
+                        response.setSesameStatus(con);
+                        con.commit();
+                    } finally {
+                        con.close();
+                    }
+                } catch (final Throwable t) {
+                    throw new Exception("trimple-store not available", t);
+                }
+
+                // Check available modules
+                response.setModuleStatus(moduleService);
+
+                return Response.ok(response).build();
+            } catch (Throwable t) {
+                return 
Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(t.getMessage()).build();
+            }
+        }
+    }
+
+    @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY)
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    protected static class PingResponse {
+
+        @JsonIgnore
+        private final int detailLevel;
+
+        private String message;
+        private final Map<String, String> user = new HashMap<>();
+        private final Map<String, String> config = new HashMap<>();
+        private final Map<String, String> tripleStore = new HashMap<>();
+        private final Set<String> modules = new HashSet<>();
+
+        public PingResponse(String message, int requestedDetailLevle, 
UserService userService) {
+            this.message = message;
+            final URI u = checkNotNull(userService.getCurrentUser(), "no user 
available");
+
+            int allowedDetailLevel;
+            if (userService.isAnonymous(u)) {
+                allowedDetailLevel = 0;
+            } else if (Objects.equals(u, userService.getAdminUser())) {
+                allowedDetailLevel = 2;
+            } else {
+                allowedDetailLevel = 1;
+            }
+            this.detailLevel = Math.min(allowedDetailLevel, 
requestedDetailLevle);
+            setUserStatus(userService);
+        }
+
+        public void setUserStatus(UserService userService) {
+            final URI u = userService.getCurrentUser();
+
+            user.clear();
+            switch (detailLevel) {
+                case 2:
+                    user.put("name", u.getLocalName());
+                case 1:
+                    user.put("uri", u.stringValue());
+                    break;
+                default:
+                    // nop;
+            }
+        }
+
+        public void setConfigStatus(ConfigurationService configurationService) 
{
+            config.clear();
+            switch (detailLevel) {
+                case 2:
+                    config.put("homeDir", configurationService.getHome());
+                    config.put("context.default", 
configurationService.getDefaultContext());
+                    config.put("context.enhanced", 
configurationService.getEnhancerContex());
+                    config.put("context.inferred", 
configurationService.getInferredContext());
+                case 1:
+                    config.put("baseUri", configurationService.getBaseUri());
+                    config.put("serverUrl", 
configurationService.getServerUri());
+                    break;
+                case 0:
+                default:
+                    //nop;
+            }
+        }
+
+        public void setSesameStatus(RepositoryConnection repositoryConnection) 
throws RepositoryException {
+            final Repository repository = repositoryConnection.getRepository();
+            tripleStore.clear();
+            switch (detailLevel) {
+                case 2:
+                    tripleStore.put("statements", 
String.valueOf(repositoryConnection.size()));
+                    tripleStore.put("namespaces", 
String.valueOf(sizeOf(repositoryConnection.getNamespaces())));
+                    tripleStore.put("contexts", 
String.valueOf(sizeOf(repositoryConnection.getContextIDs())));
+                case 1:
+                    tripleStore.put("writeable", 
String.valueOf(repository.isWritable()));
+                    break;
+                case 0:
+                default:
+                    //nop;
+            }
+        }
+
+        private static long sizeOf(RepositoryResult<?> repositoryResult) 
throws RepositoryException {
+            try {
+                long result = 0;
+                while (repositoryResult.hasNext()) {
+                    repositoryResult.next();
+                    result++;
+                }
+                return result;
+            } finally {
+                repositoryResult.close();
+            }
+        }
+
+        public void setModuleStatus(ModuleService moduleStatus) {
+            switch (detailLevel) {
+                case 2:
+                case 1:
+                    modules.addAll(moduleStatus.listModules());
+                    break;
+                case 0:
+                default:
+                    modules.clear();
+            }
+        }
+    }
+
+    @Provider
+    @Produces("text/plain")
+    public static class PingResponseMessageBodyWriter implements 
MessageBodyWriter<PingResponse> {
+        /**
+         * Ascertain if the MessageBodyWriter supports a particular type.
+         *
+         * @param type        the class of instance that is to be written.
+         * @param genericType the type of instance to be written, obtained 
either
+         *                    by reflection of a resource method return type 
or via inspection
+         *                    of the returned instance. {@link 
javax.ws.rs.core.GenericEntity}
+         *                    provides a way to specify this information at 
runtime.
+         * @param annotations an array of the annotations attached to the 
message entity instance.
+         * @param mediaType   the media type of the HTTP entity.
+         * @return {@code true} if the type is supported, otherwise {@code 
false}.
+         */
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, 
Annotation[] annotations, MediaType mediaType) {
+            return PingResponse.class.isAssignableFrom(type);
+        }
+
+        /**
+         * Originally, the method has been called before {@code writeTo} to 
ascertain the length in bytes of
+         * the serialized form of {@code t}. A non-negative return value has 
been used in a HTTP
+         * {@code Content-Length} header.
+         * <p>
+         * As of JAX-RS 2.0, the method has been deprecated and the value 
returned by the method is ignored
+         * by a JAX-RS runtime. All {@code MessageBodyWriter} implementations 
are advised to return {@code -1}
+         * from the method. Responsibility to compute the actual {@code 
Content-Length} header value has been
+         * delegated to JAX-RS runtime.
+         * </p>
+         *
+         * @param pingResponse the instance to write
+         * @param type         the class of instance that is to be written.
+         * @param genericType  the type of instance to be written. {@link 
javax.ws.rs.core.GenericEntity}
+         *                     provides a way to specify this information at 
runtime.
+         * @param annotations  an array of the annotations attached to the 
message entity instance.
+         * @param mediaType    the media type of the HTTP entity.
+         * @return length in bytes or -1 if the length cannot be determined in 
advance.
+         */
+        @Override
+        public long getSize(PingResponse pingResponse, Class<?> type, Type 
genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        /**
+         * Write a type to an HTTP message. The message header map is mutable
+         * but any changes must be made before writing to the output stream 
since
+         * the headers will be flushed prior to writing the message body.
+         *
+         * @param pingResponse the instance to write.
+         * @param type         the class of instance that is to be written.
+         * @param genericType  the type of instance to be written. {@link 
javax.ws.rs.core.GenericEntity}
+         *                     provides a way to specify this information at 
runtime.
+         * @param annotations  an array of the annotations attached to the 
message entity instance.
+         * @param mediaType    the media type of the HTTP entity.
+         * @param httpHeaders  a mutable map of the HTTP message headers.
+         * @param entityStream the {@link java.io.OutputStream} for the HTTP 
entity. The
+         *                     implementation should not close the output 
stream.
+         * @throws java.io.IOException                 if an IO error arises.
+         * @throws javax.ws.rs.WebApplicationException if a specific HTTP 
error response needs to be produced.
+         *                                             Only effective if 
thrown prior to the message being committed.
+         */
+        @Override
+        public void writeTo(PingResponse pingResponse, Class<?> type, Type 
genericType, Annotation[] annotations, MediaType mediaType, 
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws 
IOException, WebApplicationException {
+            final PrintStream out = new PrintStream(entityStream);
+
+            out.printf("Message: %s%n", pingResponse.message);
+            writeMapTo("Config", pingResponse.config, out);
+            writeMapTo("TripleStore", pingResponse.tripleStore, out);
+            writeMapTo("User", pingResponse.user, out);
+            writeSetTo("Modules", pingResponse.modules, out);
+        }
+
+        private void writeSetTo(String title, Set<String> set, PrintStream 
out) {
+            if (set != null) {
+                out.printf("%s:%n", title);
+                for (String entry: set) {
+                    out.printf("\t%s%n", entry);
+                }
+            }
+        }
+
+        private void writeMapTo(String title, Map<String, String> map, 
PrintStream out) {
+            if (map != null) {
+                out.printf("%s:%n", title);
+                for (Map.Entry<String, String> e: map.entrySet()) {
+                    out.printf("\t%s: %s%n", e.getKey(), e.getValue());
+                }
+            }
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/95d580f0/platform/marmotta-core/src/main/resources/kiwi-module.properties
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/resources/kiwi-module.properties 
b/platform/marmotta-core/src/main/resources/kiwi-module.properties
index 5e758f2..40b50b7 100644
--- a/platform/marmotta-core/src/main/resources/kiwi-module.properties
+++ b/platform/marmotta-core/src/main/resources/kiwi-module.properties
@@ -21,7 +21,7 @@ name=Core Services
 container=Generic
 container.weight = 10
 
-subtitle = Configure LMF Core
+subtitle = Configure Marmotta Core
 weight = 10
 
 icon_small = /admin/img/config_small.png
@@ -73,5 +73,6 @@ 
webservices=org.apache.marmotta.platform.core.webservices.config.ConfigurationWe
   org.apache.marmotta.platform.core.webservices.resource.InspectionWebService,\
   org.apache.marmotta.platform.core.webservices.triplestore.ContextWebService,\
   org.apache.marmotta.platform.core.webservices.prefix.PrefixWebService,\
+  org.apache.marmotta.platform.core.webservices.status.StatusWebservice,\
   org.apache.marmotta.platform.core.webservices.logging.LoggingWebService
   

Reply via email to