/**
*
* Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
*
* This library 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 2.1 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
**/
package lucee.commons.io.res.util;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import lucee.commons.io.res.Resource;
import lucee.commons.io.res.ResourceLock;
import lucee.commons.lang.SerializableObject;
import lucee.commons.lang.SystemOut;
import lucee.runtime.CFMLFactory;
import lucee.runtime.CFMLFactoryImpl;
import lucee.runtime.PageContextImpl;
import lucee.runtime.config.Config;
import lucee.runtime.config.ConfigWeb;
import lucee.runtime.engine.ThreadLocalPageContext;
import lucee.runtime.net.http.ReqRspUtil;
public final class ResourceLockImpl implements ResourceLock {
private static final long serialVersionUID = 6888529579290798651L;
private long lockTimeout;
private boolean caseSensitive;
public ResourceLockImpl(long timeout,boolean caseSensitive) {
this.lockTimeout=timeout;
this.caseSensitive=caseSensitive;
}
private Object token=new SerializableObject();
private Map<String,Thread> resources=new HashMap<String,Thread>();
@Override
public void lock(Resource res) {
String path=getPath(res);
synchronized(token) {
_read(path);
resources.put(path,Thread.currentThread());
}
}
private String getPath(Resource res) {
return caseSensitive?res.getPath():res.getPath().toLowerCase();
}
@Override
public void unlock(Resource res) {
String path=getPath(res);
//if(path.endsWith(".dmg"))print.err("unlock:"+path);
synchronized(token) {
resources.remove(path);
token.notifyAll();
}
}
@Override
public void read(Resource res) {
String path=getPath(res);
synchronized(token) {
//print.ln(".......");
_read(path);
}
}
private void _read(String path) {
long start=-1,now;
Thread t;
do {
if((t=resources.get(path))==null) {
//print.ln("read ok");
return;
}
if(t==Thread.currentThread()) {
//aprint.err(path);
Config config = ThreadLocalPageContext.getConfig();
if(config!=null)
SystemOut.printDate(config.getErrWriter(),"conflict in same thread: on "+path);
//SystemOut.printDate(config.getErrWriter(),"conflict in same thread: on "+path+"\nStacktrace:\n"+StringUtil.replace(ExceptionUtil.getStacktrace(new Throwable(), false),"java.lang.Throwable\n","",true));
return;
}
// bugfix when lock von totem thread, wird es ignoriert
if(!t.isAlive()) {
resources.remove(path);
return;
}
if(start==-1)start=System.currentTimeMillis();
try {
token.wait(lockTimeout);
now=System.currentTimeMillis();
if((start+lockTimeout)<=now) {
Config config = ThreadLocalPageContext.getConfig();
if(config!=null) {
PageContextImpl pc=null;
String add="";
if(config instanceof ConfigWeb) {
CFMLFactory factory = ((ConfigWeb)config).getFactory();
if(factory instanceof CFMLFactoryImpl) {
Map<Integer, PageContextImpl> pcs = ((CFMLFactoryImpl)factory).getActivePageContexts();
Iterator<PageContextImpl> it = pcs.values().iterator();
PageContextImpl tmp;
while(it.hasNext()) {
tmp=it.next();
if(t==tmp.getThread()) {
pc=tmp;
break;
}
}
}
}
if(pc!=null) {
add=" The file is locked by a request on the following URL "+ReqRspUtil.getRequestURL(pc.getHttpServletRequest(),true)+", that request started "+(System.currentTimeMillis()-pc.getStartTime())+"ms ago.";
}
SystemOut.printDate(config.getErrWriter(),
"timeout after "+(now-start)+" ms ("+(lockTimeout)+" ms) occured while accessing file ["+path+"]."+add);
}
else
SystemOut.printDate("timeout ("+(lockTimeout)+" ms) occured while accessing file ["+path+"].");
return;
}
}
catch (InterruptedException e) {
}
}
while(true);
}
@Override
public long getLockTimeout() {
return lockTimeout;
}
/**
* @param lockTimeout the lockTimeout to set
*/
@Override
public void setLockTimeout(long lockTimeout) {
this.lockTimeout = lockTimeout;
}
/**
* @param caseSensitive the caseSensitive to set
*/
public void setCaseSensitive(boolean caseSensitive) {
this.caseSensitive = caseSensitive;
}
}