/* * 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.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.usergrid.persistence.EntityRef; import org.apache.usergrid.persistence.Query; import org.apache.usergrid.services.ServiceParameter.QueryParameter; import org.apache.usergrid.services.ServiceResults.Type; import org.apache.shiro.SecurityUtils; import static org.apache.commons.lang.StringUtils.isNotBlank; import static org.apache.usergrid.utils.ListUtils.isEmpty; import static org.apache.usergrid.utils.ListUtils.last; public class ServiceRequest { private static final Logger logger = LoggerFactory.getLogger( ServiceRequest.class ); public static long count = 0; private final long id = count++; private final ServiceManager services; private final ServiceAction action; private final ServiceRequest parent; private final EntityRef owner; private final String serviceName; private final String path; private final List<ServiceParameter> parameters; private final String childPath; private final boolean returnsTree; private final boolean returnsInboundConnections; private final boolean returnsOutboundConnections; private final ServicePayload payload; private final List<ServiceParameter> originalParameters; private final boolean analyzeQueryOnly; // return results_set, result_entity, new_service, param_list, properties public ServiceRequest(ServiceManager services, ServiceAction action, String serviceName, List<ServiceParameter> parameters, ServicePayload payload, boolean returnsTree, boolean returnsInboundConnections, boolean returnsOutboundConnections, boolean analyzeQueryOnly) { this.services = services; this.action = action; parent = null; owner = services.getApplicationRef(); childPath = null; this.serviceName = serviceName; path = "/" + serviceName; this.parameters = parameters; this.originalParameters = Collections.unmodifiableList( new ArrayList<ServiceParameter>( parameters ) ); this.returnsTree = returnsTree; this.returnsInboundConnections = returnsInboundConnections; this.returnsOutboundConnections = returnsOutboundConnections; this.analyzeQueryOnly = analyzeQueryOnly; if ( payload == null ) { payload = new ServicePayload(); } this.payload = payload; } public ServiceRequest( ServiceManager services, ServiceAction action, String serviceName, List<ServiceParameter> parameters, ServicePayload payload, boolean returnsTree) { this( services, action, serviceName, parameters, payload, returnsTree, true, true, false); } public ServiceRequest( ServiceManager services, ServiceAction action, String serviceName, List<ServiceParameter> parameters, ServicePayload payload ) { this( services, action, serviceName, parameters, payload, false, true, true, false); } public ServiceRequest( ServiceRequest parent, EntityRef owner, String path, String childPath, String serviceName, List<ServiceParameter> parameters ) { this.services = parent.services; this.returnsTree = parent.returnsTree; this.returnsInboundConnections = parent.returnsInboundConnections; this.returnsOutboundConnections = parent.returnsOutboundConnections; this.analyzeQueryOnly = parent.analyzeQueryOnly; this.action = parent.action; this.payload = parent.payload; this.parent = parent; this.owner = owner; this.serviceName = serviceName; if ( parameters == null ) { parameters = new ArrayList<ServiceParameter>(); } this.parameters = parameters; this.originalParameters = Collections.unmodifiableList( new ArrayList<ServiceParameter>( parameters ) ); this.path = path; this.childPath = childPath; } public ServiceRequest( ServiceManager services, ServiceAction action, ServiceRequest parent, EntityRef owner, String path, String childPath, String serviceName, List<ServiceParameter> parameters, ServicePayload payload, boolean returnsTree, boolean returnsInboundConnections, boolean returnsOutboundConnections, boolean analyzeQueryOnly ) { this.services = services; this.action = action; this.parent = parent; this.owner = owner; this.serviceName = serviceName; this.path = path; this.parameters = parameters; this.originalParameters = Collections.unmodifiableList( new ArrayList<ServiceParameter>( parameters ) ); this.childPath = childPath; this.returnsTree = returnsTree; this.returnsInboundConnections = returnsInboundConnections; this.returnsOutboundConnections = returnsOutboundConnections; this.analyzeQueryOnly = analyzeQueryOnly; this.payload = payload; } public ServiceRequest( ServiceManager services, ServiceAction action, ServiceRequest parent, EntityRef owner, String path, String childPath, String serviceName, List<ServiceParameter> parameters, ServicePayload payload, boolean returnsTree ) { this(services, action, parent, owner, path, childPath, serviceName, parameters, payload, returnsTree, true, true, false); } public static ServiceRequest withPath( ServiceRequest r, String path ) { return new ServiceRequest( r.services, r.action, r.parent, r.owner, path, r.childPath, r.serviceName, r.parameters, r.payload, r.returnsTree, r.returnsInboundConnections, r.returnsOutboundConnections, r.analyzeQueryOnly ); } public static ServiceRequest withChildPath( ServiceRequest r, String childPath ) { return new ServiceRequest( r.services, r.action, r.parent, r.owner, r.path, childPath, r.serviceName, r.parameters, r.payload, r.returnsTree, r.returnsInboundConnections, r.returnsOutboundConnections, r.analyzeQueryOnly ); } public ServiceRequest withPath( String path ) { return withPath( this, path ); } public ServiceRequest withChildPath( String childPath ) { return withChildPath( this, childPath ); } public long getId() { return id; } public String getPath() { return path; } public ServiceAction getAction() { return action; } public ServicePayload getPayload() { return payload; } public ServiceManager getServices() { return services; } public ServiceRequest getParent() { return parent; } public String getServiceName() { return serviceName; } public EntityRef getPreviousOwner() { if ( parent == null ) { return null; } return parent.getOwner(); } public ServiceResults execute() throws Exception { try { return execute( null ); } catch ( Exception e ) { // don't log as error because some exceptions are not actually errors, e.g. resource not found if (logger.isDebugEnabled()) { logger.debug(debugString(), e); } throw e; } } private String debugString() { StringBuffer sb = new StringBuffer(); sb.append( "request details:\n " ); sb.append( action ); sb.append( " " ); sb.append( this ); sb.append( "\n payload: " ); sb.append( payload ); sb.append( "\n owner: " ); sb.append( owner ); sb.append( "\n principal: " ); sb.append( SecurityUtils.getSubject().getPrincipal() ); return sb.toString(); } public ServiceContext getAppContext() throws Exception { Service s = services.getService( serviceName ); return s.getContext( action,this,null,payload ); } public ServiceResults execute( ServiceResults previousResults ) throws Exception { // initServiceName(); ServiceResults results = null; Service s = services.getService( serviceName ); if ( s != null ) { results = s.invoke( action, this, previousResults, payload ); if ( ( results != null ) && results.hasMoreRequests() ) { results = invokeMultiple( results ); } } if ( results == null ) { results = new ServiceResults( null, this, previousResults, null, Type.GENERIC, null, null, null ); } return results; } private ServiceResults invokeMultiple( ServiceResults previousResults ) throws Exception { List<ServiceRequest> requests = previousResults.getNextRequests(); if ( returnsTree ) { for ( ServiceRequest request : requests ) { ServiceResults rs = request.execute( previousResults ); if ( rs != null ) { previousResults.setChildResults( rs ); } } return previousResults; } else { ServiceResults aggregate_results = null; for ( ServiceRequest request : requests ) { ServiceResults rs = request.execute( previousResults ); if ( rs != null ) { if ( aggregate_results == null ) { aggregate_results = rs; } else { aggregate_results.merge( rs ); } } } return aggregate_results; } } public List<ServiceParameter> getParameters() { return parameters; } public boolean hasParameters() { return !isEmpty( parameters ); } public EntityRef getOwner() { return owner; } public Query getLastQuery() { if ( !isEmpty( parameters ) ) { return last( parameters ).getQuery(); } return null; } @Override public String toString() { StringBuilder sb = new StringBuilder(); if ( serviceName != null ) { sb.append( "/" ); sb.append( serviceName ); } for ( int i = 0; i < parameters.size(); i++ ) { ServiceParameter p = parameters.get( i ); if ( p instanceof QueryParameter ) { if ( i == ( parameters.size() - 1 ) ) { sb.append( '?' ); } else { sb.append( ';' ); } boolean has_prev_param = false; String q = p.toString(); if ( isNotBlank( q ) ) { try { sb.append("ql=").append(URLEncoder.encode(q, "UTF-8")); } catch ( UnsupportedEncodingException e ) { logger.error( "Unable to encode url", e ); } has_prev_param = true; } int limit = p.getQuery().getLimit(); if ( limit != Query.DEFAULT_LIMIT ) { if ( has_prev_param ) { sb.append( '&' ); } sb.append("limit=").append(limit); has_prev_param = true; } if ( p.getQuery().getStartResult() != null ) { if ( has_prev_param ) { sb.append( '&' ); } sb.append("start=").append(p.getQuery().getStartResult()); has_prev_param = true; } } else { sb.append( '/' ); sb.append( p.toString() ); } } return sb.toString(); } public String getChildPath() { return childPath; } public boolean isReturnsTree() { return returnsTree; } public boolean isReturnsInboundConnections() { return returnsInboundConnections; } public boolean isReturnsOutboundConnections() { return returnsOutboundConnections; } public List<ServiceParameter> getOriginalParameters() { return originalParameters; } public boolean isAnalyzeQueryOnly(){ return analyzeQueryOnly; } }