blob: 357f08371a4a077aea12fc09fbbaf6bcfc250a45 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2009 Martin Lippert and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Martin Lippert initial implementation
*******************************************************************************/
package org.eclipse.equinox.weaving.aspectj.loadtime;
import java.net.URL;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.aspectj.weaver.loadtime.definition.Definition;
import org.aspectj.weaver.loadtime.definition.DocumentParser;
import org.aspectj.weaver.loadtime.definition.Definition.ConcreteAspect;
import org.eclipse.equinox.weaving.aspectj.AspectAdmin;
import org.eclipse.osgi.util.ManifestElement;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.Constants;
import org.osgi.framework.SynchronousBundleListener;
/**
* The AspectAdmin takes care of resolving aspect definitions of resolved
* bundles and provides information which bundle should be woven with which
* aspects.
*
* The AspectAdmin takes the aop.xml files into account as well as the aspect
* definitions in the bundle manifests.
*
* All the information parsing and resolving is done at bundle resolve time, the
* removal from the cache is done at unresolved events. The initial state is
* re-created by the initialize method.
*
* @author Martin Lippert
*/
public class AspectAdminImpl implements AspectAdmin, SynchronousBundleListener {
// remember all aspect definitions for the given bundle (regardless of the way they are declared)
private final Map<Bundle, Definition> aspectDefinitions;
// remember only the exported aspect definitions for the given bundle (regardless of the way they are declared)
private final Map<Bundle, Definition> aspectDefinitionsExported;
// remember the aspect policies per exported package per bundle
private final Map<Bundle, Map<String, Integer>> aspectPolicies;
/**
* Create a registry to manage aspect definition files
*/
public AspectAdminImpl() {
this.aspectDefinitions = new ConcurrentHashMap<Bundle, Definition>();
this.aspectDefinitionsExported = new ConcurrentHashMap<Bundle, Definition>();
this.aspectPolicies = new ConcurrentHashMap<Bundle, Map<String, Integer>>();
}
/**
* @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent)
*/
public void bundleChanged(final BundleEvent event) {
if (event.getType() == BundleEvent.RESOLVED) {
bundleResolved(event.getBundle());
} else if (event.getType() == BundleEvent.UNRESOLVED) {
bundleUnresolved(event.getBundle());
}
}
/**
* Do the parsing when a bundle is resolved
*
* @param bundle The bundle that is resolved (should not be null)
*/
public void bundleResolved(final Bundle bundle) {
if (!this.aspectDefinitions.containsKey(bundle)
&& !this.aspectDefinitionsExported.containsKey(bundle)
&& !this.aspectPolicies.containsKey(bundle)) {
parseDefinitions(bundle);
}
}
/**
* Remove the cached aspect definitions from the aspect definition registry
*
* @param bundle The bundle that got unresolved (should not be null)
*/
public void bundleUnresolved(final Bundle bundle) {
this.aspectDefinitions.remove(bundle);
this.aspectDefinitionsExported.remove(bundle);
this.aspectPolicies.remove(bundle);
}
/**
* @see org.eclipse.equinox.weaving.aspectj.AspectAdmin#getAspectDefinition(org.osgi.framework.Bundle)
*/
public Definition getAspectDefinition(final Bundle bundle) {
return this.aspectDefinitions.get(bundle);
}
/**
* @see org.eclipse.equinox.weaving.aspectj.AspectAdmin#getAspectPolicy(org.osgi.framework.Bundle,
* java.lang.String)
*/
public int getAspectPolicy(final Bundle bundle, final String packageName) {
final Map<String, Integer> policies = this.aspectPolicies.get(bundle);
if (policies != null) {
final Integer policy = policies.get(packageName);
if (policy != null) {
return policy;
}
}
return AspectAdmin.ASPECT_POLICY_NOT_DEFINED;
}
/**
* Finds the location of the aspect definition within the given bundle. The
* default location is "META-INF/aop.xml", but if the bundles manifest
* contains an entry for "Eclipse-AspectContext", that value is used to
* search for the aop.xml file.
*
* @param bundle The bundle for which to calculate the location of the
* aspect definition file
* @return The path to the aspect definition relately to the given bundle
*/
public String getDefinitionLocation(final Bundle bundle) {
String aopContextHeader = (String) bundle.getHeaders().get(
AOP_CONTEXT_LOCATION_HEADER);
if (aopContextHeader != null) {
aopContextHeader = aopContextHeader.trim();
return aopContextHeader;
}
return AOP_CONTEXT_DEFAULT_LOCATION;
}
/**
* @see org.eclipse.equinox.weaving.aspectj.AspectAdmin#getExportedAspectDefinitions(org.osgi.framework.Bundle)
*/
public Definition getExportedAspectDefinitions(final Bundle bundle) {
return this.aspectDefinitionsExported.get(bundle);
}
/**
* Initialize the state of the aspect definition registry for the given
* bundles. This should typically be called when the weaving service bundle
* is started to set up the aspect definitions for all resolved bundles
*
* @param bundles All bundles that should be taken into account and searched
* for aspect definitions
*
*/
public void initialize(final Bundle[] bundles) {
for (final Bundle bundle : bundles) {
final int state = bundle.getState();
if (state != Bundle.INSTALLED && state != Bundle.UNINSTALLED) {
parseDefinitions(bundle);
}
}
}
/**
* @see org.eclipse.equinox.weaving.aspectj.AspectAdmin#resolveImportedPackage(org.osgi.framework.Bundle,
* java.lang.String, int)
*/
public Definition resolveImportedPackage(final Bundle bundle,
final String packageName, final int applyAspectsPolicy) {
if (AspectAdmin.ASPECT_APPLY_POLICY_TRUE == applyAspectsPolicy) {
final Definition exportedAspectDefinitions = getExportedAspectDefinitions(bundle);
final Definition result = new Definition();
if (exportedAspectDefinitions != null) {
final List<?> aspectClassNames = exportedAspectDefinitions
.getAspectClassNames();
for (final Iterator<?> iterator = aspectClassNames.iterator(); iterator
.hasNext();) {
final String aspectName = (String) iterator.next();
final String aspectPackageName = getPackage(aspectName);
if (aspectPackageName.equals(packageName)) {
result.getAspectClassNames().add(aspectName);
}
}
final Iterator<?> concreteAspects = exportedAspectDefinitions
.getConcreteAspects().iterator();
while (concreteAspects.hasNext()) {
final Definition.ConcreteAspect concreteAspect = (ConcreteAspect) concreteAspects
.next();
if (concreteAspect.name != null
&& getPackage(concreteAspect.name).equals(
packageName)) {
result.getConcreteAspects().add(concreteAspect);
}
}
if (exportedAspectDefinitions.getWeaverOptions().trim()
.length() > 0) {
result.appendWeaverOptions(exportedAspectDefinitions
.getWeaverOptions());
}
}
if (result.getAspectClassNames().size() > 0
|| result.getConcreteAspects().size() > 0
|| result.getWeaverOptions().length() > 0) {
return result;
} else {
return null;
}
} else if (AspectAdmin.ASPECT_APPLY_POLICY_FALSE == applyAspectsPolicy) {
return null;
} else {
final Definition exportedAspectDefinitions = getExportedAspectDefinitions(bundle);
final Definition result = new Definition();
if (exportedAspectDefinitions != null) {
final List<?> aspectClassNames = exportedAspectDefinitions
.getAspectClassNames();
for (final Iterator<?> iterator = aspectClassNames.iterator(); iterator
.hasNext();) {
final String aspectName = (String) iterator.next();
final String aspectPackageName = getPackage(aspectName);
final int aspectPolicy = getAspectPolicy(bundle,
aspectPackageName);
if (aspectPackageName.equals(packageName)
&& (AspectAdmin.ASPECT_POLICY_NOT_DEFINED == aspectPolicy || AspectAdmin.ASPECT_POLICY_OPT_OUT == aspectPolicy)) {
result.getAspectClassNames().add(aspectName);
}
}
final Iterator<?> concreteAspects = exportedAspectDefinitions
.getConcreteAspects().iterator();
while (concreteAspects.hasNext()) {
final Definition.ConcreteAspect concreteAspect = (ConcreteAspect) concreteAspects
.next();
final String aspectPackageName = getPackage(concreteAspect.name);
final int aspectPolicy = getAspectPolicy(bundle,
aspectPackageName);
if (aspectPackageName.equals(packageName)
&& (AspectAdmin.ASPECT_POLICY_NOT_DEFINED == aspectPolicy || AspectAdmin.ASPECT_POLICY_OPT_OUT == aspectPolicy)) {
result.getConcreteAspects().add(concreteAspect);
}
}
if (exportedAspectDefinitions.getWeaverOptions().trim()
.length() > 0) {
result.appendWeaverOptions(exportedAspectDefinitions
.getWeaverOptions());
}
}
if (result.getAspectClassNames().size() > 0
|| result.getConcreteAspects().size() > 0
|| result.getWeaverOptions().length() > 0) {
return result;
} else {
return null;
}
}
}
/**
* @see org.eclipse.equinox.weaving.aspectj.AspectAdmin#resolveRequiredBundle(org.osgi.framework.Bundle,
* int)
*/
public Definition resolveRequiredBundle(final Bundle bundle,
final int applyAspectsPolicy) {
if (AspectAdmin.ASPECT_APPLY_POLICY_TRUE == applyAspectsPolicy) {
return getExportedAspectDefinitions(bundle);
} else if (AspectAdmin.ASPECT_APPLY_POLICY_FALSE == applyAspectsPolicy) {
return null;
} else {
final Definition exportedAspectDefinitions = getExportedAspectDefinitions(bundle);
final Definition result = new Definition();
if (exportedAspectDefinitions != null) {
final Iterator<?> aspects = exportedAspectDefinitions
.getAspectClassNames().iterator();
while (aspects.hasNext()) {
final String aspect = (String) aspects.next();
final String aspectPackage = getPackage(aspect);
final int aspectPolicy = getAspectPolicy(bundle,
aspectPackage);
if (aspectPolicy == AspectAdmin.ASPECT_POLICY_NOT_DEFINED
|| aspectPolicy == AspectAdmin.ASPECT_POLICY_OPT_OUT) {
result.getAspectClassNames().add(aspect);
}
}
final Iterator<?> concreteAspects = exportedAspectDefinitions
.getConcreteAspects().iterator();
while (concreteAspects.hasNext()) {
final Definition.ConcreteAspect concreteAspect = (Definition.ConcreteAspect) concreteAspects
.next();
final String aspectPackage = getPackage(concreteAspect.name);
final int aspectPolicy = getAspectPolicy(bundle,
aspectPackage);
if (aspectPolicy == AspectAdmin.ASPECT_POLICY_NOT_DEFINED
|| aspectPolicy == AspectAdmin.ASPECT_POLICY_OPT_OUT) {
result.getConcreteAspects().add(concreteAspect);
}
}
if (exportedAspectDefinitions.getWeaverOptions().trim()
.length() > 0) {
result.appendWeaverOptions(exportedAspectDefinitions
.getWeaverOptions());
}
}
if (result.getAspectClassNames().size() > 0
|| result.getConcreteAspects().size() > 0
|| result.getWeaverOptions().length() > 0) {
return result;
} else {
return null;
}
}
}
/**
* Parse the aspect definition for the given bundle, if there is one.
*
* @param bundle The bundle for which the aspect definition should be parsed
*/
protected void parseDefinitions(final Bundle bundle) {
try {
Definition allAspectsDefinition = null;
final Set<String> exportedAspects = new LinkedHashSet<String>();
final Set<Definition.ConcreteAspect> exportedConcreteAspects = new HashSet<Definition.ConcreteAspect>();
final Map<String, Integer> policies = new HashMap<String, Integer>();
final Set<String> exportedPackages = new HashSet<String>();
// try to find aop.xml file
final String aopXmlLocation = getDefinitionLocation(bundle);
final URL aopXmlDef = bundle.getEntry(aopXmlLocation);
if (aopXmlDef != null) {
allAspectsDefinition = DocumentParser.parse(aopXmlDef);
}
// parse export package headers
final Dictionary<?, ?> manifest = bundle.getHeaders();
final ManifestElement[] exports = ManifestElement.parseHeader(
Constants.EXPORT_PACKAGE, (String) manifest
.get(Constants.EXPORT_PACKAGE));
for (int i = 0; exports != null && i < exports.length; i++) {
final String packageName = exports[i].getValue();
exportedPackages.add(packageName);
// policies
final String policy = exports[i]
.getDirective(ASPECT_POLICY_DIRECTIVE);
if (policy != null
&& policy.trim().toLowerCase().equals(
ASPECT_POLICY_DIRECTIVE_OPT_OUT)) {
policies
.put(packageName, AspectAdmin.ASPECT_POLICY_OPT_OUT);
}
if (policy != null
&& policy.trim().toLowerCase().equals(
ASPECT_POLICY_DIRECTIVE_OPT_IN)) {
policies.put(packageName, AspectAdmin.ASPECT_POLICY_OPT_IN);
}
// aspects
final String allaspects = exports[i]
.getAttribute(ASPECTS_ATTRIBUTE);
if (allaspects != null) {
final String[] aspects = ManifestElement
.getArrayFromList(allaspects);
if (aspects != null) {
for (int j = 0; j < aspects.length; j++) {
exportedAspects.add(packageName + "." + aspects[j]); //$NON-NLS-1$
}
}
}
}
// add aop.xml declared aspects to the list of exported aspects if their packages are exported
if (allAspectsDefinition != null
&& allAspectsDefinition.getAspectClassNames() != null) {
final Iterator<?> iterator = allAspectsDefinition
.getAspectClassNames().iterator();
while (iterator.hasNext()) {
final String aspect = (String) iterator.next();
final String packageName = getPackage(aspect);
if (exportedPackages.contains(packageName)) {
exportedAspects.add(aspect);
}
}
}
if (allAspectsDefinition != null
&& allAspectsDefinition.getConcreteAspects().size() > 0) {
final Iterator<?> iterator = allAspectsDefinition
.getConcreteAspects().iterator();
while (iterator.hasNext()) {
final Definition.ConcreteAspect concreteAspect = (Definition.ConcreteAspect) iterator
.next();
if (concreteAspect.name != null
&& exportedPackages
.contains(getPackage(concreteAspect.name))) {
exportedConcreteAspects.add(concreteAspect);
}
}
}
if (allAspectsDefinition != null) {
this.aspectDefinitions.put(bundle, allAspectsDefinition);
}
if (exportedAspects.size() > 0
|| exportedConcreteAspects.size() > 0
|| (allAspectsDefinition != null && allAspectsDefinition
.getWeaverOptions().length() > 0)) {
final Definition exportedAspectsDefinition = new Definition();
exportedAspectsDefinition.getAspectClassNames().addAll(
exportedAspects);
exportedAspectsDefinition.getConcreteAspects().addAll(
exportedConcreteAspects);
if (allAspectsDefinition != null
&& allAspectsDefinition.getWeaverOptions().trim()
.length() > 0) {
exportedAspectsDefinition
.appendWeaverOptions(allAspectsDefinition
.getWeaverOptions());
}
this.aspectDefinitionsExported.put(bundle,
exportedAspectsDefinition);
}
if (policies.size() > 0) {
this.aspectPolicies.put(bundle, policies);
}
} catch (final Exception e) {
e.printStackTrace();
}
}
private String getPackage(final String aspect) {
final int index = aspect.lastIndexOf('.');
if (index >= 0) {
return aspect.substring(0, index);
} else {
return ""; //$NON-NLS-1$
}
}
}