/* * 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.usergrid.services; import java.lang.reflect.Modifier; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.apache.usergrid.batch.service.SchedulerService; import org.apache.usergrid.locking.LockManager; import org.apache.usergrid.mq.QueueManager; import org.apache.usergrid.persistence.Entity; import org.apache.usergrid.persistence.EntityManager; import org.apache.usergrid.persistence.EntityRef; import org.apache.usergrid.persistence.entities.Application; import org.apache.usergrid.services.ServiceParameter.IdParameter; import org.apache.usergrid.services.applications.ApplicationsService; import org.apache.usergrid.services.exceptions.UndefinedServiceEntityTypeException; import org.apache.usergrid.utils.ListUtils; import org.apache.commons.lang.StringUtils; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import static org.apache.usergrid.persistence.SimpleEntityRef.ref; import static org.apache.usergrid.utils.InflectionUtils.pluralize; public class ServiceManager { private static final Logger logger = LoggerFactory.getLogger( ServiceManager.class ); /** A pointer that signals we couldn't find a class */ private static final Class<Service> NOTFOUNDPOINTER = Service.class; // because one typo can ruin your whole day public static final String ENTITY = "entity"; public static final String ENTITY_SUFFIX = "." + ENTITY; public static final String COLLECTION = "collection"; public static final String COLLECTION_SUFFIX = "." + COLLECTION; public static final String OSS_PACKAGE_PREFIX = "org.apache.usergrid.services"; public static final String COM_PACKAGE_PREFIX = "com.usergrid.services"; public static final String SERVICE_PACKAGE_PREFIXES = "usergrid.service.packages"; public static final String APPLICATION_REQUESTS = "application.requests"; public static final String APPLICATION_REQUESTS_PER = APPLICATION_REQUESTS + "."; public static final String IMPL = "Impl"; private Application application; private UUID applicationId; private EntityManager em; private ServiceManagerFactory smf; private QueueManager qm; private Properties properties; // search for commercial packages first for SaaS version public static String[] package_prefixes = { OSS_PACKAGE_PREFIX }; public ServiceManager() { } public ServiceManager init( ServiceManagerFactory smf, EntityManager em, Properties properties, QueueManager qm ) { this.smf = smf; this.em = em; this.qm = qm; this.properties = properties; // additional logging to help debug https://issues.apache.org/jira/browse/USERGRID-1291 if ( em == null ) { logger.error("EntityManager is null"); } if ( qm == null ) { logger.error("QueueManager is null"); } if ( em != null ) { try { application = em.getApplication(); if(application == null){ Exception e = new RuntimeException("application id {"+em.getApplicationId()+"} is returning null"); logger.error("Failed to get application",e); throw e; } applicationId = application.getUuid(); } catch ( Exception e ) { logger.error( "ServiceManager init failure", e ); throw new RuntimeException( e ); } } if ( properties != null ) { String packages = properties.getProperty( SERVICE_PACKAGE_PREFIXES ); if ( !StringUtils.isEmpty( packages ) ) { setServicePackagePrefixes( packages ); } } return this; } public ApplicationContext getApplicationContext() { return smf.getApplicationContext(); } private void setServicePackagePrefixes( String packages ) { List<String> packagePrefixes = new ArrayList<String>(); Collections.addAll(packagePrefixes, package_prefixes); String[] prefixes = packages.split( ";" ); for ( String prefix : prefixes ) { if ( !packagePrefixes.contains( prefix ) ) { packagePrefixes.add( prefix ); } } package_prefixes = packagePrefixes.toArray( new String[packagePrefixes.size()] ); } public EntityManager getEntityManager() { return em; } public UUID getApplicationId() { return application.getUuid(); } /** Return true if our current applicationId is the managment Id */ public boolean isMangementApplication() { return smf.getManagementAppId().equals( getApplicationId() ); } public EntityRef getApplicationRef() { return ref( Application.ENTITY_TYPE, applicationId ); } public Application getApplication() { return application; } public Service getEntityService( String entityType ) { String serviceType = "/" + pluralize( entityType ); return getService( serviceType ); } public Entity importEntity( ServiceRequest request, Entity entity ) throws Exception { Service service = getEntityService( entity.getType() ); if ( service != null ) { return service.importEntity( request, entity ); } return entity; } public Entity writeEntity( ServiceRequest request, Entity entity ) throws Exception { Service service = getEntityService( entity.getType() ); if ( service != null ) { return service.writeEntity( request, entity ); } return entity; } public Entity updateEntity( ServiceRequest request, EntityRef ref, ServicePayload payload ) throws Exception { Service service = getEntityService( ref.getType() ); if ( service != null ) { return service.updateEntity( request, ref, payload ); } return null; } public Service getService( String serviceType ) { return getService( serviceType, true ); } public Service getService( String serviceType, boolean fallback ) { if ( serviceType == null ) { return null; } if (logger.isTraceEnabled()) { logger.trace("Looking up service pattern: {}", serviceType); } ServiceInfo info = ServiceInfo.getServiceInfo( serviceType ); if ( info == null ) { return null; } Service service = getServiceInstance( info ); if ( service != null ) { if (logger.isTraceEnabled()) { logger.trace("Returning service instance: {}", service.getClass()); } } /* * if ((service == null) && fallback) { for (String pattern : * info.getPatterns()) { service = getService(pattern, false); if * (service != null) { break; } } } */ if ( service == null ) { logger.info( "Service {} not found", serviceType ); } return service; } private static LoadingCache<ServiceInfo, Class<Service>> serviceClassCache = CacheBuilder.newBuilder().maximumSize( 100 ).expireAfterAccess( 5, TimeUnit.MINUTES ) .build( new CacheLoader<ServiceInfo, Class<Service>>() { public Class<Service> load( ServiceInfo key ) { // no checked exception return findClass( key ); } } ); /** For the service info find the class that this maps */ private static Class<Service> findClass( ServiceInfo info ) { Class<Service> cls = null; String cname = null; for ( String pattern : info.getPatterns() ) { for ( String prefix : package_prefixes ) { cname = prefix.concat( "." ).concat( ServiceInfo.getClassName( pattern ) ); cls = findClass( cname ); if ( cls != null ) { return cls; } } } //We didn't find anything, return the not found pointer return NOTFOUNDPOINTER; } @SuppressWarnings("unchecked") private static Class<Service> findClass( String classname ) { Class<Service> cls; try { if (logger.isTraceEnabled()) { logger.trace("Attempting to instantiate service class {}", classname); } cls = ( Class<Service> ) Class.forName( classname ); if ( cls.isInterface() ) { cls = ( Class<Service> ) Class.forName( classname.concat( IMPL ) ); } if ( ( cls != null ) && !Modifier.isAbstract( cls.getModifiers() ) ) { return cls; } } catch ( ClassNotFoundException e1 ) { if(logger.isTraceEnabled()){ logger.trace("Could not find class", e1); } } return null; } private Class<Service> findServiceClass( ServiceInfo info ) { Class<Service> cls = null; try { cls = serviceClassCache.get( info ); } catch ( ExecutionException e ) { //shouldn't happen, just to be safe throw new RuntimeException( e ); } //Makes me feel dirty on the inside, but neccessary for the guava cache non null if ( cls == NOTFOUNDPOINTER ) { return null; } return cls; } private Service getServiceInstance( ServiceInfo info ) { Class<Service> cls = findServiceClass( info ); if ( cls != null ) { Service s = null; try { s = cls.newInstance(); } catch ( Exception e ) { logger.error( "cannot instantiate {}", cls.getName(), e ); } if ( s instanceof AbstractService ) { AbstractService as = ( ( AbstractService ) s ); as.setServiceManager( this ); as.init( info ); } if ( s != null ) { if ( s.getEntityType() == null ) { throw new UndefinedServiceEntityTypeException(); } } return s; } return null; } public ServiceRequest newRequest( ServiceAction action, List<ServiceParameter> parameters ) throws Exception { return newRequest( action, false, parameters, null, true, true, false); } public ServiceRequest newRequest( ServiceAction action, List<ServiceParameter> parameters, ServicePayload payload ) throws Exception { return newRequest( action, false, parameters, payload, true, true, false); } private ServiceRequest getApplicationRequest( ServiceAction action, boolean returnsTree, List<ServiceParameter> parameters, ServicePayload payload ) throws Exception { String serviceName = pluralize( Application.ENTITY_TYPE ); ListUtils.requeue( parameters, new IdParameter( applicationId ) ); return new ServiceRequest( this, action, serviceName, parameters, payload, returnsTree ); } static ApplicationsService appService = new ApplicationsService(); public ServiceRequest newRequest(ServiceAction action, boolean returnsTree, List<ServiceParameter> parameters, ServicePayload payload, boolean returnsInboundConnections, boolean returnsOutboundConnections, boolean analyzeQueryOnly) throws Exception { if ( em != null ) { if ( action != null ) { em.incrementAggregateCounters( null, null, null, APPLICATION_REQUESTS_PER.concat( action.toString().toLowerCase() ), 1 ); } } if ( !ServiceParameter.moreParameters( parameters ) ) { return getApplicationRequest( action, returnsTree, parameters, payload ); } if ( !ServiceParameter.firstParameterIsName( parameters ) ) { return null; } String nameParam = ServiceParameter.firstParameter( parameters ).getName(); if ( appService.hasEntityCommand( nameParam ) || appService.hasEntityDictionary( nameParam ) ) { return getApplicationRequest( action, returnsTree, parameters, payload ); } // special-case for Notifications. todo: generalize if ( action == ServiceAction.POST && ServiceParameter.lastParameterIsName( parameters ) && "notifications".equals( parameters.get( parameters.size() - 1 ).getName() ) ) { return new ServiceRequest( this, action, "notifications", parameters, payload, returnsTree ); } String serviceName = pluralize( ServiceParameter.dequeueParameter( parameters ).getName() ); return new ServiceRequest( this, action, serviceName, parameters, payload, returnsTree, returnsInboundConnections, returnsOutboundConnections, analyzeQueryOnly); } public ServiceRequest newRequest( ServiceAction action, boolean returnsTree, List<ServiceParameter> parameters, ServicePayload payload ) throws Exception { return newRequest( action, returnsTree, parameters, payload, true, true, false); } public void notifyExecutionEventListeners( ServiceAction action, ServiceRequest request, ServiceResults results, ServicePayload payload ) { smf.notifyExecutionEventListeners( action, request, results, payload ); } public void notifyCollectionEventListeners( String path, ServiceResults results ) { smf.notifyCollectionEventListeners( path, results ); } public SchedulerService getSchedulerService() { return smf.getSchedulerService(); } public LockManager getLockManager() { return smf.getLockManager(); } public QueueManager getQueueManager() { return qm; } public Properties getProperties() { return properties; } }