package com.ctrip.framework.apollo;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import com.ctrip.framework.apollo.build.MockInjector;
import com.ctrip.framework.apollo.core.dto.ServiceDTO;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.utils.ClassLoaderUtil;
import com.ctrip.framework.apollo.internals.DefaultInjector;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.gson.Gson;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public abstract class BaseIntegrationTest{
private static final int PORT = findFreePort();
private static final String metaServiceUrl = "http://localhost:" + PORT;
private static final String someAppName = "someAppName";
private static final String someInstanceId = "someInstanceId";
private static final String configServiceURL = "http://localhost:" + PORT;
protected static String someAppId;
protected static String someClusterName;
protected static String someDataCenter;
protected static int refreshInterval;
protected static TimeUnit refreshTimeUnit;
private Server server;
protected Gson gson = new Gson();
@BeforeClass
public static void beforeClass() throws Exception {
File apolloEnvPropertiesFile = new File(ClassLoaderUtil.getClassPath(), "apollo-env.properties");
Files.write("dev.meta=" + metaServiceUrl, apolloEnvPropertiesFile, Charsets.UTF_8);
apolloEnvPropertiesFile.deleteOnExit();
}
@Before
public void setUp() throws Exception {
someAppId = "1003171";
someClusterName = "someClusterName";
someDataCenter = "someDC";
refreshInterval = 5;
refreshTimeUnit = TimeUnit.MINUTES;
//as ConfigService is singleton, so we must manually clear its container
ConfigService.reset();
MockInjector.reset();
MockInjector.setDelegate(new DefaultInjector());
MockInjector.setInstance(ConfigUtil.class, new MockConfigUtil());
}
/**
* init and start a jetty server, remember to call server.stop when the task is finished
* @param handlers
* @throws Exception
*/
protected Server startServerWithHandlers(ContextHandler... handlers) throws Exception {
server = new Server(PORT);
ContextHandlerCollection contexts = new ContextHandlerCollection();
contexts.setHandlers(handlers);
contexts.addHandler(mockMetaServerHandler());
server.setHandler(contexts);
server.start();
return server;
}
@After
public void tearDown() throws Exception {
if (server != null && server.isStarted()) {
server.stop();
}
}
protected ContextHandler mockMetaServerHandler() {
return mockMetaServerHandler(false);
}
protected ContextHandler mockMetaServerHandler(final boolean failedAtFirstTime) {
final ServiceDTO someServiceDTO = new ServiceDTO();
someServiceDTO.setAppName(someAppName);
someServiceDTO.setInstanceId(someInstanceId);
someServiceDTO.setHomepageUrl(configServiceURL);
final AtomicInteger counter = new AtomicInteger(0);
ContextHandler context = new ContextHandler("/services/config");
context.setHandler(new AbstractHandler() {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
if (failedAtFirstTime && counter.incrementAndGet() == 1) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
baseRequest.setHandled(true);
return;
}
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(gson.toJson(Lists.newArrayList(someServiceDTO)));
baseRequest.setHandled(true);
}
});
return context;
}
protected void setRefreshInterval(int refreshInterval) {
BaseIntegrationTest.refreshInterval = refreshInterval;
}
protected void setRefreshTimeUnit(TimeUnit refreshTimeUnit) {
BaseIntegrationTest.refreshTimeUnit = refreshTimeUnit;
}
public static class MockConfigUtil extends ConfigUtil {
@Override
public String getAppId() {
return someAppId;
}
@Override
public String getCluster() {
return someClusterName;
}
@Override
public int getRefreshInterval() {
return refreshInterval;
}
@Override
public TimeUnit getRefreshIntervalTimeUnit() {
return refreshTimeUnit;
}
@Override
public Env getApolloEnv() {
return Env.DEV;
}
@Override
public String getDataCenter() {
return someDataCenter;
}
@Override
public int getLoadConfigQPS() {
return 200;
}
@Override
public int getLongPollQPS() {
return 200;
}
@Override
public String getDefaultLocalCacheDir() {
return ClassLoaderUtil.getClassPath();
}
@Override
public long getOnErrorRetryInterval() {
return 10;
}
@Override
public TimeUnit getOnErrorRetryIntervalTimeUnit() {
return TimeUnit.MILLISECONDS;
}
}
/**
* Returns a free port number on localhost.
*
* Heavily inspired from org.eclipse.jdt.launching.SocketUtil (to avoid a dependency to JDT just because of this).
* Slightly improved with close() missing in JDT. And throws exception instead of returning -1.
*
* @return a free port number on localhost
* @throws IllegalStateException if unable to find a free port
*/
private static int findFreePort() {
ServerSocket socket = null;
try {
socket = new ServerSocket(0);
socket.setReuseAddress(true);
int port = socket.getLocalPort();
try {
socket.close();
} catch (IOException e) {
// Ignore IOException on close()
}
return port;
} catch (IOException e) {
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
}
throw new IllegalStateException("Could not find a free TCP/IP port to start embedded Jetty HTTP Server on");
}
}