Initial Check-In
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b377fd1 --- /dev/null +++ b/.gitignore
@@ -0,0 +1,15 @@ +.git +.idea/**/*.xml +.settings +.classpath +target +/.idea/compiler.xml +/.idea/*.xml +/.idea/modules.xml +/.idea/vcs.xml +/bin/ +*.iml +.project +.classpath +.idea +/dependency-reduced-pom.xml
diff --git a/README.md b/README.md new file mode 100644 index 0000000..fc24ab4 --- /dev/null +++ b/README.md
@@ -0,0 +1,13 @@ +# micsCentral + +How to start the micsCentral application +--- + +1. Run `mvn clean install` to build your application +1. Start application with `java -jar target/mics-central-service-0.1.1-SNAPSHOT.jar server config.yml` +1. To check that your application is running enter url `http://localhost:8080` + +Health Check +--- + +To see your applications health enter url `http://localhost:8081/healthcheck`
diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..254c82a --- /dev/null +++ b/pom.xml
@@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project + xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + <prerequisites> + <maven>3.0.0</maven> + </prerequisites> + + <groupId>pta.de</groupId> + <artifactId>mics-central-service</artifactId> + <version>0.1.1-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>micsCentral</name> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + <dropwizard.version>1.2.0</dropwizard.version> + <mainClass>pta.de.micsCentralApplication</mainClass> + <easymock.version>3.4</easymock.version> + <powermock-api-easymock.version>1.6.6</powermock-api-easymock.version> + <jacoco-maven-plugin.version>0.7.9</jacoco-maven-plugin.version> + <commons-io.version>2.5</commons-io.version> + <gson.version>2.8.0</gson.version> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>io.dropwizard</groupId> + <artifactId>dropwizard-bom</artifactId> + <version>${dropwizard.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>io.dropwizard</groupId> + <artifactId>dropwizard-core</artifactId> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>${commons-io.version}</version> + </dependency> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>${gson.version}</version> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <version>${easymock.version}</version> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-junit4</artifactId> + <version>${powermock-api-easymock.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-easymock</artifactId> + <version>${powermock-api-easymock.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>${jacoco-maven-plugin.version}</version> + </dependency> + </dependencies> + + <build> + <finalName>${project.artifactId}</finalName> + <resources> + <resource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + </resource> + </resources> + <plugins> + <plugin> + <artifactId>maven-shade-plugin</artifactId> + <version>2.4.1</version> + <configuration> + <createDependencyReducedPom>true</createDependencyReducedPom> + <transformers> + <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <mainClass>${mainClass}</mainClass> + </transformer> + </transformers> + <!-- exclude signed Manifests --> + <filters> + <filter> + <artifact>*:*</artifact> + <excludes> + <exclude>META-INF/*.SF</exclude> + <exclude>META-INF/*.DSA</exclude> + <exclude>META-INF/*.RSA</exclude> + </excludes> + </filter> + </filters> + </configuration> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <version>2.6</version> + <configuration> + <archive> + <manifest> + <addClasspath>true</addClasspath> + <mainClass>${mainClass}</mainClass> + </manifest> + </archive> + </configuration> + </plugin> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.6.1</version> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + <plugin> + <artifactId>maven-source-plugin</artifactId> + <version>2.4</version> + <executions> + <execution> + <id>attach-sources</id> + <goals> + <goal>jar</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-javadoc-plugin</artifactId> + <version>2.10.3</version> + <executions> + <execution> + <id>attach-javadocs</id> + <goals> + <goal>jar</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <reporting> + <plugins> + <plugin> + <artifactId>maven-project-info-reports-plugin</artifactId> + <version>2.8.1</version> + <configuration> + <dependencyLocationsEnabled>false</dependencyLocationsEnabled> + <dependencyDetailsEnabled>false</dependencyDetailsEnabled> + </configuration> + </plugin> + <plugin> + <artifactId>maven-javadoc-plugin</artifactId> + <version>2.10.3</version> + </plugin> + </plugins> + </reporting> +</project>
diff --git a/serviceConfigDevLocal.yml b/serviceConfigDevLocal.yml new file mode 100644 index 0000000..59921dc --- /dev/null +++ b/serviceConfigDevLocal.yml
@@ -0,0 +1,25 @@ +servicesDistributionFileName: servicesDistributionDevLocal.json + + +logging: + level: INFO + appenders: + - type: file + currentLogFilename: /log/mics-central-service.log + threshold: ALL + archive: true + archivedLogFilenamePattern: /log/mics-central-service-%d.log + archivedFileCount: 5 + timeZone: UTC + loggers: + pta: DEBUG + org.eclipse.jetty.servlets: DEBUG + +server: + applicationConnectors: + - type: http + port: 9010 + adminConnectors: + - type: http + port: 9011 +
diff --git a/servicesDistributionDevLocal.json b/servicesDistributionDevLocal.json new file mode 100644 index 0000000..ed508c4 --- /dev/null +++ b/servicesDistributionDevLocal.json
@@ -0,0 +1,18 @@ +[ + { + "active": "true", + "clustername": "elogbook.openK", + "description": "elogbook service cluster for openKonsequenz", + "distributions": [ + { + "active": "true", + "name": "auth-n-auth.mics", + "protocol": "http", + "host": "172.18.22.160", + "portApp": "9002", + "portHealth": "9003", + "description": "Authentication Service" + } + ] + } +] \ No newline at end of file
diff --git a/src/main/java/pta/de/api/ServiceDistributionCluster.java b/src/main/java/pta/de/api/ServiceDistributionCluster.java new file mode 100644 index 0000000..baa8a2b --- /dev/null +++ b/src/main/java/pta/de/api/ServiceDistributionCluster.java
@@ -0,0 +1,149 @@ +package pta.de.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.hibernate.validator.constraints.NotEmpty; + + +public class ServiceDistributionCluster { + + public static class ServiceDistribution { + + public boolean active; + + @NotEmpty + public String name; + + @NotEmpty + public String host; + + @NotEmpty + public String protocol; + + @NotEmpty + public int portApp; + + public int portHealth; + + public String description; + + public ServiceDistribution() { + } + + @JsonProperty + public boolean isActive() { + return active; + } + + @JsonProperty + public void setActive(boolean active) { + this.active = active; + } + + @JsonProperty + public String getName() { + return name; + } + + @JsonProperty + public void setName(String name) { + this.name = name; + } + + @JsonProperty + public String getHost() { + return host; + } + + @JsonProperty + public void setHost(String host) { + this.host = host; + } + + @JsonProperty + public String getProtocol() { + return protocol; + } + + @JsonProperty + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + @JsonProperty + public int getPortApp() { + return portApp; + } + + @JsonProperty + public void setPortApp(int portApp) { + this.portApp = portApp; + } + + @JsonProperty + public String getDescription() { + return description; + } + + @JsonProperty + public void setDescription(String description) { + this.description = description; + } + + @JsonProperty + public int getPortHealth() { + return portHealth; + } + + @JsonProperty + public void setPortHealth(int portHealth) { + this.portHealth = portHealth; + } + } + + public boolean active; + + @NotEmpty + public String clustername; + + public String description; + + ServiceDistribution[] distributions; + + @JsonProperty + public boolean isActive() { + return active; + } + + @JsonProperty + public void setActive(boolean active) { + this.active = active; + } + + @JsonProperty + public String getClustername() { + return clustername; + } + + @JsonProperty + public void setClustername(String clustername) { + this.clustername = clustername; + } + + @JsonProperty + public String getDescription() { + return description; + } + + @JsonProperty + public void setDescription(String description) { + this.description = description; + } + + public ServiceDistribution[] getDistributions() { + return distributions; + } + + public void setDistributions(ServiceDistribution[] distributions) { + this.distributions = distributions; + } +}
diff --git a/src/main/java/pta/de/api/VersionInfo.java b/src/main/java/pta/de/api/VersionInfo.java new file mode 100644 index 0000000..1a95df3 --- /dev/null +++ b/src/main/java/pta/de/api/VersionInfo.java
@@ -0,0 +1,15 @@ +package pta.de.api; + +public class VersionInfo { + private String backendVersion; + + public String getBackendVersion() { + return backendVersion; + } + + public void setBackendVersion(String backendVersion) { + this.backendVersion = backendVersion; + } + + +}
diff --git a/src/main/java/pta/de/core/common/GsonUTCDateAdapter.java b/src/main/java/pta/de/core/common/GsonUTCDateAdapter.java new file mode 100644 index 0000000..024080b --- /dev/null +++ b/src/main/java/pta/de/core/common/GsonUTCDateAdapter.java
@@ -0,0 +1,35 @@ +package pta.de.core.common; + +import com.google.gson.*; + +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +public class GsonUTCDateAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> { + + private final DateFormat dateFormat; + + public GsonUTCDateAdapter() { + dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); //This is the format I need + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); //This is the key line which converts the date to UTC which cannot be accessed with the default serializer + } + + @Override + public synchronized JsonElement serialize(Date date, Type type, JsonSerializationContext jsonSerializationContext) { + return new JsonPrimitive(dateFormat.format(date)); + } + + @Override + public synchronized Date deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) { + try { + return dateFormat.parse(jsonElement.getAsString()); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } +} \ No newline at end of file
diff --git a/src/main/java/pta/de/core/common/JsonGeneratorBase.java b/src/main/java/pta/de/core/common/JsonGeneratorBase.java new file mode 100644 index 0000000..af20a84 --- /dev/null +++ b/src/main/java/pta/de/core/common/JsonGeneratorBase.java
@@ -0,0 +1,16 @@ +package pta.de.core.common; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.Date; + +public class JsonGeneratorBase { + private JsonGeneratorBase() {} + public static Gson getGson() { + return new GsonBuilder() + .registerTypeAdapter(Date.class, new GsonUTCDateAdapter()) + .disableHtmlEscaping() + .create(); + } +}
diff --git a/src/main/java/pta/de/core/common/util/ResourceLoaderBase.java b/src/main/java/pta/de/core/common/util/ResourceLoaderBase.java new file mode 100644 index 0000000..ba3c3e6 --- /dev/null +++ b/src/main/java/pta/de/core/common/util/ResourceLoaderBase.java
@@ -0,0 +1,26 @@ +package pta.de.core.common.util; + +import org.apache.commons.io.IOUtils; + +import java.io.InputStream; +import java.io.StringWriter; + +public class ResourceLoaderBase { + private String stream2String(InputStream is) { + StringWriter writer = new StringWriter(); + try { + IOUtils.copy(is, writer, "UTF-8"); + } catch (Exception e) { // NOSONAR + return ""; + } + return writer.toString(); + + + } + + public String loadStringFromResource(String filename) { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + InputStream jsonstream = classLoader.getResourceAsStream(filename); + return stream2String(jsonstream); + } +}
diff --git a/src/main/java/pta/de/core/controller/BackendController.java b/src/main/java/pta/de/core/controller/BackendController.java new file mode 100644 index 0000000..82e5974 --- /dev/null +++ b/src/main/java/pta/de/core/controller/BackendController.java
@@ -0,0 +1,44 @@ +package pta.de.core.controller; + +import com.google.common.collect.Lists; +import org.apache.log4j.Logger; +import org.eclipse.jetty.http.HttpStatus; +import pta.de.api.ServiceDistributionCluster; +import pta.de.api.VersionInfo; +import pta.de.core.exceptions.HttpStatusException; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class BackendController { + private static Logger logger = Logger.getLogger(BackendController.class.getName()); + + public ServiceDistributionCluster readServerDistribution(String cluster) throws HttpStatusException { + ServiceDistributionCluster[] dcs = ServicesConfigCache.getInstance().getCache(); + + for (ServiceDistributionCluster item : dcs) { + if (item.clustername.equalsIgnoreCase(cluster) && item.isActive()) { + return removeInactiveItems(item); + } + } + throw new HttpStatusException(HttpStatus.NOT_FOUND_404); + } + + public VersionInfo getVersionInfo( String version ) { + VersionInfo vi = new VersionInfo(); + vi.setBackendVersion( version ); + return vi; + } + + private ServiceDistributionCluster removeInactiveItems( ServiceDistributionCluster cluster ) { + List<ServiceDistributionCluster.ServiceDistribution> dlist = Arrays.asList(cluster.getDistributions()); + dlist = dlist.stream().filter(d -> d.isActive()).collect(Collectors.toList()); + ServiceDistributionCluster.ServiceDistribution distArr[] + = new ServiceDistributionCluster.ServiceDistribution[dlist.size()]; + cluster.setDistributions(dlist.toArray(distArr)); + return cluster; + } + + +}
diff --git a/src/main/java/pta/de/core/controller/BaseWebService.java b/src/main/java/pta/de/core/controller/BaseWebService.java new file mode 100644 index 0000000..123e859 --- /dev/null +++ b/src/main/java/pta/de/core/controller/BaseWebService.java
@@ -0,0 +1,60 @@ +package pta.de.core.controller; + +import org.apache.log4j.Logger; +import org.eclipse.jetty.http.HttpStatus; +import pta.de.core.exceptions.HttpStatusException; + +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public abstract class BaseWebService { + public interface Invokable <T> { + public T invoke() throws Exception; + } + private final Map<String, Long> currentTimeMeasures = new HashMap<>(); + private final Logger logger; + + public BaseWebService(Logger logger) { + this.logger = logger; + } + + protected Response invokeRunnable(Invokable runnable) + { + try { // NOSONAR + Object o = runnable.invoke(); + return Response.ok(o).build(); + } catch (HttpStatusException hse) { + logger.error(hse); + return Response.status(hse.getHttpStatus()).build(); + } + catch (Exception e) { + logger.error("Caught unexpected Exception:", e); + return Response.status(HttpStatus.INTERNAL_SERVER_ERROR_500).build(); + } + } + + + + protected static String getVersionString() { + try { + // determine static VersionInfo + final Properties properties = new Properties(); + + properties.load(BaseWebService.class.getClassLoader().getResourceAsStream("project.properties")); + + String beversion = properties.getProperty("backend.version"); + if( beversion.contains("$")) { + beversion = "LOCAL-DEV"; + } + return beversion; + } catch (IOException e) { + throw new RuntimeException("Exception during start up"); + } + } + + + +}
diff --git a/src/main/java/pta/de/core/controller/InitServicesConfigCacheJob.java b/src/main/java/pta/de/core/controller/InitServicesConfigCacheJob.java new file mode 100644 index 0000000..1c39866 --- /dev/null +++ b/src/main/java/pta/de/core/controller/InitServicesConfigCacheJob.java
@@ -0,0 +1,19 @@ +package pta.de.core.controller; + +import org.apache.log4j.Logger; + +import java.util.Timer; +import java.util.TimerTask; + +public class InitServicesConfigCacheJob { + + private static final Logger LOGGER = Logger.getLogger(InitServicesConfigCacheJob.class.getName()); + + + public static void init( String configFileName ) { + LOGGER.info("InitServicesConfigCacheJob called"); + TimerTask timerTask = new ServicesConfigCacheTimerTask(configFileName); + Timer timer = new Timer(); + timer.scheduleAtFixedRate(timerTask, 100, 5000L); + } +}
diff --git a/src/main/java/pta/de/core/controller/ServicesConfigCache.java b/src/main/java/pta/de/core/controller/ServicesConfigCache.java new file mode 100644 index 0000000..0fb4435 --- /dev/null +++ b/src/main/java/pta/de/core/controller/ServicesConfigCache.java
@@ -0,0 +1,74 @@ +package pta.de.core.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.log4j.Logger; +import org.eclipse.jetty.http.HttpStatus; +import pta.de.api.ServiceDistributionCluster; + +import pta.de.core.exceptions.HttpStatusException; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Set; + +public class ServicesConfigCache { + private static final Logger logger = Logger.getLogger(ServicesConfigCache.class.getName()); + private static final ServicesConfigCache SERIVCECONFIGCACHE_INSTANCE = new ServicesConfigCache(); + private String criticalSection = "CRITICAL_SECTION"; + + private ServiceDistributionCluster[] cache; + + private ServicesConfigCache(){ + } + + public static ServicesConfigCache getInstance() { + return SERIVCECONFIGCACHE_INSTANCE; + } + + + public void readServerDistribution(String configFileName) throws HttpStatusException { + try { + ServiceDistributionCluster[] dcs = readServerDistributionFromText(new String(Files.readAllBytes(Paths.get(configFileName)), Charset.forName("UTF-8"))); + cache = dcs; + + } catch (IOException e) { + logger.error("Could not read file "+configFileName+"!", e); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR_500); + } + } + + private ServiceDistributionCluster[] readServerDistributionFromText(String jsonText) throws HttpStatusException { + ServiceDistributionCluster[] dcs; + + try { + final ObjectMapper mapper = new ObjectMapper(); + dcs = mapper.readValue(jsonText, ServiceDistributionCluster[].class); + } catch (IOException e) { + logger.error("Could not parse file!", e); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR_500); + } + + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + Validator validator = factory.getValidator(); + + for (ServiceDistributionCluster item : dcs) { + Set<ConstraintViolation<ServiceDistributionCluster>> violations + = validator.validate(item); + if (violations.size() > 0) { + logger.error("Error in configfile!: "); + throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR_500); + } + } + return dcs; + } + + public ServiceDistributionCluster[] getCache() { + return cache; + } +}
diff --git a/src/main/java/pta/de/core/controller/ServicesConfigCacheTimerTask.java b/src/main/java/pta/de/core/controller/ServicesConfigCacheTimerTask.java new file mode 100644 index 0000000..47f6b02 --- /dev/null +++ b/src/main/java/pta/de/core/controller/ServicesConfigCacheTimerTask.java
@@ -0,0 +1,31 @@ +package pta.de.core.controller; + +import org.apache.log4j.Logger; +import pta.de.core.exceptions.HttpStatusException; + +import java.util.ArrayList; +import java.util.List; +import java.util.TimerTask; + +public class ServicesConfigCacheTimerTask extends TimerTask { + private String configFileName; + + public ServicesConfigCacheTimerTask( String configFileName ) { + this.configFileName = configFileName; + } + + private static final Logger logger = Logger.getLogger(ServicesConfigCacheTimerTask.class.getName()); + + @Override + public void run() { + logger.debug("ServicesConfigCacheTimerTask started"); + + try { + ServicesConfigCache.getInstance().readServerDistribution(this.configFileName); + } catch (HttpStatusException e) { + logger.error("Error reading ServerDistributionFile", e); + } + + logger.debug("ServicesConfigCacheTimerTask finished"); + } +}
diff --git a/src/main/java/pta/de/core/exceptions/HttpStatusException.java b/src/main/java/pta/de/core/exceptions/HttpStatusException.java new file mode 100644 index 0000000..ae0bb4f --- /dev/null +++ b/src/main/java/pta/de/core/exceptions/HttpStatusException.java
@@ -0,0 +1,13 @@ +package pta.de.core.exceptions; + +public class HttpStatusException extends Exception { + private int httpStatus; + + public HttpStatusException(int httpStatus ) { + this.httpStatus = httpStatus; + } + + public int getHttpStatus() { + return httpStatus; + } +}
diff --git a/src/main/java/pta/de/health/ConfigFilePresentHealthCheck.java b/src/main/java/pta/de/health/ConfigFilePresentHealthCheck.java new file mode 100644 index 0000000..9b116e7 --- /dev/null +++ b/src/main/java/pta/de/health/ConfigFilePresentHealthCheck.java
@@ -0,0 +1,20 @@ +package pta.de.health; + +import com.codahale.metrics.health.HealthCheck; +import pta.de.api.ServiceDistributionCluster; +import pta.de.core.controller.BackendController; +import pta.de.core.controller.ServicesConfigCache; + +public class ConfigFilePresentHealthCheck extends HealthCheck { + @Override + protected Result check() throws Exception { + ServiceDistributionCluster[] sdc = ServicesConfigCache.getInstance().getCache(); // Throws Exception if it fails + + if( sdc.length > 0 ) { + return Result.healthy(); + } + else { + return Result.unhealthy("No ServiceDistributionCluster available!"); + } + } +}
diff --git a/src/main/java/pta/de/micsCentralApplication.java b/src/main/java/pta/de/micsCentralApplication.java new file mode 100644 index 0000000..7f705e8 --- /dev/null +++ b/src/main/java/pta/de/micsCentralApplication.java
@@ -0,0 +1,68 @@ +package pta.de; + +import com.codahale.metrics.health.HealthCheck; +import io.dropwizard.Application; +import io.dropwizard.setup.Bootstrap; +import io.dropwizard.setup.Environment; +import org.eclipse.jetty.servlets.CrossOriginFilter; +import pta.de.core.controller.BackendController; +import pta.de.core.controller.InitServicesConfigCacheJob; +import pta.de.health.ConfigFilePresentHealthCheck; +import pta.de.resources.MicsCentralResource; + +import javax.servlet.DispatcherType; +import javax.servlet.FilterRegistration; +import java.util.EnumSet; + +public class micsCentralApplication extends Application<micsCentralConfiguration> { + + public static void main(final String[] args) throws Exception { + new micsCentralApplication().run(args); + } + + @Override + public String getName() { + return "micsCentral"; + } + + @Override + public void initialize(final Bootstrap<micsCentralConfiguration> bootstrap) { + } + + @Override + public void run(final micsCentralConfiguration configuration, + final Environment environment) { + + initAppEnvironment( configuration ); + + final MicsCentralResource micsCentralResource = new MicsCentralResource(); + final HealthCheck configFilePresentHC = new ConfigFilePresentHealthCheck(); + + environment.healthChecks().register("configFilePresent", configFilePresentHC ); + environment.jersey().register(micsCentralResource); + + configureCors(environment); + } + + private void initAppEnvironment( micsCentralConfiguration conf ) { + InitServicesConfigCacheJob.init(conf.getServicesDistributionFileName()); + } + + private void configureCors(Environment environment) { + final FilterRegistration.Dynamic cors = + environment.servlets().addFilter("CORS", CrossOriginFilter.class); + + // Configure CORS parameters + cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*"); + cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin,Authorization,X-XSRF-TOKEN"); + cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "OPTIONS,GET,PUT,POST,DELETE,HEAD"); + cors.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, "true"); + + // Add URL mapping + cors.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*"); + // DO NOT pass a preflight request to down-stream auth filters + // unauthenticated preflight requests should be permitted by spec + cors.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, Boolean.FALSE.toString()); + } + +}
diff --git a/src/main/java/pta/de/micsCentralConfiguration.java b/src/main/java/pta/de/micsCentralConfiguration.java new file mode 100644 index 0000000..3566350 --- /dev/null +++ b/src/main/java/pta/de/micsCentralConfiguration.java
@@ -0,0 +1,20 @@ +package pta.de; + +import io.dropwizard.Configuration; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.hibernate.validator.constraints.*; + +public class micsCentralConfiguration extends Configuration { + @NotEmpty + private String servicesDistributionFileName; + + @JsonProperty + public String getServicesDistributionFileName() { + return servicesDistributionFileName; + } + + @JsonProperty + public void setServicesDistributionFileName(String servicesDistributionFileName) { + this.servicesDistributionFileName = servicesDistributionFileName; + } +}
diff --git a/src/main/java/pta/de/resources/MicsCentralResource.java b/src/main/java/pta/de/resources/MicsCentralResource.java new file mode 100644 index 0000000..48e039e --- /dev/null +++ b/src/main/java/pta/de/resources/MicsCentralResource.java
@@ -0,0 +1,41 @@ +package pta.de.resources; + + +import org.apache.log4j.Logger; +import org.hibernate.validator.constraints.NotEmpty; +import pta.de.core.controller.BackendController; +import pta.de.core.controller.BaseWebService; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +@Path("/mics/central") +@Produces(MediaType.APPLICATION_JSON) +public class MicsCentralResource extends BaseWebService{ + private static Logger logger = Logger.getLogger(MicsCentralResource.class.getName()); + + public MicsCentralResource() { + super(logger); + } + + @GET + @Path("/serviceDistribution/{clustername}") + public Response getServiceDistribution(@PathParam("clustername") @NotEmpty String clustername) { + return invokeRunnable( () -> { + return new BackendController().readServerDistribution(clustername); + }); + } + + @GET + @Path("/versionInfo") + public Response getVersionInfo() { + return invokeRunnable( () -> { + return new BackendController().getVersionInfo(getVersionString()); + }); + } + +}
diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000..561b172 --- /dev/null +++ b/src/main/resources/banner.txt
@@ -0,0 +1,6 @@ +================================================================================ + + micsCentral + +================================================================================ +
diff --git a/src/main/resources/project.properties b/src/main/resources/project.properties new file mode 100644 index 0000000..25e06ec --- /dev/null +++ b/src/main/resources/project.properties
@@ -0,0 +1 @@ +backend.version =${project.version} \ No newline at end of file
diff --git a/src/test/java/pta/de/core/common/util/ResourceLoaderBaseTest.java b/src/test/java/pta/de/core/common/util/ResourceLoaderBaseTest.java new file mode 100644 index 0000000..c310452 --- /dev/null +++ b/src/test/java/pta/de/core/common/util/ResourceLoaderBaseTest.java
@@ -0,0 +1,15 @@ +package pta.de.core.common.util; + + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ResourceLoaderBaseTest { + @Test + public void testloadStringFromResourceError() { + ResourceLoaderBase rlb = new ResourceLoaderBase(); + String str = rlb.loadStringFromResource("UNKNOWN_FILE"); + assertEquals(str, ""); + } +}
diff --git a/src/test/java/pta/de/core/controller/BackendControllerTest.java b/src/test/java/pta/de/core/controller/BackendControllerTest.java new file mode 100644 index 0000000..ac3b32a --- /dev/null +++ b/src/test/java/pta/de/core/controller/BackendControllerTest.java
@@ -0,0 +1,69 @@ +package pta.de.core.controller; + +import org.eclipse.jetty.http.HttpStatus; +import org.junit.Test; +import org.powermock.reflect.Whitebox; +import pta.de.api.ServiceDistributionCluster; +import pta.de.core.common.util.ResourceLoaderBase; +import pta.de.core.exceptions.HttpStatusException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + + +public class BackendControllerTest { + + private ServiceDistributionCluster baseTestReadServerDistribution(String clustername, String jsonFile) throws Exception { + ResourceLoaderBase resourceLoaderBase = new ResourceLoaderBase(); + String json = resourceLoaderBase.loadStringFromResource(jsonFile); + + ServicesConfigCache scc = ServicesConfigCache.getInstance(); + ServiceDistributionCluster[] sdc = (ServiceDistributionCluster[]) + Whitebox.invokeMethod(scc,"readServerDistributionFromText", json); + + Whitebox.setInternalState(scc, "cache", sdc); + + BackendController be = new BackendController(); + + ServiceDistributionCluster ret = be.readServerDistribution(clustername); + + return ret; + } + + @Test + public void testReadServerDistribution() throws Exception { + + ServiceDistributionCluster sdc = baseTestReadServerDistribution("elogbook.openK", "testServiceDistributions.json"); + + assertEquals(sdc.clustername, "elogbook.openK"); + assertEquals(sdc.getDistributions().length, 1); + assertEquals(sdc.getDistributions()[0].host, "172.18.22.160"); + } + + @Test + public void testFailReadServerDistribution() throws Exception { + try { + baseTestReadServerDistribution("doesntMatter", "testServiceDist_False.json"); + } catch( HttpStatusException e ) { + assertEquals(e.getHttpStatus(), HttpStatus.INTERNAL_SERVER_ERROR_500); + } + } + + @Test + public void testReadServerDistribution_notfound() throws Exception { + try { + ServiceDistributionCluster sdc = baseTestReadServerDistribution("invalidCluster", "testServiceDistributions.json"); + } catch( HttpStatusException e ) { + assertEquals(e.getHttpStatus(), HttpStatus.NOT_FOUND_404); + } + } + + @Test + public void testReadServerDistribution_activeInactive() throws Exception { + ServiceDistributionCluster sdc = baseTestReadServerDistribution( "elogbook.openK", "testServiceDistributionsTwo_OneIsInactive.json"); + + assertEquals( 1, sdc.getDistributions().length); + assertTrue( sdc.getDistributions()[0].isActive()); + assertEquals( "172.18.22.160", sdc.getDistributions()[0].getHost()); + } +}
diff --git a/src/test/java/pta/de/core/controller/ServicesConfigCacheTest.java b/src/test/java/pta/de/core/controller/ServicesConfigCacheTest.java new file mode 100644 index 0000000..67407d5 --- /dev/null +++ b/src/test/java/pta/de/core/controller/ServicesConfigCacheTest.java
@@ -0,0 +1,48 @@ +package pta.de.core.controller; + +import org.eclipse.jetty.http.HttpStatus; +import org.junit.Test; +import org.powermock.reflect.Whitebox; +import pta.de.api.ServiceDistributionCluster; +import pta.de.core.common.util.ResourceLoaderBase; +import pta.de.core.exceptions.HttpStatusException; + +import static org.junit.Assert.assertEquals; + + +public class ServicesConfigCacheTest { + + private ServiceDistributionCluster[] baseTestReadServerDistribution(String jsonFile) throws Exception { + ResourceLoaderBase resourceLoaderBase = new ResourceLoaderBase(); + String json = resourceLoaderBase.loadStringFromResource(jsonFile); + + ServicesConfigCache scc = ServicesConfigCache.getInstance(); + + + + ServiceDistributionCluster[] sdc = (ServiceDistributionCluster[]) + Whitebox.invokeMethod(scc,"readServerDistributionFromText", json); + + return sdc; + } + + @Test + public void testReadServerDistribution() throws Exception { + + ServiceDistributionCluster[] sdc = baseTestReadServerDistribution("testServiceDistributions.json"); + + assertEquals(sdc[0].clustername, "elogbook.openK"); + assertEquals(sdc[0].getDistributions().length, 1); + assertEquals(sdc[0].getDistributions()[0].host, "172.18.22.160"); + } + + @Test + public void testFailReadServerDistribution() throws Exception { + try { + baseTestReadServerDistribution("testServiceDist_False.json"); + } catch( HttpStatusException e ) { + assertEquals(e.getHttpStatus(), HttpStatus.INTERNAL_SERVER_ERROR_500); + } + } + +}
diff --git a/src/test/java/pta/de/core/exceptions/HttpStatusExceptionTest.java b/src/test/java/pta/de/core/exceptions/HttpStatusExceptionTest.java new file mode 100644 index 0000000..9779181 --- /dev/null +++ b/src/test/java/pta/de/core/exceptions/HttpStatusExceptionTest.java
@@ -0,0 +1,15 @@ +package pta.de.core.exceptions; + + +import org.eclipse.jetty.http.HttpStatus; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class HttpStatusExceptionTest { + @Test + public void testAll() { + HttpStatusException hse = new HttpStatusException( 200 ); + assertEquals( hse.getHttpStatus(), HttpStatus.OK_200); + } +}
diff --git a/src/test/resources/testServiceDist_False.json b/src/test/resources/testServiceDist_False.json new file mode 100644 index 0000000..759fd24 --- /dev/null +++ b/src/test/resources/testServiceDist_False.json
@@ -0,0 +1,18 @@ +[ + { + "active": "true", + + "description": "elogbook service cluster for openKonsequenz", + "distributions": [ + { + "active": "true", + "name": "auth-n-auth.mics", + "protocol": "http", + "host": "172.18.22.160", + "portApp": "9002", + "portHealth": "9003", + "description": "Authentication Service" + } + ] + } +] \ No newline at end of file
diff --git a/src/test/resources/testServiceDistributions.json b/src/test/resources/testServiceDistributions.json new file mode 100644 index 0000000..40d8558 --- /dev/null +++ b/src/test/resources/testServiceDistributions.json
@@ -0,0 +1,18 @@ +[ + { + "active": "true", + "clustername": "elogbook.openK", + "description": "elogbook service cluster for openKonsequenz", + "distributions": [ + { + "active": "true", + "name": "auth-n-auth.mics", + "protocol": "http", + "host": "172.18.22.160", + "portApp": "9002", + "portHealth": "9003", + "description": "Authentication Service" + } + ] + } +] \ No newline at end of file
diff --git a/src/test/resources/testServiceDistributionsTwo_OneIsInactive.json b/src/test/resources/testServiceDistributionsTwo_OneIsInactive.json new file mode 100644 index 0000000..fe36fee --- /dev/null +++ b/src/test/resources/testServiceDistributionsTwo_OneIsInactive.json
@@ -0,0 +1,52 @@ +[ + { + "active": "false", + "clustername": "elogbook.openK", + "description": "elogbook service cluster for openKonsequenz", + "distributions": [ + { + "active": "true", + "name": "auth-n-auth.mics", + "protocol": "http", + "host": "172.18.22.160", + "portApp": "9002", + "portHealth": "9003", + "description": "Authentication Service" + }, + { + "active": "false", + "name": "auth-n-auth.mics", + "protocol": "http", + "host": "localhost", + "portApp": "9002", + "portHealth": "9003", + "description": "Authentication Service" + } + ] + }, + { + "active": "true", + "clustername": "elogbook.openK", + "description": "elogbook service cluster for openKonsequenz", + "distributions": [ + { + "active": "true", + "name": "auth-n-auth.mics", + "protocol": "http", + "host": "172.18.22.160", + "portApp": "9002", + "portHealth": "9003", + "description": "Authentication Service" + }, + { + "active": "false", + "name": "auth-n-auth.mics", + "protocol": "http", + "host": "localhost", + "portApp": "9002", + "portHealth": "9003", + "description": "Authentication Service" + } + ] + } +] \ No newline at end of file