/* * 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 io.fares.classloader.ClassLoaderFactory; import io.fares.junit.soapui.internal.ReflectionJUnitSoapUIRunner; import io.fares.junit.soapui.internal.SimpleJUnitSoapUIRunner; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; // TODO annotate with "needs class loader factory" public final class SoapUIMockExecutor implements SoapUIMock { public static final String REFELCTION_IMPL = ReflectionJUnitSoapUIRunner.class .getName(); public static final String SIMPLE_IMPL = SimpleJUnitSoapUIRunner.class .getName(); private static ExecutorService service = Executors.newCachedThreadPool(); /** * The mock runner delegate responsible for managing the actual SoapUI mock * service execution */ private SoapUIMock delegate; /** * The classloader factory which will be used to create the classloader that * will in turn be used to load and execute the soapui mock */ private ClassLoaderFactory classLoaderFactory; /** * The name of the {@link SoapUIMock} implementation used to load the * delegate */ private String implClassName; protected SoapUIMockExecutor() { } public SoapUIMockExecutor(ClassLoaderFactory classLoaderFactory, String implClassName) { this.classLoaderFactory = classLoaderFactory; this.implClassName = implClassName; } @Override public void start(final MockRunnerTask task) { if (classLoaderFactory == null) { throw new RuntimeException( "A filtering classloader factory must be configured"); } if (implClassName == null) { throw new RuntimeException( "A implementation class name must be configured"); } try { // run up a future Future<SoapUIMock> srf = service .submit(new CallableRunner(classLoaderFactory .createClassLoader(), implClassName, task)); // better to wait until this whole mock is loaded delegate = srf.get(); } catch (Exception e) { throw new RuntimeException("Failed to start soapui runner thread", e); } } @Override public void stop() { if (delegate != null) { delegate.stop(); } } @Override public boolean isRunning() { return delegate.isRunning(); } private final class CallableRunner implements Callable<SoapUIMock> { ClassLoader filteringClassLoader; ClassLoader originalClassLoader; String implClassName; MockRunnerTask task; public CallableRunner(final ClassLoader filteringClassLoader, final String implClassName, MockRunnerTask task) { this.filteringClassLoader = filteringClassLoader; this.implClassName = implClassName; this.task = task; } @Override public SoapUIMock call() throws Exception { // swap thead context classloader in case soapui does something // weird originalClassLoader = Thread.currentThread() .getContextClassLoader(); Thread.currentThread().setContextClassLoader(filteringClassLoader); try { @SuppressWarnings("unchecked") Class<SoapUIMock> rmeClass = (Class<SoapUIMock>) filteringClassLoader .loadClass(implClassName); SoapUIMock rme = rmeClass.newInstance(); rme.start(task); return rme; } finally { if (originalClassLoader != null) { Thread.currentThread().setContextClassLoader( originalClassLoader); } } } } }