blob: 2870c8da9c3d95a1b8f4e7886c1b3b6d9ee09175 [file] [log] [blame]
/*
* 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.myfaces.extensions.scripting.monitor;
import org.apache.myfaces.extensions.scripting.core.dependencyScan.api.DependencyRegistry;
import org.apache.myfaces.extensions.scripting.core.dependencyScan.registry.MasterDependencyRegistry;
import org.apache.myfaces.extensions.scripting.core.util.WeavingContext;
import javax.faces.context.FacesContext;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* <p/>
* a context which holds information regarding the refresh cycle
* which can be picked up by the request filter for refreshing strategies
* <p/>
* That way we can avoid a separate session filter and a push system
* we use a pull system instead
*
* @author Werner Punz (latest modification by $Author$)
* @version $Revision$ $Date$
*/
public class RefreshContext {
/**
* this is a timed marker which is
* a point in time the last bean refresh was issued
* every request has to dump its personal scoped
* (aka session, or custom) scoped beans
* if the point in time is newer than the personal refresh time
* application scoped beans are refreshed at the first refresh cycle
* by the calling request issuing the compile
*/
private volatile long _personalScopedBeanRefresh = -1l;
/**
* the bean synchronisation has to be dealt with
* differently, we have two volatile points in the lifecycle
* one being the compile the other one the bean refresh
* the refresh can only happen outside of a compile cycle
* and also a global refresh has to be atomic and no other
* refreshes should happen
*/
public final static Boolean BEAN_SYNC_MONITOR = new Boolean(true);
/**
* second synchronisation monitor
* all other artifacts can only be refreshed outside of a
* compile cycle otherwise the classloader would get
* half finished compile states to load
*/
public final static Boolean COMPILE_SYNC_MONITOR = new Boolean(true);
private volatile AtomicInteger _currentlyRunningRequests = null;
private MasterDependencyRegistry _dependencyRegistry = new MasterDependencyRegistry();
/**
* we keep a 10 minutes timeout period to keep the performance in place
*/
private volatile long _taintLogTimeout = 10 * 60 * 1000;
/**
* This is a log which keeps track of the taints
* over time, we need that mostly for bean refreshes
* in multiuser surroundings, because only tainted beans need
* to be refreshed.
* Now if a user misses multiple updates he has to get a full
* set of changed classes to be able to drop all personal scoped beans tainted
* since the he refreshed last! Hence we have to move away from our
* two dimensional &lt;class, taint&gt; to a three dimensional &lt;class, taint, time&gt;
* view of things
*/
private List<TaintingHistoryEntry> _taintLog = Collections.synchronizedList(new LinkedList<TaintingHistoryEntry>());
/**
* the daemon thread which marks the scripting classes
* depending on the state, changed => tainted == true, not changed
* tainted == false!
*/
volatile ResourceMonitor _daemon = null;
/**
* internal class used by our own history log
*/
static class TaintingHistoryEntry {
long _timestamp;
WatchedResource _data;
public TaintingHistoryEntry(WatchedResource data) {
_data = data.getClone();
_timestamp = System.currentTimeMillis();
}
public long getTimestamp() {
return _timestamp;
}
public WatchedResource getData() {
return _data;
}
}
/**
* adds a new entry into our taint log
* which allows us to access tainting data
* from a given point in time
*
* @param data the tainting data to be added
*/
public void addTaintLogEntry(WatchedResource data) {
_taintLog.add(new TaintingHistoryEntry(data));
}
/**
* garbage collects our tainting data
* and removes all entries which are not
* present anymore due to timeout
* this gc code is called asynchronously
* from our tainting thread to keep the
* performance intact
*/
public void gcTaintLog() {
long timeoutTimestamp = System.currentTimeMillis() - _taintLogTimeout;
Iterator<TaintingHistoryEntry> it = _taintLog.iterator();
while (it.hasNext()) {
TaintingHistoryEntry entry = it.next();
if (entry.getTimestamp() < timeoutTimestamp) {
it.remove();
}
}
}
/**
* returns the last noOfEntries entries in the taint history
*
* @param noOfEntries the number of entries to be delivered
* @return a collection of the last &lt;noOfEntries&gt; entries
*/
public Collection<WatchedResource> getLastTainted(int noOfEntries) {
Iterator<TaintingHistoryEntry> it = _taintLog.subList(Math.max(_taintLog.size() - noOfEntries, 0), _taintLog.size()).iterator();
List<WatchedResource> retVal = new LinkedList<WatchedResource>();
while (it.hasNext()) {
TaintingHistoryEntry entry = it.next();
retVal.add(entry.getData());
}
return retVal;
}
/**
* Returns a set of tainting data from a given point in time up until now
*
* @param timestamp the point in time from which the tainting data has to be derived from
* @return a set of entries which are a union of all points in time beginning from timestamp
*/
public Collection<WatchedResource> getTaintHistory(long timestamp) {
List<WatchedResource> retVal = new LinkedList<WatchedResource>();
Iterator<TaintingHistoryEntry> it = _taintLog.iterator();
while (it.hasNext()) {
TaintingHistoryEntry entry = it.next();
if (entry.getTimestamp() >= timestamp) {
retVal.add(entry.getData());
}
}
return retVal;
}
/**
* Returns a set of tainted classes from a given point in time up until now
*
* @param timestamp the point in time from which the tainting data has to be derived from
* @return a set of classnames which are a union of all points in time beginning from timestamp
*/
public Set<String> getTaintHistoryClasses(long timestamp) {
Set<String> retVal = new HashSet<String>();
Iterator<TaintingHistoryEntry> it = _taintLog.iterator();
while (it.hasNext()) {
TaintingHistoryEntry entry = it.next();
if (entry.getTimestamp() >= timestamp) {
if(entry.getData() instanceof ClassResource) {
retVal.add(((ClassResource)entry.getData()).getAClass().getName());
} else {
retVal.add(entry.getData().getFile().getAbsolutePath());
}
}
}
return retVal;
}
/**
* returns the last global personal scoped bean refresh point in time
*
* @return a long value showing which personal bean refresh was the last in time
*/
public long getPersonalScopedBeanRefresh() {
return _personalScopedBeanRefresh;
}
/**
* setter for the global personal scope bean refresh
*
* @param personalScopedBeanRefresh
*/
public void setPersonalScopedBeanRefresh(long personalScopedBeanRefresh) {
this._personalScopedBeanRefresh = personalScopedBeanRefresh;
}
/**
* checks whether it would make sense at the current point
* in time to enforce a recompile or not
*
* @param scriptingEngine
* @return
*/
public boolean isRecompileRecommended(int scriptingEngine) {
Boolean recommended = _daemon.getSystemRecompileMap().get(scriptingEngine);
return recommended == null || recommended.booleanValue();
}
public void setRecompileRecommended(int scriptingEngine, boolean recompileRecommended) {
_daemon.getSystemRecompileMap().put(scriptingEngine, recompileRecommended);
}
public DependencyRegistry getDependencyRegistry(int scriptingEngine) {
return _dependencyRegistry.getSubregistry(scriptingEngine);
}
public void setDependencyRegistry(int scriptingEngine, DependencyRegistry registry) {
_dependencyRegistry.addSubregistry(scriptingEngine, registry);
}
public boolean isDependencyScanned(int scriptingEngine) {
try {
FacesContext ctx = FacesContext.getCurrentInstance();
if (ctx == null) {
return false;
}
Map<String, Object> requestMap = (Map<String, Object>) WeavingContext.getRequestMap();
Boolean retVal = (Boolean) requestMap.get("isDependencyScanned_" + scriptingEngine);
return (retVal == null) ? false : retVal;
} catch (UnsupportedOperationException ex) {
//still in startup
return false;
}
}
public void setDependencyScanned(int scriptingEngine, Boolean val) {
try {
FacesContext ctx = FacesContext.getCurrentInstance();
if (ctx == null) {
return;
}
Map<String, Object> requestMap = (Map<String, Object>) WeavingContext.getRequestMap();
requestMap.put("isDependencyScanned_" + scriptingEngine, val);
} catch (UnsupportedOperationException ex) {
//still in startup
}
}
public ResourceMonitor getDaemon() {
return _daemon;
}
public void setDaemon(ResourceMonitor daemon) {
this._daemon = daemon;
}
/**
* @return true if a compile currently is in progress
*/
public static boolean isCompileInProgress(int engineType) {
//TODO implement this
return false;
}
/**
* returns whether a recompile now at the current point
* in time for this engine is allowed or not
* This state depends on the state of the application
* if non locked compiles is enabled it always will return true
* <p/>
* if a synchronized locking compile is enabled
* it will return true if the calling request is the only
* one currently issued because no request is allowed to compile
* until others have run out
*
* @param engineType
* @return
*/
public boolean isComileAllowed(int engineType) {
return getCurrentlyRunningRequests().get() == 1;
}
/**
* getter for our request counter
* we need this variable to keep a lock
* on the number of requests
* we only can compile if the currently
* running request is the only one currently
* active, to keep the compilation results in sync
*
* @return the request counter holder which is an atomic integer
* <p/>
* probably deprecred
*/
public AtomicInteger getCurrentlyRunningRequests() {
return _currentlyRunningRequests;
}
/**
* setter for our currently running requests
*
* @param currentlyRunning the number of currently running requests
*/
public void setCurrentlyRunningRequests(AtomicInteger currentlyRunning) {
_currentlyRunningRequests = currentlyRunning;
}
/**
* checks outside of the request
* scope for changes and taints
* the corresponding engine
*/
public static void scanAndMarkChange() {
WeavingContext.getWeaver();
}
/**
* Returns our dependency registry
*
* @return the Master Dependency registry holding all subregistries
*/
public MasterDependencyRegistry getDependencyRegistry() {
return _dependencyRegistry;
}
/**
* Sets our master dependency registry
*
* @param dependencyRegistry the master dependency registry to be set
*/
public void setDependencyRegistry(MasterDependencyRegistry dependencyRegistry) {
_dependencyRegistry = dependencyRegistry;
}
/**
* getter for the taintlog timeout
*
* @return the taintlog timeout
*/
public long getTaintLogTimeout() {
return _taintLogTimeout;
}
/**
* setter for the taintlog timeout
*
* @param taintLogTimeout a new timeout for the taintlog
*/
public void setTaintLogTimeout(long taintLogTimeout) {
_taintLogTimeout = taintLogTimeout;
}
}