/** * $Id: $ * $Date: $ * */ package org.xmlsh.sh.module; import static org.xmlsh.util.UnifiedFileAttributes.MatchFlag.DIRECTORIES; import static org.xmlsh.util.UnifiedFileAttributes.MatchFlag.READABLE; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collector; import java.util.stream.Collectors; import net.sf.saxon.s9api.QName; import net.sf.saxon.s9api.XdmNode; import org.xmlsh.core.CoreException; import org.xmlsh.core.InvalidArgumentException; import org.xmlsh.core.ScriptCommand; import org.xmlsh.core.ScriptSource; import org.xmlsh.core.SearchPath; import org.xmlsh.core.XClassLoader; import org.xmlsh.core.XValue; import org.xmlsh.core.ScriptCommand.SourceMode; import org.xmlsh.sh.shell.Shell; import org.xmlsh.sh.shell.ShellConstants; import org.xmlsh.util.FileUtils; import org.xmlsh.util.PathMatchOptions; import org.xmlsh.util.StreamUtils; import org.xmlsh.util.Util; public class ExternalModule extends PackageModule { /* * Constructor for external modules nameuri can either be a name found in * XMODPATH or a full URI/filename of the module.xml file (must end in * ".xml") */ private static PathMatchOptions matchDirectory = new PathMatchOptions().withFlagsMatching(DIRECTORIES,READABLE); private String mURI; protected ExternalModule(ModuleConfig config , XClassLoader loader ) throws CoreException { super(config , loader ); } public static ModuleConfig getConfiguration(Shell shell, String nameuri, List<URL> at) throws CoreException { mLogger.entry(shell, nameuri, at); try { URL configURL; File modDir = null; if(nameuri.endsWith(".xml") ) { configURL = new URL( nameuri); if(configURL.getProtocol().equals("file")) modDir = shell.getExplicitFile(configURL.getPath(),true).getParentFile(); } else { SearchPath path = shell.getModulePath(); modDir = path.getFirstFileInPath(shell, nameuri, matchDirectory ); if(modDir == null) throw new InvalidArgumentException("Cannot find module directory for : " + nameuri); File config = new File(modDir, "module.xml"); if(!config.exists()) throw new InvalidArgumentException("Cannot find module.xml in directory : " + modDir.getAbsolutePath()); configURL = config.toURI().toURL(); } URL modRoot = modDir.toURI().toURL(); XdmNode configNode; configNode = Util.asXdmNode(configURL); XValue xv = XValue.newXValue(configNode); List<String> packages = xv.xpath(shell, "/module/(@package|packages/package/(.|@url))/string()").asStringList().stream() .collect(Collectors.toList() ); final File md = modDir; List<URL> modpath = xv.xpath(shell, "/module/modpath/directory/(.|@url)/string()").asStringList().stream() .filter( Util::notBlank) .map( s -> safeNewUrl(configURL, s) ) .filter( Objects::nonNull ) .collect(Collectors.toList() ); String name = xv.xpath(shell, "/module/@name/string()").toString(); String require = xv.xpath(shell, "/module/@require/string()").toString(); if(!Util.isBlank(require)) { if( ! shell.requireVersion(name, require) ) throw new InvalidArgumentException("Module " + name + " requires version " + require); } List<URL> classpath = xv.xpath(shell, "/module/classpath/(@file|file)/string()").asStringList().stream() .filter( Util::notBlank ) .map( s -> safeNewUrl(configURL, s) ) .filter( Objects::nonNull ) .collect(Collectors.toList() ); mLogger.debug("modDir: {} file classpaths: ", modDir , classpath ); if(modDir != null){ xv.xpath(shell, "/module/classpath/directory/(.|@url)/string()").asStringList().stream() .filter( Util::notBlank ) .forEach( s -> { mLogger.debug("directory: {}", s ); try { File sFile = new File( md , s ); if( sFile.isDirectory() ){ listJarFiles(sFile) .forEach( u -> classpath.add(u)) ; } } catch (IOException e){ mLogger.catching( e ); } } ); } String modClassName = xv.xpath(shell, "/module/main/classname/string()").toString(); String modScriptName = xv.xpath(shell, "/module/main/scriptname/string()").toString(); ModuleConfig config = new ModuleConfig("external", name , modClassName, modRoot , classpath, modpath , shell.getSerializeOpts() , packages, "commands.xml" ); if( ! Util.isBlank(modScriptName)) config.setModuleScriptName(modScriptName); return mLogger.exit(config); } catch (CoreException e) { throw e; } catch (Exception e) { throw new CoreException(e); } } private static URL safeNewUrl(URL baseUrl, String s) { mLogger.entry( baseUrl , s ); try { return mLogger.exit(new URL( baseUrl , s )); } catch (MalformedURLException e) { mLogger.catching(e); return null; } } private static URL safeNewUrl( java.nio.file.Path p) { mLogger.entry( p ); try { return mLogger.exit( p.toUri().toURL() ); } catch (MalformedURLException e) { mLogger.catching(e); return null; } } public URL getHelpURL() { return getClassLoader().getResource(toResourceName(getPackageConfig().getHelpURI(), getPackages().get(0))); } @Override public String describe() { return getName() + "[ at " + mURI + " ]"; } private static List<URL> listJarFiles(File dir) throws IOException { mLogger.entry( dir ); return Files.list( dir.toPath() ) .filter( (p) -> p.toString().endsWith(".jar")) .map( (p) -> safeNewUrl(p) ) .filter( Objects::nonNull) .collect(Collectors.toList() ); } @Override public void onInit(Shell shell, List<XValue> args) throws Exception { super.onInit(shell, args); ScriptSource script = getConfig().getModuleScript(); // Auto run main script if any if( script != null ) { mLogger.debug("Running onInit module script {} " , script ); try ( Shell sh = shell.clone() ) { if( args != null ) sh.setArgs(args); Module hThis = this ; ScriptCommand cmd = new ScriptCommand( // Holds a refernce to module within cmd getConfig().getModuleScript() , SourceMode.IMPORT, shell.getLocation() , hThis ) ; if( cmd.run(sh, getName(), args) != 0 ) shell.printErr("Failed to init script:" + getName() ); else { // Extracts a clone of the this modules shell context mStaticContext = sh.getExportedContext(); } } } } @Override public void onLoad(Shell shell) { super.onLoad(shell); mLogger.entry(shell); String scriptName = getConfig().getModuleScriptName(); if( Util.notBlank(scriptName ) ){ mLogger.trace("Locating init script {}", scriptName); try { // Try to find script source by usual means URL moduleRoot = getConfig().getModuleRoot(); ScriptSource script = CommandFactory.getScriptSource(shell, new PName(scriptName) , SourceMode.IMPORT , Collections.singletonList( moduleRoot) ); if( script != null ){ getConfig().setModuleScript(script); mLogger.debug("Loaded module script from {}" , moduleRoot); } } catch( IOException | CoreException | URISyntaxException e ){ mLogger.catching(e); } } } } /* * Copyright (C) 2008-2012 David A. Lee. * * The contents of this file are subject to the "Simplified BSD License" (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.opensource.org/licenses/bsd-license.php * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. * See the License for the specific language governing rights and limitations under the License. * * The Original Code is: all this file. * * The Initial Developer of the Original Code is David A. Lee * * Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved. * * Contributor(s): David A. Lee * */