Merge pull request 'health_check' (#21) from health_check into master
Reviewed-on: #21
This commit is contained in:
commit
3ec3738784
|
@ -3,6 +3,7 @@
|
|||
## [v1.0.6] - 2024-10-01
|
||||
|
||||
- Included the file size to reduce/optimize the time to upload files to the storage hub [#28150]
|
||||
- Checked if the user is `null` in the `UserUtils` class [#28301]
|
||||
|
||||
## [v1.0.5] - 2024-07-03
|
||||
|
||||
|
|
|
@ -21,28 +21,31 @@ import lombok.extern.slf4j.Slf4j;
|
|||
@Slf4j
|
||||
public class UserUtils {
|
||||
|
||||
public static List<String> DEFAULT_ROLES=new ArrayList<>();
|
||||
public static List<String> DEFAULT_ROLES = new ArrayList<>();
|
||||
|
||||
public static AuthenticatedUser getCurrent() throws SecurityException {
|
||||
log.debug("Loading caller info..");
|
||||
|
||||
SecretManager cm = SecretManagerProvider.instance.get();
|
||||
String context = cm.getContext();
|
||||
if(context==null) throw new SecurityException("Cannot determine context");
|
||||
if (context == null)
|
||||
throw new SecurityException("Cannot determine context");
|
||||
Set<String> roles = new HashSet<>();
|
||||
org.gcube.common.authorization.utils.user.User user = cm.getUser();
|
||||
log.info("Identified caller {} in context {}",user.getUsername(),context);
|
||||
|
||||
Set<String> roles=new HashSet<>();
|
||||
roles.addAll(user.getRoles());
|
||||
if (user == null) {
|
||||
log.warn("No user found in the session work, context is {}", context);
|
||||
} else {
|
||||
log.info("Identified caller {} in context {}", user.getUsername(), context);
|
||||
roles.addAll(user.getRoles());
|
||||
}
|
||||
AuthenticatedUser toReturn = new AuthenticatedUser(user, roles, AccessTokenProvider.instance.get(),
|
||||
SecurityTokenProvider.instance.get(), context);
|
||||
|
||||
AuthenticatedUser toReturn =
|
||||
new AuthenticatedUser(user,roles, AccessTokenProvider.instance.get(),SecurityTokenProvider.instance.get(),context);
|
||||
|
||||
log.info("Current User is {} ",toReturn);
|
||||
log.info("Current User is {} ", toReturn);
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public static class AuthenticatedUser {
|
||||
|
@ -63,10 +66,10 @@ public class UserUtils {
|
|||
builder.append("User [user=");
|
||||
builder.append(user);
|
||||
builder.append(", uma_token=");
|
||||
builder.append(uma_token==null?uma_token:"***");
|
||||
builder.append(uma_token == null ? uma_token : "***");
|
||||
|
||||
builder.append(", gcube_token=");
|
||||
builder.append(gcube_token==null?gcube_token:"***");
|
||||
builder.append(gcube_token == null ? gcube_token : "***");
|
||||
|
||||
builder.append(", roles=");
|
||||
builder.append(roles);
|
||||
|
@ -77,14 +80,14 @@ public class UserUtils {
|
|||
return builder.toString();
|
||||
}
|
||||
|
||||
public AccountingInfo asInfo(){
|
||||
AccountingInfo info=new AccountingInfo();
|
||||
public AccountingInfo asInfo() {
|
||||
AccountingInfo info = new AccountingInfo();
|
||||
User user = new User();
|
||||
try{
|
||||
try {
|
||||
user.setUsername(this.getUser().getUsername());
|
||||
user.setRoles(roles);
|
||||
}catch(Exception e){
|
||||
log.warn("Unable to determine user id, using FAKE",e);
|
||||
} catch (Exception e) {
|
||||
log.warn("Unable to determine user id, using FAKE", e);
|
||||
user.setUsername("FAKE");
|
||||
user.setRoles(new HashSet<>());
|
||||
user.getRoles().addAll(DEFAULT_ROLES);
|
||||
|
@ -92,9 +95,9 @@ public class UserUtils {
|
|||
|
||||
info.setUser(user);
|
||||
info.setInstant(LocalDateTime.now());
|
||||
Context c=new Context();
|
||||
Context c = new Context();
|
||||
c.setId(this.context);
|
||||
c.setName(context.contains("/")?context.substring(context.lastIndexOf("/")):context);
|
||||
c.setName(context.contains("/") ? context.substring(context.lastIndexOf("/")) : context);
|
||||
info.setContext(c);
|
||||
return info;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
# Changelog for org.gcube.application.geoportal-service
|
||||
|
||||
## [v1.2.2] - 2024-10-16
|
||||
|
||||
- Included the health check at https://{geoporta_endpoint}/health via `smallrye-health` (that implements the https://microprofile.io/specifications/microprofile-health/) [#28301]
|
||||
|
||||
## [v1.2.1] - 2024-10-02
|
||||
|
||||
- Included the file size to reduce/optimize the time to upload files to the storage hub [#28150]
|
||||
|
|
|
@ -3,13 +3,10 @@
|
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://enunciate.webcohesion.com/schemas/enunciate-2.14.0.xsd">
|
||||
<api-classes>
|
||||
<!-- Use patterns to exclude classes...
|
||||
|
||||
e.g. for URI-Resolver
|
||||
|
||||
-->
|
||||
<!-- Use patterns to exclude classes... -->
|
||||
<exclude pattern="org.gcube.application.geoportal.service.rest.DocsGenerator" />
|
||||
<exclude pattern="org.gcube.application.geoportal.service.rest.ConcessioniOverMongo" />
|
||||
<exclude pattern="org.gcube.application.geoportal.service.rest.GeoportalHealth" />
|
||||
</api-classes>
|
||||
<modules>
|
||||
<gwt-json-overlay disabled="true" />
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE xml>
|
||||
<application mode='online'>
|
||||
<name>${project.artifactId}</name>
|
||||
<group>${project.groupId}</group>
|
||||
<version>${project.version}</version>
|
||||
<description>${project.description}</description>
|
||||
<exclude>/srv/docs/*</exclude>
|
||||
<exclude>/srv/api-docs/*</exclude>
|
||||
<name>${project.artifactId}</name>
|
||||
<group>${project.groupId}</group>
|
||||
<version>${project.version}</version>
|
||||
<description>${project.description}</description>
|
||||
<!-- <exclude>/srv/docs/*</exclude> -->
|
||||
<!-- <exclude>/srv/api-docs/*</exclude> -->
|
||||
<exclude>/srv/health</exclude>
|
||||
<exclude>/srv/health/*</exclude>
|
||||
</application>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.gcube.application</groupId>
|
||||
<artifactId>geoportal-service</artifactId>
|
||||
<version>1.2.1</version>
|
||||
<version>1.2.2</version>
|
||||
<name>Geoportal Service</name>
|
||||
<packaging>war</packaging>
|
||||
|
||||
|
@ -155,6 +155,17 @@
|
|||
</dependency> <dependency> <groupId>javax.xml.ws</groupId> <artifactId>jaxws-api</artifactId>
|
||||
<scope>test</scope> </dependency> -->
|
||||
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.eclipse.microprofile.health</groupId> -->
|
||||
<!-- <artifactId>microprofile-health-api</artifactId> -->
|
||||
<!-- <version>2.2</version> -->
|
||||
<!-- </dependency> -->
|
||||
|
||||
<dependency>
|
||||
<groupId>io.smallrye</groupId>
|
||||
<artifactId>smallrye-health</artifactId>
|
||||
<version>2.2.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Plugins related tests -->
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.gcube.application.geoportal.service.engine.providers.ucd.ProfileMap;
|
|||
import org.gcube.application.geoportal.service.engine.providers.ucd.SingleISResourceUCDProvider;
|
||||
import org.gcube.application.geoportal.service.engine.providers.ucd.UCDManager;
|
||||
import org.gcube.application.geoportal.service.model.internal.db.Mongo;
|
||||
import org.gcube.application.geoportal.service.rest.GeoportalHealth;
|
||||
import org.gcube.application.geoportal.service.rest.Plugins;
|
||||
import org.gcube.application.geoportal.service.rest.ProfiledDocuments;
|
||||
import org.gcube.application.geoportal.service.rest.UseCaseDescriptors;
|
||||
|
@ -32,28 +33,28 @@ import lombok.extern.slf4j.Slf4j;
|
|||
|
||||
@ApplicationPath(InterfaceConstants.APPLICATION_PATH)
|
||||
@Slf4j
|
||||
public class GeoPortalService extends ResourceConfig{
|
||||
public class GeoPortalService extends ResourceConfig {
|
||||
|
||||
public <T> Map<Engine<T>,Class<T>> customImplementations(){
|
||||
public <T> Map<Engine<T>, Class<T>> customImplementations() {
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
|
||||
|
||||
public GeoPortalService() {
|
||||
super();
|
||||
//Register interrfaces
|
||||
// Register interfaces
|
||||
|
||||
log.info("Initializing serialization");
|
||||
JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
|
||||
provider.setMapper(Serialization.mapper);
|
||||
register(provider);
|
||||
|
||||
|
||||
registerClasses(RequestFilter.class);
|
||||
|
||||
registerClasses(ProfiledDocuments.class);
|
||||
registerClasses(UseCaseDescriptors.class);
|
||||
registerClasses(Plugins.class);
|
||||
//registerClasses(DocsGenerator.class);
|
||||
registerClasses(GeoportalHealth.class);
|
||||
// registerClasses(DocsGenerator.class);
|
||||
|
||||
log.info("Setting implementations .. ");
|
||||
|
||||
|
@ -62,25 +63,19 @@ public class GeoPortalService extends ResourceConfig{
|
|||
ImplementationProvider.get().setEngine(new StorageClientProvider(), StorageUtils.class);
|
||||
ImplementationProvider.get().setEngine(new SingleISResourceUCDProvider(), ProfileMap.class);
|
||||
ImplementationProvider.get().setEngine(new PluginManager(), PluginManager.PluginMap.class);
|
||||
ImplementationProvider.get().setEngine(new UCDManager(),UCDManagerI.class);
|
||||
ImplementationProvider.get().setEngine(new UCDManager(), UCDManagerI.class);
|
||||
ImplementationProvider.get().setEngine(new ConfigurationCache(), ConfigurationCache.ConfigurationMap.class);
|
||||
|
||||
|
||||
for(Map.Entry<Engine<Object>, Class<Object>> entry : customImplementations().entrySet()){
|
||||
log.warn("LOADING CUSTOM ENGINE : {} serving {}",entry.getKey(),entry.getValue());
|
||||
for (Map.Entry<Engine<Object>, Class<Object>> entry : customImplementations().entrySet()) {
|
||||
log.warn("LOADING CUSTOM ENGINE : {} serving {}", entry.getKey(), entry.getValue());
|
||||
ImplementationProvider.get().setEngine(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
log.debug("ENGINES ARE : ");
|
||||
ImplementationProvider.get().getManagerList().forEach(
|
||||
(aClass, s) -> log.debug("{} serving {} ",aClass,s));
|
||||
ImplementationProvider.get().getManagerList().forEach((aClass, s) -> log.debug("{} serving {} ", aClass, s));
|
||||
|
||||
ImplementationProvider.get().initEngines();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
package org.gcube.application.geoportal.service;
|
||||
|
||||
|
||||
public class ServiceConstants {
|
||||
|
||||
public static final String SE_GNA_DB_FLAG="GNA_DB";
|
||||
public static final String SE_GNA_DB_CATEGORY="Database";
|
||||
|
||||
public static final String MONGO_SE_PLATFORM="mongodb";
|
||||
public static final String MONGO_SE_GNA_FLAG="internal-db";
|
||||
|
||||
|
||||
|
||||
|
||||
// SE DB flagName
|
||||
public static final String SE_GNA_DB_FLAGNAME = "GNA_DB";
|
||||
// SE DB flagValue
|
||||
public static final String SE_GNA_DB_FLAGVALUE = "Concessioni";
|
||||
// SE DB category
|
||||
public static final String SE_GNA_DB_CATEGORY = "Database";
|
||||
// SE DB platform
|
||||
public static final String SE_GNA_DB_PLATFORM = "postgis";
|
||||
|
||||
public static final String MONGO_SE_PLATFORM = "mongodb";
|
||||
public static final String MONGO_SE_GNA_FLAG = "internal-db";
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public class MongoClientProvider extends AbstractScopedMap<Mongo> {
|
|||
getProvidedObjectByClass(ISInterface.class),
|
||||
ServiceConstants.SE_GNA_DB_CATEGORY,
|
||||
ServiceConstants.MONGO_SE_PLATFORM,
|
||||
ServiceConstants.SE_GNA_DB_FLAG,
|
||||
ServiceConstants.SE_GNA_DB_FLAGNAME,
|
||||
ServiceConstants.MONGO_SE_GNA_FLAG);
|
||||
|
||||
log.debug("Connecting to "+conn);
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
package org.gcube.application.geoportal.service.rest;
|
||||
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse.State;
|
||||
import org.gcube.application.geoportal.service.rest.health.DatabaseHealthCheck;
|
||||
import org.gcube.application.geoportal.service.rest.health.GeoportalHealthCheck;
|
||||
import org.gcube.application.geoportal.service.rest.health.HealthCheckResponseSerializer;
|
||||
import org.gcube.application.geoportal.service.rest.health.MongoHealthCheck;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* The Class GeoportalHealth.
|
||||
*
|
||||
* @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
|
||||
*
|
||||
* Oct 22, 2024
|
||||
*/
|
||||
@Path("/health")
|
||||
@Slf4j
|
||||
public class GeoportalHealth {
|
||||
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* Instantiates a new geoportal health.
|
||||
*/
|
||||
public GeoportalHealth() {
|
||||
SimpleModule module = new SimpleModule();
|
||||
module.addSerializer(HealthCheckResponse.class, new HealthCheckResponseSerializer());
|
||||
mapper.registerModule(module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Service check.
|
||||
*
|
||||
* @return the response compliant to `microprofile-health` specification. 200 if
|
||||
* is OK. Otherwise it fails.
|
||||
* @throws JsonProcessingException the json processing exception
|
||||
*/
|
||||
@GET
|
||||
@Path("")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public Response serviceCheck() throws JsonProcessingException {
|
||||
log.debug("serviceCheck called");
|
||||
HealthCheckResponse response = new GeoportalHealthCheck().call();
|
||||
String json = healthCheckSerializer(response);
|
||||
log.info("serviceCheck response is {}", json);
|
||||
return Response.ok().entity(json).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mongo check.
|
||||
*
|
||||
* @param context the gcube context
|
||||
* @param include_collections if the check has to include the mongo collections
|
||||
* in the response
|
||||
* @return the response compliant to `microprofile-health` specification
|
||||
* @throws JsonProcessingException the json processing exception
|
||||
*/
|
||||
@GET
|
||||
@Path("/mongo")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public Response mongoCheck(@QueryParam("context") String context,
|
||||
@DefaultValue("false") @QueryParam("include_collections") Boolean includeCollections)
|
||||
throws JsonProcessingException {
|
||||
log.debug("mongoCheck called in the context {}, includeCollections {}", context, includeCollections);
|
||||
if (context == null) {
|
||||
HealthCheckResponse response = HealthCheckResponse.named(MongoHealthCheck.SERVICE_NAME)
|
||||
.withData("context", "is required parameter (e.g. context=/gcube/devsec/devVRE)").down().build();
|
||||
String json = healthCheckSerializer(response);
|
||||
log.info("mongoCheck error response is {}", json);
|
||||
// Bad request
|
||||
return Response.status(400).entity(json).build();
|
||||
}
|
||||
|
||||
HealthCheckResponse response = new MongoHealthCheck(context, includeCollections).call();
|
||||
ResponseBuilder responseBuilder = Response.ok();
|
||||
if (response.getState().equals(State.DOWN)) {
|
||||
responseBuilder = responseBuilder.status(503);
|
||||
}
|
||||
String json = healthCheckSerializer(response);
|
||||
log.info("mongoCheck response is {}", json);
|
||||
return responseBuilder.entity(json).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Database check.
|
||||
*
|
||||
* @param context the gcube context
|
||||
* @return the response compliant to `microprofile-health` specification
|
||||
* @throws JsonProcessingException the json processing exception
|
||||
*/
|
||||
@GET
|
||||
@Path("/database")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public Response databaseCheck(@QueryParam("context") String context) throws JsonProcessingException {
|
||||
log.debug("databaseCheck called in the context {}", context);
|
||||
if (context == null) {
|
||||
HealthCheckResponse response = HealthCheckResponse.named(DatabaseHealthCheck.SERVICE_NAME)
|
||||
.withData("context", "is required parameter (e.g. context=/gcube/devsec/devVRE)").down().build();
|
||||
String json = healthCheckSerializer(response);
|
||||
log.info("databaseCheck error response is {}", json);
|
||||
// Bad request
|
||||
return Response.status(400).entity(json).build();
|
||||
}
|
||||
|
||||
HealthCheckResponse response = new DatabaseHealthCheck(context).call();
|
||||
ResponseBuilder responseBuilder = Response.ok();
|
||||
if (response.getState().equals(State.DOWN)) {
|
||||
responseBuilder = responseBuilder.status(503);
|
||||
}
|
||||
String json = healthCheckSerializer(response);
|
||||
log.info("databaseCheck response is {}", json);
|
||||
return responseBuilder.entity(json).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Health check serializer.
|
||||
*
|
||||
* @param response the response
|
||||
* @return the string
|
||||
* @throws JsonProcessingException the json processing exception
|
||||
*/
|
||||
private String healthCheckSerializer(HealthCheckResponse response) throws JsonProcessingException {
|
||||
// Serializes HealthCheckResponse in JSON with custom
|
||||
// HealthCheckResponseSerializer
|
||||
return mapper.writeValueAsString(response);
|
||||
}
|
||||
|
||||
}
|
|
@ -19,18 +19,15 @@ import lombok.extern.slf4j.Slf4j;
|
|||
@Slf4j
|
||||
public abstract class GuardedMethod<T> {
|
||||
|
||||
private static List<Runnable> preoperations=new ArrayList<>();
|
||||
private static List<Runnable> preoperations = new ArrayList<>();
|
||||
|
||||
public static void addPreoperation(Runnable preoperation){
|
||||
public static void addPreoperation(Runnable preoperation) {
|
||||
preoperations.add(preoperation);
|
||||
}
|
||||
|
||||
private T result = null;
|
||||
|
||||
|
||||
|
||||
private T result=null;
|
||||
|
||||
public GuardedMethod<T> execute() throws WebApplicationException{
|
||||
public GuardedMethod<T> execute() throws WebApplicationException {
|
||||
try {
|
||||
if (!preoperations.isEmpty()) {
|
||||
log.trace("Running preops (size : {} )", preoperations.size());
|
||||
|
@ -40,41 +37,41 @@ public abstract class GuardedMethod<T> {
|
|||
log.trace("Executing actual method..");
|
||||
result = run();
|
||||
return this;
|
||||
}catch (InvalidUserRoleException e){
|
||||
log.error("Returning exception ",e);
|
||||
throw new WebApplicationException("Invalid Step ID ", e,Status.FORBIDDEN);
|
||||
}catch (UnrecognizedStepException e){
|
||||
log.error("Returning exception ",e);
|
||||
throw new WebApplicationException("Invalid Step ID ", e,Status.BAD_REQUEST);
|
||||
}catch (ConfigurationException e){
|
||||
log.error("Returning exception ",e);
|
||||
throw new WebApplicationException("Environment is not properly configured", e,Status.EXPECTATION_FAILED);
|
||||
}catch (InsufficientPrivileges e){
|
||||
log.error("Returning exception ",e);
|
||||
throw new WebApplicationException("User has insufficient privileges for requested action", e,Status.FORBIDDEN);
|
||||
}catch(WebApplicationException e) {
|
||||
} catch (InvalidUserRoleException e) {
|
||||
log.error("Returning exception ", e);
|
||||
throw new WebApplicationException("Invalid Step ID ", e, Status.FORBIDDEN);
|
||||
} catch (UnrecognizedStepException e) {
|
||||
log.error("Returning exception ", e);
|
||||
throw new WebApplicationException("Invalid Step ID ", e, Status.BAD_REQUEST);
|
||||
} catch (ConfigurationException e) {
|
||||
log.error("Returning exception ", e);
|
||||
throw new WebApplicationException("Environment is not properly configured", e, Status.EXPECTATION_FAILED);
|
||||
} catch (InsufficientPrivileges e) {
|
||||
log.error("Returning exception ", e);
|
||||
throw new WebApplicationException("User has insufficient privileges for requested action", e,
|
||||
Status.FORBIDDEN);
|
||||
} catch (WebApplicationException e) {
|
||||
log.error("Throwing Web Application Exception ", e);
|
||||
throw e;
|
||||
}catch(ProjectNotFoundException e){
|
||||
log.error("Returning exception ",e);
|
||||
throw new WebApplicationException("Project not found", e,Status.NOT_FOUND);
|
||||
}catch(ProjectLockedException e){
|
||||
log.error("Returning exception ",e);
|
||||
throw new WebApplicationException("Project is currently locked", e,Status.PRECONDITION_FAILED);
|
||||
}catch(InvalidLockException e){
|
||||
log.error("Lock exception ",e);
|
||||
throw new WebApplicationException("Conflicts found in locks", e,Status.CONFLICT);
|
||||
}catch(Throwable t) {
|
||||
log.error("Unexpected error ",t);
|
||||
throw new WebApplicationException("Unexpected internal error", t,Status.INTERNAL_SERVER_ERROR);
|
||||
} catch (ProjectNotFoundException e) {
|
||||
log.error("Returning exception ", e);
|
||||
throw new WebApplicationException("Project not found", e, Status.NOT_FOUND);
|
||||
} catch (ProjectLockedException e) {
|
||||
log.error("Returning exception ", e);
|
||||
throw new WebApplicationException("Project is currently locked", e, Status.PRECONDITION_FAILED);
|
||||
} catch (InvalidLockException e) {
|
||||
log.error("Lock exception ", e);
|
||||
throw new WebApplicationException("Conflicts found in locks", e, Status.CONFLICT);
|
||||
} catch (Throwable t) {
|
||||
log.error("Unexpected error ", t);
|
||||
throw new WebApplicationException("Unexpected internal error", t, Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public T getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
protected abstract T run() throws Exception,WebApplicationException;
|
||||
|
||||
protected abstract T run() throws Exception, WebApplicationException;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
package org.gcube.application.geoportal.service.rest.health;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheck;
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
import org.eclipse.microprofile.health.HealthCheckResponseBuilder;
|
||||
import org.eclipse.microprofile.health.Liveness;
|
||||
import org.eclipse.microprofile.health.Readiness;
|
||||
import org.gcube.application.cms.implementations.ISInterface;
|
||||
import org.gcube.application.cms.implementations.ImplementationProvider;
|
||||
import org.gcube.application.geoportal.common.model.rest.ConfigurationException;
|
||||
import org.gcube.application.geoportal.common.model.rest.DatabaseConnection;
|
||||
import org.gcube.application.geoportal.service.ServiceConstants;
|
||||
import org.gcube.common.scope.api.ScopeProvider;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* The Class DatabaseHealthCheck.
|
||||
*
|
||||
* @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
|
||||
*
|
||||
* Oct 23, 2024
|
||||
*/
|
||||
@Readiness
|
||||
@Liveness
|
||||
@Slf4j
|
||||
public class DatabaseHealthCheck implements HealthCheck {
|
||||
|
||||
private String context;
|
||||
public static final String SERVICE_NAME = "database";
|
||||
|
||||
private static final int CONNECTION_TIMEOUT = 30;
|
||||
|
||||
/**
|
||||
* Call.
|
||||
*
|
||||
* @return the health check response
|
||||
*/
|
||||
@Override
|
||||
public HealthCheckResponse call() {
|
||||
return checkDatabase(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new database health check.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public DatabaseHealthCheck(String context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check database.
|
||||
*
|
||||
* @param context the context
|
||||
* @return the health check response
|
||||
*/
|
||||
private HealthCheckResponse checkDatabase(String context) {
|
||||
log.debug("checkMongo in the context: {}", context);
|
||||
HealthCheckResponseBuilder buildHCRBuilder = HealthCheckResponse.named(SERVICE_NAME);
|
||||
|
||||
ScopeProvider.instance.set(context);
|
||||
|
||||
try {
|
||||
|
||||
DatabaseConnection databaseConnection = null;
|
||||
ISInterface isInterface = ImplementationProvider.get().getProvidedObjectByClass(ISInterface.class);
|
||||
|
||||
try {
|
||||
if (isInterface == null)
|
||||
throw new Exception(ISInterface.class.getSimpleName() + " configuration is null for "
|
||||
+ DatabaseConnection.class.getSimpleName());
|
||||
|
||||
databaseConnection = isInterface.queryForDatabase(ServiceConstants.SE_GNA_DB_CATEGORY,
|
||||
ServiceConstants.SE_GNA_DB_PLATFORM, ServiceConstants.SE_GNA_DB_FLAGNAME,
|
||||
ServiceConstants.SE_GNA_DB_FLAGVALUE);
|
||||
|
||||
if (databaseConnection == null)
|
||||
throw new Exception(DatabaseConnection.class.getSimpleName() + " configuration is null");
|
||||
} catch (Exception e) {
|
||||
log.error("Error on checking DB configuration: ", e);
|
||||
buildHCRBuilder.state(false);
|
||||
return buildHCRBuilder.build();
|
||||
}
|
||||
|
||||
boolean connectionStatus = checkDatabaseConnection(databaseConnection);
|
||||
buildHCRBuilder = appendDBInfo(buildHCRBuilder, databaseConnection);
|
||||
buildHCRBuilder.state(connectionStatus);
|
||||
log.info("checkDatabase is OK in the context: {}. State is {}", context, connectionStatus);
|
||||
return buildHCRBuilder.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Error on checkDatabase: ", e);
|
||||
log.warn("checkDatabase is KO in the context: {}", context);
|
||||
buildHCRBuilder.state(false);
|
||||
return buildHCRBuilder.build();
|
||||
} finally {
|
||||
ScopeProvider.instance.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append DB info.
|
||||
*
|
||||
* @param buildHCRBuilder the build HCR builder
|
||||
* @param connection the connection
|
||||
* @return the health check response builder
|
||||
*/
|
||||
private HealthCheckResponseBuilder appendDBInfo(HealthCheckResponseBuilder buildHCRBuilder,
|
||||
DatabaseConnection connection) {
|
||||
buildHCRBuilder.withData("host", connection.getUrl() + "");
|
||||
|
||||
// anonymize the DB username
|
||||
String userNotClear = "***";
|
||||
if (connection.getUser() != null && connection.getUser().length() > 3) {
|
||||
userNotClear = connection.getUser().substring(0, 3) + userNotClear;
|
||||
}
|
||||
buildHCRBuilder.withData("user ", userNotClear);
|
||||
buildHCRBuilder.withData("pwd ", "****");
|
||||
return buildHCRBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check database connection.
|
||||
*
|
||||
* @param connectionParameters the connection parameters
|
||||
* @return true, if successful
|
||||
*/
|
||||
private boolean checkDatabaseConnection(DatabaseConnection connectionParameters) {
|
||||
|
||||
try {
|
||||
|
||||
if (connectionParameters == null)
|
||||
throw new ConfigurationException("connectionParameters is null");
|
||||
|
||||
// Getting connection
|
||||
Connection connection = DriverManager.getConnection(connectionParameters.getUrl(),
|
||||
connectionParameters.getUser(), connectionParameters.getPwd());
|
||||
// Check if the connection is valid (timeout 30 seconds)
|
||||
if (connection != null && connection.isValid(CONNECTION_TIMEOUT)) {
|
||||
log.debug("Connection to DB " + connectionParameters.getUrl() + " is OK!");
|
||||
return true;
|
||||
} else {
|
||||
log.debug("Connection to DB " + connectionParameters.getUrl() + " is KO!");
|
||||
return false;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.warn("Error on connecting to DB: " + connectionParameters, e);
|
||||
return false;
|
||||
} catch (ConfigurationException e1) {
|
||||
log.warn("Error on reading connection configuration: " + connectionParameters, e1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package org.gcube.application.geoportal.service.rest.health;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheck;
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
import org.eclipse.microprofile.health.Liveness;
|
||||
import org.eclipse.microprofile.health.Readiness;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
/**
|
||||
* The Class GeoportalHealthCheck.
|
||||
*
|
||||
* @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
|
||||
*
|
||||
* Oct 22, 2024
|
||||
*/
|
||||
@Readiness
|
||||
@Liveness
|
||||
@Slf4j
|
||||
public class GeoportalHealthCheck implements HealthCheck {
|
||||
|
||||
private static final String SERVICE_NAME = "geooportal-service";
|
||||
|
||||
/**
|
||||
* Call.
|
||||
*
|
||||
* @return the health check response
|
||||
*/
|
||||
@Override
|
||||
public HealthCheckResponse call() {
|
||||
log.info(GeoportalHealthCheck.class.getSimpleName() + " call");
|
||||
HealthCheckResponse response = HealthCheckResponse.named(SERVICE_NAME).state(true).build();
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package org.gcube.application.geoportal.service.rest.health;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class HealthCheckResponseSerializer extends JsonSerializer<HealthCheckResponse> {
|
||||
|
||||
@Override
|
||||
public void serialize(HealthCheckResponse response, JsonGenerator gen, SerializerProvider serializers)
|
||||
throws IOException {
|
||||
gen.writeStartObject();
|
||||
if (response.getName() != null)
|
||||
gen.writeStringField("name", response.getName());
|
||||
if (response.getState() != null)
|
||||
gen.writeStringField("state", response.getState().toString());
|
||||
|
||||
response.getData().ifPresent(data -> {
|
||||
try {
|
||||
gen.writeObjectFieldStart("data");
|
||||
for (Map.Entry<String, Object> entry : data.entrySet()) {
|
||||
gen.writeObjectField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
gen.writeEndObject();
|
||||
} catch (IOException e) {
|
||||
log.warn("Error on serializing the data field", e);
|
||||
}
|
||||
});
|
||||
|
||||
gen.writeEndObject();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package org.gcube.application.geoportal.service.rest.health;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheck;
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
import org.eclipse.microprofile.health.HealthCheckResponseBuilder;
|
||||
import org.eclipse.microprofile.health.Liveness;
|
||||
import org.eclipse.microprofile.health.Readiness;
|
||||
import org.gcube.application.cms.implementations.ImplementationProvider;
|
||||
import org.gcube.application.geoportal.common.model.configuration.MongoConnection;
|
||||
import org.gcube.application.geoportal.service.model.internal.db.Mongo;
|
||||
import org.gcube.common.scope.api.ScopeProvider;
|
||||
|
||||
import com.mongodb.client.MongoIterable;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
/**
|
||||
* The Class MongoHealthCheck.
|
||||
*
|
||||
* @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
|
||||
*
|
||||
* Oct 22, 2024
|
||||
*/
|
||||
@Readiness
|
||||
@Liveness
|
||||
@Slf4j
|
||||
public class MongoHealthCheck implements HealthCheck {
|
||||
|
||||
private String context;
|
||||
private Boolean includeCollections = false;
|
||||
public static final String SERVICE_NAME = "mongo";
|
||||
|
||||
/**
|
||||
* Call.
|
||||
*
|
||||
* @return the health check response
|
||||
*/
|
||||
@Override
|
||||
public HealthCheckResponse call() {
|
||||
return checkMongo(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new mongo health check.
|
||||
*
|
||||
* @param context the context
|
||||
* @param includeCollections the include collections
|
||||
*/
|
||||
public MongoHealthCheck(String context, Boolean includeCollections) {
|
||||
this.context = context;
|
||||
this.includeCollections = includeCollections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check mongo.
|
||||
*
|
||||
* @param context the context
|
||||
* @return the health check response
|
||||
*/
|
||||
private HealthCheckResponse checkMongo(String context) {
|
||||
log.debug("checkMongo in the context: {}", context);
|
||||
HealthCheckResponseBuilder buildHCRBuilder = HealthCheckResponse.named(SERVICE_NAME);
|
||||
Mongo mongo = null;
|
||||
try {
|
||||
ScopeProvider.instance.set(context);
|
||||
mongo = ImplementationProvider.get().getProvidedObjectByClass(Mongo.class);
|
||||
buildHCRBuilder = appendMongoInfo(buildHCRBuilder, mongo.getConnection());
|
||||
buildHCRBuilder.state(true);
|
||||
if (includeCollections!=null && includeCollections) {
|
||||
MongoIterable<String> collections = mongo.getTheClient()
|
||||
.getDatabase(mongo.getConnection().getDatabase()).listCollectionNames();
|
||||
log.info("listCollectionNames is null: {}", collections == null);
|
||||
int i = 1;
|
||||
for (String coll : collections) {
|
||||
log.debug("adding collection: {}", coll);
|
||||
buildHCRBuilder.withData("collection_" + i, coll);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
log.info("checkMongo is OK in the context: {}", context);
|
||||
return buildHCRBuilder.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Error on checkMongo: ", e);
|
||||
log.warn("checkMongo is KO in the context: {}", context);
|
||||
buildHCRBuilder.state(false);
|
||||
if (mongo != null) {
|
||||
MongoConnection connection = null;
|
||||
try {
|
||||
connection = mongo.getConnection();
|
||||
buildHCRBuilder = appendMongoInfo(buildHCRBuilder, connection);
|
||||
} catch (Exception e1) {
|
||||
buildHCRBuilder.withData("hosts", connection.getHosts() + "");
|
||||
}
|
||||
|
||||
}
|
||||
return buildHCRBuilder.build();
|
||||
} finally {
|
||||
ScopeProvider.instance.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append mongo info.
|
||||
*
|
||||
* @param buildHCRBuilder the build HCR builder
|
||||
* @param connection the connection
|
||||
* @return the health check response builder
|
||||
*/
|
||||
private HealthCheckResponseBuilder appendMongoInfo(HealthCheckResponseBuilder buildHCRBuilder,
|
||||
MongoConnection connection) {
|
||||
buildHCRBuilder.withData("hosts", connection.getHosts() + "");
|
||||
buildHCRBuilder.withData("db_name ", connection.getDatabase());
|
||||
return buildHCRBuilder;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
<web-app>
|
||||
<servlet>
|
||||
<servlet-name>org.gcube.application.geoportal.service.GeoPortalService</servlet-name>
|
||||
<servlet-name>org.gcube.application.geoportal.service.GeoPortalService
|
||||
</servlet-name>
|
||||
<servlet-class>org.glassfish.jersey.servlet.ServletContainer
|
||||
</servlet-class>
|
||||
<init-param>
|
||||
<param-name>javax.ws.rs.Application</param-name>
|
||||
<param-value>org.gcube.application.geoportal.service.GeoPortalService</param-value>
|
||||
<param-value>org.gcube.application.geoportal.service.GeoPortalService
|
||||
</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>jersey.config.server.provider.classnames</param-name>
|
||||
|
@ -20,16 +22,17 @@
|
|||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>default</servlet-name>
|
||||
<url-pattern>/docs/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
<servlet-mapping>
|
||||
<servlet-name>default</servlet-name>
|
||||
<url-pattern>/api-docs/*</url-pattern>
|
||||
<servlet-name>default</servlet-name>
|
||||
<url-pattern>/docs/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>org.gcube.application.geoportal.service.GeoPortalService</servlet-name>
|
||||
<servlet-name>default</servlet-name>
|
||||
<url-pattern>/api-docs/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>org.gcube.application.geoportal.service.GeoPortalService
|
||||
</servlet-name>
|
||||
<url-pattern>/srv/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
</web-app>
|
|
@ -0,0 +1,32 @@
|
|||
package org.gcube.application.geoportal.service.health;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
import org.gcube.application.geoportal.service.rest.health.HealthCheckResponseSerializer;
|
||||
|
||||
public class HealthCheckSerializer {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
HealthCheckResponse response = HealthCheckResponse.named("geooportal-service")
|
||||
.state(true)
|
||||
.withData("status", "healthy")
|
||||
.build();
|
||||
|
||||
// Configura ObjectMapper con il serializer personalizzato
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
SimpleModule module = new SimpleModule();
|
||||
module.addSerializer(HealthCheckResponse.class, new HealthCheckResponseSerializer());
|
||||
mapper.registerModule(module);
|
||||
|
||||
// Serializza l'oggetto HealthCheckResponse in JSON
|
||||
String json = mapper.writeValueAsString(response);
|
||||
|
||||
// Stampa il JSON serializzato
|
||||
System.out.println(json);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
## [v1.1.4]
|
||||
- Improved logs
|
||||
- Added fallback on /geoserver/rest path [#28150#note-8]
|
||||
- Improved some business logics
|
||||
|
||||
## [v1.1.3]
|
||||
- Added apply regex business logic [#26322]
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>sdi-plugins</artifactId>
|
||||
<version>1.1.4</version>
|
||||
<version>1.1.4-SNAPSHOT</version>
|
||||
<name>gCube CMS - SDI Plugins</name>
|
||||
|
||||
<parent>
|
||||
|
|
|
@ -23,7 +23,11 @@ import lombok.extern.slf4j.Slf4j;
|
|||
@Slf4j
|
||||
public abstract class SDIAbstractPlugin extends AbstractPlugin implements InitializablePlugin {
|
||||
|
||||
protected static AbstractScopedMap<SDIManagerWrapper> sdiCache;
|
||||
public static final String POSTGIS_CREDENTIALS = "POSTGIS-CREDENTIALS";
|
||||
|
||||
public static final String SDI_CACHE = "SDI-CACHE";
|
||||
|
||||
protected static AbstractScopedMap<SDIManagerWrapper> sdiCache;
|
||||
|
||||
protected static AbstractScopedMap<DatabaseConnection> postgisCache;
|
||||
|
||||
|
@ -31,7 +35,7 @@ public abstract class SDIAbstractPlugin extends AbstractPlugin implements Initia
|
|||
private static void initCache(){
|
||||
if(sdiCache==null) {
|
||||
log.info("Creating internal caches.. ");
|
||||
sdiCache = new AbstractScopedMap<SDIManagerWrapper>("SDI-CACHE") {
|
||||
sdiCache = new AbstractScopedMap<SDIManagerWrapper>(SDI_CACHE) {
|
||||
@Override
|
||||
protected SDIManagerWrapper retrieveObject(String context) throws ConfigurationException {
|
||||
try {
|
||||
|
@ -44,7 +48,7 @@ public abstract class SDIAbstractPlugin extends AbstractPlugin implements Initia
|
|||
sdiCache.setTTL(Duration.of(10, ChronoUnit.MINUTES));
|
||||
}
|
||||
if(postgisCache==null) {
|
||||
postgisCache = new AbstractScopedMap<DatabaseConnection>("POSTGIS-CREDENTIALS") {
|
||||
postgisCache = new AbstractScopedMap<DatabaseConnection>(POSTGIS_CREDENTIALS) {
|
||||
@Override
|
||||
protected DatabaseConnection retrieveObject(String context) throws ConfigurationException {
|
||||
try {
|
||||
|
|
|
@ -463,6 +463,10 @@ public class SDIIndexerPlugin extends SDIAbstractPlugin implements IndexerPlugin
|
|||
DatabaseConnection connectionParameters = null;
|
||||
try {
|
||||
connectionParameters = postgisCache.getObject();
|
||||
|
||||
if(connectionParameters==null)
|
||||
throw new ConfigurationException("connectionParameters is null");
|
||||
|
||||
// Getting connection
|
||||
Connection connection = DriverManager.getConnection(connectionParameters.getUrl(),
|
||||
connectionParameters.getUser(), connectionParameters.getPwd());
|
||||
|
|
Loading…
Reference in New Issue