/* * 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.server; import java.lang.reflect.Method ; import java.util.ArrayList ; import java.util.Arrays ; import java.util.List ; import org.apache.jena.assembler.Assembler ; import org.apache.jena.assembler.JA ; import org.apache.jena.atlas.iterator.Iter ; import org.apache.jena.atlas.lib.StrUtils ; import org.apache.jena.fuseki.Fuseki ; import org.apache.jena.fuseki.FusekiConfigException ; import org.apache.jena.fuseki.HttpNames ; import org.apache.jena.query.* ; import org.apache.jena.rdf.model.* ; import org.apache.jena.shared.PrefixMapping ; import org.apache.jena.sparql.core.DatasetGraph ; import org.apache.jena.sparql.core.DatasetGraphFactory ; import org.apache.jena.sparql.core.DatasetGraphReadOnly ; import org.apache.jena.sparql.core.assembler.AssemblerUtils ; import org.apache.jena.util.FileManager ; import org.apache.jena.vocabulary.RDF ; import org.apache.jena.vocabulary.RDFS ; import org.slf4j.Logger ; public class FusekiConfig { static { Fuseki.init(); } // The datastructure that captures a servers configuration. // Server port int port ; // Management command port - -1 for none. int mgtPort ; List<DatasetRef> datasets = null ; private static Logger log = Fuseki.configLog ; private static String prefixes = StrUtils.strjoinNL( "PREFIX fu: <http://jena.apache.org/fuseki#>" , "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>", "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>", "PREFIX tdb: <http://jena.hpl.hp.com/2008/tdb#>", "PREFIX list: <http://jena.apache.org/ARQ/list#>", "PREFIX list: <http://jena.apache.org/ARQ/list#>", "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>", "PREFIX apf: <http://jena.apache.org/ARQ/property#>", "PREFIX afn: <http://jena.apache.org/ARQ/function#>" , "") ; public static ServerConfig defaultConfiguration(String datasetPath, DatasetGraph dsg, boolean allowUpdate, boolean listenLocal) { DatasetRef dbDesc = new DatasetRef() ; dbDesc.name = datasetPath ; dbDesc.dataset = dsg ; dbDesc.query.endpoints.add(HttpNames.ServiceQuery) ; dbDesc.query.endpoints.add(HttpNames.ServiceQueryAlt) ; if ( allowUpdate ) { dbDesc.update.endpoints.add(HttpNames.ServiceUpdate) ; dbDesc.upload.endpoints.add(HttpNames.ServiceUpload) ; dbDesc.readWriteGraphStore.endpoints.add(HttpNames.ServiceData) ; dbDesc.allowDatasetUpdate = true ; } else dbDesc.readGraphStore.endpoints.add(HttpNames.ServiceData) ; ServerConfig config = new ServerConfig() ; config.datasets = Arrays.asList(dbDesc) ; config.port = 3030 ; config.mgtPort = 3031 ; config.pagesPort = config.port ; config.loopback = listenLocal ; config.jettyConfigFile = null ; config.pages = Fuseki.PagesStatic ; config.enableCompression = true ; config.verboseLogging = false ; return config ; } public static ServerConfig configure(String filename) { // Be absolutely sure everything has initialized. // Some initialization registers assemblers and sets abbreviation vocabulary. Fuseki.init() ; Model m = FileManager.get().loadModel(filename) ; // Find one server. List<Resource> servers = getByType(FusekiVocab.tServer, m) ; if ( servers.size() == 0 ) throw new FusekiConfigException("No server found (no resource with type "+strForResource(FusekiVocab.tServer)) ; 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) ; // ---- Services ResultSet rs = query("SELECT * { ?s fu:services [ list:member ?member ] }", m) ; if ( ! rs.hasNext() ) log.warn("No services found") ; List<DatasetRef> services = new ArrayList<DatasetRef>() ; for ( ; rs.hasNext() ; ) { QuerySolution soln = rs.next() ; Resource svc = soln.getResource("member") ; DatasetRef sd = processService(svc) ; services.add(sd) ; } // TODO Properties for the other fields. ServerConfig config = new ServerConfig() ; config.datasets = services ; config.port = 3030 ; config.mgtPort = 3031 ; config.pagesPort = config.port ; config.jettyConfigFile = null ; config.pages = Fuseki.PagesStatic ; config.enableCompression = true ; config.verboseLogging = false ; return config ; } // DatasetRef used where there isn't a real Dataset e.g. the SPARQL processor. private static DatasetRef noDataset = new DatasetRef() ; private static DatasetGraph dummyDSG = new DatasetGraphReadOnly(DatasetGraphFactory.create()) ; static { noDataset.name = "" ; noDataset.dataset = dummyDSG ; noDataset.query.endpoints.add(HttpNames.ServiceQuery) ; noDataset.query.endpoints.add(HttpNames.ServiceQueryAlt) ; noDataset.allowDatasetUpdate = false ; noDataset.init(); // Don't register it. // This is used as a placeholder and shoudl not be found by "all datasets" // DatasetRegistry.get().put("", noDataset) ; } /** Return the DatasetRef (read-only) for when there is no dataset, just a SPARQL Query processor */ public static DatasetRef serviceOnlyDatasetRef() { return noDataset ; } 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 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 DatasetRef processService(Resource svc) { log.info("Service: "+nodeLabel(svc)) ; DatasetRef sDesc = new DatasetRef() ; sDesc.name = ((Literal)getOne(svc, "fu:name")).getLexicalForm() ; log.info(" name = "+sDesc.name) ; addServiceEP("query", sDesc.name, sDesc.query, svc, "fu:serviceQuery") ; addServiceEP("update", sDesc.name, sDesc.update, svc, "fu:serviceUpdate") ; addServiceEP("upload", sDesc.name, sDesc.upload, svc, "fu:serviceUpload") ; addServiceEP("graphStore(RW)", sDesc.name, sDesc.readWriteGraphStore, svc, "fu:serviceReadWriteGraphStore") ; addServiceEP("graphStore(R)", sDesc.name, sDesc.readGraphStore, svc, "fu:serviceReadGraphStore") ; // Extract timeout overriding configuration if present. if (svc.hasProperty(FusekiVocab.pAllowTimeoutOverride)) { sDesc.allowTimeoutOverride = svc.getProperty(FusekiVocab.pAllowTimeoutOverride).getObject().asLiteral().getBoolean(); if (svc.hasProperty(FusekiVocab.pMaximumTimeoutOverride)) { sDesc.maximumTimeoutOverride = (int) (svc.getProperty(FusekiVocab.pMaximumTimeoutOverride).getObject().asLiteral().getFloat() * 1000); } } Resource datasetDesc = ((Resource)getOne(svc, "fu:dataset")) ; // Check if it is in the model. if ( ! datasetDesc.hasProperty(RDF.type) ) throw new FusekiConfigException("No rdf:type for dataset "+nodeLabel(datasetDesc)) ; Dataset ds = (Dataset)Assembler.general.open(datasetDesc) ; sDesc.dataset = ds.asDatasetGraph() ; return sDesc ; } private static RDFNode getOne(Resource svc, String property) { String ln = property.substring(property.indexOf(':')+1) ; ResultSet rs = query("SELECT * { ?svc "+property+" ?x}", svc.getModel(), "svc", svc) ; if ( ! rs.hasNext() ) throw new FusekiConfigException("No "+ln+" for service "+nodeLabel(svc)) ; RDFNode x = rs.next().get("x") ; if ( rs.hasNext() ) throw new FusekiConfigException("Multiple "+ln+" for service "+nodeLabel(svc)) ; return x ; } private static List<Resource> getByType(Resource type, Model m) { ResIterator rIter = m.listSubjectsWithProperty(RDF.type, type) ; return Iter.toList(rIter) ; } private static void addServiceEP(String label, String name, ServiceRef service, Resource svc, String property) { ResultSet rs = query("SELECT * { ?svc "+property+" ?ep}", svc.getModel(), "svc", svc) ; for ( ; rs.hasNext() ; ) { QuerySolution soln = rs.next() ; String epName = soln.getLiteral("ep").getLexicalForm() ; service.endpoints.add(epName) ; log.info(" "+label+" = /"+name+"/"+epName) ; } } private static ResultSet query(String string, Model m) { return query(string, m, null, null) ; } private static ResultSet query(String string, Model m, String varName, RDFNode value) { Query query = QueryFactory.create(prefixes+string) ; QuerySolutionMap initValues = null ; if ( varName != null ) initValues = querySolution(varName, value) ; try(QueryExecution qExec = QueryExecutionFactory.create(query, m, initValues)) { ResultSet rs = ResultSetFactory.copyResults(qExec.execSelect()) ; return rs ; } } private static QuerySolutionMap querySolution(String varName, RDFNode value) { QuerySolutionMap qsm = new QuerySolutionMap() ; querySolution(qsm, varName, value) ; return qsm ; } private static QuerySolutionMap querySolution(QuerySolutionMap qsm, String varName, RDFNode value) { qsm.add(varName, value) ; return qsm ; } // Node presentation private static String nodeLabel(RDFNode n) { if ( n == null ) return "<null>" ; if ( n instanceof Resource ) return strForResource((Resource)n) ; Literal lit = (Literal)n ; return lit.getLexicalForm() ; } private static String strForResource(Resource r) { return strForResource(r, r.getModel()) ; } private static String strForResource(Resource r, PrefixMapping pm) { if ( r == null ) return "NULL "; if ( r.hasProperty(RDFS.label)) { RDFNode n = r.getProperty(RDFS.label).getObject() ; if ( n instanceof Literal ) return ((Literal)n).getString() ; } if ( r.isAnon() ) return "<<blank node>>" ; if ( pm == null ) pm = r.getModel() ; return strForURI(r.getURI(), pm ) ; } private static String strForURI(String uri, PrefixMapping pm) { if ( pm != null ) { String x = pm.shortForm(uri) ; if ( ! x.equals(uri) ) return x ; } return "<"+uri+">" ; } }