Java tutorial
/* * Copyright 2008 Alin Dreghiciu. * * Licensed 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.ops4j.pax.exam.rbc.client; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.rmi.ConnectException; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.osgi.framework.BundleException; import org.ops4j.pax.exam.Constants; import org.ops4j.pax.exam.rbc.internal.RemoteBundleContext; import org.ops4j.pax.exam.spi.container.TestContainer; import org.ops4j.pax.exam.spi.container.TestContainerException; import org.ops4j.pax.exam.spi.container.TimeoutException; /** * A {@link RemoteBundleContext} client, that takes away RMI handling. * * @author Alin Dreghiciu (adreghiciu@gmail.com) * @since 0.3.0, December 15, 2008 */ public class RemoteBundleContextClient implements TestContainer { /** * JCL logger. */ private static final Log LOG = LogFactory.getLog(RemoteBundleContextClient.class); /** * RMI communication port. */ private final Integer m_rmiPort; /** * Timeout for looking up the remote bundle context via RMI. */ private final long m_rmiLookupTimeout; /** * Remote bundle context instance. */ private RemoteBundleContext m_remoteBundleContext; /** * Constructor. * * @param rmiPort RMI communication port (cannot be null) * @param rmiLookupTimeout timeout for looking up the remote bundle context via RMI (cannot be null) */ public RemoteBundleContextClient(final Integer rmiPort, final long rmiLookupTimeout) { m_rmiPort = rmiPort; m_rmiLookupTimeout = rmiLookupTimeout; } /** * {@inheritDoc} */ public <T> T getService(final Class<T> serviceType) { return getService(serviceType, Constants.NO_WAIT); } /** * {@inheritDoc} * Returns a dynamic proxy in place of the actual service, forwarding the calls via the remote bundle context. */ @SuppressWarnings("unchecked") public <T> T getService(final Class<T> serviceType, final long timeoutInMillis) { return (T) Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { serviceType }, new InvocationHandler() { /** * {@inheritDoc} * Delegates the call to remote bundle context. */ public Object invoke(final Object proxy, final Method method, final Object[] params) throws Throwable { try { return getRemoteBundleContext().remoteCall(method.getDeclaringClass(), method.getName(), method.getParameterTypes(), timeoutInMillis, params); } catch (InvocationTargetException e) { throw e.getCause(); } catch (RemoteException e) { throw new TestContainerException("Remote exception", e); } catch (Exception e) { throw new TestContainerException("Invocation exception", e); } } }); } /** * {@inheritDoc} */ public long installBundle(final String bundleUrl) { try { return getRemoteBundleContext().installBundle(bundleUrl); } catch (RemoteException e) { throw new TestContainerException("Remote exception", e); } catch (BundleException e) { throw new TestContainerException("Bundle cannot be installed", e); } } /** * {@inheritDoc} */ public long installBundle(final String bundleLocation, final byte[] bundle) throws TestContainerException { try { return getRemoteBundleContext().installBundle(bundleLocation, bundle); } catch (RemoteException e) { throw new TestContainerException("Remote exception", e); } catch (BundleException e) { throw new TestContainerException("Bundle cannot be installed", e); } } /** * {@inheritDoc} */ public void startBundle(final long bundleId) throws TestContainerException { try { getRemoteBundleContext().startBundle(bundleId); } catch (RemoteException e) { throw new TestContainerException("Remote exception", e); } catch (BundleException e) { throw new TestContainerException("Bundle cannot be started", e); } } /** * {@inheritDoc} */ public void setBundleStartLevel(final long bundleId, final int startLevel) throws TestContainerException { try { getRemoteBundleContext().setBundleStartLevel(bundleId, startLevel); } catch (RemoteException e) { throw new TestContainerException("Remote exception", e); } catch (BundleException e) { throw new TestContainerException("Start level cannot be set", e); } } /** * {@inheritDoc} */ public void start() { try { getRemoteBundleContext().startBundle(0); } catch (RemoteException e) { throw new TestContainerException("Remote exception", e); } catch (BundleException e) { throw new TestContainerException("System bundle cannot be started", e); } } /** * {@inheritDoc} */ public void stop() { try { getRemoteBundleContext().stopBundle(0); } catch (RemoteException e) { throw new TestContainerException("Remote exception", e); } catch (BundleException e) { throw new TestContainerException("System bundle cannot be stopped", e); } } /** * {@inheritDoc} */ public void waitForState(final long bundleId, final int state, final long timeoutInMillis) throws TimeoutException { try { getRemoteBundleContext().waitForState(bundleId, state, timeoutInMillis); } catch (org.ops4j.pax.exam.rbc.internal.TimeoutException e) { throw new TimeoutException(e.getMessage()); } catch (RemoteException e) { throw new TestContainerException("Remote exception", e); } catch (BundleException e) { throw new TestContainerException("Bundle cannot be found", e); } } /** * Looks up the {@link RemoteBundleContext} via RMI. The lookup will timeout in the specified number of millis. * * @return remote bundle context */ private RemoteBundleContext getRemoteBundleContext() { if (m_remoteBundleContext == null) { long startedTrying = System.currentTimeMillis(); //!! Absolutely necesary for RMI class loading to work // TODO maybe use ContextClassLoaderUtils.doWithClassLoader Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); Throwable reason = null; try { final Registry registry = LocateRegistry.getRegistry(m_rmiPort); do { try { m_remoteBundleContext = (RemoteBundleContext) registry .lookup(RemoteBundleContext.class.getName()); } catch (ConnectException e) { reason = e; } catch (NotBoundException e) { reason = e; } } while (m_remoteBundleContext == null && (m_rmiLookupTimeout == Constants.WAIT_FOREVER || System.currentTimeMillis() < startedTrying + m_rmiLookupTimeout)); } catch (RemoteException e) { reason = e; } if (m_remoteBundleContext == null) { throw new TestContainerException("Cannot get the remote bundle context", reason); } LOG.info("Remote bundle context found after " + (System.currentTimeMillis() - startedTrying) + " millis"); } return m_remoteBundleContext; } /** * Getter. * * @return rmi port */ public Integer getRmiPort() { return m_rmiPort; } }