package de.zib.gndms.GORFX.service;
import de.zib.gndms.common.GORFX.service.TaskFlowService;
import de.zib.gndms.common.model.gorfx.types.*;
import de.zib.gndms.common.rest.*;
import de.zib.gndms.common.stuff.devel.NotYetImplementedException;
import de.zib.gndms.gndmc.gorfx.TaskClient;
import de.zib.gndms.infra.GridConfig;
import de.zib.gndms.kit.security.SpringSecurityContextHolder;
import de.zib.gndms.kit.util.WidAux;
import de.zib.gndms.logic.model.TaskAction;
import de.zib.gndms.logic.model.TaskExecutionService;
import de.zib.gndms.logic.model.gorfx.taskflow.TaskFlowAux;
import de.zib.gndms.logic.model.gorfx.taskflow.TaskFlowFactory;
import de.zib.gndms.logic.model.gorfx.taskflow.TaskFlowProvider;
import de.zib.gndms.logic.model.gorfx.taskflow.UnsatisfiableOrderException;
import de.zib.gndms.model.common.NoSuchResourceException;
import de.zib.gndms.model.common.PermissionInfo;
import de.zib.gndms.model.common.PersistentContract;
import de.zib.gndms.model.gorfx.types.DelegatingOrder;
import de.zib.gndms.neomodel.common.Dao;
import de.zib.gndms.neomodel.gorfx.TaskBuilder;
import de.zib.gndms.neomodel.gorfx.TaskFlow;
import de.zib.gndms.neomodel.gorfx.Taskling;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.util.*;
/*
* Copyright 2008-2011 Zuse Institute Berlin (ZIB)
*
* Licensed 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.
*/
/**
* @author try ma ik jo rr a zib
* @date 13.01.2011 15:17:47
*
* @brief A controller for a REST taskflow resource which implements the
* TaskFlowService interface.
*
* The taskflow service acts as interface to instantiated taskflow
* resources. The instantiation or creation happens through the GORFX
* service itself.
*
* This implicates that all method invocation concern a single
* taskflow, not the service as a whole.
*/
@Controller
@RequestMapping( "/gorfx" )
public class TaskFlowServiceImpl implements TaskFlowService {
// private ORQDao orqDao;
private String localBaseUrl;
private String baseUrl; // inject or read from properties, doesn't contain gorfx postfix
private TaskFlowProvider taskFlowProvider;
private final List<String> facetsNames = new ArrayList<String>( 6 );
private UriFactory uriFactory;
private TaskClient taskClient;
private final Logger logger = LoggerFactory.getLogger( this.getClass() );
private TaskExecutionService executorService;
private Dao dao;
private GridConfig gridConfig;
@PostConstruct
public void init() {
facetsNames.add( "order" );
facetsNames.add( "quote" );
facetsNames.add( "task" );
facetsNames.add( "result" );
facetsNames.add( "status" );
facetsNames.add( "errors" );
uriFactory = new UriFactory(baseUrl);
taskClient.setServiceURL( localBaseUrl );
}
@RequestMapping( value = "/_{type}/_{id}", method = RequestMethod.GET )
@Secured( "ROLE_USER" )
public ResponseEntity<Facets> getFacets( @PathVariable String type, @PathVariable String id, @RequestHeader( "DN" ) String dn ) {
Map<String, String> uriargs = taskFlowUriMap( type, id );
GNDMSResponseHeader header = new GNDMSResponseHeader( uriFactory.taskFlowTypeUri( uriargs, null ), null, baseUrl, dn, null );
if ( taskFlowProvider.exists( type ) ) {
TaskFlow tf = taskFlowProvider.getFactoryForTaskFlow( type ).find( id );
if ( tf != null ) {
ArrayList<Facet> fl = new ArrayList<Facet>( 6 );
for ( String f : facetsNames ) {
String fn = uriFactory.taskFlowUri( uriargs, f );
fl.add( new Facet( f, fn ) );
}
return new ResponseEntity<Facets>( new Facets( fl ), header, HttpStatus.OK );
} else
logger.debug( "request for non existing " + type + ": " + id );
} else
logger.debug( "request for non existing type " + type + ": " + id );
return new ResponseEntity<Facets>( new Facets( ), header, HttpStatus.NOT_FOUND );
}
@RequestMapping( value = "/_{type}/_{id}", method = RequestMethod.DELETE )
@Secured( "ROLE_USER" )
public ResponseEntity< Integer > deleteTaskflow( @PathVariable String type, @PathVariable String id,
@RequestHeader( "DN" ) String dn,
@RequestHeader( "WId" ) String wid ) {
logger.debug( "removing " + type + " taskflow: " + id );
HttpStatus hs = HttpStatus.NOT_FOUND;
if ( taskFlowProvider.exists( type ) ) {
TaskFlowFactory tff = taskFlowProvider.getFactoryForTaskFlow( type );
TaskFlow tf = tff.find( id );
if ( tf != null ) {
if ( tf.hasTaskling() )
taskClient.deleteTask( tf.getTaskling().getId(), dn, wid );
tff.delete( id );
hs = HttpStatus.OK;
}
}
return new ResponseEntity< Integer >( new Integer( 0 ), getHeader( type, id, null, dn, wid ), hs );
}
@RequestMapping( value = "/_{type}/_{id}/order", method = RequestMethod.GET )
@Secured( "ROLE_USER" )
public ResponseEntity<Order> getOrder( @PathVariable String type, @PathVariable String id,
@RequestHeader( "DN" ) String dn,
@RequestHeader( "WId" ) String wid ) {
HttpStatus hs = HttpStatus.NOT_FOUND;
Order order = null;
try {
TaskFlow tf = findTF( type, id );
order = tf.getOrder().getOrderBean();
if ( order != null )
hs = HttpStatus.OK;
} catch ( NoSuchResourceException e ) { /* intentionally */ }
return new ResponseEntity<Order>( order, getHeader( type, id, "order", dn, wid ), hs );
}
@RequestMapping( value = "/_{type}/_{id}/order", method = RequestMethod.POST )
@Secured( "ROLE_USER" )
public ResponseEntity< Integer > setOrder( @PathVariable String type, @PathVariable String id,
@RequestBody Order orq, @RequestHeader( "DN" ) String dn,
@RequestHeader( "WId" ) String wid ) {
HttpStatus hs = HttpStatus.NOT_FOUND;
if ( taskFlowProvider.exists( type ) ) {
final TaskFlowFactory tff = taskFlowProvider.getFactoryForTaskFlow( type );
final TaskFlow tf = tff.find( id );
if ( tf != null )
hs = TaskFlowServiceAux.setAndValidateOrder( orq, tf, tff );
}
return new ResponseEntity< Integer >( 0, getHeader( type, id, "order", dn, wid ), hs );
}
@RequestMapping( value = "/_{type}/_{id}/quote", method = RequestMethod.GET )
@Secured( "ROLE_USER" )
public ResponseEntity<List<Specifier<Quote>>> getQuotes( @PathVariable String type, @PathVariable String id,
@RequestHeader( "DN" ) String dn,
@RequestHeader( "WId" ) String wid ) {
WidAux.initWid(wid);
logger.debug( "quote called" );
HttpStatus hs = HttpStatus.NOT_FOUND;
List< Specifier< Quote > > res = null;
if ( taskFlowProvider.exists( type ) ) {
TaskFlowFactory tff = taskFlowProvider.getFactoryForTaskFlow( type );
TaskFlow tf = tff.find( id );
if ( tf != null ) {
try {
List< Quote > quoteList = TaskFlowServiceAux.createQuotes( tff, tf );
// set origin of quotes if not already set
for( Quote quote: quoteList ) {
if( quote.getSite() == null )
quote.setSite( getGridConfig().getBaseUrl() );
}
res = new ArrayList<Specifier<Quote>>( quoteList.size() );
HashMap<String, String> urimap = new HashMap<String, String>( 3 );
urimap.put( "service", "gorfx" );
urimap.put( UriFactory.TASKFLOW_ID, id );
urimap.put( "type", type );
for ( int i = 0; i < quoteList.size(); ++i ) {
urimap.put( "idx", String.valueOf( i ) );
Specifier< Quote > sq = new Specifier< Quote >();
sq.setUriMap( Collections.unmodifiableMap( urimap ) );
sq.setUrl( uriFactory.quoteUri( urimap ) );
sq.setPayload( quoteList.get( i ) );
res.add( sq );
}
hs = HttpStatus.OK;
} catch ( UnsatisfiableOrderException e ) {
DelegatingOrder o = tf.getOrder();
logger.debug( "Unsatisfiable order: " + o.getLoggableDescription() );
logger.debug( e.getStackTrace().toString() );
tf.setUnfulfillableOrder( true );
hs = HttpStatus.BAD_REQUEST;
} catch ( Exception e ) {
logger.warn( "Exception on order calculation for " + tf.getOrder()
.getLoggableDescription(), e );
throw new RuntimeException( "Could not calculate quotes for order: " + e.getMessage(), e );
}
}
}
WidAux.removeWid();
return new ResponseEntity<List<Specifier<Quote>>>( res, getHeader( type, id, "quote", dn, wid ), hs );
}
@RequestMapping( value = "/_{type}/_{id}/quote", method = RequestMethod.POST )
@Secured( "ROLE_USER" )
public ResponseEntity<Integer> setQuote( @PathVariable String type, @PathVariable String id,
@RequestBody Quote cont,
@RequestHeader( "DN" ) String dn,
@RequestHeader( "WId" ) String wid ) {
HttpStatus hs = HttpStatus.NOT_FOUND;
try {
TaskFlow tf = findTF( type, id );
tf.setPreferredQuote( cont );
hs = HttpStatus.OK;
} catch ( NoSuchResourceException e ) {
}
return new ResponseEntity<Integer>( 37, getHeader( type, id, "quote", dn, wid ), hs );
}
@RequestMapping( value = "/_{type}/_{id}/quote/_{idx}", method = RequestMethod.GET )
@Secured( "ROLE_USER" )
public ResponseEntity<Quote> getQuote( @PathVariable String type, @PathVariable String id, @PathVariable int idx,
@RequestHeader( "DN" ) String dn, @RequestHeader( "WId" ) String wid ) {
HttpStatus hs = HttpStatus.NOT_FOUND;
Quote quote = null;
try {
TaskFlow tf = findTF( type, id );
List<Quote> quoteList = tf.getQuotes();
if ( quoteList != null && idx >= 0 && idx < quoteList.size() ) {
quote = quoteList.get( idx );
hs = HttpStatus.OK;
}
} catch ( NoSuchResourceException e ) {
// intentionally
}
return new ResponseEntity<Quote>( quote, getHeader( type, id, "quote", dn, wid ), hs );
}
@RequestMapping( value = "/_{type}/_{id}/quote/_{idx}", method = RequestMethod.DELETE )
@Secured( "ROLE_USER" )
public ResponseEntity< Integer > deleteQuotes( @PathVariable String type, @PathVariable String id,
@PathVariable int idx, @RequestHeader( "DN" ) String dn,
@RequestHeader( "WId" ) String wid ) {
throw new NotYetImplementedException(); // together with the ProperlyNeverWillBeException
}
@RequestMapping( value = "/_{type}/_{id}/task", method = RequestMethod.GET )
@Secured( "ROLE_USER" )
public ResponseEntity<Specifier<Facets>> getTask( @PathVariable String type, @PathVariable String id,
@RequestHeader( "DN" ) String dn, @RequestHeader( "WId" ) String wid ) {
HttpStatus hs = HttpStatus.NOT_FOUND;
Specifier<Facets> spec = null;
WidAux.initWid( wid );
try {
TaskFlow tf = findTF( type, id );
Taskling t = tf.getTaskling();
if ( t != null ) {
logger.debug( "getTask task called" );
spec = TaskClient.TaskServiceAux
.getTaskSpecifier( taskClient, t.getId(), uriFactory,
taskFlowUriMap( type, id ), dn );
hs = HttpStatus.OK;
}
} catch ( Exception e ) {
logger.warn( "Exception while getting task", e );
}
logger.debug( "returning with " + hs.name() );
WidAux.removeWid();
return new ResponseEntity<Specifier<Facets>>( spec, getHeader( type, id, "task", dn, wid ), hs );
}
@RequestMapping( value = "/_{type}/_{id}/task", method = RequestMethod.PUT )
@Secured( "ROLE_USER" )
public ResponseEntity<Specifier<Facets>> createTask( @PathVariable String type, @PathVariable String id,
@RequestParam( value = "quote",
required = false ) Integer quoteId,
@RequestHeader( "DN" ) String dn, @RequestHeader( "WId" ) String wid ) {
HttpStatus hs = HttpStatus.NOT_FOUND;
WidAux.initWid( wid );
logger.debug( "create task called" );
HttpHeaders headers = getHeader( type, id, "task", dn, wid );
if ( taskFlowProvider.exists( type ) ) {
TaskFlowFactory tff = taskFlowProvider.getFactoryForTaskFlow( type );
TaskFlow tf = tff.find( id );
if ( tf != null ) {
if ( tf.getTaskling() != null )
hs = HttpStatus.CONFLICT;
else {
tf.getOrder().setSecurityContextHolder( new SpringSecurityContextHolder(
SecurityContextHolder.getContext() ) );
TaskAction ta;
Taskling taskling;
if ( tf.hasQuotes() ) {
logger.debug( "submitting using quote: " + quoteId);
ta = tff.createAction( ( Quote )tf.getQuotes().get( quoteId == null ? 0 : quoteId ) );
taskling = submitTaskActionWithQuotes( quoteId, tf, ta, wid );
} else {
ta = tff.createAction();
taskling = executorService.submitTaskAction( dao, ta, tf.getOrder(), wid );
}
hs = HttpStatus.CREATED;
Specifier<Facets> spec = null;
try {
spec = TaskClient.TaskServiceAux
.getTaskSpecifier( taskClient, taskling.getId(),
uriFactory, taskFlowUriMap( type, id ), dn );
} catch ( Exception e ) {
logger.warn( "Exception while getting task", e );
}
return new ResponseEntity<Specifier<Facets>>( spec, headers, hs );
}
}
}
logger.debug( "Problem, returning " + hs.name() );
WidAux.removeWid();
return new ResponseEntity<Specifier<Facets>>( null, headers, hs );
}
private Taskling submitTaskActionWithQuotes( final Integer quoteId, final TaskFlow tf,
final TaskAction ta, final String wid )
{
Taskling taskling;List<Quote> quotes = tf.getQuotes();
Quote quote = quoteId != null && quoteId >= 0 && quoteId < quotes.size()
? quotes.get( quoteId ) : quotes.get( 0 );
taskling = executorService.submitTaskAction( dao, ta,
new TaskBuilder().setOrder( tf.getOrder() )
.setContract( PersistentContract.acceptQuoteNow( quote ) )
.setPermissionInfo( new PermissionInfo( "default",
"PermissionConfiglet" ) )
, wid );
return taskling;
}
@RequestMapping( value = "/_{type}/_{id}/status", method = RequestMethod.GET )
@Secured( "ROLE_USER" )
public ResponseEntity<TaskFlowStatus> getStatus( @PathVariable String type, @PathVariable String id,
@RequestHeader( "DN" ) String dn,
@RequestHeader( "WId" ) String wid ) {
HttpStatus hs = HttpStatus.NOT_FOUND;
TaskFlowStatus tfs = null;
try {
TaskFlow tf = findTF( type, id );
tfs = TaskFlowAux.statusFromTaskFlow( dao, tf );
Taskling t = tf.getTaskling();
if ( t != null ) {
// maybe call task client
Specifier<Void> spec = new Specifier<Void>();
Map<String, String> urimap = taskUriMap( type, id, t );
spec.setUrl( uriFactory.taskUri( urimap, "status" ) );
spec.setUriMap( urimap );
tfs.setTaskSpecifier( spec );
hs = HttpStatus.OK;
}
} catch ( NoSuchResourceException e ) {
// intentionally
}
return new ResponseEntity<TaskFlowStatus>( tfs, getHeader( type, id, "status", dn, wid ), hs );
}
@RequestMapping( value = "/_{type}/_{id}/result", method = RequestMethod.GET )
@Secured( "ROLE_USER" )
public ResponseEntity<Specifier<TaskResult>> getResult( @PathVariable String type, @PathVariable String id,
@RequestHeader( "DN" ) String dn,
@RequestHeader( "WId" ) String wid ) {
HttpStatus hs = HttpStatus.NOT_FOUND;
Specifier<TaskResult> spec = null;
try {
spec = createTaskSpecifier( TaskResult.class, type, id, "result" );
ResponseEntity<TaskResult> res = taskClient.getResult( spec.getUriMap().get( "taskId" ), dn, wid );
if ( HttpStatus.OK.equals( res.getStatusCode() ) ) {
spec.setPayload( res.getBody() );
hs = HttpStatus.OK;
} else spec = null;
} catch ( NoSuchResourceException e ) {
// intentionally
}
return new ResponseEntity<Specifier<TaskResult>>( spec, getHeader( type, id, "result", dn, wid ), hs );
}
@RequestMapping( value = "/_{type}/_{id}/errors", method = RequestMethod.GET )
@Secured( "ROLE_USER" )
public ResponseEntity<TaskFlowFailure> getErrors( @PathVariable String type,
@PathVariable String id,
@RequestHeader( "DN" ) String dn,
@RequestHeader( "WId" ) String wid ) {
HttpStatus hs = HttpStatus.NOT_FOUND;
TaskFlowFailureImpl taskFlowFailure = new TaskFlowFailureImpl();
try {
Specifier<TaskFailure> spec = createTaskSpecifier( TaskFailure.class, type, id, "errors" );
ResponseEntity<TaskFailure> res = taskClient.getErrors( spec.getUriMap().get( "taskId" ), dn, wid );
if ( res.getStatusCode() == HttpStatus.OK ) {
spec.setPayload( res.getBody() );
hs = HttpStatus.OK;
}
} catch ( NoSuchResourceException e ) {
// intentionally
}
return new ResponseEntity<TaskFlowFailure>( taskFlowFailure,
getHeader( type, id, "result", dn, wid ), hs );
}
protected GNDMSResponseHeader getHeader( String type, String id, String facet, String dn, String wid ) {
Map<String, String> uriargs = taskFlowUriMap( type, id );
return new GNDMSResponseHeader( uriFactory.taskFlowTypeUri( uriargs, facet ), facet, baseUrl, dn, wid );
}
private Map<String, String> taskFlowUriMap( String type, String id ) {
HashMap<String, String> urimap = new HashMap<String, String>( 3 );
urimap.put( UriFactory.SERVICE, "gorfx" );
urimap.put( UriFactory.TASKFLOW_TYPE, type );
urimap.put( UriFactory.TASKFLOW_ID, id );
return urimap;
}
private Map<String, String> taskUriMap( String type, String id, Taskling t ) {
return TaskClient.TaskServiceAux.taskUriMap( t.getId(), taskFlowUriMap( type, id ) );
}
private <T> Specifier<T> createTaskSpecifier( Class<T> resClass, String type, String id, String facet ) throws NoSuchResourceException {
final TaskFlow tf = findTF( type, id );
final Taskling t = tf.getTaskling();
final Map<String, String> urimap = taskUriMap( type, id, t );
final Specifier<T> spec;
if ( t != null ) {
spec = new Specifier<T>();
spec.setUrl( uriFactory.taskUri( urimap, facet ) );
spec.setUriMap( urimap );
} else
throw new NoSuchResourceException( "task", urimap );
return spec;
}
protected TaskFlow findTF( String type, String id ) throws NoSuchResourceException {
if ( taskFlowProvider.exists( type ) ) {
TaskFlowFactory tff = taskFlowProvider.getFactoryForTaskFlow( type );
TaskFlow tf = tff.find( id );
if ( tf != null )
return tf;
else
return TaskFlowServiceAux.fromTask( dao, taskFlowProvider, type, id );
}
throw new NoSuchResourceException();
}
public Dao getDao() {
return dao;
}
@Inject
public void setDao( Dao dao ) {
this.dao = dao;
}
public TaskExecutionService getExecutorService() {
return executorService;
}
@Inject
public void setExecutorService( TaskExecutionService executorService ) {
this.executorService = executorService;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
public void setLocalBaseUrl( String localBaseUrl ) {
this.localBaseUrl = localBaseUrl;
}
@Inject
public void setTaskFlowProvider( TaskFlowProvider taskFlowProvider ) {
this.taskFlowProvider = taskFlowProvider;
}
@Inject
public void setTaskClient( TaskClient taskClient ) {
this.taskClient = taskClient;
}
public GridConfig getGridConfig() {
return gridConfig;
}
@Inject
public void setGridConfig( GridConfig gridConfig ) {
this.gridConfig = gridConfig;
}
}