org.ops4j.pax.exam.rbc.client.RemoteBundleContextClient.java Source code

Java tutorial

Introduction

Here is the source code for org.ops4j.pax.exam.rbc.client.RemoteBundleContextClient.java

Source

/*
 * 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;
    }

}