blob: 77e7c5d6ebc5b7113f5eb667e053e5cdf560e477 [file] [log] [blame]
/*
* 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();
}
}