package org.phms.sling.mvp.impl.presenter; import org.apache.commons.lang3.StringUtils; import org.apache.sling.commons.osgi.PropertiesUtil; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.ServiceRegistration; import org.osgi.util.tracker.BundleTracker; import org.osgi.util.tracker.BundleTrackerCustomizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URL; import java.util.ArrayList; import java.util.Dictionary; import java.util.Enumeration; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class PresenterBundleListener implements BundleTrackerCustomizer { static final String HEADER = "Sling-MVP-Presenter-Packages"; private final ConcurrentMap<String, Class<?>> presenters = new ConcurrentHashMap<>(); private final ConcurrentMap<String, List<String>> bundleResourceTypeMap = new ConcurrentHashMap<>(); private static final Logger LOG = LoggerFactory.getLogger(PresenterBundleListener.class); private final BundleTracker bundleTracker; public PresenterBundleListener(BundleContext bundleContext) { this.bundleTracker = new BundleTracker(bundleContext, Bundle.ACTIVE, this); this.bundleTracker.open(); } public ConcurrentMap<String, Class<?>> getPresenters() { return presenters; } @Override public Object addingBundle(Bundle bundle, BundleEvent event) { List<ServiceRegistration> regs = new ArrayList<>(); List<String> resourceTypesManagedByBundle = new ArrayList<>(); Dictionary<?, ?> headers = bundle.getHeaders(); String packageList = PropertiesUtil.toString(headers.get(HEADER), null); if (packageList != null) { packageList = StringUtils.deleteWhitespace(packageList); String[] packages = packageList.split(","); for (String singlePackage : packages) { @SuppressWarnings("unchecked") Enumeration<URL> classUrls = bundle.findEntries("/" + singlePackage.replace('.', '/'), "*.class", true); if (classUrls == null) { LOG.warn("No adaptable classes found in package {}, ignoring", singlePackage); continue; } while (classUrls.hasMoreElements()) { URL url = classUrls.nextElement(); String className = toClassName(url); try { Class<?> implType = bundle.loadClass(className); Presenter annotation = implType.getAnnotation(Presenter.class); if (annotation != null) { String[] resourceTypes = annotation.resourceTypes(); for (String resourceType : resourceTypes) { presenters.put(resourceType, implType); resourceTypesManagedByBundle.add(resourceType); } } } catch (ClassNotFoundException e) { LOG.warn("Unable to load class", e); } } bundleResourceTypeMap.put(bundle.getSymbolicName(), resourceTypesManagedByBundle); } } return regs.toArray(new ServiceRegistration[0]); } @Override public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) { } @Override public void removedBundle(Bundle bundle, BundleEvent event, Object object) { List<String> resourceTypesManagedByBundle = bundleResourceTypeMap.get(bundle.getSymbolicName()); if (resourceTypesManagedByBundle != null) { for (String resourceType : resourceTypesManagedByBundle) { presenters.remove(resourceType); } } } public synchronized void unregisterAll() { this.bundleTracker.close(); } /** * Convert class URL to class name */ private String toClassName(URL url) { final String f = url.getFile(); final String cn = f.substring(1, f.length() - ".class".length()); return cn.replace('/', '.'); } }