/* * 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 io.fares.junit.soapui; import java.io.File; import java.net.URL; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.eclipse.aether.repository.Authentication; import org.eclipse.aether.repository.Proxy; import org.eclipse.aether.util.repository.AuthenticationBuilder; import static io.fares.junit.soapui.SoapUI.*; import io.fares.classloader.AetherClasspathResolver; import io.fares.classloader.ClasspathResolver; import io.fares.classloader.FilteringClassLoaderFactory; public class SoapUIMockRunner implements TestRule { // the test case in progress private Statement base; // used to run new thread in the jailed classloader private String implName = SoapUIMockExecutor.SIMPLE_IMPL; private String soapuiVersion; // used to build the jar dependencies of the soapui runtime ClasspathResolver resolver = new AetherClasspathResolver(); // used to setup the classloader jail List<String> passFilters; List<String> blockFilters; List<Class<?>> includeClazzContainerURLs; // used to flag to include the unit test jar or file based classpath to the // jail class loader (e.g. to run a soapui wsdl from within the jail) private boolean includeUnitTestLocation = false; // used to control the startup and teardown of the soapui mock private MockRunnerTask task = new MockRunnerTask(); /** * Need to keep track of the executing runner */ private SoapUIMockExecutor runner; /** * Creates a basic configured {@link SoapUIMockRunner} */ public SoapUIMockRunner() { this(DEFAULT_PASSFILTER, DEFAULT_BLOCKFILTER); } /** * If you really must, just make sure your filters allow the SoapUI to find * all its dependencies above the filtering class loader, else class cast * nightmare. * * @param passFilters * class or package names that need to be passed to the parent classloader * * @param blockFilters * class or package names that must be blocked from going to the parent classloader * */ public SoapUIMockRunner(String[] passFilters, String[] blockFilters) { addPassFilters(passFilters); addBlockFilters(blockFilters); } public Statement apply(Statement base, Description description) { this.base = base; return statement(base); } private Statement statement(final Statement base) { return new Statement() { @Override public void evaluate() throws Throwable { before(); try { base.evaluate(); } finally { after(); } } }; } protected void before() throws Throwable { if (task.getProjectFile() == null) { throw new RuntimeException( "a project resource location must be provided to the rule"); } // first need to configure the resolver with soapui dependency and repo resolver.addArtifact(newSoapUIArtifact(soapuiVersion)); // it pays to add central as well cause sometimes soapui does not // contain all dependencies and we are not really reading the soapui pom resolver.addRemoteRepository(newSoapUIRepository(), newCentralRepository()); // then we need to create the filtering classloader FilteringClassLoaderFactory clf = new FilteringClassLoaderFactory( resolver); // add all filter rules the filtering classloader has to abide by clf.addPassFilters(passFilters); clf.addBlockFilters(blockFilters); // if requested, the location of the unit test will be added to the // classpath that is visible to the soapui itself (e.g. one can add any // extensions here) if (includeUnitTestLocation) { clf.addIncludeClazzContainerURLs(base.getClass()); } // alsways need to add the container of this class (self) as we need to // get this through the filtering classloader else our code will fail // with class cast exception clf.addIncludeClazzContainerURLs(SoapUIMock.class, MockRunnerTask.class); // add classloader of the base test as parent, thats obviously the // context class loader here clf.setParentClassLoader(base.getClass().getClassLoader()); // lets do this runner = new SoapUIMockExecutor(clf, implName); runner.start(task); } protected void after() { if (runner != null && runner.isRunning()) { runner.stop(); } } public SoapUIMockRunner withMockServiceName(String name) { task.setMockServiceName(name); return this; } public SoapUIMockRunner withProjectFile(File file) { if (file == null) { throw new RuntimeException("File must not be null"); } else if (!file.exists()) { throw new RuntimeException("File " + file.getAbsolutePath() + " does not exist"); } else if (!file.isFile()) { throw new RuntimeException("FileName " + file.getAbsolutePath() + " is not a file"); } try { task.setProjectFile(file.toURI().toURL()); } catch (MalformedURLException e) { throw new RuntimeException("Cannot parse project file " + file.getAbsolutePath(), e); } return this; } public SoapUIMockRunner withProjectFileName(String fileName) { return withProjectFile(new File(fileName)); } /** * set the project path of the SoapUI project * * @param resourcePath * the resource's classpath * * @return this rule */ public SoapUIMockRunner withProjectPath(String resourcePath) { // try the local classloader URL url = getClass().getClassLoader().getResource(resourcePath); if (url == null) { throw new RuntimeException( "project cannot be loaded from resource path " + resourcePath); } task.setProjectFile(url); return this; } public SoapUIMockRunner withProjectPath(URL resource) { task.setProjectFile(resource); return this; } public SoapUIMockRunner securePort() { task.securePort(); return this; } public SoapUIMockRunner withMockHost(String host) { task.setMockHost(host); return this; } public SoapUIMockRunner withMockPort(int port) { task.setMockPort(port); return this; } public SoapUIMockRunner withMockPath(String path) { task.setMockPath(path); return this; } public SoapUIMockRunner simpleBinding() { this.implName = SoapUIMockExecutor.SIMPLE_IMPL; return this; } public SoapUIMockRunner reflectionBinding() { this.implName = SoapUIMockExecutor.REFELCTION_IMPL; return this; } /** * Sets the version of soapui to use. Obviously this is dependent on the * version of soapui this library was compiled against. if not specified, it * will use the default version with which this module was compiled with. * * Check which version is default by running {@link SoapUI#version()}. * * @param version * the soapui version to use (note it currently does not do any * checks so one could force down anything) * * @return a configured mock runner */ public SoapUIMockRunner soapuiVersion(String version) { this.soapuiVersion = version; return this; } public String getMockEndpoint() { return task.getMockEndpoint(); } public SoapUIMockRunner withImplementation(String implName) { this.implName = implName; return this; } public boolean isRunning() { return runner != null && runner.isRunning(); } public SoapUIMockRunner includeUnitTestLocation() { includeUnitTestLocation = true; return this; } public List<String> getPassFilters() { if (passFilters == null) { passFilters = new ArrayList<String>(); } return passFilters; } public SoapUIMockRunner addPassFilters(String... filters) { if (filters != null && filters.length > 0) { getPassFilters().addAll(Arrays.asList(filters)); } return this; } public List<String> getBlockFilters() { if (blockFilters == null) { blockFilters = new ArrayList<String>(); } return blockFilters; } public SoapUIMockRunner addBlockFilters(String... filters) { if (filters != null && filters.length > 0) { getBlockFilters().addAll(Arrays.asList(filters)); } return this; } public SoapUIMockRunner setProxy(String type, String host, int port, String username, String password) { Authentication auth = new AuthenticationBuilder().addUsername(username) .addPassword(password).build(); resolver.setProxy(new Proxy(type, host, port, auth)); return this; } }