package org.springframework.roo.metadata; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; import org.apache.felix.scr.annotations.Component; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.service.component.ComponentContext; import org.springframework.roo.support.logging.HandlerUtils; import org.springframework.roo.support.osgi.ServiceInstaceManager; /** * Allows a {@link MetadataProvider} or other class to track hash codes of * {@link MetadataItem}s and only invoke * {@link MetadataDependencyRegistry#notifyDownstream(String)} if there has been * an actual change since the last notification. * <p> * IMPORTANT: Before subclassing this class, ensure the {@link MetadataItem}s * that you will be presenting are all of the same type AND they provide a * reliable {@link Object#hashCode()} method. Failure to observe this * requirement will result in erroneous notifications. * * @author Ben Alex * @since 1.1 */ @Component(componentAbstract = true) public abstract class AbstractHashCodeTrackingMetadataNotifier { protected final static Logger LOGGER = HandlerUtils .getLogger(AbstractHashCodeTrackingMetadataNotifier.class); // ------------ OSGi component attributes ---------------- /** * @deprecated this property should be _private_ and set by {@link #activate(ComponentContext)} * method */ public BundleContext context; protected ServiceInstaceManager serviceManager = null; protected void activate(final ComponentContext cContext) { context = cContext.getBundleContext(); serviceManager = new ServiceInstaceManager(); this.serviceManager.activate(this.context); } private final Map<String, Integer> hashes = new HashMap<String, Integer>(); /** * Notifies downstream dependencies of a change if and only if the passed * metadata item has a different hash code than the existing metadata item. * This is aimed at reducing needless notifications if nothing has actually * changed since the last notification. * * @param metadataItem the potentially-updated metadata item (required; must * be a metadata item of the same class as all other items * presented to this class) */ protected void notifyIfRequired(final MetadataItem metadataItem) { final String instanceId = MetadataIdentificationUtils.getMetadataInstance(metadataItem.getId()); final Integer existing = hashes.get(instanceId); final int newHash = metadataItem.hashCode(); if (existing != null && newHash == existing) { // No need to notify return; } // To get this far, we need to notify and replace/add the metadata // item's hash for future reference hashes.put(instanceId, newHash); // Eagerly insert into the cache to so any recursive gets for this // metadata item will be returned successfully getMetadataService().put(metadataItem); if (getMetadataDependencyRegistry() != null) { getMetadataDependencyRegistry().notifyDownstream(metadataItem.getId()); } } /** * * @deprecated this method should be removed as {@link #serviceManager} * should be created and activated thru {@link #activate(ComponentContext)} * method of this class. Child classes should call super.activate in its activate * method */ public ServiceInstaceManager getServiceManager() { // TODO this method should be removed this if (serviceManager == null) { serviceManager = new ServiceInstaceManager(); serviceManager.activate(context); } return serviceManager; } public MetadataDependencyRegistry getMetadataDependencyRegistry() { return getServiceManager().getServiceInstance(this, MetadataDependencyRegistry.class); } public MetadataService getMetadataService() { return getServiceManager().getServiceInstance(this, MetadataService.class); } }