/*
* Copyright (C) 2011 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.component.test;
import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Field;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import junit.framework.AssertionFailedError;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.container.PortalContainer;
import org.exoplatform.container.RootContainer;
/**
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
*/
public class KernelBootstrap {
/** The system property for gatein tmp dir. */
private static final String TMP_DIR = "gatein.test.tmp.dir";
/** . */
private File tmpDir;
/** . */
private File targetDir;
/** . */
private EnumMap<ContainerScope, Set<String>> configs;
/** . */
private ClassLoader realClassLoader;
/** The portal container available once the kernel is booted. */
private PortalContainer container;
public KernelBootstrap() {
this(Thread.currentThread().getContextClassLoader());
}
public KernelBootstrap(ClassLoader realClassLoader) {
//
Set<String> rootConfigPaths = new LinkedHashSet<String>();
rootConfigPaths.add("conf/root-configuration.xml");
Set<String> portalConfigPaths = new LinkedHashSet<String>();
portalConfigPaths.add("conf/portal-configuration.xml");
EnumMap<ContainerScope, Set<String>> configs = new EnumMap<ContainerScope, Set<String>>(ContainerScope.class);
configs.put(ContainerScope.ROOT, rootConfigPaths);
configs.put(ContainerScope.PORTAL, portalConfigPaths);
//
File targetDir = new File(System.getProperty("gatein.test.output.path"));
if (!targetDir.exists()) {
throw new AssertionFailedError("Target dir for unit test does not exist");
}
if (!targetDir.isDirectory()) {
throw new AssertionFailedError("Target dir is not a directory");
}
if (!targetDir.canWrite()) {
throw new AssertionFailedError("Target dir is not writable");
}
//
this.configs = configs;
this.targetDir = targetDir;
this.tmpDir = findTmpDir(targetDir);
this.realClassLoader = realClassLoader;
}
private static File findTmpDir(File dir) {
Set<String> fileNames = new HashSet<String>();
for (File child : dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.startsWith("gateintest-");
}
})) {
fileNames.add(child.getName());
}
//
String fileName;
int count = 0;
while (true) {
fileName = "gateintest-" + count;
if (!fileNames.contains(fileName)) {
break;
}
count++;
}
//
return new File(dir, fileName);
}
public File getTargetDir() {
return targetDir;
}
public File getTmpDir() {
return tmpDir;
}
/**
* Set the tmp dir of the test to a new file location. The new tmp dir must be a strict descendant of the {@link #targetDir}
* file.
*
* @param tmpDir the new tmp dir
* @throws IllegalArgumentException if the tmp dir is not a descendant of the target dir
*/
public void setTmpDir(File tmpDir) throws IllegalArgumentException {
for (File parent = tmpDir.getParentFile(); !targetDir.equals(parent); parent = parent.getParentFile()) {
if (parent == null) {
throw new IllegalArgumentException("Wrong tmp dir " + tmpDir);
}
}
//
this.tmpDir = tmpDir;
}
public PortalContainer getContainer() {
return container;
}
public void addConfiguration(ContainerScope scope, String path) {
configs.get(scope).add(path);
}
private void addConfiguration(ConfigurationUnit unit) {
addConfiguration(unit.scope(), unit.path());
}
public void addConfiguration(ConfiguredBy configuredBy) {
for (ConfigurationUnit unit : configuredBy.value()) {
addConfiguration(unit);
}
}
public void addConfiguration(Class<?> clazz) {
ConfiguredBy cfBy = clazz.getAnnotation(ConfiguredBy.class);
if (cfBy != null) {
addConfiguration(cfBy);
}
}
/**
* Boot the kernel.
*
* @throws IllegalStateException if the kernel is already booted
*/
public void boot() throws IllegalStateException {
if (container != null) {
throw new IllegalStateException("Already booted");
}
try {
// Must clear the top container first otherwise it's not going to work well
// it's a bit ugly but I don't want to change anything in the ExoContainerContext class for now
// and this is for unit testing
Field topContainerField = ExoContainerContext.class.getDeclaredField("topContainer");
topContainerField.setAccessible(true);
topContainerField.set(null, null);
// Same remark than above
Field singletonField = RootContainer.class.getDeclaredField("singleton_");
singletonField.setAccessible(true);
singletonField.set(null, null);
if (!tmpDir.exists()) {
if (!tmpDir.mkdirs()) {
throw new AssertionFailedError("Could not create directory " + tmpDir.getAbsolutePath());
}
}
// Set property globally available for configuration XML
System.setProperty(TMP_DIR, tmpDir.getCanonicalPath());
//
ClassLoader testClassLoader = new GateInTestClassLoader(realClassLoader, configs.get(ContainerScope.ROOT),
configs.get(ContainerScope.PORTAL));
Thread.currentThread().setContextClassLoader(testClassLoader);
// Boot the container, this will associate the current thread with the portal container
this.container = PortalContainer.getInstance();
} catch (Exception e) {
AssertionFailedError afe = new AssertionFailedError();
afe.initCause(e);
throw afe;
} finally {
Thread.currentThread().setContextClassLoader(realClassLoader);
}
}
public void dispose() {
if (container != null) {
RootContainer.getInstance().stop();
container = null;
ExoContainerContext.setCurrentContainer(null);
}
}
}