package org.springframework.roo.addon.layers.service;

import java.util.Collection;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.classpath.TypeLocationService;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.MemberFindingUtils;
import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.details.annotations.ArrayAttributeValue;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.support.util.Assert;
import org.springframework.roo.support.util.CollectionUtils;
import org.springframework.roo.support.util.Filter;

/**
 * Locates interfaces annotated with {@link RooService} that meet certain
 * criteria.
 * 
 * Factored out of {@link ServiceLayerProvider} to simplify unit testing of that
 * class.
 *
 * @author Andrew Swan
 * @since 1.2
 */
@Component
@Service
public class ServiceInterfaceLocatorImpl implements ServiceInterfaceLocator {
	
	// Constants
	private static final JavaType ROO_SERVICE = new JavaType(RooService.class.getName());
	
	// Fields
	@Reference private TypeLocationService typeLocationService;
	
	/**
	 * Returns the details of any interfaces annotated with {@link RooService}
	 * that claim to support the given type of entity.
	 * 
	 * @param entityType can't be <code>null</code>
	 * @return a non-<code>null</code> collection; empty if there's no such
	 * services or the given entity is <code>null</code>
	 */
	public Collection<ClassOrInterfaceTypeDetails> getServiceInterfaces(final JavaType entityType) {
		Assert.notNull(entityType, "Entity type is required");
		final Iterable<ClassOrInterfaceTypeDetails> allServices = typeLocationService.findClassesOrInterfaceDetailsWithAnnotation(ROO_SERVICE);
		final Filter<ClassOrInterfaceTypeDetails> entityServicefilter = new Filter<ClassOrInterfaceTypeDetails>() {
			public boolean include(final ClassOrInterfaceTypeDetails serviceInterface) {
				final AnnotationMetadata annotation = MemberFindingUtils.getAnnotationOfType(serviceInterface.getAnnotations(), ROO_SERVICE);
				// Find the domain type(s) supported by this service interface
				@SuppressWarnings("unchecked")
				final ArrayAttributeValue<AnnotationAttributeValue<JavaType>> domainTypes = (ArrayAttributeValue<AnnotationAttributeValue<JavaType>>) annotation.getAttribute(RooService.DOMAIN_TYPES_ATTRIBUTE);
				if (domainTypes != null) {
					// Look through those domain types for the given one
					for (final AnnotationAttributeValue<JavaType> javaTypeValue : domainTypes.getValue()) {
						if (entityType.equals(javaTypeValue.getValue())) {
							return true;
						}
					}
				}
				return false;
			}
		};
		return CollectionUtils.filter(allServices, entityServicefilter);
	}
}
package org.springframework.roo.addon.layers.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.component.ComponentContext;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.ItdTypeDetails;
import org.springframework.roo.metadata.MetadataDependencyRegistry;
import org.springframework.roo.metadata.MetadataItem;
import org.springframework.roo.metadata.MetadataNotificationListener;
import org.springframework.roo.metadata.MetadataService;
import org.springframework.roo.model.JavaType;

/**
 * Locates interfaces annotated with {@link RooService} that meet certain
 * criteria.
 * 
 * Factored out of {@link ServiceLayerProvider} to simplify unit testing of that
 * class.
 *
 * @author Stefan Schmidt
 * @author Andrew Swan
 * @since 1.2
 */
@Component
@Service
public class ServiceInterfaceLocatorImpl implements ServiceInterfaceLocator, MetadataNotificationListener {
	
	// Constants
	private static final Map<JavaType, Collection<ClassOrInterfaceTypeDetails>> domainTypeToServiceMap = new HashMap<JavaType, Collection<ClassOrInterfaceTypeDetails>>();

	// Fields
	@Reference private MetadataService metadataService;
	@Reference private MetadataDependencyRegistry metadataDependencyRegistry;
	
	protected void activate(ComponentContext context) {
		metadataDependencyRegistry.addNotificationListener(this);
	}
	
	protected void deactivate(ComponentContext context) {
		metadataDependencyRegistry.removeNotificationListener(this);
	}

	public void notify(String upstreamDependency, String downstreamDependency) {
		MetadataItem metadataItem = metadataService.get(upstreamDependency);
		if (metadataItem == null) {
			return;
		}
		if (metadataItem instanceof ServiceInterfaceMetadata) {
			ServiceInterfaceMetadata serviceMetadata = (ServiceInterfaceMetadata) metadataItem;
			ItdTypeDetails serviceItd = serviceMetadata.getMemberHoldingTypeDetails();
			if (serviceItd == null) {
				return;
			}
			for (JavaType domainType : serviceMetadata.getServiceAnnotationValues().getDomainTypes()) {
				initMapValue(domainType);
				domainTypeToServiceMap.get(domainType).add(serviceItd.getGovernor());
			}
		}
	}
	
	public Collection<ClassOrInterfaceTypeDetails> getServiceInterfaces(JavaType domainType) {
		initMapValue(domainType);
		return domainTypeToServiceMap.get(domainType);
	}

	private void initMapValue(JavaType domainType) {
		if (!domainTypeToServiceMap.containsKey(domainType)) {
			domainTypeToServiceMap.put(domainType, new ArrayList<ClassOrInterfaceTypeDetails>());
		}
	}
}
Choose between data access patterns - improve performance of layer support (initial work on ServiceInterfaceLocator)