/** * Copyright (C) 2014 Guillaume Nodet * * Red Hat 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.ops4j.pax.url.mvn.internal; import java.io.IOException; import java.lang.reflect.Method; import java.net.URL; import java.net.URLConnection; import java.util.Dictionary; import java.util.Hashtable; import java.util.concurrent.atomic.AtomicReference; import org.ops4j.pax.url.mvn.MavenResolver; import org.ops4j.pax.url.mvn.ServiceConstants; import org.ops4j.pax.url.mvn.internal.config.MavenConfiguration; import org.ops4j.pax.url.mvn.internal.config.MavenConfigurationImpl; import org.ops4j.util.property.DictionaryPropertyResolver; import org.ops4j.util.property.PropertyResolver; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceRegistration; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedService; import org.osgi.service.url.AbstractURLStreamHandlerService; import org.osgi.service.url.URLConstants; import org.osgi.service.url.URLStreamHandlerService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Bundle activator for protocol handlers. */ public class Activator extends AbstractURLStreamHandlerService implements BundleActivator { /** * Logger. */ private static final Logger LOG = LoggerFactory.getLogger( Activator.class ); /** * Bundle context in use. */ private BundleContext m_bundleContext; /** * Handler service registration. Used for cleanup. */ private ServiceRegistration<URLStreamHandlerService> m_handlerReg; /** * Managed service registration. Used for cleanup. */ private ServiceRegistration<ManagedService> m_managedServiceReg; /** * Maven resolver. */ private final AtomicReference<MavenResolver> m_resolver = new AtomicReference<MavenResolver>(); /** * Managed service registration. Used for cleanup. */ private final AtomicReference<ServiceRegistration<MavenResolver>> m_resolverReg = new AtomicReference<ServiceRegistration<MavenResolver>>(); /** * Registers Handler as a wrap: protocol stream handler service and as a configuration managed service if * possible. * * @param bundleContext the bundle context. * * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) */ @Override public void start( final BundleContext bundleContext ) { m_bundleContext = bundleContext; updated(null); registerManagedService(); } /** * Performs cleanup:<br/> * * Unregister handler;<br/> * * Unregister managed service;<br/> * * Release bundle context. * * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) */ @Override public void stop( final BundleContext bundleContext ) { if ( m_handlerReg != null ) { m_handlerReg.unregister(); m_handlerReg = null; } if ( m_managedServiceReg != null ) { m_managedServiceReg.unregister(); m_managedServiceReg = null; } ServiceRegistration<MavenResolver> registration = m_resolverReg.getAndSet( null ); if ( registration != null ) { registration.unregister(); } MavenResolver resolver = m_resolver.getAndSet( null ); if ( resolver != null ) { try { resolver.close(); } catch (IOException e) { // Ignore } } m_bundleContext = null; LOG.debug( "Handler for protocols " + ServiceConstants.PROTOCOL + " stopped" ); } /** * Register the handler service. */ private void registerHandler() { final Dictionary<String, Object> props = new Hashtable<String, Object>(); props.put( URLConstants.URL_HANDLER_PROTOCOL, ServiceConstants.PROTOCOL ); m_handlerReg = safeRegisterService( URLStreamHandlerService.class, this, props); } /** * Registers a managed service to listen on configuration updates. */ private void registerManagedService() { try { m_managedServiceReg = OptionalConfigAdminHelper.registerManagedService( this ); } catch ( Throwable ignore ) { updated( null ); m_managedServiceReg = null; } } public void updated(Dictionary<String, ?> config) { PropertyResolver propertyResolver; if (config == null) { propertyResolver = new PropertyResolver() { @Override public String get(String propertyName) { return m_bundleContext.getProperty(propertyName); } }; } else { propertyResolver = new DictionaryPropertyResolver(config); } MavenConfiguration mavenConfig = new MavenConfigurationImpl(propertyResolver, ServiceConstants.PID); if (!((MavenConfigurationImpl) mavenConfig).isValid()) { return; } MavenResolver resolver = new AetherBasedResolver(mavenConfig); MavenResolver oldResolver = m_resolver.getAndSet( resolver ); Dictionary<String, Object> properties = new Hashtable<String, Object>(); properties.put("configuration", config == null ? "bundlecontext" : "configadmin"); ServiceRegistration<MavenResolver> registration = safeRegisterService( MavenResolver.class, resolver, properties); registration = m_resolverReg.getAndSet(registration); if (registration != null) { registration.unregister(); } if ( oldResolver != null ) { try { oldResolver.close(); } catch (IOException e) { // Ignore } } else { // first registration of URLStreamHandlerService registerHandler(); } } @Override public URLConnection openConnection( final URL url ) throws IOException { return new Connection( url, m_resolver.get() ); } /** * Safely registers the specified service to the {@link BundleContext}. * <p> * This method is introduced to support backward compatibility with the older 4.0.0 * version because org.osgi.framework in version 1.5 has method * <code>safeRegisterService(String,Object,Dictionary)</code> while in version * 1.6 it is changed to <code>safeRegisterService(String,Object,Dictionary<String, ? >)</code>. * The dictionary is not a raw typed. * <p> * The method can be replaced to the single <code>registerService</code> method * invocation if the backward compatibility will not be supported any more. In * such case the specified version ranges in <i>osgi.bnd</i> should be replaced * or removed as well: <pre> * {@code * org.osgi.framework;version="[1.5,2)" * org.osgi.util.tracker;version="[1.4,2)" }</pre> */ @SuppressWarnings("unchecked") private <T> ServiceRegistration<T> safeRegisterService(Class<T> clazz, T service, Dictionary<String, ?> properties) { Method[] methods = BundleContext.class.getMethods(); for (Method method : methods) { Class<?>[] params = method.getParameterTypes(); if ("registerService".equals(method.getName()) && params.length == 3 && String.class.equals(params[0]) && Object.class.equals(params[1]) && Dictionary.class.equals(params[2])) { try { return (ServiceRegistration<T>) method.invoke( m_bundleContext, clazz.getName(), service, properties); } catch (Exception e) { LOG.error("Unable to register service {}", service, e); return null; } } } LOG.error("Method registerService is not found in BundleContext"); return null; } static class OptionalConfigAdminHelper { /** * Registers a managed service to listen on configuration updates. */ static ServiceRegistration<ManagedService> registerManagedService(final Activator activator) { final Dictionary<String, String> props = new Hashtable<String, String>(); props.put(Constants.SERVICE_PID, ServiceConstants.PID); return activator.safeRegisterService( ManagedService.class, new ManagedService() { @Override public void updated(Dictionary<String, ?> dictionary) throws ConfigurationException { activator.updated(dictionary); } }, props ); } } }