blob: 60a79e5d573bc0c2a3ed58f5bdfba412ef0f751b [file] [log] [blame]
/*
* 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 com.codahale.metrics.Timer;
import com.google.inject.Injector;
import com.google.inject.Key;
import org.apache.commons.lang.NotImplementedException;
import org.apache.shiro.subject.Subject;
import org.apache.usergrid.corepersistence.rx.impl.ResponseImportTasks;
import org.apache.usergrid.corepersistence.service.ServiceSchedulerFig;
import org.apache.usergrid.persistence.*;
import org.apache.usergrid.persistence.cache.CacheFactory;
import org.apache.usergrid.persistence.core.metrics.MetricsFactory;
import org.apache.usergrid.persistence.core.metrics.ObservableTimer;
import org.apache.usergrid.persistence.core.rx.RxTaskScheduler;
import org.apache.usergrid.security.shiro.utils.LocalShiroCache;
import org.apache.usergrid.security.shiro.utils.SubjectUtils;
import org.apache.usergrid.services.ServiceParameter.IdParameter;
import org.apache.usergrid.services.ServiceParameter.NameParameter;
import org.apache.usergrid.services.ServiceParameter.QueryParameter;
import org.apache.usergrid.services.ServiceResults.Type;
import org.apache.usergrid.services.exceptions.ServiceInvocationException;
import org.apache.usergrid.services.exceptions.ServiceResourceNotFoundException;
import org.apache.usergrid.services.exceptions.UnsupportedServiceOperationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import rx.Observable;
import rx.Scheduler;
import rx.Subscriber;
import java.util.*;
import static org.apache.usergrid.security.shiro.utils.SubjectUtils.getPermissionFromPath;
import static org.apache.usergrid.security.shiro.utils.SubjectUtils.isServiceAdmin;
import static org.apache.usergrid.services.ServiceParameter.filter;
import static org.apache.usergrid.services.ServiceParameter.mergeQueries;
import static org.apache.usergrid.utils.ClassUtils.cast;
import static org.apache.usergrid.utils.InflectionUtils.pluralize;
import static org.apache.usergrid.utils.ListUtils.dequeueCopy;
import static org.apache.usergrid.utils.ListUtils.isEmpty;
/**
* Implementation of Service inferface. Builds a method call fanout from the invoke() method so that overriding specific
* behvaviors can be done easily.
*
* @author edanuff
*/
public abstract class AbstractService implements Service {
private static final Logger logger = LoggerFactory.getLogger( AbstractService.class );
private ServiceInfo info;
protected EntityManager em;
protected ServiceManager sm;
protected Set<String> privateConnections;
protected Set<String> declaredConnections;
protected Set<String> privateCollections;
protected Set<String> declaredCollections;
protected Map<List<String>, List<String>> replaceParameters;
protected Set<String> serviceCommands;
protected Set<EntityDictionaryEntry> entityDictionaries;
protected Set<String> metadataTypes;
protected Set<String> entityCommands;
protected Map<String, Object> defaultEntityMetadata;
private Scheduler rxScheduler;
private ServiceSchedulerFig rxSchedulerFig;
private MetricsFactory metricsFactory;
private Timer entityGetTimer;
private Timer entitiesGetTimer;
private Timer entitiesParallelGetTimer;
private Timer invokeTimer;
protected CacheFactory cacheFactory;
protected LocalShiroCache localShiroCache;
public AbstractService() {
}
public void setServiceManager( ServiceManager sm ) {
this.sm = sm;
em = sm.getEntityManager();
final Injector injector = sm.getApplicationContext().getBean( Injector.class );
rxScheduler = injector.getInstance( Key.get(RxTaskScheduler.class, ResponseImportTasks.class ) )
.getAsyncIOScheduler();
rxSchedulerFig = injector.getInstance(ServiceSchedulerFig.class );
metricsFactory = injector.getInstance(MetricsFactory.class);
this.entityGetTimer = metricsFactory.getTimer(this.getClass(), "importEntity.get");
this.entitiesGetTimer = metricsFactory.getTimer(this.getClass(), "importEntities.get");
this.entitiesParallelGetTimer = metricsFactory.getTimer( this.getClass(),"importEntitiesP.get" );
this.invokeTimer = metricsFactory.getTimer( this.getClass(),"service.invoke" );
this.cacheFactory = injector.getInstance( CacheFactory.class );
this.localShiroCache = injector.getInstance( LocalShiroCache.class );
}
public ApplicationContext getApplicationContext() {
return sm.getApplicationContext();
}
public void init( ServiceInfo info ) {
this.info = info;
}
public ServiceInfo getServiceInfo() {
return info;
}
@Override
public String getServiceType() {
if ( info == null ) {
return null;
}
return info.getName();
}
@Override
public Class<? extends Entity> getEntityClass() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getEntityType() {
if ( info == null ) {
return null;
}
return info.getItemType();
}
@Override
public boolean isRootService() {
if ( info == null ) {
return false;
}
return info.isRootService();
}
public boolean isCollectionReversed( ServiceContext context ) {
if ( info == null ) {
return false;
}
if ( "application".equals( context.getOwner().getType() ) ) {
return Schema.getDefaultSchema().isCollectionReversed("application", pluralize(info.getItemType()));
}
return Schema.getDefaultSchema().isCollectionReversed(info.getContainerType(), info.getCollectionName());
}
public String getCollectionSort( ServiceContext context ) {
if ( info == null ) {
return null;
}
if ( "application".equals( context.getOwner().getType() ) ) {
return Schema.getDefaultSchema().getCollectionSort("application", pluralize(info.getItemType()));
}
return Schema.getDefaultSchema().getCollectionSort(info.getContainerType(), info.getCollectionName());
}
public void makeConnectionPrivate( String connection ) {
if ( privateConnections == null ) {
privateConnections = new LinkedHashSet<String>();
}
privateConnections.add(connection);
}
public void makeConnectionsPrivate( List<String> connections ) {
if ( privateConnections == null ) {
privateConnections = new LinkedHashSet<String>();
}
privateConnections.addAll(connections);
}
public void declareConnection( String connection ) {
if ( declaredConnections == null ) {
declaredConnections = new LinkedHashSet<String>();
}
declaredConnections.add(connection);
}
public void declareConnections( List<String> connections ) {
if ( declaredConnections == null ) {
declaredConnections = new LinkedHashSet<String>();
}
declaredConnections.addAll(connections);
}
public void makeCollectionPrivate( String collection ) {
if ( privateCollections == null ) {
privateCollections = new LinkedHashSet<String>();
}
privateCollections.add(collection);
}
public void makeCollectionsPrivate( List<String> collections ) {
if ( privateCollections == null ) {
privateCollections = new LinkedHashSet<String>();
}
privateCollections.addAll(collections);
}
public void declareVirtualCollection( String collection ) {
if ( declaredCollections == null ) {
declaredCollections = new LinkedHashSet<String>();
}
declaredCollections.add(collection);
}
public void declareVirtualCollections( List<String> collections ) {
if ( declaredCollections == null ) {
declaredCollections = new LinkedHashSet<String>();
}
declaredCollections.addAll(collections);
}
public void addReplaceParameters( List<String> find, List<String> replace ) {
if ( replaceParameters == null ) {
replaceParameters = new LinkedHashMap<List<String>, List<String>>();
}
replaceParameters.put(find, replace);
}
public void declareServiceCommands( String command ) {
if ( serviceCommands == null ) {
serviceCommands = new LinkedHashSet<String>();
}
serviceCommands.add(command);
}
public void declareServiceCommands( List<String> commands ) {
if ( serviceCommands == null ) {
serviceCommands = new LinkedHashSet<String>();
}
serviceCommands.addAll(commands);
}
public void declareEntityDictionary( EntityDictionaryEntry dictionary ) {
if ( entityDictionaries == null ) {
entityDictionaries = new LinkedHashSet<EntityDictionaryEntry>();
}
entityDictionaries.add(dictionary);
}
public void declareEntityDictionary( String dictionary ) {
if ( entityDictionaries == null ) {
entityDictionaries = new LinkedHashSet<EntityDictionaryEntry>();
}
entityDictionaries.add( new EntityDictionaryEntry( dictionary ) );
}
public void declareEntityDictionaries( List<String> dictionaries ) {
if ( entityDictionaries == null ) {
entityDictionaries = new LinkedHashSet<EntityDictionaryEntry>();
}
for ( String dict : dictionaries ) {
entityDictionaries.add( new EntityDictionaryEntry( dict ) );
}
}
public void declareMetadataType( String type ) {
if ( metadataTypes == null ) {
metadataTypes = new LinkedHashSet<String>();
}
metadataTypes.add(type);
}
public void declareMetadataTypes( List<String> typeList ) {
if ( metadataTypes == null ) {
metadataTypes = new LinkedHashSet<String>();
}
metadataTypes.addAll(typeList);
}
public void declareEntityCommand( String command ) {
if ( entityCommands == null ) {
entityCommands = new LinkedHashSet<String>();
}
entityCommands.add(command);
}
public void declareEntityCommands( List<String> commands ) {
if ( entityCommands == null ) {
entityCommands = new LinkedHashSet<String>();
}
entityCommands.addAll(commands);
}
@Override
public Entity getEntity( ServiceRequest request, UUID uuid ) throws Exception {
// TODO Auto-generated method stub
return null;
}
@Override
public Entity getEntity( ServiceRequest request, String name ) throws Exception {
// TODO Auto-generated method stub
return null;
}
public Entity importEntity( ServiceContext context, Entity entity ) throws Exception {
return importEntity( context.getRequest(), entity );
}
@Override
public Entity importEntity( ServiceRequest request, Entity entity ) throws Exception {
Timer.Context getEntityTimer = entityGetTimer.time();
try {
if (entity == null) {
return null;
}
if (!isRootService()) {
return sm.importEntity(request, entity);
}
String path = request.getPath() + "/" + entity.getUuid();
Map<String, Object> metadata = new LinkedHashMap<String, Object>();
metadata.put("path", path);
if (defaultEntityMetadata != null) {
metadata.putAll(defaultEntityMetadata);
}
if (request.isReturnsOutboundConnections()) {
Set<Object> connections = getConnectedTypesSet(entity);
if (connections != null) {
Map<String, Object> m = new LinkedHashMap<String, Object>();
for (Object n : connections) {
m.put(n.toString(), path + "/" + n);
}
metadata.put("connections", m);
}
}
if (request.isReturnsInboundConnections()) {
Set<Object> connecting = getConnectingTypesSet(entity);
if (connecting != null) {
Map<String, Object> m = new LinkedHashMap<String, Object>();
for (Object n : connecting) {
m.put(n.toString(), path + "/connecting/" + n);
}
metadata.put("connecting", m);
}
}
Set<String> collections = getCollectionSet(entity);
if (collections != null) {
Map<String, Object> m = new LinkedHashMap<String, Object>();
for (Object n : collections) {
m.put(n.toString(), path + "/" + n);
}
metadata.put("collections", m);
}
if (entityDictionaries != null) {
Map<String, Object> m = new LinkedHashMap<String, Object>();
for (EntityDictionaryEntry dict : entityDictionaries) {
m.put(dict.getName(), path + "/" + dict.getPath());
}
metadata.put("sets", m);
}
if (metadata.size() > 0) {
entity.mergeMetadata(metadata);
}
return entity;
}finally {
getEntityTimer.stop();
}
}
public void importEntities( ServiceRequest request, Results results ) throws Exception {
Timer.Context timer = entitiesGetTimer.time();
try {
List<Entity> entities = results.getEntities();
if (entities != null) {
importEntitiesParallel(request, results);
}
}finally {
timer.stop();
}
}
/**
* Import entities in parallel
* @param request
* @param results
*/
private void importEntitiesParallel(final ServiceRequest request, final Results results ) {
//create our tuples
final Observable<EntityTuple> tuples = Observable.create(new Observable.OnSubscribe<EntityTuple>() {
@Override
public void call(final Subscriber<? super EntityTuple> subscriber) {
subscriber.onStart();
final List<Entity> entities = results.getEntities();
final int size = entities.size();
for (int i = 0; i < size && !subscriber.isUnsubscribed(); i++) {
subscriber.onNext(new EntityTuple(i, entities.get(i)));
}
subscriber.onCompleted();
}
});
//now process them in parallel up to 10 threads
Observable tuplesObservable = tuples.flatMap(tuple -> {
//map the entity into the tuple
return Observable.just(tuple).doOnNext(parallelTuple -> {
//import the entity and set it at index
try {
final Entity imported = importEntity(request, parallelTuple.entity);
if (imported != null) {
results.setEntity(parallelTuple.index, imported);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}).subscribeOn(rxScheduler);
}, rxSchedulerFig.getImportConcurrency());
ObservableTimer.time(tuplesObservable, entitiesParallelGetTimer).toBlocking().lastOrDefault(null);
}
/**
* Simple tuple representing and entity and it's location within the results
*/
private static final class EntityTuple {
private final int index;
private final Entity entity;
private EntityTuple( final int index, final Entity entity ) {
this.index = index;
this.entity = entity;
}
}
public void importEntities( ServiceContext context, Results results ) throws Exception {
importEntities( context.getRequest(), results );
}
@Override
public Entity writeEntity( ServiceRequest request, Entity entity ) throws Exception {
if ( !isRootService() ) {
return sm.writeEntity( request, entity );
}
return entity;
}
public void writeEntities( ServiceRequest request, Results results ) throws Exception {
List<Entity> entities = results.getEntities();
if ( entities != null ) {
for ( Entity entity : entities ) {
writeEntity( request, entity );
}
}
}
public Entity updateEntity( ServiceContext context, EntityRef ref, ServicePayload payload ) throws Exception {
return updateEntity( context.getRequest(), ref, payload );
}
public Entity updateEntity( ServiceContext context, EntityRef ref ) throws Exception {
return updateEntity( context.getRequest(), ref, context.getPayload() );
}
@Override
public Entity updateEntity( ServiceRequest request, EntityRef ref, ServicePayload payload ) throws Exception {
if ( !isRootService() ) {
return sm.updateEntity( request, ref, payload );
}
if ( ref instanceof Entity ) {
Entity entity = ( Entity ) ref;
em.updateProperties( entity, payload.getProperties() );
entity.addProperties( payload.getProperties() );
return entity;
}
logger.error("Attempted update of entity reference rather than full entity, currently unsupported");
throw new NotImplementedException();
}
public void updateEntities( ServiceContext context, Results results, ServicePayload payload ) throws Exception {
updateEntities( context.getRequest(), results, payload );
}
public void updateEntities( ServiceContext context, Results results ) throws Exception {
updateEntities( context.getRequest(), results, context.getPayload() );
}
public void updateEntities( ServiceRequest request, Results results, ServicePayload payload ) throws Exception {
List<Entity> entities = results.getEntities();
if ( entities != null ) {
for ( Entity entity : entities ) {
updateEntity( request, entity, payload );
}
}
}
public Set<Object> getConnectedTypesSet( EntityRef ref ) throws Exception {
final Set<String> connections = em.getConnectionsAsSource(ref);
if ( connections == null ) {
return null;
}
if ( connections.size() > 0 ) {
connections.remove( "connection" );
if ( privateConnections != null ) {
connections.removeAll( privateConnections );
}
if ( connections.size() > 0 ) {
return new HashSet<Object>( connections );
}
}
return null;
}
public Set<Object> getConnectingTypesSet( EntityRef ref ) throws Exception {
final Set<String> connections = em.getConnectionsAsTarget(ref);
if ( connections == null ) {
return null;
}
if ( connections.size() > 0 ) {
connections.remove( "connection" );
if ( privateConnections != null ) {
connections.removeAll( privateConnections );
}
if ( connections.size() > 0 ) {
return new LinkedHashSet<Object>( connections );
}
}
return null;
}
public Set<String> getCollectionSet( EntityRef ref ) {
Set<String> set = Schema.getDefaultSchema().getCollectionNames(ref.getType());
set = new LinkedHashSet<String>( set );
if ( declaredCollections != null ) {
set.addAll( declaredCollections );
}
if ( privateCollections != null ) {
set.removeAll( privateCollections );
}
if ( set.size() > 0 ) {
return set;
}
return null;
}
@Override
public ServiceResults invoke( ServiceAction action, ServiceRequest request, ServiceResults previousResults,
ServicePayload payload ) throws Exception {
ServiceContext context = getContext(action, request, previousResults, payload);
return invoke( context );
}
/**
* Create context from parameter queue. Returns context containing a query object that represents the parameters in
* the queue. Remaining parameters are left for next service request to allow for request chaining.
*/
@Override
public ServiceContext getContext( ServiceAction action, ServiceRequest request, ServiceResults previousResults,
ServicePayload payload ) throws Exception {
EntityRef owner = request.getOwner();
String collectionName =
"application".equals( owner.getType() ) ? pluralize( info.getItemType() ) : info.getCollectionName();
List<ServiceParameter> parameters = filter(request.getParameters(), replaceParameters);
ServiceParameter first_parameter = null;
if ( !isEmpty( parameters ) ) {
first_parameter = parameters.get( 0 );
parameters = dequeueCopy( parameters );
}
if ( first_parameter instanceof NameParameter ) {
if ( hasServiceMetadata( first_parameter.getName() ) ) {
return new ServiceContext( this, action, request, previousResults, owner, collectionName, parameters,
payload ).withServiceMetadata( first_parameter.getName() );
}
else if ( hasServiceCommand( first_parameter.getName() ) ) {
return new ServiceContext( this, action, request, previousResults, owner, collectionName, parameters,
payload ).withServiceCommand( first_parameter.getName() );
}
}
Query query = null;
if ( first_parameter instanceof QueryParameter ) {
query = first_parameter.getQuery();
}
parameters = mergeQueries(query, parameters);
if ( first_parameter instanceof IdParameter ) {
UUID id = first_parameter.getId();
return new ServiceContext( this, action, request, previousResults, owner, collectionName,
Query.fromUUID( id ), parameters, payload );
}
else if ( first_parameter instanceof NameParameter ) {
String name = first_parameter.getName();
return new ServiceContext( this, action, request, previousResults, owner, collectionName,
Query.fromIdentifier( name ), parameters, payload );
}
else if ( query != null ) {
query.setAnalyzeOnly(request.isAnalyzeQueryOnly());
query.setReturnQuery(request.shouldReturnQuery());
return new ServiceContext( this, action, request, previousResults, owner, collectionName, query, parameters,
payload );
}
else if ( first_parameter == null ) {
return new ServiceContext( this, action, request, previousResults, owner, collectionName, null, null,
payload );
}
return null;
}
public ServiceResults invoke( ServiceContext context ) throws Exception {
ServiceResults results = null;
Timer.Context time = invokeTimer.time();
String metadataType = checkForServiceMetadata( context );
if ( metadataType != null ) {
return handleServiceMetadata( context, metadataType );
}
String serviceCommand = checkForServiceCommand( context );
if ( serviceCommand != null ) {
return handleServiceCommand( context, serviceCommand );
}
EntityDictionaryEntry entityDictionary = checkForEntityDictionaries( context );
String entityCommand = checkForEntityCommands( context );
if ( context.isByQuery() ) {
results = invokeItemsWithQuery( context, context.getQuery() );
}
else if ( context.isByName() ) {
results = invokeItemWithName( context, context.getName() );
}
else if ( context.isByUuid() ) {
results = invokeItemWithId( context, context.getUuid() );
}
else {
results = invokeCollection( context );
}
results = handleEntityDictionary( context, results, entityDictionary );
results = handleEntityCommand(context, results, entityCommand);
time.stop();
return results;
}
public ServiceResults invokeItemWithId( ServiceContext context, UUID id ) throws Exception {
switch ( context.getAction() ) {
case GET:
return getItemById(context, id);
case POST:
return postItemById(context, id);
case PUT:
return putItemById(context, id);
case DELETE:
return deleteItemById(context, id);
case HEAD:
return headItemById(context, id);
}
throw new ServiceInvocationException( context, "Request action unhandled " + context.getAction() );
}
public ServiceResults invokeItemWithName( ServiceContext context, String name ) throws Exception {
switch (context.getAction()) {
case GET:
return getItemByName(context, name);
case POST:
return postItemByName(context, name);
case PUT:
return putItemByName(context, name);
case DELETE:
return deleteItemByName(context, name);
case HEAD:
return headItemByName(context, name);
default:
throw new ServiceInvocationException(context, "Request action unhandled " + context.getAction());
}
}
public ServiceResults invokeItemsWithQuery( ServiceContext context, Query query ) throws Exception {
switch ( context.getAction() ) {
case GET:
return getItemsByQuery( context, query );
case POST:
return postItemsByQuery( context, query );
case PUT:
return putItemsByQuery( context, query );
case DELETE:
return deleteItemsByQuery( context, query );
case HEAD:
return headItemsByQuery( context, query );
}
throw new ServiceInvocationException( context, "Request action unhandled " + context.getAction() );
}
public ServiceResults invokeCollection( ServiceContext context ) throws Exception {
switch ( context.getAction() ) {
case GET:
return getCollection( context );
case POST:
return postCollection( context );
case PUT:
return putCollection( context );
case DELETE:
return deleteCollection( context );
case HEAD:
return headCollection( context );
}
throw new ServiceInvocationException( context, "Request action unhandled " + context.getAction() );
}
public ServiceResults getItemById( ServiceContext context, UUID id ) throws Exception {
throw new ServiceResourceNotFoundException( context );
}
public ServiceResults getItemByName( ServiceContext context, String name ) throws Exception {
throw new ServiceResourceNotFoundException( context );
}
public ServiceResults getItemsByQuery( ServiceContext context, Query query ) throws Exception {
throw new ServiceResourceNotFoundException( context );
}
public ServiceResults getCollection( ServiceContext context ) throws Exception {
throw new ServiceResourceNotFoundException( context );
}
public ServiceResults putItemById( ServiceContext context, UUID id ) throws Exception {
return getItemById( context, id );
}
public ServiceResults putItemByName( ServiceContext context, String name ) throws Exception {
return getItemByName( context, name );
}
public ServiceResults putItemsByQuery( ServiceContext context, Query query ) throws Exception {
return getItemsByQuery( context, query );
}
public ServiceResults putCollection( ServiceContext context ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults postItemById( ServiceContext context, UUID id ) throws Exception {
return getItemById( context, id );
}
public ServiceResults postItemByName( ServiceContext context, String name ) throws Exception {
return getItemByName( context, name );
}
public ServiceResults postItemsByQuery( ServiceContext context, Query query ) throws Exception {
return getItemsByQuery( context, query );
}
public abstract ServiceResults postCollectionSettings( ServiceRequest serviceRequest ) throws Exception;
public abstract ServiceResults getCollectionSettings( ServiceRequest serviceRequest ) throws Exception;
public ServiceResults postCollection( ServiceContext context ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults deleteItemById( ServiceContext context, UUID id ) throws Exception {
return getItemById( context, id );
}
public ServiceResults deleteItemByName( ServiceContext context, String name ) throws Exception {
return getItemByName( context, name );
}
public ServiceResults deleteItemsByQuery( ServiceContext context, Query query ) throws Exception {
return getItemsByQuery( context, query );
}
public ServiceResults deleteCollection( ServiceContext context ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults headItemById( ServiceContext context, UUID id ) throws Exception {
return getItemById( context, id );
}
public ServiceResults headItemByName( ServiceContext context, String name ) throws Exception {
return getItemByName( context, name );
}
public ServiceResults headItemsByQuery( ServiceContext context, Query query ) throws Exception {
return getItemsByQuery( context, query );
}
public ServiceResults headCollection( ServiceContext context ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public boolean hasServiceCommand( String command ) {
return ( serviceCommands != null ) && ( command != null ) && serviceCommands.contains( command );
}
public String checkForServiceCommand( ServiceContext context ) {
if ( serviceCommands == null ) {
return null;
}
if ( !context.moreParameters() ) {
return null;
}
String name = null;
if ( context.firstParameterIsName() ) {
name = context.firstParameter().getName();
if ( serviceCommands.contains( name ) ) {
return name;
}
}
return null;
}
public ServiceResults handleServiceCommand( ServiceContext context, String command ) throws Exception {
switch ( context.getAction() ) {
case GET:
return getServiceCommand( context, command );
case POST:
return postServiceCommand( context, command, context.getPayload() );
case PUT:
return putServiceCommand( context, command, context.getPayload() );
case DELETE:
return deleteServiceCommand( context, command );
case HEAD:
return headServiceCommand( context, command );
}
throw new ServiceInvocationException( context, "Request action unhandled " + context.getAction() );
}
public ServiceResults getServiceCommand( ServiceContext context, String command ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults putServiceCommand( ServiceContext context, String command, ServicePayload payload )
throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults postServiceCommand( ServiceContext context, String command, ServicePayload payload )
throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults deleteServiceCommand( ServiceContext context, String command ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults headServiceCommand( ServiceContext context, String command ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public boolean hasEntityDictionary( String dictionary ) {
if ( entityDictionaries == null ) {
return false;
}
for ( EntityDictionaryEntry entry : entityDictionaries ) {
if ( entry.getName().equalsIgnoreCase( dictionary ) ) {
return true;
}
}
return false;
}
public EntityDictionaryEntry checkForEntityDictionaries( ServiceContext context ) {
if ( entityDictionaries == null ) {
return null;
}
if ( !context.moreParameters() ) {
return null;
}
String name = null;
if ( context.firstParameterIsName() ) {
name = context.firstParameter().getName();
for ( EntityDictionaryEntry entry : entityDictionaries ) {
if ( entry.getName().equalsIgnoreCase( name ) ) {
return entry;
}
}
}
return null;
}
public ServiceResults handleEntityDictionary( ServiceContext context, ServiceResults results,
EntityDictionaryEntry dictionary ) throws Exception {
if ( dictionary != null ) {
if ( results.size() == 1 ) {
results = handleEntityDictionary( context, results.getRef(), dictionary );
}
else if ( results.size() > 1 ) {
results = handleEntityDictionary( context, results.getRefs(), dictionary );
}
}
return results;
}
public ServiceResults handleEntityDictionary( ServiceContext context, EntityRef ref,
EntityDictionaryEntry dictionary ) throws Exception {
if ( ref == null ) {
throw new UnsupportedServiceOperationException( context );
}
List<EntityRef> refs = new ArrayList<EntityRef>();
refs.add( ref );
return handleEntityDictionary( context, refs, dictionary );
}
public ServiceResults handleEntityDictionary( ServiceContext context, List<EntityRef> refs,
EntityDictionaryEntry dictionary ) throws Exception {
if ( ( refs == null ) || ( refs.size() == 0 ) ) {
throw new UnsupportedServiceOperationException( context );
}
switch ( context.getAction() ) {
case GET:
return getEntityDictionary( context, refs, dictionary );
case POST:
return postEntityDictionary( context, refs, dictionary, context.getPayload() );
case PUT:
return putEntityDictionary( context, refs, dictionary, context.getPayload() );
case DELETE:
return deleteEntityDictionary( context, refs, dictionary );
case HEAD:
return headEntityDictionary( context, refs, dictionary );
}
throw new ServiceInvocationException( context, "Request action unhandled " + context.getAction() );
}
public ServiceResults getEntityDictionary( ServiceContext context, List<EntityRef> refs,
EntityDictionaryEntry dictionary ) throws Exception {
for ( EntityDictionaryEntry entry : entityDictionaries ) {
if ( entry.getName().equalsIgnoreCase( dictionary.getName() ) ) {
EntityRef entityRef = refs.get( 0 );
checkPermissionsForEntitySubPath( context, entityRef, entry.getPath() );
Set<String> items = cast( em.getDictionaryAsSet( entityRef, entry.getName() ) );
return new ServiceResults( this, context, Type.GENERIC, Results.fromData( items ), null, null );
}
}
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults putEntityDictionary( ServiceContext context, List<EntityRef> refs,
EntityDictionaryEntry dictionary, ServicePayload payload )
throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults postEntityDictionary( ServiceContext context, List<EntityRef> refs,
EntityDictionaryEntry dictionary, ServicePayload payload )
throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults deleteEntityDictionary( ServiceContext context, List<EntityRef> refs,
EntityDictionaryEntry dictionary ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults headEntityDictionary( ServiceContext context, List<EntityRef> refs,
EntityDictionaryEntry dictionary ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public boolean hasEntityCommand( String command ) {
return ( entityCommands != null ) && ( command != null ) && entityCommands.contains( command );
}
public String checkForEntityCommands( ServiceContext context ) {
if ( entityCommands == null ) {
return null;
}
if ( !context.moreParameters() ) {
return null;
}
String name = null;
if ( context.firstParameterIsName() ) {
name = context.firstParameter().getName();
if ( entityCommands.contains( name ) ) {
return name;
}
}
return null;
}
public ServiceResults handleEntityCommand( ServiceContext context, ServiceResults results, String command )
throws Exception {
if ( command != null ) {
if ( results.size() == 1 ) {
results = handleEntityCommand( context, results.getRef(), command );
}
else if ( results.size() > 1 ) {
results = handleEntityCommand( context, results.getRefs(), command );
}
}
return results;
}
public ServiceResults handleEntityCommand( ServiceContext context, EntityRef ref, String command )
throws Exception {
if ( ref == null ) {
throw new UnsupportedServiceOperationException( context );
}
List<EntityRef> refs = new ArrayList<EntityRef>();
refs.add( ref );
return handleEntityCommand( context, refs, command );
}
public ServiceResults handleEntityCommand( ServiceContext context, List<EntityRef> refs, String command )
throws Exception {
if ( ( refs == null ) || ( refs.size() == 0 ) ) {
throw new UnsupportedServiceOperationException( context );
}
switch ( context.getAction() ) {
case GET:
return getEntityCommand( context, refs, command );
case POST:
return postEntityCommand( context, refs, command, context.getPayload() );
case PUT:
return putEntityCommand( context, refs, command, context.getPayload() );
case DELETE:
return deleteEntityCommand( context, refs, command );
case HEAD:
return headEntityCommand( context, refs, command );
}
throw new ServiceInvocationException( context, "Request action unhandled " + context.getAction() );
}
public ServiceResults getEntityCommand( ServiceContext context, List<EntityRef> refs, String command )
throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults putEntityCommand( ServiceContext context, List<EntityRef> refs, String command,
ServicePayload payload ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults postEntityCommand( ServiceContext context, List<EntityRef> refs, String command,
ServicePayload payload ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults deleteEntityCommand( ServiceContext context, List<EntityRef> refs, String command )
throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults headEntityCommand( ServiceContext context, List<EntityRef> refs, String command )
throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public boolean hasServiceMetadata( String metadata ) {
return ( metadataTypes != null ) && ( metadata != null ) && metadataTypes.contains( metadata );
}
public String checkForServiceMetadata( ServiceContext context ) {
if ( metadataTypes == null ) {
return null;
}
if ( context.getServiceMetadata() == null ) {
return null;
}
if ( metadataTypes.contains( context.getServiceMetadata() ) ) {
return context.getServiceMetadata();
}
return null;
}
public ServiceResults handleServiceMetadata( ServiceContext context, String metadataType ) throws Exception {
switch ( context.getAction() ) {
case GET:
return getServiceMetadata( context, metadataType );
case POST:
return postServiceMetadata( context, metadataType, context.getPayload() );
case PUT:
return putServiceMetadata( context, metadataType, context.getPayload() );
case DELETE:
return deleteServiceMetadata( context, metadataType );
case HEAD:
return headServiceMetadata( context, metadataType );
}
throw new ServiceInvocationException( context, "Request action unhandled " + context.getAction() );
}
public ServiceResults getServiceMetadata( ServiceContext context, String metadataType ) throws Exception {
if ( metadataTypes.contains( metadataType ) ) {
// return new ServiceResults(this, context, Type.GENERIC,
// Results.fromData(items), null, null);
}
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults putServiceMetadata( ServiceContext context, String metadataType, ServicePayload payload )
throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults postServiceMetadata( ServiceContext context, String metadataType, ServicePayload payload )
throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults deleteServiceMetadata( ServiceContext context, String metadataType ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public ServiceResults headServiceMetadata( ServiceContext context, String metadataType ) throws Exception {
throw new UnsupportedServiceOperationException( context );
}
public void checkPermissionsForCollection( ServiceContext context ) {
String path = context.getPath();
checkPermissionsForPath( context, path );
}
public void checkPermissionsForEntity( ServiceContext context, UUID entityId ) {
String path = context.getPath( entityId );
checkPermissionsForPath( context, path );
}
public void checkPermissionsForEntity( ServiceContext context, EntityRef entity ) {
String path = context.getPath( entity );
checkPermissionsForPath( context, path );
}
public void checkPermissionsForEntitySubPath( ServiceContext context, UUID entityId, String subPath ) {
String path = context.getPath( entityId, subPath );
checkPermissionsForPath( context, path );
}
public void checkPermissionsForEntitySubPath( ServiceContext context, EntityRef entity, String subPath ) {
String path = context.getPath( entity, subPath );
checkPermissionsForPath( context, path );
}
public void checkPermissionsForPath( ServiceContext context, String path ) {
Subject currentUser = SubjectUtils.getSubject();
if ( currentUser == null ) {
return;
}
if( isServiceAdmin() ){
if(logger.isDebugEnabled()){
logger.debug("Subject is the sysadmin, short-circuiting and allowing access");
}
return;
}
String perm = getPermissionFromPath(
em.getApplicationRef().getUuid(), context.getAction().toString().toLowerCase(), path );
boolean permitted = currentUser.isPermitted( perm );
if ( logger.isDebugEnabled() ) {
logger.debug( PATH_MSG, path, context.getAction(), perm, permitted );
}
SubjectUtils.checkPermission( perm );
Subject subject = SubjectUtils.getSubject();
if (logger.isDebugEnabled()) {
logger.debug("Checked subject {} for perm {}", subject != null ? subject.toString() : "", perm);
logger.debug("------------------------------------------------------------------------------");
}
}
private static final String PATH_MSG =
"---- Checked permissions for path --------------------------------------------\n" + "Requested path: {} \n"
+ "Requested action: {} \n" + "Requested permission: {} \n" + "Permitted: {} \n";
/** Purpose is to enable entity dictionary entries to have name not equal to path segment. */
protected static class EntityDictionaryEntry {
private String name;
private String path; // path segment used in URL
public EntityDictionaryEntry( String name ) {
this.name = this.path = name;
}
public EntityDictionaryEntry( String name, String path ) {
this.name = name;
this.path = path;
}
public String getName() {
return name;
}
public String getPath() {
return path;
}
}
}