001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.commons.io.res.util;
020
021import java.util.HashMap;
022import java.util.Map;
023
024import lucee.commons.io.res.Resource;
025import lucee.commons.io.res.ResourceLock;
026import lucee.commons.lang.SerializableObject;
027import lucee.commons.lang.SystemOut;
028import lucee.runtime.config.Config;
029import lucee.runtime.engine.ThreadLocalPageContext;
030
031public final class ResourceLockImpl implements ResourceLock {
032        
033        private static final long serialVersionUID = 6888529579290798651L;
034        
035        private long lockTimeout;
036        private boolean caseSensitive;
037
038        public ResourceLockImpl(long timeout,boolean caseSensitive) {
039                this.lockTimeout=timeout;
040                this.caseSensitive=caseSensitive;
041        }
042
043        private Object token=new SerializableObject();
044        private Map<String,Thread> resources=new HashMap<String,Thread>();
045        
046        @Override
047        public void lock(Resource res) {
048                String path=getPath(res);
049                
050                synchronized(token)  {
051                        _read(path);
052                        resources.put(path,Thread.currentThread());
053                }
054        }
055
056        private String getPath(Resource res) {
057                return caseSensitive?res.getPath():res.getPath().toLowerCase();
058        }
059
060        @Override
061        public void unlock(Resource res) {
062                String path=getPath(res);
063                //if(path.endsWith(".dmg"))print.err("unlock:"+path);
064                synchronized(token)  {
065                        resources.remove(path);
066                        token.notifyAll();
067                }
068        }
069
070        @Override
071        public void read(Resource res) {
072                String path=getPath(res);
073                synchronized(token)  {
074                        //print.ln(".......");
075                        _read(path);
076                }
077        }
078
079        private void _read(String path) {
080                long start=-1,now;
081                Thread t;
082                do {
083                        if((t=resources.get(path))==null) {
084                                //print.ln("read ok");
085                                return;
086                        }
087                        if(t==Thread.currentThread()) {
088                                //aprint.err(path);
089                                Config config = ThreadLocalPageContext.getConfig();
090                                if(config!=null)
091                                        SystemOut.printDate(config.getErrWriter(),"conflict in same thread: on "+path);
092                                //SystemOut.printDate(config.getErrWriter(),"conflict in same thread: on "+path+"\nStacktrace:\n"+StringUtil.replace(ExceptionUtil.getStacktrace(new Throwable(), false),"java.lang.Throwable\n","",true));
093                                return;
094                        }
095                        // bugfix when lock von totem thread, wird es ignoriert
096                        if(!t.isAlive()) {
097                                resources.remove(path);
098                                return;
099                        }
100                        if(start==-1)start=System.currentTimeMillis();
101                        try {
102                                token.wait(lockTimeout);
103                                now=System.currentTimeMillis();
104                                if((start+lockTimeout)<=now) {
105                                        Config config = ThreadLocalPageContext.getConfig();
106                                        if(config!=null)
107                                                SystemOut.printDate(config.getErrWriter(),"timeout after "+(now-start)+" ms ("+(lockTimeout)+" ms) occured while accessing file ["+path+"]");
108                                        else 
109                                                SystemOut.printDate("timeout ("+(lockTimeout)+" ms) occured while accessing file ["+path+"]");
110                                        return;
111                                }
112                        } 
113                        catch (InterruptedException e) {
114                        }
115                }
116                while(true);
117        }
118
119        @Override
120        public long getLockTimeout() {
121                return lockTimeout;
122        }
123
124        /**
125         * @param lockTimeout the lockTimeout to set
126         */
127        public void setLockTimeout(long lockTimeout) {
128                this.lockTimeout = lockTimeout;
129        }
130
131        /**
132         * @param caseSensitive the caseSensitive to set
133         */
134        public void setCaseSensitive(boolean caseSensitive) {
135                this.caseSensitive = caseSensitive;
136        }
137}