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.net;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.PrintWriter;
024import java.net.MalformedURLException;
025import java.net.URL;
026import java.util.ArrayList;
027import java.util.List;
028
029import lucee.commons.io.IOUtil;
030import lucee.commons.io.SystemUtil;
031import lucee.commons.io.res.Resource;
032import lucee.commons.io.res.util.ResourceClassLoader;
033import lucee.commons.io.res.util.ResourceUtil;
034import lucee.commons.lang.SystemOut;
035import lucee.loader.TP;
036import lucee.loader.engine.CFMLEngineFactory;
037import lucee.runtime.Info;
038import lucee.runtime.config.Config;
039import lucee.runtime.config.ConfigWeb;
040
041public class JarLoader {
042
043        public static final short WHEN_EXISTS_UPDATE=1;
044        public static final short WHEN_EXISTS_RETURN_JAR=2;
045        public static final short WHEN_EXISTS_THROW_EXP=4;
046        public static final short WHEN_EXISTS_RETURN_NULL=8;
047        
048        
049
050
051        /**
052         * this loads the given jars from update provider, copy it to lib directory (where lucee.jar is located) and load them to a ClassLoader 
053         * @param pc
054         * @param jars jars names to Load
055         * @return Classloader with loaded jars for temporary use, after restart the engine this jars are loaded by the servlet engine
056         * @throws IOException
057         */
058        public static ClassLoader loadJars(Config config, String[] jars,ClassLoader parent) throws IOException {
059                return new ResourceClassLoader(download(config, jars),parent);
060        }
061        
062
063        public static Resource[] download(Config config, String[] jars) throws IOException {
064                List<Resource> list=new ArrayList<Resource>();
065                Resource jar;
066                lastCheck=-1;
067                for(int i=0;i<jars.length;i++){
068                        jar=download(config, jars[i], WHEN_EXISTS_UPDATE);
069                        if(jar!=null) list.add(jar);
070                }
071                return list.toArray(new Resource[list.size()]);
072        }
073        
074        
075        private static Resource download(Config config,String jarName, short whenExists) throws IOException {
076        // some variables nned later
077                PrintWriter out = config.getOutWriter();
078                
079                URL dataUrl=toURL(config,jarName);
080        
081                // destination file
082                ClassLoader mainClassLoader = new TP().getClass().getClassLoader();
083                
084                Resource lib = config.getResource(CFMLEngineFactory.getClassLoaderRoot(mainClassLoader).getCanonicalPath());
085                        
086                Resource jar=lib.getRealResource(jarName);
087                SystemOut.printDate(out,"Check for jar at "+dataUrl);
088        if(jar.exists()){
089                        if(whenExists==WHEN_EXISTS_RETURN_JAR) return jar;
090                        else if(whenExists==WHEN_EXISTS_RETURN_NULL) return null;
091                        else if(whenExists==WHEN_EXISTS_UPDATE) {
092                                // compare local and remote
093                                long localLen=jar.length();
094                                long remoteLengh=HTTPUtil.length(dataUrl);
095                                // only update when size change more than 10
096                                if(localLen==remoteLengh){
097                                        SystemOut.printDate(out,"jar "+jar+" is up to date");
098                                        return jar;
099                                }
100                                //if(!jar.delete()) throw new IOException("cannot update jar ["+jar+"], jar is locked or write protected, stop the servlet engine and delete this jar manually."); 
101                        }
102                        else throw new IOException("jar ["+jar+"] exists already"); 
103                }
104                
105                Resource tmp = SystemUtil.getTempFile(".jar", false);
106                try {
107                //long len=HTTPUtil.length();
108                InputStream is = (InputStream)dataUrl.getContent();
109                // copy input stream to lib directory
110                IOUtil.copy(is, tmp,true);
111                jar.delete();
112                tmp.moveTo(jar);
113                }
114                finally{
115                        tmp.delete();
116                }
117        SystemOut.printDate(out,"created/updated jar  "+jar);
118        
119        return jar;
120    }
121
122        private static URL toURL(Config config, String jarName) throws MalformedURLException {
123                URL hostUrl=config.getUpdateLocation();
124        if(hostUrl==null)hostUrl=new URL("http://stable.lucee.org");
125        return new URL(hostUrl,"/lucee/remote/jars/"+(Info.getMajorVersion()+"."+Info.getMinorVersion())+"/"+jarName);
126        }
127
128
129        public static boolean exists(ConfigWeb config,String[] jarNames) {
130                for(int i=0;i<jarNames.length;i++){
131                        if(!exists(config, jarNames[i])) return false;
132                }
133                return true;
134        }
135        
136        /**
137         * check if one of given jar has changed or not exist
138         * @param config
139         * @param jarNames
140         * @return
141         */
142        private static boolean changed=false;
143    private static long lastCheck=-1;
144    public static boolean changed(ConfigWeb config,String[] jarNames) {
145                if((lastCheck+300000)<System.currentTimeMillis()) {
146                        changed=false;
147                for(int i=0;i<jarNames.length;i++){
148                                if(changed(config, jarNames[i])) {
149                                        changed=true;
150                                        break;
151                                }
152                        }
153                        lastCheck=System.currentTimeMillis();
154        }
155        return changed;
156        }
157        
158        private static boolean exists(ConfigWeb config,String jarName) {
159                Resource res = _toResource(config, jarName);
160        if(res==null) return false;
161        return res.exists();
162    }
163        
164        private static boolean changed(ConfigWeb config,String jarName) {
165        Resource res = _toResource(config, jarName);
166        if(res==null) {
167                return true;
168        }
169        
170        try {
171                        URL dataUrl = toURL(config,jarName);
172                        boolean changed=res.length()!=HTTPUtil.length(dataUrl);
173                        
174                        return changed;
175                } catch (IOException e) {
176                        return false;
177                }
178    }
179        
180        private static Resource _toResource(ConfigWeb config,String jarName) {
181        // destination file
182                ClassLoader mainClassLoader = new TP().getClass().getClassLoader();
183                try {
184                        Resource lib = ResourceUtil.toResourceNotExisting(config,CFMLEngineFactory.getClassLoaderRoot(mainClassLoader).getCanonicalPath());
185                        return lib.getRealResource(jarName);
186                } catch (IOException e) {
187                        return null;
188                }
189    }
190}