package org.cloudifysource.usm;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.FileUtils;
import org.cloudifysource.dsl.internal.CloudifyConstants;
import org.cloudifysource.dsl.internal.CloudifyConstants.USMState;
import org.cloudifysource.dsl.internal.debug.DebugModes;
import org.cloudifysource.dsl.utils.ServiceUtils;
import org.cloudifysource.usm.shutdown.DefaultProcessKiller;
import org.cloudifysource.utilitydomain.data.ServiceInstanceAttemptData;
import org.cloudifysource.utilitydomain.openspaces.OpenspacesConstants;
import org.hyperic.sigar.ProcExe;
import org.hyperic.sigar.ProcState;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.openspaces.admin.Admin;
import org.openspaces.admin.AdminFactory;
import org.openspaces.admin.space.Space;
import org.openspaces.core.GigaSpace;
import org.openspaces.core.GigaSpaceConfigurer;
import org.openspaces.core.cluster.ClusterInfo;
import org.openspaces.core.properties.BeanLevelProperties;
import org.openspaces.core.space.UrlSpaceConfigurer;
import org.openspaces.pu.container.ProcessingUnitContainer;
import org.openspaces.pu.container.integrated.IntegratedProcessingUnitContainer;
import org.openspaces.pu.container.integrated.IntegratedProcessingUnitContainerProvider;
import org.openspaces.pu.service.ServiceMonitors;
import org.springframework.context.ApplicationContext;
import com.j_spaces.core.IJSpace;
public class FeaturesTest {
private static GigaSpace gigaspace;
private static GigaSpaceConfigurer gigaSpaceConfigurer;
private static UrlSpaceConfigurer urlSpaceConfigurer;
private static Admin admin;
private static final String DEBUG_LOCK_FILE_PATH = "target/test-classes/debug/ext/.cloudify_debugging.lock";;
@BeforeClass
public static void beforeClass() {
System.out.println("java.library.path is: " + System.getProperty("java.library.path"));
System.setProperty("com.sun.jini.reggie.initialUnicastDiscoveryPort", "4176");
System.setProperty("java.rmi.server.hostname", "127.0.0.1");
System.setProperty("com.gs.jini_lus.locators", "127.0.0.1:4176");
// System.setProperty("org.hyperic.sigar.path", Environment.getHomeDirectory() + "/lib/platform/sigar");
final ClusterInfo clusterInfo = new ClusterInfo(null, 1, null, 1, null);
urlSpaceConfigurer =
new UrlSpaceConfigurer("/./" + CloudifyConstants.MANAGEMENT_SPACE_NAME + "?locators=127.0.0.1:"
+ OpenspacesConstants.DEFAULT_LOCALCLOUD_LUS_PORT);
final IJSpace space =
urlSpaceConfigurer
.clusterInfo(clusterInfo)
.addProperty("com.j_spaces.core.container.directory_services.jini_lus.start-embedded-lus",
"true")
.space();
gigaSpaceConfigurer = new GigaSpaceConfigurer(space);
gigaspace = gigaSpaceConfigurer.gigaSpace();
admin = new AdminFactory().discoverUnmanagedSpaces().addLocator("127.0.0.1:4176").create();
final boolean found = admin.getLookupServices().waitFor(1, 5, TimeUnit.SECONDS);
if (!found) {
throw new IllegalStateException("Could not find a lookup service");
}
final Space testSpace = admin.getSpaces().waitFor(CloudifyConstants.MANAGEMENT_SPACE_NAME, 5, TimeUnit.SECONDS);
if (testSpace == null) {
throw new IllegalStateException("Could not locate management space in admin");
}
}
@Before
public void beforeTest() {
// delete all objects currently in space
gigaspace.clear(new Object());
USMUtils.shutdownAdmin();
}
@Ignore
@Test
public void testRetriesWithRetryLeft() throws IOException, InterruptedException {
final IntegratedProcessingUnitContainer ipuc = createContainer("classpath:/retries/META-INF/spring/pu.xml");
try {
final ApplicationContext ctx = ipuc.getApplicationContext();
final UniversalServiceManagerBean usm = ctx.getBean(UniversalServiceManagerBean.class);
Assert.assertNotNull(usm);
final ServiceInstanceAttemptData attempt = gigaspace.read(new ServiceInstanceAttemptData(), 20000);
Assert.assertNotNull("Expected to find attempt data in space", attempt);
Assert.assertEquals((Integer) 2, attempt.getCurrentAttemptNumber());
Assert.assertEquals((Integer) 1, attempt.getInstanceId());
Assert.assertEquals("groovyError", attempt.getServiceName());
Assert.assertEquals("default", attempt.getApplicationName());
} finally {
ipuc.close();
}
}
@Ignore
@Test
public void testRetriesWithNoRetryLeft() throws IOException, InterruptedException {
final ServiceInstanceAttemptData data = createServiceInstanceAttempDataTemplate();
data.setCurrentAttemptNumber(2);
gigaspace.write(data);
final IntegratedProcessingUnitContainer ipuc = createContainer("classpath:/retries/META-INF/spring/pu.xml");
try {
final ApplicationContext ctx = ipuc.getApplicationContext();
final UniversalServiceManagerBean usm = ctx.getBean(UniversalServiceManagerBean.class);
Assert.assertNotNull(usm);
waitForInstanceToReachStatus(usm, USMState.ERROR);
} finally {
ipuc.close();
}
}
@Ignore
@Test
public void testDebug() throws IOException, InterruptedException {
if (ServiceUtils.isWindows()) {
// The debug feature only works for linux
return;
}
final BeanLevelProperties blp = new BeanLevelProperties();
final Properties contextProperties = new Properties();
contextProperties.setProperty(CloudifyConstants.CONTEXT_PROPERTY_DEBUG_ALL, Boolean.TRUE.toString());
contextProperties.setProperty(CloudifyConstants.CONTEXT_PROPERTY_DEBUG_MODE, DebugModes.INSTEAD.toString());
blp.setContextProperties(contextProperties);
deleteLockFile();
final IntegratedProcessingUnitContainer ipuc =
createContainer("classpath:/debug/META-INF/spring/pu.xml", blp);
try {
final ApplicationContext ctx = ipuc.getApplicationContext();
final UniversalServiceManagerBean usm = ctx.getBean(UniversalServiceManagerBean.class);
Assert.assertNotNull(usm);
final USMState stateAtBreakpoint = getUsmState(usm);
Assert.assertNotNull(stateAtBreakpoint);
Assert.assertEquals(USMState.INITIALIZING, stateAtBreakpoint);
final File lockFile = waitForDebugLockFile();
System.out.println("Deleting lock file: " + lockFile.getAbsolutePath());
FileUtils.deleteQuietly(lockFile);
waitForInstanceToReachStatus(usm, USMState.RUNNING);
} finally {
ipuc.close();
}
}
private void deleteLockFile() {
final File lockFile = new File(DEBUG_LOCK_FILE_PATH);
if (lockFile.exists()) {
FileUtils.deleteQuietly(lockFile);
}
}
private File waitForDebugLockFile() {
final long endTime = System.currentTimeMillis() + 20000;
while (System.currentTimeMillis() < endTime) {
final File lockFile = new File(DEBUG_LOCK_FILE_PATH);
if (lockFile.exists()) {
return lockFile;
}
}
Assert.fail("Expected debug lock file: " + DEBUG_LOCK_FILE_PATH + " was not created");
return null;
}
private void waitForInstanceToReachStatus(final UniversalServiceManagerBean usm, final USMState targetState)
throws InterruptedException {
waitForInstanceToReachStatus(usm, targetState, 20000);
}
private void waitForInstanceToReachStatus(final UniversalServiceManagerBean usm, final USMState targetState,
final long timeoutMillis)
throws InterruptedException {
final long start = System.currentTimeMillis();
final long end = start + timeoutMillis;
USMState currentState = null;
while (System.currentTimeMillis() < end) {
currentState = getUsmState(usm);
if (currentState != null) {
// System.out.println("State is: " + currentState);
if (currentState.equals(targetState)) {
break;
}
}
// sleeping before trying monitors again.
Thread.sleep(2000);
}
Assert.assertEquals(targetState, currentState);
}
private USMState getUsmState(final UniversalServiceManagerBean usm) {
final ServiceMonitors[] monitors = usm.getServicesMonitors();
final Object state = monitors[0].getMonitors().get("USM_State");
if (state != null) {
final USMState stateEnum = USMState.values()[(Integer) state];
return stateEnum;
} else {
return null;
}
}
private ServiceInstanceAttemptData createServiceInstanceAttempDataTemplate() {
final ServiceInstanceAttemptData data = new ServiceInstanceAttemptData();
data.setApplicationName("default");
data.setServiceName("groovyError");
data.setGscPid(new Sigar().getPid());
data.setInstanceId(1);
return data;
}
@Ignore
@Test
public void testRecoveryAfterRetry() throws IOException, InterruptedException {
final ServiceInstanceAttemptData data = createServiceInstanceAttempDataTemplate();
data.setCurrentAttemptNumber(2);
gigaspace.write(data);
final IntegratedProcessingUnitContainer ipuc =
createContainer("classpath:/retries-recovery/META-INF/spring/pu.xml");
try {
final ApplicationContext ctx = ipuc.getApplicationContext();
final UniversalServiceManagerBean usm = ctx.getBean(UniversalServiceManagerBean.class);
Assert.assertNotNull(usm);
waitForInstanceToReachStatus(usm, USMState.RUNNING);
} finally {
ipuc.close();
}
}
private IntegratedProcessingUnitContainer createContainer(final String classpath) throws IOException {
return createContainer(classpath, null);
}
private IntegratedProcessingUnitContainer createContainer(final String classpath,
final BeanLevelProperties beanLevelProperties) throws IOException {
final IntegratedProcessingUnitContainerProvider provider = new IntegratedProcessingUnitContainerProvider();
// provide cluster information for the specific PU instance
final ClusterInfo clusterInfo = new ClusterInfo();
// clusterInfo.setSchema("partitioned-sync2backup");
clusterInfo.setNumberOfInstances(1);
clusterInfo.setInstanceId(1);
clusterInfo.setName("default.groovyError");
provider.setClusterInfo(clusterInfo);
// set the config location (override the default one - classpath:/META-INF/spring/pu.xml)
provider.addConfigLocation(classpath);
if (beanLevelProperties != null) {
provider.setBeanLevelProperties(beanLevelProperties);
}
// Build the Spring application context and "start" it
final ProcessingUnitContainer container = provider.createContainer();
return (IntegratedProcessingUnitContainer) container;
}
@AfterClass
public static void afterClass() throws Exception {
admin.close();
urlSpaceConfigurer.destroy();
System.out.println("Closed down embedded space");
}
@After
public void after() throws SigarException {
final Sigar sigar = new Sigar();
final long[] allPids = sigar.getProcList();
final long myPid = sigar.getPid();
final Set<Long> childPids = new HashSet<Long>();
for (final long pid : allPids) {
try {
final ProcState state = sigar.getProcState(pid);
final long ppid = state.getPpid();
if (ppid == myPid) {
System.out.println("Found a leaking process: " + pid);
childPids.add(pid);
}
} catch (final SigarException e) {
// probably means that the process belongs to another user to may not have permissions
// to read its data. Should be safe to ignore
}
}
if (!childPids.isEmpty()) {
System.out.println("Warning: found leaked processes after test finished: " + childPids);
final DefaultProcessKiller killer = new DefaultProcessKiller();
for (final Long pid : childPids) {
final ProcExe procExe = sigar.getProcExe(pid);
final String[] procArgs = sigar.getProcArgs(pid);
System.out.println("Killing process: " + pid + ". Name: " + procExe.getName() + ", Args: "
+ Arrays.toString(procArgs) + "Directory: " + procExe.getCwd());
try {
killer.killProcess(pid);
} catch (final USMException e) {
System.out.println("Failed to kill process: " + pid);
}
}
Assert.fail("Leaked processes found after test.");
}
}
}