OSGiServiceImporter.java

/*
 * 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.qi4j.library.osgi;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.qi4j.api.service.ImportedServiceDescriptor;
import org.qi4j.api.service.ServiceImporter;
import org.qi4j.api.service.ServiceImporterException;
import org.qi4j.api.service.ServiceUnavailableException;

/**
 * Import OSGi services in a Zest Module.
 */
public class OSGiServiceImporter
    implements ServiceImporter
{

    @Override
    public Object importService( ImportedServiceDescriptor serviceDescriptor )
        throws ServiceImporterException
    {
        OSGiImportInfo info = serviceDescriptor.metaInfo( OSGiImportInfo.class );
        BundleContext context = info.bundleContext();
        ServiceTrackerHandler handler = new ServiceTrackerHandler( context, serviceDescriptor.type() );
        handler.open();
        return Proxy.newProxyInstance( serviceDescriptor.type().getClassLoader(), info.interfaces(), handler );
    }

    @Override
    public boolean isAvailable( Object instance )
    {
        ServiceTrackerHandler handler = (ServiceTrackerHandler) Proxy.getInvocationHandler( instance );
        return handler.isAvailable();
    }

    public static class ServiceTrackerHandler
        extends ServiceTracker
        implements InvocationHandler
    {

        private volatile Object service;
        private Class typeToImport;

        public ServiceTrackerHandler( BundleContext context, Class typeToImport )
        {
            super( context, typeToImport.getName(), null );
            this.typeToImport = typeToImport;
        }

        @Override
        public Object invoke( Object proxy, Method method, Object[] args )
            throws Throwable
        {
            Object temporary;
            synchronized( this )
            {
                if( service == null )
                {
                    wait( 5000 );  // Max wait 5 seconds.
                    if( service == null )
                    {
                        throw new ServiceUnavailableException(
                            "The OSGi service is currently not available: " + typeToImport.getName() );
                    }
                }
                temporary = service;
            }
            return method.invoke( temporary, args );
        }

        @Override
        public Object addingService( ServiceReference reference )
        {
            synchronized( this )
            {
                service = super.addingService( reference );
                notifyAll();
                return service;
            }
        }

        @Override
        public void removedService( ServiceReference reference, Object service )
        {
            synchronized( this )
            {
                super.removedService( reference, service );
                notifyAll();
                this.service = null;
            }
        }

        public boolean isAvailable()
        {
            return service != null;
        }

    }

}