Author: gnodet Date: Fri Nov 26 13:02:17 2010 New Revision: 1039339 URL: http://svn.apache.org/viewvc?rev=1039339&view=rev Log: [KARAF-301] Create integration tests for the distribution itself
Added: karaf/trunk/itests/kittests/ karaf/trunk/itests/kittests/pom.xml karaf/trunk/itests/kittests/src/ karaf/trunk/itests/kittests/src/test/ karaf/trunk/itests/kittests/src/test/java/ karaf/trunk/itests/kittests/src/test/java/org/ karaf/trunk/itests/kittests/src/test/java/org/apache/ karaf/trunk/itests/kittests/src/test/java/org/apache/karaf/ karaf/trunk/itests/kittests/src/test/java/org/apache/karaf/kittests/ karaf/trunk/itests/kittests/src/test/java/org/apache/karaf/kittests/Helper.java karaf/trunk/itests/kittests/src/test/java/org/apache/karaf/kittests/KitTest.java Modified: karaf/trunk/itests/pom.xml karaf/trunk/pom.xml Added: karaf/trunk/itests/kittests/pom.xml URL: http://svn.apache.org/viewvc/karaf/trunk/itests/kittests/pom.xml?rev=1039339&view=auto ============================================================================== --- karaf/trunk/itests/kittests/pom.xml (added) +++ karaf/trunk/itests/kittests/pom.xml Fri Nov 26 13:02:17 2010 @@ -0,0 +1,188 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <!-- + + 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. + --> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.karaf.itests</groupId> + <artifactId>itests</artifactId> + <version>2.1.99-SNAPSHOT</version> + </parent> + + <artifactId>kittests</artifactId> + <packaging>jar</packaging> + <name>Apache Karaf :: Integration Tests :: Kit Tests</name> + + <description> + Kits Integration tests + </description> + + <properties> + <appendedResourcesDirectory>${basedir}/../etc/appended-resources</appendedResourcesDirectory> + </properties> + + <dependencies> + <!-- This is required to be first so that pax-exam classloader is not messed up with a newer version of felix + which would lead to java.lang.RuntimeException: Could not create [service.obr] --> + <dependency> + <groupId>org.apache.karaf</groupId> + <artifactId>apache-karaf</artifactId> + <type>tar.gz</type> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.karaf</groupId> + <artifactId>apache-karaf</artifactId> + <type>zip</type> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-compress</artifactId> + <version>1.1</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-io</artifactId> + <version>1.3.2</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.karaf.admin</groupId> + <artifactId>org.apache.karaf.admin.core</artifactId> + <scope>test</scope> + </dependency> + + </dependencies> + + <build> + <plugins> + <!-- generate dependencies versions --> + <plugin> + <groupId>org.apache.servicemix.tooling</groupId> + <artifactId>depends-maven-plugin</artifactId> + <version>1.1</version> + <executions> + <execution> + <id>generate-depends-file</id> + <goals> + <goal>generate-depends-file</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <version>2.1</version> + <executions> + <execution> + <id>copy-unix</id> + <phase>generate-resources</phase> + <goals> + <goal>copy</goal> + </goals> + <configuration> + <artifactItems> + <artifactItem> + <groupId>org.apache.karaf</groupId> + <artifactId>apache-karaf</artifactId> + <type>tar.gz</type> + <outputDirectory>target/distributions</outputDirectory> + <destFileName>karaf.tar.gz</destFileName> + </artifactItem> + </artifactItems> + </configuration> + </execution> + <execution> + <id>copy-win</id> + <phase>generate-resources</phase> + <goals> + <goal>copy</goal> + </goals> + <configuration> + <artifactItems> + <artifactItem> + <groupId>org.apache.karaf</groupId> + <artifactId>apache-karaf</artifactId> + <type>zip</type> + <outputDirectory>target/distributions</outputDirectory> + <destFileName>karaf.zip</destFileName> + </artifactItem> + </artifactItems> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>1.5</version> + <executions> + <execution> + <id>add-resource</id> + <phase>generate-resources</phase> + <goals> + <goal>add-resource</goal> + </goals> + <configuration> + <resources> + <resource> + <directory>target/distributions</directory> + </resource> + </resources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>ci-build-profile</id> + <activation> + <property> + <name>maven.repo.local</name> + </property> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <!-- when the local repo location has been specified, we need to pass on this information to PAX mvn url --> + <argLine>-Dorg.ops4j.pax.url.mvn.localRepository=${maven.repo.local}</argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + +</project> Added: karaf/trunk/itests/kittests/src/test/java/org/apache/karaf/kittests/Helper.java URL: http://svn.apache.org/viewvc/karaf/trunk/itests/kittests/src/test/java/org/apache/karaf/kittests/Helper.java?rev=1039339&view=auto ============================================================================== --- karaf/trunk/itests/kittests/src/test/java/org/apache/karaf/kittests/Helper.java (added) +++ karaf/trunk/itests/kittests/src/test/java/org/apache/karaf/kittests/Helper.java Fri Nov 26 13:02:17 2010 @@ -0,0 +1,544 @@ +/* + * 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.karaf.kittests; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; +import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.felix.utils.properties.InterpolationHelper; +import org.apache.karaf.admin.Instance; +import org.apache.karaf.admin.internal.InstanceImpl; +import org.apache.karaf.shell.commands.utils.StreamPumper; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.Socket; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +public final class Helper { + + private Helper() { + } + + public static class Instance { + public static final String STOPPED = "Stopped"; + public static final String STARTING = "Starting"; + public static final String STARTED = "Started"; + public static final String ERROR = "Error"; + + private static final String CONFIG_PROPERTIES_FILE_NAME = "config.properties"; + private static final String KARAF_SHUTDOWN_PORT = "karaf.shutdown.port"; + private static final String KARAF_SHUTDOWN_HOST = "karaf.shutdown.host"; + private static final String KARAF_SHUTDOWN_PORT_FILE = "karaf.shutdown.port.file"; + private static final String KARAF_SHUTDOWN_COMMAND = "karaf.shutdown.command"; + private static final String KARAF_SHUTDOWN_PID_FILE = "karaf.shutdown.pid.file"; + private static final String DEFAULT_SHUTDOWN_COMMAND = "SHUTDOWN"; + + private String location; + private Process process; + + public Instance(String location, Process process) { + this.location = location; + this.process = process; + } + + public boolean exists() { + return new File(location).isDirectory(); + } + + public int getSshPort() { + InputStream is = null; + try { + File f = new File(location, "etc/org.apache.karaf.shell.cfg"); + is = new FileInputStream(f); + Properties props = new Properties(); + props.load(is); + String loc = props.getProperty("sshPort"); + return Integer.parseInt(loc); + } catch (Exception e) { + return 0; + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + // Ignore + } + } + } + } + + public synchronized String getState() { + int port = getSshPort(); + if (!exists() || port <= 0) { + return ERROR; + } + checkProcess(); + if (this.process == null) { + return STOPPED; + } else { + try { + Socket s = new Socket("localhost", port); + s.close(); + return STARTED; + } catch (Exception e) { + // ignore + } + return STARTING; + } + } + + protected void checkProcess() { + if (this.process != null) { + try { + this.process.exitValue(); + this.process = null; + } catch (IllegalThreadStateException e) { + } + } + } + + private static String getProperty(Map<String,String> props, String key, String deflt) { + String res = props.get(key); + if (res == null) { + res = deflt; + } + return res; + } + + public void stop() throws Exception { + checkProcess(); + if (this.process != null) { + // Try a clean shutdown + cleanShutdown(); + if (this.process != null) { + this.process.destroy(); + } + } + } + + protected void cleanShutdown() { + try { + File file = new File(new File(location, "etc"), CONFIG_PROPERTIES_FILE_NAME); + URL configPropURL = file.toURI().toURL(); + Map<String,String> props = loadPropertiesFile(configPropURL); + props.put("karaf.base", new File(location).getCanonicalPath()); + props.put("karaf.home", new File(location).getCanonicalPath()); + props.put("karaf.data", new File(new File(location), "data").getCanonicalPath()); + InterpolationHelper.performSubstitution(props); + int port = Integer.parseInt(getProperty(props, KARAF_SHUTDOWN_PORT, "0")); + String host = getProperty(props, KARAF_SHUTDOWN_HOST, "localhost"); + String portFile = props.get(KARAF_SHUTDOWN_PORT_FILE); + String shutdown = getProperty(props, KARAF_SHUTDOWN_COMMAND, DEFAULT_SHUTDOWN_COMMAND); + if (port == 0 && portFile != null) { + BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(portFile))); + String portStr = r.readLine(); + port = Integer.parseInt(portStr); + r.close(); + } + // We found the port, try to send the command + if (port > 0) { + Socket s = new Socket(host, port); + s.getOutputStream().write(shutdown.getBytes()); + s.close(); + long t = System.currentTimeMillis() + 5000; + do { + Thread.sleep(100); + checkProcess(); + } while (System.currentTimeMillis() < t && process != null); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + protected static Map<String,String> loadPropertiesFile(URL configPropURL) throws Exception { + // Read the properties file. + Properties configProps = new Properties(); + InputStream is = null; + try { + is = configPropURL.openConnection().getInputStream(); + configProps.load(is); + is.close(); + } + catch (Exception ex) { + System.err.println( + "Error loading config properties from " + configPropURL); + System.err.println("Main: " + ex); + try { + if (is != null) is.close(); + } + catch (IOException ex2) { + // Nothing we can do. + } + return null; + } + return (Map) configProps; + } + + } + + protected static final boolean IS_WINDOWS_OS = System.getProperty("os.name").toLowerCase().contains("windows"); + + public static void extractKit(File targetDir) throws Exception { + if (isWindowsOs()) { + extractWindowsKit(targetDir); + } else { + extractUnixKit(targetDir); + } + } + + public static boolean isWindowsOs() { + return IS_WINDOWS_OS; + } + + protected static void extractWindowsKit(File targetDir) throws Exception { + InputStream is = Helper.class.getResourceAsStream("/karaf.zip"); + extract(new ZipArchiveInputStream(is), targetDir); + } + + protected static void extractUnixKit(File targetDir) throws Exception { + InputStream is = Helper.class.getResourceAsStream("/karaf.tar.gz"); + extract(new TarArchiveInputStream(new GzipCompressorInputStream(is)), targetDir); + File bin = new File(targetDir, "bin"); + String[] files = bin.list(); + List<String> args = new ArrayList(); + Collections.addAll(args, "chmod", "+x"); + Collections.addAll(args, files); + Process chmod = new ProcessBuilder() + .directory(new File(targetDir, "bin")) + .command(args) + .start(); + PumpStreamHandler pump = new PumpStreamHandler(System.in, System.out, System.err); + pump.attach(chmod); + pump.start(); + waitForProcessEnd(chmod, 5000); + } + + protected static void extract(ArchiveInputStream is, File targetDir) throws IOException { + try { + if (targetDir.exists()) { + FileUtils.forceDelete(targetDir); + } + targetDir.mkdirs(); + ArchiveEntry entry = is.getNextEntry(); + while (entry != null) { + String name = entry.getName(); + name = name.substring(name.indexOf("/") + 1); + File file = new File(targetDir, name); + if (entry.isDirectory()) { + file.mkdirs(); + } else { + file.getParentFile().mkdirs(); + OutputStream os = new FileOutputStream(file); + try { + IOUtils.copy(is, os); + } finally { + IOUtils.closeQuietly(os); + } + } + entry = is.getNextEntry(); + } + } finally { + is.close(); + } + } + + public static Process launchScript(File homeDir, String script, String args) throws IOException { + ProcessBuilder builder = new ProcessBuilder(); + builder.directory(homeDir); + if (isWindowsOs()) { + builder.command("cmd.exe", "/c", new File(homeDir, "bin\\" + script + ".bat").getAbsolutePath(), args); + } else { + builder.command("/bin/sh", new File(homeDir, "bin/" + script).getAbsolutePath(), args); + } + builder.redirectErrorStream(true); + Process process = builder.start(); + PumpStreamHandler pump = new PumpStreamHandler(System.in, System.out, System.err); + pump.attach(process); + pump.start(); + return process; + } + + public static Instance startKaraf(File home) throws Exception { + Process karaf = launchScript(home, "karaf", "server"); + return new Instance(home.getAbsolutePath(), karaf); +// InstanceImpl instance = new InstanceImpl(null, "root", home.getAbsolutePath(), null, true); +// instance.attach(karaf.getPid()); +// return instance; + } + + public static void waitForKarafStarted(Instance karaf, long timeout) throws Exception { + for (int i = 0; i < timeout / 100; i++) { + if (Instance.STARTING.equals(karaf.getState())) { + Thread.sleep(100); + } else { + break; + } + } + if (!Instance.STARTED.equals(karaf.getState())) { + throw new Exception("Karaf did not start correctly"); + } + } + + public static void waitForKarafStopped(Instance karaf, long timeout) throws Exception { + waitForProcessEnd(karaf.process, timeout); + } + + public static void waitForProcessEnd(Process process, long timeout) throws Exception { + for (int i = 0; i < timeout / 100; i++) { + try { + process.exitValue(); + return; + } catch (IllegalThreadStateException e) { + } + Thread.sleep(100); + } + throw new Exception("Process is still running"); + } + + public static void kill(Process process) { + try { + process.destroy(); + } catch (Throwable e) { + } + } + + public static void kill(Instance instance) { + try { + instance.stop(); + } catch (Throwable e) { + } + } + + public static class PumpStreamHandler + { + private InputStream in; + + private OutputStream out; + + private OutputStream err; + + private Thread outputThread; + + private Thread errorThread; + + private StreamPumper inputPump; + + // + // NOTE: May want to use a ThreadPool here, 3 threads per/pair seems kinda expensive :-( + // + + public PumpStreamHandler(final InputStream in, final OutputStream out, final OutputStream err) { + assert in != null; + assert out != null; + assert err != null; + + this.in = in; + this.out = out; + this.err = err; + } + + public PumpStreamHandler(final OutputStream out, final OutputStream err) { + this(null, out, err); + } + + public PumpStreamHandler(final OutputStream outAndErr) { + this(outAndErr, outAndErr); + } + + /** + * Set the input stream from which to read the standard output of the child. + */ + public void setChildOutputStream(final InputStream in) { + assert in != null; + + createChildOutputPump(in, out); + } + + /** + * Set the input stream from which to read the standard error of the child. + */ + public void setChildErrorStream(final InputStream in) { + assert in != null; + + if (err != null) { + createChildErrorPump(in, err); + } + } + + /** + * Set the output stream by means of which input can be sent to the child. + */ + public void setChildInputStream(final OutputStream out) { + assert out != null; + + if (in != null) { + inputPump = createInputPump(in, out, true); + } + else { + try { + out.close(); + } catch (IOException e) { } + } + } + + /** + * Attach to a child streams from the given process. + * + * @param p The process to attach to. + */ + public void attach(final Process p) { + assert p != null; + + setChildInputStream(p.getOutputStream()); + setChildOutputStream(p.getInputStream()); + setChildErrorStream(p.getErrorStream()); + } + /** + * Start pumping the streams. + */ + public void start() { + if (outputThread != null) { + outputThread.start(); + } + + if (errorThread != null) { + errorThread.start(); + } + + if (inputPump != null) { + Thread inputThread = new Thread(inputPump); + inputThread.setDaemon(true); + inputThread.start(); + } + } + + /** + * Stop pumping the streams. + */ + public void stop() { + if (outputThread != null) { + try { + outputThread.join(); + } + catch (InterruptedException e) { + // ignore + } + } + + if (errorThread != null) { + try { + errorThread.join(); + } + catch (InterruptedException e) { + // ignore + } + } + + if (inputPump != null) { + inputPump.stop(); + } + + try { + err.flush(); + } catch (IOException e) { } + try { + out.flush(); + } catch (IOException e) { } + } + + /** + * Create the pump to handle child output. + */ + protected void createChildOutputPump(final InputStream in, final OutputStream out) { + assert in != null; + assert out != null; + + outputThread = createPump(in, out); + } + + /** + * Create the pump to handle error output. + */ + protected void createChildErrorPump(final InputStream in, final OutputStream out) { + assert in != null; + assert out != null; + + errorThread = createPump(in, out); + } + + /** + * Creates a stream pumper to copy the given input stream to the given output stream. + */ + protected Thread createPump(final InputStream in, final OutputStream out) { + assert in != null; + assert out != null; + + return createPump(in, out, false); + } + + /** + * Creates a stream pumper to copy the given input stream to the + * given output stream. + * + * @param in The input stream to copy from. + * @param out The output stream to copy to. + * @param closeWhenExhausted If true close the inputstream. + * @return A thread object that does the pumping. + */ + protected Thread createPump(final InputStream in, final OutputStream out, final boolean closeWhenExhausted) { + assert in != null; + assert out != null; + + final Thread result = new Thread(new StreamPumper(in, out, closeWhenExhausted)); + result.setDaemon(true); + return result; + } + + /** + * Creates a stream pumper to copy the given input stream to the + * given output stream. Used for standard input. + */ + protected StreamPumper createInputPump(final InputStream in, final OutputStream out, final boolean closeWhenExhausted) { + assert in != null; + assert out != null; + + StreamPumper pumper = new StreamPumper(in, out, closeWhenExhausted); + pumper.setAutoflush(true); + return pumper; + } + } +} \ No newline at end of file Added: karaf/trunk/itests/kittests/src/test/java/org/apache/karaf/kittests/KitTest.java URL: http://svn.apache.org/viewvc/karaf/trunk/itests/kittests/src/test/java/org/apache/karaf/kittests/KitTest.java?rev=1039339&view=auto ============================================================================== --- karaf/trunk/itests/kittests/src/test/java/org/apache/karaf/kittests/KitTest.java (added) +++ karaf/trunk/itests/kittests/src/test/java/org/apache/karaf/kittests/KitTest.java Fri Nov 26 13:02:17 2010 @@ -0,0 +1,59 @@ +/* + * 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.karaf.kittests; + +import org.junit.Test; + +import java.io.File; + +import static org.apache.karaf.kittests.Helper.*; + +public class KitTest { + + @Test + public void testKit() throws Exception { + File home = new File("target/karaf").getAbsoluteFile(); + System.out.println("Karaf home: " + home); + System.out.println("Extracting Karaf"); + extractKit(home); + + System.out.println("Starting Karaf"); + Instance karaf = startKaraf(home); + try { + System.out.println("Wait for Karaf to be started"); + waitForKarafStarted(karaf, 20000); + + System.out.println("Launching stop script to shutdown"); + Process client = launchScript(home, "stop", ""); + try { + waitForProcessEnd(client, 20000); + System.out.println("Client terminated"); + } finally { + kill(client); + } + + System.out.println("Waiting for karaf to stop"); + waitForKarafStopped(karaf, 20000); + System.out.println("Karaf stopped"); + + } finally { + kill(karaf); + } + + } + +} \ No newline at end of file Modified: karaf/trunk/itests/pom.xml URL: http://svn.apache.org/viewvc/karaf/trunk/itests/pom.xml?rev=1039339&r1=1039338&r2=1039339&view=diff ============================================================================== --- karaf/trunk/itests/pom.xml (original) +++ karaf/trunk/itests/pom.xml Fri Nov 26 13:02:17 2010 @@ -35,6 +35,7 @@ <modules> <module>dependencies</module> <module>tests</module> + <module>kittests</module> </modules> </project> Modified: karaf/trunk/pom.xml URL: http://svn.apache.org/viewvc/karaf/trunk/pom.xml?rev=1039339&r1=1039338&r2=1039339&view=diff ============================================================================== --- karaf/trunk/pom.xml (original) +++ karaf/trunk/pom.xml Fri Nov 26 13:02:17 2010 @@ -410,6 +410,12 @@ <dependency> <groupId>org.apache.karaf</groupId> <artifactId>apache-karaf</artifactId> + <type>tar.gz</type> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.karaf</groupId> + <artifactId>apache-karaf</artifactId> <type>zip</type> <version>${project.version}</version> </dependency>