| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.jena.util; |
| |
| import java.io.* ; |
| import java.util.* ; |
| |
| import org.apache.jena.atlas.logging.FmtLog; |
| import org.apache.jena.rdf.model.Model ; |
| import org.apache.jena.rdf.model.ModelFactory ; |
| import org.apache.jena.shared.JenaException ; |
| import org.apache.jena.shared.NotFoundException ; |
| import org.apache.jena.shared.WrappedIOException ; |
| import org.apache.jena.sys.JenaSystem ; |
| import org.slf4j.Logger ; |
| import org.slf4j.LoggerFactory ; |
| |
| /** FileManager |
| * |
| * A FileManager provides access to named file-like resources by opening |
| * InputStreams to things in the filing system, by URL (http: and file:) and |
| * found by the classloader. It can also load RDF data from such a system |
| * resource into an existing model or create a new (Memory-based) model. |
| * There is a global FileManager which provide uniform access to system |
| * resources: applications may also create specialised FileManagers. |
| * |
| * A FileManager contains a list of location functions to try: the global |
| * FileManger has one {@link LocatorFile}, one {@link LocatorClassLoader} and |
| * one {@link LocatorURL} |
| * |
| * Main operations: |
| * * <ul> |
| * <li>loadModel, readModel : URI to model</li> |
| * <li>open, openNoMap : URI to input stream</li> |
| * <li>mapURI : map URI to another by {@link LocationMapper}</li> |
| * </ul> |
| * |
| * Utilities: |
| * <ul> |
| * <li>readWholeFileAsUTF8</li> |
| * <li>optional caching of models<li> |
| * </ul> |
| * |
| * A FileManager works in conjunction with a LocationMapper. |
| * A {@link LocationMapper} is a set of alternative locations for system |
| * resources and a set of alternative prefix locations. For example, a local |
| * copy of a common RDF dataset may be used whenever the usual URL is used by |
| * the application. |
| * |
| * The {@link LocatorFile} also supports the idea of "current directory". |
| * |
| * @see LocationMapper |
| * @see FileUtils |
| */ |
| |
| public class FileManager |
| { |
| // The case of the FileManager used first. |
| private static Logger log = LoggerFactory.getLogger(FileManager.class) ; |
| |
| /** Delimiter between path entries : because URI scheme names use : we only allow ; */ |
| public static final String PATH_DELIMITER = ";"; |
| public static final String filePathSeparator = java.io.File.separator ; |
| |
| static FileManager fmInstance = null ; |
| |
| static boolean logAllLookups = true ; |
| protected List<Locator> fmHandlers = new ArrayList<>() ; |
| protected LocationMapper fmMapper = null ; |
| |
| static { JenaSystem.init(); } |
| |
| /** Get the global file manager. |
| * @return the global file manager |
| */ |
| public static FileManager get() |
| { |
| // Singleton pattern adopted in case we later have several file managers. |
| if ( fmInstance == null ) |
| fmInstance = makeGlobal() ; |
| return fmInstance ; |
| } |
| |
| /** Set the global file manager (as returned by get()) |
| * If called before any call to get(), then the usual default filemanager is not created |
| * @param globalFileManager |
| */ |
| public static void setGlobalFileManager(FileManager globalFileManager) |
| { |
| fmInstance = globalFileManager ; |
| } |
| |
| /** Create an uninitialized FileManager */ |
| public FileManager() {} |
| |
| @Override |
| public FileManager clone() { return clone(this) ; } |
| |
| // Isolate to help avoid copy errors. |
| private static FileManager clone(FileManager filemanager) { |
| FileManager newFm = new FileManager() ; |
| newFm.fmHandlers.addAll(filemanager.fmHandlers) ; |
| newFm.fmMapper = null ; |
| if ( filemanager.getLocationMapper() != null ) |
| newFm.fmMapper = new LocationMapper(filemanager.getLocationMapper()) ; |
| newFm.cacheModelLoads = false ; |
| newFm.modelCache = null ; |
| return newFm ; |
| } |
| |
| /** Create a "standard" FileManager. */ |
| public static FileManager makeGlobal() |
| { |
| FileManager fMgr = new FileManager(LocationMapper.get()) ; |
| setStdLocators(fMgr) ; |
| return fMgr ; |
| } |
| |
| /** Force a file handler to have the default configuration. */ |
| public static void setStdLocators(FileManager fMgr) |
| { |
| fMgr.fmHandlers.clear() ; |
| fMgr.addLocatorFile() ; |
| fMgr.addLocatorURL() ; |
| fMgr.addLocatorClassLoader(fMgr.getClass().getClassLoader()) ; |
| } |
| /** Create with the given location mapper */ |
| public FileManager(LocationMapper _mapper) { setLocationMapper(_mapper) ; } |
| |
| /** Set the location mapping */ |
| public void setLocationMapper(LocationMapper _mapper) { fmMapper = _mapper ; } |
| |
| /** Get the location mapping */ |
| public LocationMapper getLocationMapper() { return fmMapper ; } |
| |
| /** Return an iterator over all the handlers */ |
| public Iterator<Locator> locators() { return fmHandlers.listIterator() ; } |
| |
| /** Add a locator to the end of the locators list */ |
| public void addLocator(Locator loc) |
| { |
| log.debug("Add location: "+loc.getName()) ; |
| fmHandlers.add(loc) ; } |
| |
| /** Add a file locator */ |
| public void addLocatorFile() { addLocatorFile(null) ; } |
| |
| /** Add a file locator which uses dir as its working directory */ |
| public void addLocatorFile(String dir) |
| { |
| LocatorFile fLoc = new LocatorFile(dir) ; |
| addLocator(fLoc) ; |
| } |
| |
| /** Add a class loader locator */ |
| public void addLocatorClassLoader(ClassLoader cLoad) |
| { |
| LocatorClassLoader cLoc = new LocatorClassLoader(cLoad) ; |
| addLocator(cLoc) ; |
| } |
| |
| /** Add a URL locator */ |
| public void addLocatorURL() |
| { |
| Locator loc = new LocatorURL() ; |
| addLocator(loc) ; |
| } |
| |
| /** Add a zip file locator */ |
| public void addLocatorZip(String zfn) |
| { |
| Locator loc = new LocatorZip(zfn) ; |
| addLocator(loc) ; |
| } |
| |
| |
| /** Remove a locator */ |
| public void remove(Locator loc) { fmHandlers.remove(loc) ; } |
| |
| // -------- Cache operations |
| boolean cacheModelLoads = false ; |
| Map<String, Model> modelCache = null ; |
| |
| /** Reset the model cache */ |
| public void resetCache() |
| { |
| if ( modelCache != null ) |
| modelCache.clear() ; |
| } |
| |
| /** Change the state of model cache : does not clear the cache */ |
| public void setModelCaching(boolean state) |
| { |
| cacheModelLoads = state ; |
| if ( cacheModelLoads && modelCache == null ) |
| modelCache = new HashMap<String, Model>() ; |
| } |
| |
| /** return whether caching is on of off */ |
| public boolean isCachingModels() { return cacheModelLoads ; } |
| |
| /** Read out of the cache - return null if not in the cache */ |
| public Model getFromCache(String filenameOrURI) |
| { |
| if ( ! isCachingModels() ) |
| return null; |
| return modelCache.get(filenameOrURI) ; |
| } |
| |
| public boolean hasCachedModel(String filenameOrURI) |
| { |
| if ( ! isCachingModels() ) |
| return false ; |
| return modelCache.containsKey(filenameOrURI) ; |
| } |
| |
| public void addCacheModel(String uri, Model m) |
| { |
| if ( isCachingModels() ) |
| modelCache.put(uri, m) ; |
| } |
| |
| public void removeCacheModel(String uri) |
| { |
| if ( isCachingModels() ) |
| modelCache.remove(uri) ; |
| } |
| |
| // -------- Cache operations (end) |
| |
| /** Load a model from a file (local or remote). |
| * This operation may attempt content negotiation for http URLs. |
| * @param filenameOrURI The filename or a URI (file:, http:) |
| * @return a new model |
| * @exception JenaException if there is syntax error in file. |
| */ |
| |
| public Model loadModel(String filenameOrURI) |
| { |
| if ( log.isDebugEnabled() ) |
| log.debug("loadModel("+filenameOrURI+")") ; |
| |
| return loadModelWorker(filenameOrURI, null, null) ; |
| } |
| |
| /** Load a model from a file (local or remote). |
| * URI is the base for reading the model. |
| * |
| * @param filenameOrURI The filename or a URI (file:, http:) |
| * @param rdfSyntax RDF Serialization syntax. |
| * @return a new model |
| * @exception JenaException if there is syntax error in file. |
| */ |
| |
| public Model loadModel(String filenameOrURI, String rdfSyntax) |
| { |
| if ( log.isDebugEnabled() ) |
| log.debug("loadModel("+filenameOrURI+", "+rdfSyntax+")") ; |
| return loadModelWorker(filenameOrURI, null, rdfSyntax) ; |
| } |
| |
| /** Load a model from a file (local or remote). |
| * |
| * @param filenameOrURI The filename or a URI (file:, http:) |
| * @param baseURI Base URI for loading the RDF model. |
| * @param rdfSyntax RDF Serialization syntax. |
| * @return a new model |
| * @exception JenaException if there is syntax error in file. |
| */ |
| |
| |
| public Model loadModel(String filenameOrURI, String baseURI, String rdfSyntax) |
| { |
| if ( log.isDebugEnabled() ) |
| log.debug("loadModel("+filenameOrURI+", "+baseURI+", "+rdfSyntax+")") ; |
| |
| return loadModelWorker(filenameOrURI, baseURI, rdfSyntax) ; |
| } |
| |
| private Model loadModelWorker(String filenameOrURI, String baseURI, String rdfSyntax) |
| { |
| if ( hasCachedModel(filenameOrURI) ) |
| { |
| if ( log.isDebugEnabled() ) |
| log.debug("Model cache hit: "+filenameOrURI) ; |
| return getFromCache(filenameOrURI) ; |
| } |
| |
| Model m = ModelFactory.createDefaultModel() ; |
| readModelWorker(m, filenameOrURI, baseURI, rdfSyntax) ; |
| |
| if ( isCachingModels() ) |
| addCacheModel(filenameOrURI, m) ; |
| return m ; |
| } |
| |
| /** |
| * Read a file of RDF into a model. Guesses the syntax of the file based on filename extension, |
| * defaulting to RDF/XML. |
| * @param model |
| * @param filenameOrURI |
| * @return The model or null, if there was an error. |
| * @exception JenaException if there is syntax error in file. |
| */ |
| |
| public Model readModel(Model model, String filenameOrURI) |
| { |
| if ( log.isDebugEnabled() ) |
| log.debug("readModel(model,"+filenameOrURI+")") ; |
| return readModel(model, filenameOrURI, null); |
| } |
| |
| /** |
| * Read a file of RDF into a model. |
| * @param model |
| * @param filenameOrURI |
| * @param rdfSyntax RDF Serialization syntax. |
| * @return The model or null, if there was an error. |
| * @exception JenaException if there is syntax error in file. |
| */ |
| |
| public Model readModel(Model model, String filenameOrURI, String rdfSyntax) |
| { |
| if ( log.isDebugEnabled() ) |
| log.debug("readModel(model,"+filenameOrURI+", "+rdfSyntax+")") ; |
| return readModelWorker(model, filenameOrURI, null, rdfSyntax); |
| } |
| |
| /** |
| * Read a file of RDF into a model. |
| * @param model |
| * @param filenameOrURI |
| * @param baseURI |
| * @param syntax |
| * @return The model |
| * @exception JenaException if there is syntax error in file. |
| */ |
| |
| public Model readModel(Model model, String filenameOrURI, String baseURI, String syntax) |
| { |
| |
| if ( log.isDebugEnabled() ) |
| log.debug("readModel(model,"+filenameOrURI+", "+baseURI+", "+syntax+")") ; |
| return readModelWorker(model, filenameOrURI, baseURI, syntax) ; |
| } |
| |
| protected Model readModelWorker(Model model, String filenameOrURI, String baseURI, String syntax) |
| { |
| // Doesn't call open() - we want to make the syntax guess based on the mapped URI. |
| String mappedURI = mapURI(filenameOrURI) ; |
| |
| if ( log.isDebugEnabled() && ! mappedURI.equals(filenameOrURI) ) |
| log.debug("Map: "+filenameOrURI+" => "+mappedURI) ; |
| |
| if ( syntax == null && baseURI == null && mappedURI.startsWith( "http:" ) ) |
| { |
| syntax = FileUtils.guessLang(mappedURI) ; |
| // Content negotation in next version (FileManager2) |
| model.read(mappedURI, syntax) ; |
| return model ; |
| } |
| |
| if ( syntax == null ) |
| { |
| syntax = FileUtils.guessLang(mappedURI) ; |
| if ( syntax == null || syntax.equals("") ) |
| syntax = FileUtils.langXML ; |
| if ( log.isDebugEnabled() ) |
| log.debug("Syntax guess: "+syntax); |
| } |
| |
| if ( baseURI == null ) |
| baseURI = chooseBaseURI(filenameOrURI) ; |
| |
| TypedStream in = openNoMapOrNull(mappedURI) ; |
| if ( in == null ) |
| { |
| FmtLog.debug(log, "Failed to locate '%s'", mappedURI); |
| throw new NotFoundException("Not found: "+filenameOrURI) ; |
| } |
| if ( in.getMimeType() != null ) |
| { |
| //syntax |
| } |
| model.read(in.getInput(), baseURI, syntax) ; |
| try { in.getInput().close(); } catch (IOException ex) {} |
| return model ; |
| } |
| |
| private static String chooseBaseURI(String baseURI) |
| { |
| String scheme = FileUtils.getScheme(baseURI) ; |
| |
| if ( scheme != null ) |
| { |
| if ( scheme.equals("file") ) |
| { |
| if ( ! baseURI.startsWith("file:///") ) |
| { |
| try { |
| // Fix up file URIs. Yuk. |
| String tmp = baseURI.substring("file:".length()) ; |
| File f = new File(tmp) ; |
| baseURI = "file:///"+f.getCanonicalPath() ; |
| baseURI = baseURI.replace('\\','/') ; |
| |
| // baseURI = baseURI.replace(" ","%20"); |
| // baseURI = baseURI.replace("~","%7E"); |
| // Convert to URI. Except that it removes /// |
| // Could do that and fix up (again) |
| //java.net.URL u = new java.net.URL(baseURI) ; |
| //baseURI = u.toExternalForm() ; |
| } catch (Exception ex) {} |
| } |
| } |
| return baseURI ; |
| } |
| |
| if ( baseURI.startsWith("/") ) |
| return "file://"+baseURI ; |
| return "file:"+baseURI ; |
| } |
| |
| /** Open a file using the locators of this FileManager */ |
| public InputStream open(String filenameOrURI) |
| { |
| if ( log.isDebugEnabled()) |
| log.debug("open("+filenameOrURI+")") ; |
| |
| String uri = mapURI(filenameOrURI) ; |
| |
| if ( log.isDebugEnabled() && ! uri.equals(filenameOrURI) ) |
| log.debug("open: mapped to "+uri) ; |
| |
| return openNoMap(uri) ; |
| } |
| |
| |
| /** Apply the mapping of a filename or URI */ |
| public String mapURI(String filenameOrURI) |
| { |
| if ( fmMapper == null ) |
| return filenameOrURI ; |
| |
| String uri = fmMapper.altMapping(filenameOrURI, null) ; |
| |
| if ( uri == null ) |
| { |
| if ( FileManager.logAllLookups && log.isDebugEnabled() ) |
| log.debug("Not mapped: "+filenameOrURI) ; |
| uri = filenameOrURI ; |
| } |
| else |
| { |
| if ( log.isDebugEnabled() ) |
| log.debug("Mapped: "+filenameOrURI+" => "+uri) ; |
| } |
| return uri ; |
| } |
| |
| /** Slurp up a whole file */ |
| public String readWholeFileAsUTF8(InputStream in) |
| { |
| try (Reader r = FileUtils.asBufferedUTF8(in); StringWriter sw = new StringWriter(1024)) { |
| char buff[] = new char[1024] ; |
| while (true) { |
| int l = r.read(buff) ; |
| if ( l <= 0 ) |
| break ; |
| sw.write(buff, 0, l) ; |
| } |
| return sw.toString() ; |
| } catch (IOException ex) |
| { throw new WrappedIOException(ex) ; } |
| } |
| |
| /** Slurp up a whole file: map filename as necessary */ |
| public String readWholeFileAsUTF8(String filename) |
| { |
| InputStream in = open(filename) ; |
| if ( in == null ) |
| throw new NotFoundException("File not found: "+filename) ; |
| return readWholeFileAsUTF8(in) ; |
| } |
| |
| /** Open a file using the locators of this FileManager |
| * but without location mapping */ |
| public InputStream openNoMap(String filenameOrURI) |
| { |
| TypedStream in = openNoMapOrNull(filenameOrURI) ; |
| if ( in == null ) |
| return null ; |
| // if ( in == null ) |
| // throw new NotFoundException(filenameOrURI) ; |
| return in.getInput() ; |
| } |
| |
| /** Open a file using the locators of this FileManager |
| * but without location mapping. |
| * Return null if not found |
| */ |
| |
| public TypedStream openNoMapOrNull(String filenameOrURI) |
| { |
| for (Locator loc : fmHandlers) |
| { |
| TypedStream in = loc.open(filenameOrURI) ; |
| if ( in != null ) |
| { |
| if ( log.isDebugEnabled() ) |
| log.debug("Found: "+filenameOrURI+" ("+loc.getName()+")") ; |
| return in ; |
| } |
| } |
| return null; |
| } |
| } |