/* * 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.apache.felix.dm.impl; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.Dictionary; import org.apache.felix.dm.BundleDependency; import org.apache.felix.dm.Component; import org.apache.felix.dm.ComponentDependencyDeclaration; import org.apache.felix.dm.context.AbstractDependency; import org.apache.felix.dm.context.DependencyContext; import org.apache.felix.dm.context.Event; import org.apache.felix.dm.context.EventType; import org.apache.felix.dm.tracker.BundleTracker; import org.apache.felix.dm.tracker.BundleTrackerCustomizer; import org.osgi.framework.Bundle; import org.osgi.framework.BundleEvent; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; /** * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ public class BundleDependencyImpl extends AbstractDependency<BundleDependency> implements BundleDependency, BundleTrackerCustomizer, ComponentDependencyDeclaration { private BundleTracker m_tracker; private volatile int m_stateMask = Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE; private Bundle m_bundleInstance; private volatile Filter m_filter; private volatile long m_bundleId = -1; private Object m_nullObject; private volatile boolean m_propagate; private volatile Object m_propagateCallbackInstance; private volatile String m_propagateCallbackMethod; public BundleDependencyImpl() { } public BundleDependencyImpl(BundleDependencyImpl prototype) { super(prototype); m_stateMask = prototype.m_stateMask; m_nullObject = prototype.m_nullObject; m_bundleInstance = prototype.m_bundleInstance; m_filter = prototype.m_filter; m_bundleId = prototype.m_bundleId; m_propagate = prototype.m_propagate; m_propagateCallbackInstance = prototype.m_propagateCallbackInstance; m_propagateCallbackMethod = prototype.m_propagateCallbackMethod; } @Override public DependencyContext createCopy() { return new BundleDependencyImpl(this); } @Override public void start() { m_tracker = new BundleTracker(m_component.getBundleContext(), m_stateMask, this); m_tracker.open(); super.start(); } @Override public void stop() { m_tracker.close(); m_tracker = null; super.stop(); } @Override public String getName() { StringBuilder sb = new StringBuilder(); getSimpleName(sb); if (m_filter != null) { sb.append(" "); sb.append(m_filter.toString()); } if (m_bundleId != -1) { sb.append("{bundle.id=" + m_bundleId + "}"); } return sb.toString(); } @Override public String getSimpleName() { // Return the state mask, but don't include the filter or bundle id. StringBuilder sb = new StringBuilder(); if ((m_stateMask & Bundle.ACTIVE) != 0) { sb.append("active"); } if ((m_stateMask & Bundle.INSTALLED) != 0) { if (sb.length() > 0) { sb.append(" "); } sb.append("installed"); } if ((m_stateMask & Bundle.RESOLVED) != 0) { if (sb.length() > 0) { sb.append(" "); } sb.append("resolved"); } return sb.toString(); } @Override public String getFilter() { if (m_filter != null || m_bundleId != -1) { StringBuilder sb = new StringBuilder(); if (m_filter != null) { sb.append(m_filter.toString()); } if (m_bundleId != -1) { sb.append("{bundle.id=" + m_bundleId + "}"); } return sb.toString(); } return null; } @Override public String getType() { return "bundle"; } public Object addingBundle(Bundle bundle, BundleEvent event) { // if we don't like a bundle, we could reject it here by returning null long bundleId = bundle.getBundleId(); if (m_bundleId >= 0 && m_bundleId != bundleId) { return null; } Filter filter = m_filter; if (filter != null) { Dictionary<String,?> headers = bundle.getHeaders(); if (!m_filter.match(headers)) { return null; } } return bundle; } public void addedBundle(Bundle bundle, BundleEvent event, Object object) { m_component.handleEvent(this, EventType.ADDED, new BundleEventImpl(bundle, event)); } public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) { m_component.handleEvent(this, EventType.CHANGED, new BundleEventImpl(bundle, event)); } public void removedBundle(Bundle bundle, BundleEvent event, Object object) { m_component.handleEvent(this, EventType.REMOVED, new BundleEventImpl(bundle, event)); } @Override public void invokeCallback(EventType type, Event ... e) { switch (type) { case ADDED: if (m_add != null) { invoke(m_add, e[0]); } break; case CHANGED: if (m_change != null) { invoke (m_change, e[0]); } break; case REMOVED: if (m_remove != null) { invoke (m_remove, e[0]); } break; default: break; } } private void invoke(String method, Event e) { BundleEventImpl be = (BundleEventImpl) e; m_component.invokeCallbackMethod(getInstances(), method, new Class[][] {{Bundle.class}, {Object.class}, {Component.class, Bundle.class}, {}}, new Object[][] {{be.getBundle()}, {be.getBundle()}, {m_component, be.getBundle()}, {}}); } public BundleDependency setBundle(Bundle bundle) { m_bundleId = bundle.getBundleId(); return this; } public BundleDependency setFilter(String filter) throws IllegalArgumentException { if (filter != null) { try { m_filter = FrameworkUtil.createFilter(filter); } catch (InvalidSyntaxException e) { throw new IllegalArgumentException(e.getMessage()); } } return this; } public BundleDependency setStateMask(int mask) { m_stateMask = mask; return this; } @Override public Class<?> getAutoConfigType() { return Bundle.class; } @SuppressWarnings("unchecked") @Override public Dictionary<String, Object> getProperties() { Event event = getService(); if (event != null) { Bundle bundle = (Bundle) event.getEvent(); if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) { try { CallbackTypeDef callbackInfo = new CallbackTypeDef(Bundle.class, bundle); return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, callbackInfo.m_sigs, callbackInfo.m_args); } catch (InvocationTargetException e) { m_component.getLogger().warn("Exception while invoking callback method", e.getCause()); } catch (Throwable e) { m_component.getLogger().warn("Exception while trying to invoke callback method", e); } throw new IllegalStateException("Could not invoke callback"); } else { return ServiceUtil.toR6Dictionary(bundle.getHeaders()); } } else { throw new IllegalStateException("cannot find bundle"); } } @Override public Object getDefaultService(boolean nullObject) { Object service = null; if (isAutoConfig()) { // TODO does it make sense to add support for custom bundle impls? // service = getDefaultImplementation(); if (service == null && nullObject) { service = getNullObject(); } } return service; } private Bundle getNullObject() { if (m_nullObject == null) { try { m_nullObject = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { Bundle.class }, new DefaultNullObject()); } catch (Throwable e) { m_component.getLogger().err("Could not create null object for Bundle.", e); } } return (Bundle) m_nullObject; } private void getSimpleName(StringBuilder sb) { if ((m_stateMask & Bundle.ACTIVE) != 0) { sb.append("active"); } if ((m_stateMask & Bundle.INSTALLED) != 0) { if (sb.length() > 0) { sb.append(" "); } sb.append("installed"); } if ((m_stateMask & Bundle.RESOLVED) != 0) { if (sb.length() > 0) { sb.append(" "); } sb.append("resolved"); } } }