/**
* EasySOA Proxy
* Copyright 2011 Open Wide
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Contact : easysoa-dev@googlegroups.com
*/
package org.easysoa.sca.intents;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osoa.sca.ServiceUnavailableException;
import org.osoa.sca.annotations.Scope;
import org.osoa.sca.annotations.Service;
import org.ow2.frascati.tinfi.api.IntentHandler;
import org.ow2.frascati.tinfi.api.IntentJoinPoint;
import org.easysoa.sca.intents.utils.RequestElement;
import org.eclipse.jetty.server.Request;
@Scope("COMPOSITE")
@Service(IntentHandler.class)
public class AutoRearmFuseIntent implements IntentHandler {
// Logger
private static Log log = LogFactory.getLog(AutoRearmFuseIntent.class);
/**
* Max number of requests in a given time period (default value)
*/
//@Property
protected int maxRequestsNumber = 10;
/**
* Period of time during the requests number must not be > to the
* maxRequestNumber (in Ms) (default value)
*/
//@Property
protected int maxTimePeriod = 120000;
/**
* The queue to stack the requests
*/
private Queue<RequestElement> requestQueue = null;
/**
* Constructor
*/
public AutoRearmFuseIntent() {
//System.out.println("[AUTOREARMFUSE INTENT] AUTOREARMFUSE INTENT default Constructor !!");
// Queue initialization -> max size = maxRequestNumber
requestQueue = new ArrayBlockingQueue<RequestElement>(maxRequestsNumber);
}
/** Set the maxRequestsNumber property. */
public void setMaxRequestsNumber(final String maxRequestsNumber) {
//System.out.println("[AUTOREARMFUSE INTENT] : setting maxRequestsNumber to '" + maxRequestsNumber + "'.");
log.info("[AUTOREARMFUSE INTENT] : setting maxRequestsNumber to '" + maxRequestsNumber + "'.");
try {
int mrn = Integer.parseInt(maxRequestsNumber);
if (mrn > 0) {
this.maxRequestsNumber = mrn;
requestQueue = new ArrayBlockingQueue<RequestElement>(
this.maxRequestsNumber);
}
} catch (Exception ex) {
//System.out.println("[AUTOREARMFUSE INTENT] : Invalid value for maxRequestsNumber property. Default value will be use instead !");
log.error("[AUTOREARMFUSE INTENT] : Invalid value for maxRequestsNumber property. Default value will be use instead !");
}
}
/** Set the maxTimePeriod property. */
public void setMaxTimePeriod(final String maxTimePeriod) {
//System.out.println("[AUTOREARMFUSE INTENT] : setting maxTimePeriod to '" + maxTimePeriod + "'.");
log.info("[AUTOREARMFUSE INTENT] : setting maxTimePeriod to '" + maxTimePeriod + "'.");
try {
int mtp = Integer.parseInt(maxTimePeriod);
if (mtp > 500) {
this.maxTimePeriod = mtp;
}
} catch (Exception ex) {
//System.out.println("[AUTOREARMFUSE INTENT] : Invalid value for maxTimePeriod property. Default value will be use instead !");
log.error("[AUTOREARMFUSE INTENT] : Invalid value for maxTimePeriod property. Default value will be use instead !");
}
}
/**
* Check if the request respect the conditions
*
* @throws Throwable
* If the request does not respect the conditions
*/
public void checkFuseConditions() throws ServiceUnavailableException, Throwable {
// get the current time in Ms
long currentTime = System.currentTimeMillis();
//System.out.println("[AUTOREARMFUSE INTENT] Current time (Ms) : " + currentTime);
log.debug("[AUTOREARMFUSE INTENT] Current time (Ms) : " + currentTime);
//System.out.println("[AUTOREARMFUSE INTENT] Requests in queue : " + requestQueue.size());
log.debug("[AUTOREARMFUSE INTENT] Requests in queue : " + requestQueue.size());
if(requestQueue.size() > 0){
//System.out.println("[AUTOREARMFUSE INTENT] Older request time in queue : " + requestQueue.peek().getRequestTime());
log.debug("[AUTOREARMFUSE INTENT] Older request time in queue : " + requestQueue.peek().getRequestTime());
}
// if the queue is not full, add the new request in the queue
if(requestQueue.size() < maxRequestsNumber){
requestQueue.add(createRequestElement(currentTime));
}
// Otherwise check if the difference between the oldest element time and the current time is > to the maxTimePeriod
else {
RequestElement olderRequest = requestQueue.peek();
// if true, add the new request in the queue
if((currentTime - olderRequest.getRequestTime()) > maxTimePeriod){
requestQueue.poll();
requestQueue.add(createRequestElement(currentTime));
}
// otherwise, throw an exception
else {
String errorMessage = "[AUTOREARMFUSE INTENT] Too much requests detected for the time period, this resquest has been blocked !";
//System.out.println(errorMessage);
log.error(errorMessage);
throw new ServiceUnavailableException(errorMessage);
}
}
}
/*
* Create a request element
*/
private RequestElement createRequestElement(long currentTime)
throws IllegalArgumentException {
return new RequestElement("request-" + currentTime, currentTime);
}
/**
* Implementation of the IntentHandler interface
*
* @see org.ow2.frascati.tinfi.control.intent.IntentHandler#invoke(IntentJoinPoint)
*/
public Object invoke(IntentJoinPoint ijp) throws Throwable {
// TODO : The fuse intent works for all received calls => WSDL file requests or service requests
// And makes no differences between the calls : there is only one list where the call are stored
// So 2 main improvements are necessary :
// - First : add a key (eg for localhost:8084 to have a fuse for each different service or endpoint
// - Second : add a filter intent working with a list of endpoint to avoid to activate the fuse for these endpoints
Object ret;
Request request = null;
// Get the request to return error message if the fuse must be activated
if(ijp.getArguments().length >= 2){
if(ijp.getArguments()[1] instanceof org.eclipse.jetty.server.Request){
request = (org.eclipse.jetty.server.Request) (ijp.getArguments()[1]);
}
}
try{
checkFuseConditions();
}
catch(ServiceUnavailableException ex){
// This exception is returned if the fuse max request limit is reached,send a error response
if(request != null && !request.getResponse().isCommitted()){
request.getResponse().sendError(500, ex.getMessage());
}
}
// Proceed the request
ret = ijp.proceed();
return ret;
}
}