/* Copyright (c) 2001 - 2008 TOPP - www.openplans.org. All rights reserved. * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.flow; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.geoserver.flow.config.DefaultControlFlowConfigurator; import org.geoserver.ows.AbstractDispatcherCallback; import org.geoserver.ows.HttpErrorCodeException; import org.geoserver.ows.Request; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.platform.Operation; import org.geotools.util.logging.Logging; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * Callback that controls the flow of OWS requests based on user specified rules and makes sure * GeoServer does not get overwhelmed by too many concurrent ones. Can also be used to provide * different quality of service on different users. * * @author Andrea Aime - OpenGeo */ public class ControlFlowCallback extends AbstractDispatcherCallback implements ApplicationContextAware { static final Logger LOGGER = Logging.getLogger(ControlFlowCallback.class); static ThreadLocal<List<FlowController>> REQUEST_CONTROLLERS = new ThreadLocal<List<FlowController>>(); static NestedRequestSentinel SENTINEL = new NestedRequestSentinel(); List<FlowController> controllers = Collections.emptyList(); long timeout = -1; ControlFlowConfigurator configurator; public void finished(Request request) { if(SENTINEL.isOutermostRequest()) { // call back the same controllers we used when the operation started if (REQUEST_CONTROLLERS.get() != null) { List<FlowController> fcl = REQUEST_CONTROLLERS.get(); for (FlowController flowController : fcl) { flowController.requestComplete(request); } } // clean up the thread local REQUEST_CONTROLLERS.remove(); } SENTINEL.stop(); } public Operation operationDispatched(Request request, Operation operation) { // check if we need to rebuild the flow controller list if (configurator.isStale()) reloadConfiguration(); // tell the recursion sentinel we're starting a request SENTINEL.start(); if(SENTINEL.isOutermostRequest()) { // scan through the existing controllers and set the list in a thread local // so that this request will get exactly the same list when the operation finishes List<FlowController> controllers = this.controllers; if (controllers.size() > 0) { REQUEST_CONTROLLERS.set(controllers); long maxTime = timeout > 0 ? System.currentTimeMillis() + timeout : -1; for (FlowController flowController : controllers) { if(timeout > 0) { long maxWait = maxTime - System.currentTimeMillis(); if(!flowController.requestIncoming(request, maxWait)) throw new HttpErrorCodeException(503, "Requested timeout out while waiting to be executed"); } else { flowController.requestIncoming(request, -1); } } } } return operation; } /** * Reloads the flow controller list and replaces the existing ones */ void reloadConfiguration() { try { List<FlowController> newControllers = new ArrayList<FlowController>(configurator .buildFlowControllers()); Collections.sort(newControllers, new ControllerPriorityComparator()); controllers = newControllers; timeout = configurator.getTimeout(); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Error occurerd during flow controllers reconfiguration"); } } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // look for a ControlFlowConfigurator in the application context, if none is found, use the // default one configurator = GeoServerExtensions.bean(ControlFlowConfigurator.class, applicationContext); if (configurator == null) configurator = new DefaultControlFlowConfigurator(); } }