/* * 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.fuseki.build ; import static org.apache.jena.riot.RDFLanguages.filenameToLang; import static org.apache.jena.riot.RDFParserRegistry.isRegistered; import java.io.File ; import java.io.FilenameFilter ; import java.io.IOException ; import java.lang.reflect.Method ; import java.nio.file.DirectoryStream ; import java.nio.file.Files ; import java.nio.file.Path ; import java.nio.file.Paths ; import java.util.ArrayList ; import java.util.Collections ; import java.util.List ; import org.apache.jena.assembler.JA ; import org.apache.jena.atlas.iterator.Iter ; import org.apache.jena.atlas.lib.IRILib ; import org.apache.jena.atlas.lib.StrUtils ; import org.apache.jena.fuseki.Fuseki ; import org.apache.jena.fuseki.FusekiConfigException ; import org.apache.jena.fuseki.FusekiLib ; import org.apache.jena.fuseki.server.* ; import org.apache.jena.query.Dataset ; import org.apache.jena.query.QuerySolution ; import org.apache.jena.query.ReadWrite ; import org.apache.jena.query.ResultSet ; import org.apache.jena.rdf.model.* ; import org.apache.jena.riot.Lang; import org.apache.jena.sparql.core.assembler.AssemblerUtils ; import org.apache.jena.update.UpdateAction ; import org.apache.jena.update.UpdateFactory ; import org.apache.jena.update.UpdateRequest ; import org.apache.jena.vocabulary.RDF ; import org.slf4j.Logger ; public class FusekiConfig { static { Fuseki.init() ; } private static Logger log = Fuseki.configLog ; private static FilenameFilter visibleFiles = new FilenameFilter() { @Override public boolean accept(File dir, String name) { if ( name.startsWith(".") ) return false ; File f = new File(dir, name) ; return f.isFile() ; } } ; /** Has side effects in server setup */ public static List<DataAccessPoint> readServerConfigFile(String filename) { // Old-style config file. Model model = readAssemblerFile(filename) ; if ( model.size() == 0 ) return Collections.emptyList() ; server(model) ; return servicesAndDatasets(model) ; } private static void server(Model model) { // Find one server. List<Resource> servers = getByType(FusekiVocab.tServer, model) ; if ( servers.size() == 0 ) return ; if ( servers.size() > 1 ) throw new FusekiConfigException(servers.size() + " servers found (must be exactly one in a configuration file)") ; // ---- Server Resource server = servers.get(0) ; processServer(server) ; } private static void processServer(Resource server) { // Global, currently. AssemblerUtils.setContext(server, Fuseki.getContext()) ; StmtIterator sIter = server.listProperties(JA.loadClass) ; for ( ; sIter.hasNext() ; ) { Statement s = sIter.nextStatement() ; RDFNode rn = s.getObject() ; String className = null ; if ( rn instanceof Resource ) { String uri = ((Resource)rn).getURI() ; if ( uri == null ) { log.warn("Blank node for class to load") ; continue ; } String javaScheme = "java:" ; if ( !uri.startsWith(javaScheme) ) { log.warn("Class to load is not 'java:': " + uri) ; continue ; } className = uri.substring(javaScheme.length()) ; } if ( rn instanceof Literal ) className = ((Literal)rn).getLexicalForm() ; /* Loader. */loadAndInit(className) ; } } private static List<DataAccessPoint> servicesAndDatasets(Model model) { // Old style configuration file : server to services. DatasetDescriptionRegistry dsDescMap = FusekiServer.registryForBuild() ; // ---- Services ResultSet rs = FusekiLib.query("SELECT * { ?s fu:services [ list:member ?service ] }", model) ; List<DataAccessPoint> accessPoints = new ArrayList<>() ; if ( ! rs.hasNext() ) // No "fu:services ( .... )" so try looking for services directly. // This means Fuseki2, service configuration files (no server section) work for --conf. rs = FusekiLib.query("SELECT ?service { ?service a fu:Service }", model) ; for ( ; rs.hasNext() ; ) { QuerySolution soln = rs.next() ; Resource svc = soln.getResource("service") ; DataAccessPoint acc = FusekiBuilder.buildDataAccessPoint(svc, dsDescMap) ; accessPoints.add(acc) ; } return accessPoints ; } private static void loadAndInit(String className) { try { Class<? > classObj = Class.forName(className) ; log.info("Loaded " + className) ; Method initMethod = classObj.getMethod("init") ; initMethod.invoke(null) ; } catch (ClassNotFoundException ex) { log.warn("Class not found: " + className) ; } catch (Exception e) { throw new FusekiConfigException(e) ; } } private static Model readAssemblerFile(String filename) { return AssemblerUtils.readAssemblerFile(filename) ; } private static void execute(Model m, String x) { UpdateRequest req = UpdateFactory.create(x) ; UpdateAction.execute(req, m); } // XXX Move to a library private static List<Resource> getByType(Resource type, Model m) { ResIterator rIter = m.listSubjectsWithProperty(RDF.type, type) ; return Iter.toList(rIter) ; } // ---- Directory of assemblers /** Read service descriptions in the given directory */ public static List<DataAccessPoint> readConfigurationDirectory(String dir) { Path pDir = Paths.get(dir).normalize() ; File dirFile = pDir.toFile() ; if ( ! dirFile.exists() ) { log.warn("Not found: directory for assembler files for services: '"+dir+"'") ; return Collections.emptyList() ; } if ( ! dirFile.isDirectory() ) { log.warn("Not a directory: '"+dir+"'") ; return Collections.emptyList() ; } // Files that are not hidden. DirectoryStream.Filter<Path> filter = (entry)-> { File f = entry.toFile() ; final Lang lang = filenameToLang(f.getName()); return ! f.isHidden() && f.isFile() && lang != null && isRegistered(lang) ; } ; List<DataAccessPoint> dataServiceRef = new ArrayList<>() ; try (DirectoryStream<Path> stream = Files.newDirectoryStream(pDir, filter)) { for ( Path p : stream ) { DatasetDescriptionRegistry dsDescMap = FusekiServer.registryForBuild() ; String fn = IRILib.filenameToIRI(p.toString()) ; log.info("Load configuration: "+fn); Model m = readAssemblerFile(fn) ; readConfiguration(m, dsDescMap, dataServiceRef) ; } } catch (IOException ex) { log.warn("IOException:"+ex.getMessage(), ex); } return dataServiceRef ; } /** Read and process one file */ public static List<DataAccessPoint> readConfigurationFile(String fn) { List<DataAccessPoint> acc = new ArrayList<>() ; Model m = readAssemblerFile(fn) ; DatasetDescriptionRegistry dsDescMap = new DatasetDescriptionRegistry() ; readConfiguration(m, dsDescMap, acc) ; return acc ; } /** Read a configuration in a model. * Allow dataset descriptions to be carried over from anothe rplace. * Add to a list. */ private static void readConfiguration(Model m, DatasetDescriptionRegistry dsDescMap, List<DataAccessPoint> dataServiceRef) { List<Resource> services = getByType(FusekiVocab.fusekiService, m) ; if ( services.size() == 0 ) { log.error("No services found") ; throw new FusekiConfigException() ; } for ( Resource service : services ) { DataAccessPoint acc = FusekiBuilder.buildDataAccessPoint(service, dsDescMap) ; dataServiceRef.add(acc) ; } } // ---- System database /** Read the system database */ public static List<DataAccessPoint> readSystemDatabase(Dataset ds) { DatasetDescriptionRegistry dsDescMap = FusekiServer.registryForBuild() ; String qs = StrUtils.strjoinNL (SystemState.PREFIXES , "SELECT * {" , " GRAPH ?g {", " ?s fu:name ?name ;" , " fu:status ?status ." , " }", "}" ) ; List<DataAccessPoint> refs = new ArrayList<>() ; ds.begin(ReadWrite.WRITE) ; try { ResultSet rs = FusekiLib.query(qs, ds) ; // ResultSetFormatter.out(rs); // ((ResultSetRewindable)rs).reset(); for ( ; rs.hasNext() ; ) { QuerySolution row = rs.next() ; Resource s = row.getResource("s") ; Resource g = row.getResource("g") ; Resource rStatus = row.getResource("status") ; //String name = row.getLiteral("name").getLexicalForm() ; DatasetStatus status = DatasetStatus.status(rStatus) ; Model m = ds.getNamedModel(g.getURI()) ; // Rebase the resource of the service description to the containing graph. Resource svc = m.wrapAsResource(s.asNode()) ; DataAccessPoint ref = FusekiBuilder.buildDataAccessPoint(svc, dsDescMap) ; refs.add(ref) ; } ds.commit(); return refs ; } finally { ds.end() ; } } }