You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2015/09/18 17:56:20 UTC
svn commit: r1703893 - in /sling/trunk/testing:
junit/core/src/main/java/org/apache/sling/junit/rules/ teleporter/
teleporter/src/ teleporter/src/main/ teleporter/src/main/java/
teleporter/src/main/java/org/ teleporter/src/main/java/org/apache/ telepor...
Author: bdelacretaz
Date: Fri Sep 18 15:56:19 2015
New Revision: 1703893
URL: http://svn.apache.org/viewvc?rev=1703893&view=rev
Log:
SLING-5040 - new TeleporterRule and teleporter client module
Added:
sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/ServerSideTeleporter.java
sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/ServiceGetter.java
sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/TeleporterRule.java
sling/trunk/testing/teleporter/ (with props)
sling/trunk/testing/teleporter/pom.xml
sling/trunk/testing/teleporter/src/
sling/trunk/testing/teleporter/src/main/
sling/trunk/testing/teleporter/src/main/java/
sling/trunk/testing/teleporter/src/main/java/org/
sling/trunk/testing/teleporter/src/main/java/org/apache/
sling/trunk/testing/teleporter/src/main/java/org/apache/sling/
sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/
sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/
sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/
sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/ClientSideTeleporter.java
sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/DependencyAnalyzer.java
sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/MultipartAdapter.java
sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClient.java
Modified:
sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/Service.java
Added: sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/ServerSideTeleporter.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/ServerSideTeleporter.java?rev=1703893&view=auto
==============================================================================
--- sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/ServerSideTeleporter.java (added)
+++ sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/ServerSideTeleporter.java Fri Sep 18 15:56:19 2015
@@ -0,0 +1,56 @@
+/*
+ * 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.sling.junit.rules;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sling.junit.Activator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/** Server-side variant of the TeleporterRule, which provides
+ * access to OSGi services for convenience, but does not do
+ * much more.
+ */
+class ServerSideTeleporter extends TeleporterRule {
+ private final List<ServiceReference> toUnget = new ArrayList<ServiceReference>();
+ private final BundleContext bundleContext;
+
+ ServerSideTeleporter() {
+ bundleContext = Activator.getBundleContext();
+ if(bundleContext == null) {
+ throw new IllegalStateException("Null BundleContext, should not happen when this class is used");
+ }
+ }
+
+ @Override
+ protected void after() {
+ super.after();
+ for(ServiceReference r : toUnget) {
+ if(r != null) {
+ bundleContext.ungetService(r);
+ }
+ }
+ }
+
+ public <T> T getService (Class<T> serviceClass, String ldapFilter) {
+ final ServiceGetter sg = new ServiceGetter(bundleContext, serviceClass, ldapFilter);
+ toUnget.add(sg.serviceReference);
+ return serviceClass.cast(sg.service);
+ }
+}
Modified: sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/Service.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/Service.java?rev=1703893&r1=1703892&r2=1703893&view=diff
==============================================================================
--- sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/Service.java (original)
+++ sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/Service.java Fri Sep 18 15:56:19 2015
@@ -22,7 +22,6 @@ import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
/**
* Allows a test class to obtain a reference to an OSGi service. This rule embodies the logic to get a bundle context,
@@ -55,32 +54,15 @@ public class Service implements TestRule
return;
}
- ServiceReference serviceReference = null;
- if(serviceClass.equals(BundleContext.class)) {
- // Special case to provide the BundleContext to tests
- Service.this.service = bundleContext;
- } else {
- serviceReference = bundleContext.getServiceReference(serviceClass.getName());
-
- if (serviceReference == null) {
- throw new IllegalStateException("unable to get a service reference");
- }
-
- final Object service = bundleContext.getService(serviceReference);
-
- if (service == null) {
- throw new IllegalStateException("unable to get an instance of the service");
- }
-
- Service.this.service = serviceClass.cast(service);
- }
+ final ServiceGetter sg = new ServiceGetter(bundleContext, serviceClass, null);
+ Service.this.service = serviceClass.cast(sg.service);
try {
base.evaluate();
} finally {
Service.this.service = null;
- if(serviceReference != null) {
- bundleContext.ungetService(serviceReference);
+ if(sg.serviceReference != null) {
+ bundleContext.ungetService(sg.serviceReference);
}
}
}
Added: sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/ServiceGetter.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/ServiceGetter.java?rev=1703893&view=auto
==============================================================================
--- sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/ServiceGetter.java (added)
+++ sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/ServiceGetter.java Fri Sep 18 15:56:19 2015
@@ -0,0 +1,60 @@
+/*
+ * 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.sling.junit.rules;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/** Implements the logic used to get a service */
+class ServiceGetter {
+ final ServiceReference serviceReference;
+ final Object service;
+
+ ServiceGetter(BundleContext bundleContext, Class<?> serviceClass, String ldapFilter) {
+ Object s;
+
+ if(ldapFilter != null && !ldapFilter.isEmpty()) {
+ throw new UnsupportedOperationException("LDAP service filter is not supported so far");
+ }
+
+ if (serviceClass.equals(BundleContext.class)) {
+ // Special case to provide the BundleContext to tests
+ s = serviceClass.cast(bundleContext);
+ serviceReference = null;
+ } else {
+ serviceReference = bundleContext.getServiceReference(serviceClass
+ .getName());
+
+ if (serviceReference == null) {
+ throw new IllegalStateException(
+ "unable to get a service reference");
+ }
+
+ final Object service = bundleContext.getService(serviceReference);
+
+ if (service == null) {
+ throw new IllegalStateException(
+ "unable to get an instance of the service");
+ }
+
+ s = serviceClass.cast(service);
+ }
+
+ service = s;
+ }
+}
Added: sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/TeleporterRule.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/TeleporterRule.java?rev=1703893&view=auto
==============================================================================
--- sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/TeleporterRule.java (added)
+++ sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/rules/TeleporterRule.java Fri Sep 18 15:56:19 2015
@@ -0,0 +1,85 @@
+/*
+ * 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.sling.junit.rules;
+
+import org.apache.sling.junit.Activator;
+import org.junit.rules.ExternalResource;
+
+/** JUnit Rule used to teleport a server-side test to a Sling instance
+ * to execute it there. See the launchpad/integration-tests module for
+ * usage examples (coming soon).
+ * A concrete TeleporterRule class is selected to match the different required
+ * behaviors of the server-side and client-side variants of this rule.
+ * The junit.core module only contains the server-side code, to minimize
+ * its dependencies, and the client-side part is in the sling testing.teleporter
+ * module.
+ */
+public abstract class TeleporterRule extends ExternalResource {
+ protected Class<?> classUnderTest;
+
+ /** Name of the implementation class to use by default when running on the client side */
+ public static final String DEFAULT_CLIENT_CLASS = "org.apache.sling.testing.teleporter.client.ClientSideTeleporter";
+
+ protected TeleporterRule() {
+ }
+
+ private void setClassUnderTest(Class<?> c) {
+ this.classUnderTest = c;
+ }
+
+ /** Build a TeleporterRule for the given class, using the default
+ * client class name.
+ */
+ public static TeleporterRule forClass(Class <?> classUnderTest) {
+ return forClass(classUnderTest, null);
+ }
+
+ /** Build a TeleporterRule for the given class, optionally using
+ * a specific client class.
+ */
+ public static TeleporterRule forClass(Class <?> classUnderTest, String clientTeleporterClassName) {
+ TeleporterRule result = null;
+
+ if(Activator.getBundleContext() != null) {
+ // We're running on the server side
+ result = new ServerSideTeleporter();
+ } else {
+ // Client-side. Instantiate the class dynamically to
+ // avoid bringing its dependencies into this module when
+ // it's running on the server side
+ final String clientClass = clientTeleporterClassName == null ? DEFAULT_CLIENT_CLASS : clientTeleporterClassName;
+ try {
+ result = (TeleporterRule)(TeleporterRule.class.getClassLoader().loadClass(clientClass).newInstance());
+ } catch(Exception e) {
+ throw new RuntimeException("Unable to instantiate Teleporter client " + clientClass, e);
+ }
+ }
+
+ result.setClassUnderTest(classUnderTest);
+ return result;
+ }
+
+ /** If running on the server side, get an OSGi service */
+ public final <T> T getService (Class<T> serviceClass) {
+ return getService(serviceClass, null);
+ }
+
+ /** If running on the server side, get an OSGi service specified by an LDAP service filter */
+ public <T> T getService (Class<T> serviceClass, String ldapFilter) {
+ throw new UnsupportedOperationException("This TeleporterRule does not implement getService()");
+ }
+}
Propchange: sling/trunk/testing/teleporter/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Fri Sep 18 15:56:19 2015
@@ -0,0 +1,19 @@
+target
+sling
+bin
+logs
+jackrabbit-repository
+derby.log
+*.iml
+*.ipr
+*.iws
+.settings
+.project
+.classpath
+.externalToolBuilders
+maven-eclipse.xml
+jackrabbit
+
+
+
+
Added: sling/trunk/testing/teleporter/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/testing/teleporter/pom.xml?rev=1703893&view=auto
==============================================================================
--- sling/trunk/testing/teleporter/pom.xml (added)
+++ sling/trunk/testing/teleporter/pom.xml Fri Sep 18 15:56:19 2015
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<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">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>23</version>
+ <relativePath/>
+ </parent>
+
+ <artifactId>org.apache.sling.testing.teleporter</artifactId>
+ <version>0.1.1-SNAPSHOT</version>
+ <packaging>jar</packaging>
+ <name>Apache Sling side Test Teleporter</name>
+ <description>Client-side implementation of the Teleporter mechanism for server-side tests</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.tinybundles</groupId>
+ <artifactId>tinybundles</artifactId>
+ <version>2.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.shared</groupId>
+ <artifactId>maven-dependency-analyzer</artifactId>
+ <version>1.6</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.junit.core</artifactId>
+ <version>1.0.11-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
Added: sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/ClientSideTeleporter.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/ClientSideTeleporter.java?rev=1703893&view=auto
==============================================================================
--- sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/ClientSideTeleporter.java (added)
+++ sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/ClientSideTeleporter.java Fri Sep 18 15:56:19 2015
@@ -0,0 +1,120 @@
+/*
+ * 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.sling.testing.teleporter.client;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.sling.junit.rules.TeleporterRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.ops4j.pax.tinybundles.core.TinyBundle;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Constants;
+
+public class ClientSideTeleporter extends TeleporterRule {
+
+ // TODO All this should be configurable with Options
+ private final int testReadyTimeoutSeconds = 5;
+ private String baseUrl;
+ private final Set<String> dependencyAnalyzerIncludes = new HashSet<String>();
+ private final Set<String> dependencyAnalyzerExcludes = new HashSet<String>();
+ private final Set<Class<?>> embeddedClasses = new HashSet<Class<?>>();
+
+ private InputStream buildTestBundle(Class<?> c, Collection<Class<?>> embeddedClasses, String bundleSymbolicName) {
+ final TinyBundle b = TinyBundles.bundle()
+ .set(Constants.BUNDLE_SYMBOLICNAME, bundleSymbolicName)
+ .set("Sling-Test-Regexp", c.getName() + ".*")
+ .add(c);
+ for(Class<?> clz : embeddedClasses) {
+ b.add(clz);
+ }
+ return b.build(TinyBundles.withBnd());
+ }
+
+ protected void setBaseUrl(String url) {
+ baseUrl = url;
+ if(baseUrl.endsWith("/")) {
+ baseUrl = baseUrl.substring(0, baseUrl.length() -1);
+ }
+ }
+
+ public void embedClass(Class<?> c) {
+ embeddedClasses.add(c);
+ }
+
+ private String installTestBundle(TeleporterHttpClient httpClient) throws MalformedURLException, IOException {
+ final SimpleDateFormat fmt = new SimpleDateFormat("HH-mm-ss-");
+ final String bundleSymbolicName = getClass().getSimpleName() + "." + fmt.format(new Date()) + "." + UUID.randomUUID();
+ final InputStream bundle = buildTestBundle(classUnderTest, embeddedClasses, bundleSymbolicName);
+ httpClient.installBundle(bundle, bundleSymbolicName);
+ return bundleSymbolicName;
+ }
+
+ @Override
+ public Statement apply(final Statement base, final Description description) {
+ if(baseUrl == null) {
+ // TODO - need a more flexible mechanism + need to wait for Sling to be ready
+ final String propName = "launchpad.http.server.url";
+ baseUrl = System.getProperty(propName);
+ if(baseUrl == null || baseUrl.isEmpty()) {
+ fail("Missing system property " + propName);
+ }
+ }
+ if(baseUrl.endsWith("/")) {
+ baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
+ }
+
+ final DependencyAnalyzer da = DependencyAnalyzer.forClass(classUnderTest);
+ for(String include : dependencyAnalyzerIncludes) {
+ da.include(include);
+ }
+ for(String exclude : dependencyAnalyzerExcludes) {
+ da.exclude(exclude);
+ }
+ for(Class<?> c : da.getDependencies()) {
+ embeddedClasses.add(c);
+ }
+
+ final TeleporterHttpClient httpClient = new TeleporterHttpClient(baseUrl);
+ // As this is not a ClassRule (which wouldn't map the test results correctly in an IDE)
+ // we currently create and install a test bundle for every test method. It might be good
+ // to optimize this, but as those test bundles are usually very small that doesn't seem
+ // to be a real problem in terms of performance.
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ final String bundleSymbolicName = installTestBundle(httpClient);
+ final String testPath = description.getClassName() + "/" + description.getMethodName();
+ try {
+ httpClient.runTests(testPath, testReadyTimeoutSeconds);
+ } finally {
+ httpClient.uninstallBundle(bundleSymbolicName);
+ }
+ }
+ };
+ }
+}
Added: sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/DependencyAnalyzer.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/DependencyAnalyzer.java?rev=1703893&view=auto
==============================================================================
--- sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/DependencyAnalyzer.java (added)
+++ sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/DependencyAnalyzer.java Fri Sep 18 15:56:19 2015
@@ -0,0 +1,137 @@
+/*
+ * 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.sling.testing.teleporter.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.maven.shared.dependency.analyzer.asm.DependencyClassFileVisitor;
+
+/** Find the class dependencies of a class, recursively,
+ * using the maven-dependency-analyzer and optionally considering only
+ * specific package prefixes to avoid exploring the whole graph.
+ */
+class DependencyAnalyzer {
+ private final Class<?> [] classes;
+ private final Set<String> dependencyNames = new HashSet<String>();
+ private final Set<String> includes = new HashSet<String>();
+ private final Set<String> excludes = new HashSet<String>();
+
+ private DependencyAnalyzer(Class <?> ... classes) {
+ this.classes = classes;
+ }
+
+ static DependencyAnalyzer forClass(Class<?> ... classes) {
+ return new DependencyAnalyzer(classes);
+ }
+
+ /** Classes with names that match this prefix will be included,
+ * unless they also match an exclude prefix.
+ */
+ DependencyAnalyzer include(String prefix) {
+ includes.add(prefix);
+ return this;
+ }
+
+ /** Classes with names that match this prefix will not be included,
+ * even if their name matches an include pattern */
+ DependencyAnalyzer exclude(String prefix) {
+ excludes.add(prefix);
+ return this;
+ }
+
+ /** Get the aggregate dependencies of our classes, based on a recursive
+ * analysis that takes our include/exclude prefixes into account
+ */
+ Collection<Class<?>> getDependencies() {
+ final Set<Class<?>> result = new HashSet<Class<?>>();
+ for(Class<?> c : classes) {
+ analyze(c);
+ }
+ for(String dep : dependencyNames) {
+ result.add(toClass(dep));
+ }
+ return result;
+ }
+
+ /** Analyze a single class, recursively */
+ private void analyze(Class<?> c) {
+ final Set<String> deps = new HashSet<String>();
+ final String path = "/" + c.getName().replace('.', '/') + ".class";
+ final InputStream input = getClass().getResourceAsStream(path);
+ if(input == null) {
+ throw new RuntimeException("Class resource not found: " + path);
+ }
+ try {
+ try {
+ final DependencyClassFileVisitor v = new DependencyClassFileVisitor();
+ v.visitClass(c.getName(), input);
+ deps.addAll(v.getDependencies());
+ } finally {
+ input.close();
+ }
+ } catch(IOException ioe) {
+ throw new RuntimeException("IOException while reading " + path);
+ }
+
+ // Keep only accepted dependencies, and recursively analyze them
+ for(String dep : deps) {
+ if(dep.equals(c.getName())) {
+ continue;
+ }
+ if(accept(dep)) {
+ dependencyNames.add(dep);
+ analyze(toClass(dep));
+ }
+ }
+ }
+
+ /** True if given class name matches our include/exclude prefixes */
+ private boolean accept(String className) {
+ boolean result = false;
+
+ for(String s : includes) {
+ if(className.startsWith(s)) {
+ result = true;
+ break;
+ }
+ }
+
+ // Excludes win over includes
+ if(result) {
+ for(String s : excludes) {
+ if(className.startsWith(s)) {
+ result = false;
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ /** Convert a class name to its Class object */
+ private Class<?> toClass(String className) {
+ try {
+ return getClass().getClassLoader().loadClass(className);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Class not found :" + className, e);
+ }
+ }
+}
\ No newline at end of file
Added: sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/MultipartAdapter.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/MultipartAdapter.java?rev=1703893&view=auto
==============================================================================
--- sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/MultipartAdapter.java (added)
+++ sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/MultipartAdapter.java Fri Sep 18 15:56:19 2015
@@ -0,0 +1,79 @@
+/*
+ * 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.sling.testing.teleporter.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+
+/** Minimal HTTP client functionality for multipart POST requests */
+class MultipartAdapter {
+ final OutputStream out;
+ final PrintWriter pw;
+ final String boundary = "______________" + Double.toHexString(Math.random()) + "______________";
+ final String eol = "\r\n";
+ final String dashes = "--";
+ final String charset;
+ int counter = 0;
+
+ MultipartAdapter(HttpURLConnection c, String charset) throws IOException {
+ this.charset = charset;
+ c.setUseCaches(false);
+ c.setDoOutput(true);
+ c.setInstanceFollowRedirects(false);
+ c.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
+ out = c.getOutputStream();
+ pw = new PrintWriter(new OutputStreamWriter(out, charset), true);
+ }
+
+ void close() throws IOException {
+ pw.append(dashes).append(boundary).append(dashes).append(eol).flush();
+ out.close();
+ }
+
+ MultipartAdapter parameter(String name, String value) {
+ pw.append(dashes).append(boundary).append(eol);
+ pw.append("Content-Disposition: form-data; name=\"" + name + "\"").append(eol);
+ pw.append("Content-Type: text/plain; charset=").append(charset).append(eol);
+ pw.append(eol).append(value).append(eol).flush();
+ return this;
+ }
+
+ MultipartAdapter file(String fieldName, String filename, String contentType, InputStream data) throws IOException {
+ pw.append(dashes).append(boundary).append(eol);
+ pw.append("Content-Disposition: form-data; name=\"").append(fieldName).append("\"");
+ pw.append("; filename=\"").append(filename).append("\"").append(eol);
+ pw.append("Content-Type: ").append(contentType).append(eol);
+ pw.append("Content-Transfer-Encoding: binary").append(eol);
+ pw.append(eol).flush();
+ copy(data, out);
+ pw.append(eol).flush();
+ return this;
+ }
+
+ private void copy(InputStream is, OutputStream os) throws IOException {
+ final byte[] buffer = new byte[16384];
+ int n=0;
+ while((n = is.read(buffer, 0, buffer.length)) > 0) {
+ os.write(buffer, 0, n);
+ }
+ os.flush();
+ }
+}
Added: sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClient.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClient.java?rev=1703893&view=auto
==============================================================================
--- sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClient.java (added)
+++ sling/trunk/testing/teleporter/src/main/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClient.java Fri Sep 18 15:56:19 2015
@@ -0,0 +1,155 @@
+/*
+ * 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.sling.testing.teleporter.client;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+import org.junit.runners.model.MultipleFailureException;
+
+
+/** Barebones HTTP client that supports just what the teleporter needs,
+ * with no dependencies outstide of java.*. Prevents us from imposing
+ * a particular HTTP client version.
+ */
+class TeleporterHttpClient {
+ private final String CHARSET = "UTF-8";
+ private final String baseUrl;
+
+ // TODO dynamic/configurable!!
+ private final String credentials = "admin:admin";
+
+ TeleporterHttpClient(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+
+ public void setCredentials(URLConnection c) {
+ final String basicAuth = "Basic " + new String(DatatypeConverter.printBase64Binary(credentials.getBytes()));
+ c.setRequestProperty ("Authorization", basicAuth);
+ }
+
+ void installBundle(InputStream bundle, String bundleSymbolicName) throws MalformedURLException, IOException {
+ // Equivalent of
+ // curl -u admin:admin -F action=install -Fbundlestart=1 -Fbundlefile=@somefile.jar http://localhost:8080/system/console/bundles
+ final String url = baseUrl + "/system/console/bundles";
+ final String contentType = "application/octet-stream";
+ final HttpURLConnection c = (HttpURLConnection)new URL(url).openConnection();
+
+ try {
+ setCredentials(c);
+ new MultipartAdapter(c, CHARSET)
+ .parameter("action", "install")
+ .parameter("bundlestart", "1")
+ .file("bundlefile", bundleSymbolicName + ".jar", contentType, bundle)
+ .close();
+ final int status = c.getResponseCode();
+ if(status != 302) {
+ throw new IOException("Got status code " + status + " for " + url);
+ }
+ } finally {
+ c.disconnect();
+ }
+ }
+
+ void uninstallBundle(String bundleSymbolicName) throws MalformedURLException, IOException {
+ // equivalent of
+ // curl -u admin:admin -F action=uninstall http://localhost:8080/system/console/bundles/$N
+ final String url = baseUrl + "/system/console/bundles/" + bundleSymbolicName;
+ final HttpURLConnection c = (HttpURLConnection)new URL(url).openConnection();
+
+ try {
+ setCredentials(c);
+ new MultipartAdapter(c, CHARSET)
+ .parameter("action", "uninstall")
+ .close();
+ final int status = c.getResponseCode();
+ if(status != 200) {
+ throw new IOException("Got status code " + status + " for " + url);
+ }
+ } finally {
+ c.disconnect();
+ }
+ }
+
+ private int getHttpGetStatus(String url) throws MalformedURLException, IOException {
+ final HttpURLConnection c = (HttpURLConnection)new URL(url).openConnection();
+ c.setUseCaches(false);
+ c.setDoOutput(true);
+ c.setDoInput(true);
+ c.setInstanceFollowRedirects(false);
+ try {
+ return c.getResponseCode();
+ } finally {
+ c.disconnect();
+ }
+ }
+
+ void runTests(String testSelectionPath, int testReadyTimeoutSeconds) throws MalformedURLException, IOException, MultipleFailureException {
+ final String testUrl = baseUrl + "/system/sling/junit/" + testSelectionPath + ".junit_result";
+
+ // Wait for non-404 response that signals that test bundle is ready
+ final long timeout = System.currentTimeMillis() + (testReadyTimeoutSeconds * 1000L);
+ while(true) {
+ if(getHttpGetStatus(testUrl) == 200) {
+ break;
+ }
+ if(System.currentTimeMillis() > timeout) {
+ fail("Timeout waiting for test at " + testUrl + " (" + testReadyTimeoutSeconds + " seconds)");
+ break;
+ }
+ }
+
+ final HttpURLConnection c = (HttpURLConnection)new URL(testUrl).openConnection();
+ try {
+ c.setRequestMethod("POST");
+ c.setUseCaches(false);
+ c.setDoOutput(true);
+ c.setDoInput(true);
+ c.setInstanceFollowRedirects(false);
+ final int status = c.getResponseCode();
+ if(status != 200) {
+ throw new IOException("Got status code " + status + " for " + testUrl);
+ }
+
+ final Result result = (Result)new ObjectInputStream(c.getInputStream()).readObject();
+ if(result.getFailureCount() > 0) {
+ final List<Throwable> failures = new ArrayList<Throwable>(result.getFailureCount());
+ for (Failure f : result.getFailures()) {
+ failures.add(f.getException());
+ }
+ throw new MultipleFailureException(failures);
+ }
+ } catch(ClassNotFoundException e) {
+ throw new IOException("Exception reading test results:" + e, e);
+ } finally {
+ c.disconnect();
+ }
+ }
+}
\ No newline at end of file