/** * Copyright (c) 2014, the Railo Company Ltd. * Copyright (c) 2015, Lucee Assosication Switzerland * * 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.runtime.config; import java.io.IOException; import java.net.URL; import java.util.Iterator; import java.util.List; import lucee.commons.io.IOUtil; import lucee.commons.io.SystemUtil; import lucee.commons.io.log.Log; import lucee.commons.io.res.Resource; import lucee.commons.io.res.filter.ExtensionResourceFilter; import lucee.commons.io.res.filter.ResourceFilter; import lucee.commons.io.res.util.ResourceUtil; import lucee.commons.lang.ExceptionUtil; import lucee.commons.lang.StringUtil; import lucee.commons.net.http.HTTPEngine; import lucee.commons.net.http.HTTPResponse; import lucee.commons.net.http.Header; import lucee.commons.net.http.httpclient.HeaderImpl; import lucee.runtime.exp.PageException; import lucee.runtime.extension.ExtensionDefintion; import lucee.runtime.extension.RHExtension; import lucee.runtime.extension.RHExtensionProvider; import lucee.runtime.functions.conversion.DeserializeJSON; import lucee.runtime.net.http.ReqRspUtil; import lucee.runtime.op.Caster; import lucee.runtime.type.Struct; import lucee.runtime.type.util.ArrayUtil; import lucee.runtime.type.util.KeyConstants; public class DeployHandler { private static final ResourceFilter ALL_EXT = new ExtensionResourceFilter(new String[]{".lex",".lar",".lco"}); //private static final ResourceFilter ARCHIVE_EXT = new ExtensionResourceFilter(new String[]{".ra",".ras"}); /** * deploys all files found * @param config */ public static void deploy(Config config){ if(!contextIsValid(config)) return; synchronized (config) { Resource dir = config.getDeployDirectory(); if(!dir.exists()) dir.mkdirs(); Resource[] children = dir.listResources(ALL_EXT); Resource child; String ext; for(int i=0;i<children.length;i++){ child=children[i]; try { // Lucee archives ext=ResourceUtil.getExtension(child, null); if("lar".equalsIgnoreCase(ext)) { //deployArchive(config,child,true); XMLConfigAdmin.updateArchive((ConfigImpl) config, child,true); } // Lucee Extensions else if("lex".equalsIgnoreCase(ext)) XMLConfigAdmin._updateRHExtension((ConfigImpl) config, child,true); // Lucee core else if(config instanceof ConfigServer && "lco".equalsIgnoreCase(ext)) XMLConfigAdmin.updateCore((ConfigServerImpl) config, child,true); } catch(Throwable t) { ExceptionUtil.rethrowIfNecessary(t); Log log = config.getLog("deploy"); log.error("Extension", t); } } } } private static boolean contextIsValid(Config config) { // this test is not very good but it works ConfigWeb[] webs; if(config instanceof ConfigWeb) webs =new ConfigWeb[]{((ConfigWeb)config)}; else webs=((ConfigServer)config).getConfigWebs(); for(int i=0;i<webs.length;i++){ try{ ReqRspUtil.getRootPath(webs[i].getServletContext()); } catch(Throwable t){ ExceptionUtil.rethrowIfNecessary(t); return false; } } return true; } public static void moveToFailedFolder(Resource deployDirectory,Resource res) { Resource dir = deployDirectory.getRealResource("failed-to-deploy"); Resource dst = dir.getRealResource(res.getName()); dir.mkdirs(); try { if(dst.exists()) dst.remove(true); ResourceUtil.moveTo(res, dst,true); } catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);} // TODO Auto-generated method stub } public static boolean deployExtensions(Config config, ExtensionDefintion[] eds, Log log) { boolean allSucessfull=true; if(!ArrayUtil.isEmpty(eds)) { ExtensionDefintion ed; for(int i=0;i<eds.length;i++){ ed=eds[i]; if(StringUtil.isEmpty(ed.getId(),true)) continue; if(!deployExtension(config, ed,log,i+1==eds.length)) allSucessfull=false; } } return allSucessfull; } /** * install a extension based on the given id and version * @param config * @param id the id of the extension * @param version pass null if you don't need a specific version * @return * @throws IOException * @throws PageException */ public static boolean deployExtension(Config config, ExtensionDefintion ed, Log log, boolean reload) { ConfigImpl ci=(ConfigImpl) config; // is the extension already installed try { if(XMLConfigAdmin.hasRHExtensions(ci, ed)!=null) return false; } catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);} // check if a local extension is matching our id Iterator<ExtensionDefintion> it = getLocalExtensions(config).iterator(); ExtensionDefintion ext=null,tmp; log.info("extension", "installing the extension "+ed); while(it.hasNext()){ tmp=it.next(); if(ed.equals(tmp)) { ext=tmp; break; } } // if we have one and also the defined version matches, there is no need to check online if(ext!=null && ed.getVersion()!=null) { try{ log.info("extension", "installing the extension "+ed+" from local provider"); Resource res = SystemUtil.getTempDirectory().getRealResource(ed.getId()+"-"+ed.getVersion()+".lex"); ResourceUtil.touch(res); IOUtil.copy(ext.getSource(), res); XMLConfigAdmin._updateRHExtension((ConfigImpl) config, res, reload); return true; } catch(Throwable t) { ExceptionUtil.rethrowIfNecessary(t); ext=null; t.printStackTrace(); } } String apiKey = config.getIdentification().getApiKey(); RHExtensionProvider[] providers = ci.getRHExtensionProviders(); URL url; // if we have a local version, we look if there is a newer remote version if(ext!=null) { String content; for(int i=0;i<providers.length;i++) { try{ url=providers[i].getURL(); StringBuilder qs=new StringBuilder(); qs.append("?withLogo=false"); if(ed.getVersion()!=null)qs.append("&version=").append(ed.getVersion()); if(apiKey!=null)qs.append("&ioid=").append(apiKey); url=new URL(url,"/rest/extension/provider/info/"+ed.getId()+qs); HTTPResponse rsp = HTTPEngine.get(url, null, null, -1, false, "UTF-8", "", null, new Header[]{new HeaderImpl("accept","application/json")}); if(rsp.getStatusCode()!=200) continue; content=rsp.getContentAsString(); Struct sct = Caster.toStruct(DeserializeJSON.call(null, content)); String remoteVersion=Caster.toString(sct.get(KeyConstants._version)); // the local version is as good as the remote if(remoteVersion!=null && remoteVersion.compareTo(ext.getVersion())<=0) { log.info("extension", "installing the extension "+ed+" from local provider"); // avoid that the exzension from provider get removed Resource res = SystemUtil.getTempDirectory().getRealResource(ed.getId()+"-"+ed.getVersion()+".lex"); ResourceUtil.touch(res); IOUtil.copy(ext.getSource(), res); XMLConfigAdmin._updateRHExtension((ConfigImpl) config, res, reload); return true; } } catch(Throwable t){ExceptionUtil.rethrowIfNecessary(t);} } } // if we have an ext at this stage this mean the remote providers was not acessible or have not this extension if(ext!=null) { try{ log.info("extension", "installing the extension "+ed+" from local provider"); Resource res = SystemUtil.getTempDirectory().getRealResource(ext.getSource().getName()); ResourceUtil.touch(res); IOUtil.copy(ext.getSource(), res); XMLConfigAdmin._updateRHExtension((ConfigImpl) config, res, reload); return true; } catch(Throwable t){ExceptionUtil.rethrowIfNecessary(t);} } // if not we try to download it log.info("extension", "installing the extension "+ed+" from remote extension provider"); Resource res = downloadExtension(ci, ed, log); if(res!=null) { try { XMLConfigAdmin._updateRHExtension((ConfigImpl) config, res,reload); return true; } catch(Throwable t){ ExceptionUtil.rethrowIfNecessary(t); log.error("extension", t); } } return false; } public static Resource downloadExtension(Config config, ExtensionDefintion ed, Log log){ String apiKey = config.getIdentification().getApiKey(); URL url; RHExtensionProvider[] providers = ((ConfigImpl)config).getRHExtensionProviders(); for(int i=0;i<providers.length;i++){ try{ url=providers[i].getURL(); StringBuilder qs=new StringBuilder(); addQueryParam(qs,"ioid",apiKey); addQueryParam(qs,"version",ed.getVersion()); url=new URL(url,"/rest/extension/provider/full/"+ed.getId()+qs); HTTPResponse rsp = HTTPEngine.get(url, null, null, -1, false, "UTF-8", "", null, new Header[]{new HeaderImpl("accept","application/cfml")}); if(rsp.getStatusCode()!=200) throw new IOException("failed to load extension: "+ed); // copy it locally Resource res = SystemUtil.getTempDirectory().getRealResource(ed.getId()+"-"+ed.getVersion()+".lex"); ResourceUtil.touch(res); IOUtil.copy(rsp.getContentAsStream(), res, true); return res; } catch(Throwable t) { ExceptionUtil.rethrowIfNecessary(t); if(log!=null)log.error("extension", t); } } return null; } private static void addQueryParam(StringBuilder qs, String name,String value) { if(StringUtil.isEmpty(value)) return; qs .append(qs.length()==0?"?":"&") .append(name) .append("=") .append(value); } public static Resource getExtension(Config config, ExtensionDefintion ed, Log log) { // local ExtensionDefintion ext = getLocalExtension(config, ed,null); if(ext!=null) { try { Resource src=ext.getSource(); if(src.exists()) { Resource res = SystemUtil.getTempDirectory().getRealResource(ed.getId()+"-"+ed.getVersion()+".lex"); ResourceUtil.touch(res); IOUtil.copy(ext.getSource(), res); return res; } } catch(Exception e){} } // remote return downloadExtension(config, ed, log); } public static ExtensionDefintion getLocalExtension(Config config, ExtensionDefintion ed, ExtensionDefintion defaultValue) { Iterator<ExtensionDefintion> it = getLocalExtensions(config).iterator(); ExtensionDefintion ext; while(it.hasNext()){ ext=it.next(); if(ed.equals(ext)) { return ext; } } return defaultValue; } public static List<ExtensionDefintion> getLocalExtensions(Config config) { return ((ConfigImpl)config).loadLocalExtensions(); } }