| /* |
| * 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.fineract.cn.test.servicestarter; |
| |
| import org.apache.fineract.cn.api.util.ApiFactory; |
| import org.apache.fineract.cn.test.env.ExtraProperties; |
| import org.apache.fineract.cn.test.env.TestEnvironment; |
| import org.eclipse.aether.resolution.ArtifactResolutionException; |
| import org.junit.rules.ExternalResource; |
| import org.springframework.cloud.client.ServiceInstance; |
| import org.springframework.cloud.client.discovery.DiscoveryClient; |
| import org.springframework.cloud.netflix.feign.FeignClient; |
| import org.springframework.core.annotation.AnnotatedElementUtils; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.List; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * @author Myrle Krantz |
| */ |
| @SuppressWarnings({"unused", "WeakerAccess"}) |
| public class Microservice<T> extends ExternalResource { |
| |
| private static final long MAX_WAIT_DEFAULT = 150; |
| |
| private final Class<T> clazz; |
| private final String artifactName; |
| private final String artifactVersion; |
| final TestEnvironment processEnvironment; |
| final IntegrationTestEnvironment integrationTestEnvironment; |
| final String applicationName; |
| private ApiFactory apiFactory; |
| private Process process; |
| private T api; |
| private Integer debuggingPort = null; |
| private boolean debugSuspend = false; |
| private long maxWait = MAX_WAIT_DEFAULT; |
| |
| public Microservice( |
| final Class<T> clazz, |
| final String artifactName, |
| final String artifactVersion, |
| final IntegrationTestEnvironment integrationTestEnvironment) { |
| this.clazz = clazz; |
| this.artifactName = artifactName; |
| this.artifactVersion = artifactVersion; |
| applicationName = AnnotatedElementUtils.getMergedAnnotationAttributes(clazz, FeignClient.class).getString("value"); |
| this.processEnvironment = new TestEnvironment(applicationName); |
| processEnvironment.setProperty(TestEnvironment.SERVER_PORT_PROPERTY, integrationTestEnvironment.getFreshPort().toString()); |
| processEnvironment.setKeyPair(integrationTestEnvironment.getSeshatKeyTimestamp(), integrationTestEnvironment.getSeshatPublicKey(), integrationTestEnvironment.getSeshatPrivateKey()); |
| |
| //https://github.com/spring-cloud/spring-cloud-netflix/issues/373 |
| //http://blog.abhijitsarkar.org/technical/netflix-eureka/ |
| processEnvironment.setProperty("eureka.client.serviceUrl.defaultZone", EurekaForTest.DEFAULT_ZONE); |
| processEnvironment.setProperty(TestEnvironment.SPRING_CLOUD_DISCOVERY_ENABLED_PROPERTY, "true"); |
| processEnvironment.setProperty("eureka.instance.hostname", "localhost"); |
| processEnvironment.setProperty("eureka.client.fetchRegistry", "true"); |
| processEnvironment.setProperty("eureka.registration.enabled", "true"); |
| processEnvironment.setProperty("eureka.instance.leaseRenewalIntervalInSeconds", "1"); //Speed up registration for test purposes. |
| processEnvironment.setProperty("eureka.client.initialInstanceInfoReplicationIntervalSeconds", "0"); //Speed up initial registration for test purposes. |
| processEnvironment.setProperty("eureka.client.instanceInfoReplicationIntervalSeconds", "1"); |
| processEnvironment.setProperty("activemq.brokerUrl", ActiveMQForTest.BIND_ADDRESS); |
| |
| processEnvironment.setProperty(TestEnvironment.RIBBON_USES_EUREKA_PROPERTY, "true"); |
| |
| this.integrationTestEnvironment = integrationTestEnvironment; |
| } |
| |
| public Microservice<T> addProperties(final ExtraProperties properties) { |
| properties.entrySet().forEach(x -> this.processEnvironment.setProperty(x.getKey(), x.getValue())); |
| |
| return this; |
| } |
| |
| public Microservice<T> debug(boolean suspend, int port) { |
| this.debuggingPort = port; |
| this.debugSuspend = suspend; |
| |
| return this; |
| } |
| |
| public Microservice<T> runInDebug() { |
| this.debuggingPort = integrationTestEnvironment.getFreshDebugPort(); |
| this.debugSuspend = false; |
| |
| return this; |
| } |
| |
| public Microservice<T> maxWait(final long maxWait) { |
| this.maxWait = maxWait; |
| return this; |
| } |
| |
| public Microservice<T> setApiFactory(final ApiFactory newValue) |
| { |
| this.apiFactory = newValue; |
| return this; |
| } |
| |
| public boolean waitTillRegistered(final DiscoveryClient discoveryClient) throws InterruptedException { |
| if (discoveryClient == null) { |
| return false; |
| } |
| |
| long nextWait = 1; |
| long sumWait = 0; |
| |
| boolean found = false; |
| |
| while (!found) { |
| final List<ServiceInstance> thisApp = discoveryClient.getInstances(applicationName); |
| found = !thisApp.isEmpty(); |
| if (!found) { |
| TimeUnit.SECONDS.sleep(nextWait); |
| sumWait += nextWait; |
| nextWait = nextWait * 2; |
| |
| if (sumWait > maxWait) { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| @Override |
| protected void before() throws InterruptedException, IOException, ArtifactResolutionException { |
| start(); |
| } |
| |
| @Override |
| protected void after() { |
| try { |
| kill(); |
| } catch (final InterruptedException e) { |
| System.out.println("Interrupt raised, but microservice is already going down, so ignoring."); |
| } |
| } |
| |
| public void start() throws ArtifactResolutionException, IOException, InterruptedException { |
| |
| final ArtifactResolver artifactResolver = new ArtifactResolver( |
| IntegrationTestEnvironment.getArtifactoryDirectory()); |
| |
| final File jarFile = artifactResolver.getJarFile(artifactName, "org.apache.fineract.cn." + artifactName, "service-boot", artifactVersion); |
| |
| final ProcessBuilder processBuilder; |
| if (debuggingPort == null) { |
| processBuilder = new ProcessBuilder(IntegrationTestEnvironment.getJava(), "-jar", jarFile.getAbsolutePath()); |
| } |
| else { |
| processBuilder = new ProcessBuilder(IntegrationTestEnvironment.getJava(), "-agentlib:jdwp=transport=dt_socket,server=y,suspend=" + (debugSuspend ? "y" : "n") + ",address=" + debuggingPort, |
| "-jar", jarFile.getAbsolutePath()); |
| } |
| processEnvironment.populateProcessEnvironment(processBuilder); |
| processBuilder.inheritIO(); |
| |
| process = processBuilder.start(); |
| } |
| |
| public int kill() throws InterruptedException { |
| process.destroy(); |
| process.waitFor(); |
| return process.exitValue(); |
| } |
| |
| public T api() { |
| if (this.api == null) { |
| this.api = this.apiFactory.create(clazz, processEnvironment.serverURI()); |
| } |
| return this.api; |
| } |
| |
| public String uri() { |
| return processEnvironment.serverURI(); |
| } |
| |
| public TestEnvironment getProcessEnvironment() |
| { |
| return processEnvironment; |
| } |
| |
| public String name() { |
| return applicationName; |
| } |
| |
| public Integer debuggingPort() { |
| return debuggingPort; |
| } |
| |
| public String toString() { |
| final StringBuilder ret = new StringBuilder(applicationName); |
| ret.append(" address:"); |
| ret.append(this.getProcessEnvironment().serverURI()); |
| |
| if (debuggingPort != null) { |
| ret.append(", debuggingPort: "); |
| ret.append(debuggingPort); |
| } |
| |
| return ret.toString(); |
| } |
| } |