/* * Copyright 2005,2006 WSO2, Inc. http://wso2.com * * 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. * * */ package org.apache.synapse.commons.throttle.core; import org.apache.axis2.context.ConfigurationContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.synapse.commons.throttle.core.factory.ThrottleContextFactory; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; /** * Holds the all runtime data corresponding to call remote callers. * In addition to that this hold clean list for callers. */ public abstract class ThrottleContext { private static Log log = LogFactory.getLog(ThrottleContext.class.getName()); /* The callersMap that contains all registered callers for a particular throttle */ private Map callersMap; /* For mapping id (ip | domainame) to TimeStamp */ private Map keyToTimeStampMap; /* The Time which next cleaning for this throttle will have to take place */ private long nextCleanTime; /* The configuration of a throttle */ private ThrottleConfiguration throttleConfiguration; /* The configuration that corresponding to this context – this holds all static (configuration) data */ private String throttleId; /* Configuration Context for the current environment */ private ConfigurationContext configctx; /* ThrottleDataHolder is used to keep the CallerContexts pertaining to a Throttle Context.*/ private volatile ThrottleDataHolder dataHolder; /* The pre-fix of key for any caller */ private String keyPrefix; /*is log level has set to debug */ private boolean debugOn; /* Throttle replicating to replicate Throttle data */ private ThrottleReplicator throttleReplicator; private ThrottleWindowReplicator throttleWindowReplicator; /** * default constructor – expects a throttle configuration. * * @param throttleConfiguration - configuration data according to the policy */ public ThrottleContext(ThrottleConfiguration throttleConfiguration, ThrottleReplicator throttleReplicator) { if (throttleConfiguration == null) { throw new InstantiationError("Couldn't create the throttle context " + "from null a throttle configuration"); } this.throttleReplicator = throttleReplicator; this.keyToTimeStampMap = new ConcurrentHashMap(); this.callersMap = new ConcurrentSkipListMap(); this.nextCleanTime = 0; this.throttleConfiguration = throttleConfiguration; this.debugOn = log.isDebugEnabled(); this.throttleWindowReplicator = ThrottleContextFactory.getThrottleWindowReplicatorInstance(); ThrottleContextFactory.getThrottleContextCleanupTaskInstance().addThrottleContext(this); } /** * To get the ThrottleConfiguration * * @return ThrottleConfiguration returns the ThrottleConfiguration of this context */ public ThrottleConfiguration getThrottleConfiguration() { return throttleConfiguration; } /** * To get the runtime states of a remote caller * * @param id the remote caller id ex: domain , ip * @return Returns the CallerContext which holds runtime state of a remote caller */ public CallerContext getCallerContext(String id) { if (id != null) { if (debugOn) { log.debug("Found a configuration with id :" + id); } // for cluster env , caller state is contained in the axis configuration context if (dataHolder != null && keyPrefix != null) { return dataHolder.getCallerContext(keyPrefix + id); } // for non - clustered env Long timeKey = (Long) keyToTimeStampMap.get(id); if (timeKey != null) { Object co = callersMap.get(timeKey); if (co != null) { if (co instanceof CallerContext) { return (CallerContext) co; } else if (co instanceof LinkedList) { // callers with same time window LinkedList callers = (LinkedList) co; for (Iterator it = callers.iterator(); it.hasNext(); ) { CallerContext cc = (CallerContext) it.next(); if (cc != null && id.equals(cc.getId())) { return cc; } } } } } } else { if (debugOn) { log.debug("Couldn't find a configuration for the remote caller : " + id); } } return null; } /** * setting callerContext - put callersMap against time and * put time against remote caller id (ip/domain) * * @param callerContext - The remote caller's runtime data. * @param id - The id of the remote caller */ public void addCallerContext(CallerContext callerContext, String id) { if (callerContext != null && id != null) { addCaller(callerContext, id); } } /** * Helper method to add a caller context * * @param callerContext The CallerContext * @param id The id of the remote caller */ private void addCaller(CallerContext callerContext, String id) { if (debugOn) { log.debug("Setting the caller with an id " + id); } //if this is a cluster env.,put the context into axis configuration context if (dataHolder != null && keyPrefix != null) { dataHolder.addCallerContext(keyPrefix + id, callerContext); } // for clean up list Long time = new Long(callerContext.getNextTimeWindow()); if (!callersMap.containsKey(time)) { callersMap.put(time, callerContext); } else { //if there are callersMap with same timewindow ,then use linkedList to hold those Object callerObject = callersMap.get(time); if (callerObject != null) { if (callerObject instanceof CallerContext) { LinkedList callersWithSameTimeStamp = new LinkedList(); callersWithSameTimeStamp.add(callerObject); callersWithSameTimeStamp.add(callerContext); callersMap.remove(time); callersMap.put(time, callersWithSameTimeStamp); } else if (callerObject instanceof LinkedList) { LinkedList callersWithSameTimeStamp = (LinkedList) callerObject; callersWithSameTimeStamp.add(callerContext); } } } //set Time Vs key keyToTimeStampMap.put(id, time); } /** * removing a caller with a given id - caller will remove from clean list * * @param id Caller ID */ public void removeCallerContext(String id) { if (id != null) { if(log.isDebugEnabled()) { log.debug("REMOVING CALLER CONTEXT WITH ID" + id); } removeCaller(id); } } /** * Helper method to remove a caller * * @param id The id of the caller */ private void removeCaller(String id) { Long time = (Long) keyToTimeStampMap.get(id); // if (time != null) { if (dataHolder != null && keyPrefix != null) { log.debug("Removing the caller with the configuration id " + id); dataHolder.removeCaller(keyPrefix + id); } if (time != null) { callersMap.remove(time); keyToTimeStampMap.remove(id); } } /** * /** * processing cleaning list- only process callerContexts which unit time already had over * * @param time - the current System Time * @throws ThrottleException */ public void processCleanList(long time) { if (debugOn) { log.debug("Cleaning up process is executing"); } if (time > nextCleanTime) { SortedMap map = ((ConcurrentNavigableMap) callersMap).headMap(new Long(time)); if (map != null && map.size() > 0) { for (Iterator it = map.values().iterator(); it.hasNext(); ) { Object o = it.next(); if (o != null) { if (o instanceof CallerContext) { // In the case nextAccessTime is unique CallerContext c = ((CallerContext) o); String key = c.getId(); String role = c.getRoleId(); if (key != null) { if (dataHolder != null && keyPrefix != null) { c = dataHolder.getCallerContext(keyPrefix + key); } if (c != null) { c.cleanUpCallers( this.throttleConfiguration.getCallerConfiguration(role) , this , time); } } } if (o instanceof LinkedList) { //In the case nextAccessTime of callers are same LinkedList callers = (LinkedList) o; for (Iterator ite = callers.iterator(); ite.hasNext(); ) { CallerContext c = (CallerContext) ite.next(); String key = c.getId(); String role = c.getRoleId(); if (key != null) { if (dataHolder != null && keyPrefix != null) { c = (CallerContext) dataHolder.getCallerContext(keyPrefix + key); } if (c != null) { c.cleanUpCallers( this.throttleConfiguration.getCallerConfiguration(role) , this , time); } } } } } } } nextCleanTime = time + ThrottleConstants.DEFAULT_THROTTLE_CLEAN_PERIOD; } } public void setThrottleId(String throttleId) { if (throttleId == null) { throw new IllegalArgumentException("The throttle id cannot be null"); } this.throttleId = throttleId; this.keyPrefix = ThrottleConstants.THROTTLE_PROPERTY_PREFIX + throttleId; } public String getThrottleId() { return this.throttleId; } public ConfigurationContext getConfigurationContext() { return this.configctx; } public void setConfigurationContext(ConfigurationContext configurationContext) { this.configctx = configurationContext; if (dataHolder == null) { initDataHolder(); } } private void initDataHolder() { if (configctx != null) { if (dataHolder == null) { dataHolder = (ThrottleDataHolder) configctx.getPropertyNonReplicable(ThrottleConstants.THROTTLE_INFO_KEY); synchronized (configctx) { if (dataHolder == null) { dataHolder = new ThrottleDataHolder(); configctx.setNonReplicableProperty(ThrottleConstants.THROTTLE_INFO_KEY, dataHolder); } } } } } /** * @return Returns the type of throttle ex : ip /domain */ public abstract int getType(); /** * To add the caller and replicates the states of the given caller * * @param callerContext The states of the caller * @param id The id of the caller */ public void addAndFlushCallerContext(CallerContext callerContext, String id) { if (callerContext != null && id != null) { addCaller(callerContext, id); replicateCaller(id); } } /** * To replicates the states of the already exist caller * * @param callerContext The states of the caller * @param id The id of the remote caller */ public void flushCallerContext(CallerContext callerContext, String id) { if (dataHolder != null && callerContext != null && id != null) { String key = keyPrefix + id; dataHolder.addCallerContext(key, callerContext); // have to do ,because we always gets // any property as non-replicable replicateCaller(id); } } /** * Removes the caller and replicate the states * * @param id The Id of the caller */ public void removeAndFlushCaller(String id) { if (id != null) { if(log.isDebugEnabled()) { log.debug("REMOVING AND FLUSHING CALLER CONTEXT WITH ID " + id); } removeCaller(id); replicateCaller(id); } } /** * Removes the caller and destroy shared params of caller * * @param id The Id of the caller */ public void removeAndDestroyShareParamsOfCaller(String id) { if (id != null) { if(log.isDebugEnabled()) { log.info("REMOVE AND DESTROY OF SHARED PARAM OF CALLER WITH ID " + id); } removeCaller(id); SharedParamManager.removeTimestamp(id); SharedParamManager.removeCounter(id); } } /** * Helper method to replicates states of the caller with given key * * @param id The id of the caller */ private void replicateCaller(String id) { if (configctx != null && keyPrefix != null) { try { if (debugOn) { log.debug("Going to replicate the states of the caller : " + id); } throttleReplicator.setConfigContext(configctx); throttleReplicator.add(keyPrefix + id); } catch (Exception clusteringFault) { log.error("Error during the replicating states ", clusteringFault); } } } /** * Replicate the time window of this caller * @param id */ public void replicateTimeWindow(String id) { if (configctx != null && keyPrefix != null) { try { if (debugOn) { log.debug("Going to replicate the time window states of the caller : " + id); } throttleWindowReplicator.setConfigContext(configctx); throttleWindowReplicator.add(keyPrefix + id); } catch (Exception e) { log.error("Error during the replicating window change ", e); } } } /** * This method will clean up callers which has next access time below from provided time * This will first check the prohibited period and then it will check next access time lesser than unit time before a * cleanup a caller * * @param time to clean up the caller contexts */ public void cleanupCallers(long time) { SortedMap map = ((ConcurrentNavigableMap) callersMap).headMap(new Long(time)); if (log.isDebugEnabled()) { log.debug("CallerMap Size before cleanup process : " + map.size()); } if (map != null && map.size() > 0) { for (Iterator it = map.values().iterator(); it.hasNext(); ) { Object o = it.next(); if (o != null) { if (o instanceof CallerContext) { // In the case nextAccessTime is unique CallerContext c = ((CallerContext) o); String key = c.getId(); String role = c.getRoleId(); if (key != null) { if (dataHolder != null && keyPrefix != null) { c = dataHolder.getCallerContext(keyPrefix + key); } if (c != null) { c.cleanUpCallers( this.throttleConfiguration.getCallerConfiguration(role), this, time); } } } if (o instanceof LinkedList) { //In the case nextAccessTime of callers are same LinkedList callers = (LinkedList) o; Iterator ite = callers.iterator(); while (ite.hasNext()) { CallerContext c = (CallerContext) ite.next(); String key = c.getId(); String role = c.getRoleId(); if (key != null) { if (dataHolder != null && keyPrefix != null) { c = (CallerContext) dataHolder.getCallerContext(keyPrefix + key); } if (c != null) { c.cleanUpCallers( this.throttleConfiguration.getCallerConfiguration(role), this, time); } } } } } } } if (log.isDebugEnabled()) { log.debug("CallerMap Size after cleanup process : " + map.size()); } } }