http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiCmd.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiCmd.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiCmd.java new file mode 100644 index 0000000..745d430 --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiCmd.java @@ -0,0 +1,335 @@ +/* + * 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 ; + +import java.util.List ; + +import org.apache.jena.atlas.lib.FileOps ; +import org.apache.jena.fuseki.build.Template ; +import org.apache.jena.fuseki.jetty.JettyServerConfig ; +import org.apache.jena.fuseki.jetty.JettyFuseki ; +import org.apache.jena.fuseki.server.FusekiServletContextListener ; +import org.apache.jena.fuseki.server.ServerInitialConfig ; +import org.apache.jena.riot.Lang ; +import org.apache.jena.riot.RDFDataMgr ; +import org.apache.jena.riot.RDFLanguages ; +import org.slf4j.Logger ; +import arq.cmd.CmdException ; +import arq.cmdline.ArgDecl ; +import arq.cmdline.CmdARQ ; +import arq.cmdline.ModDatasetAssembler ; + +import com.hp.hpl.jena.query.ARQ ; +import com.hp.hpl.jena.query.Dataset ; +import com.hp.hpl.jena.sparql.core.DatasetGraphFactory ; +import com.hp.hpl.jena.tdb.TDB ; +import com.hp.hpl.jena.tdb.sys.Names ; +import com.hp.hpl.jena.tdb.transaction.TransactionManager ; + +public class FusekiCmd { + // This allows us to set logging before calling FusekiCmdInner + // FusekiCmdInner inherits from CmdMain which statically sets logging. + // By java classloading, super class statics run before the + // statics of a class are run. + // (Cmd logging should be done lower down but it's normally convenient + // to have to set in all circumstances). + static { FusekiLogging.setLogging() ; } + + static public void main(String... argv) { + FusekiCmdInner.main(argv); + } + + static class FusekiCmdInner extends CmdARQ { + // Legacy. + private static ArgDecl argMgt = new ArgDecl(ArgDecl.NoValue, "mgt") ; + private static ArgDecl argMgtPort = new ArgDecl(ArgDecl.HasValue, "mgtPort", "mgtport") ; + private static ArgDecl argHome = new ArgDecl(ArgDecl.HasValue, "home") ; + private static ArgDecl argPages = new ArgDecl(ArgDecl.HasValue, "pages") ; + + private static ArgDecl argMem = new ArgDecl(ArgDecl.NoValue, "mem") ; + private static ArgDecl argAllowUpdate = new ArgDecl(ArgDecl.NoValue, "update", "allowUpdate") ; + private static ArgDecl argFile = new ArgDecl(ArgDecl.HasValue, "file") ; + private static ArgDecl argMemTDB = new ArgDecl(ArgDecl.NoValue, "memtdb", "memTDB") ; + private static ArgDecl argTDB = new ArgDecl(ArgDecl.HasValue, "loc", "location") ; + private static ArgDecl argPort = new ArgDecl(ArgDecl.HasValue, "port") ; + private static ArgDecl argLocalhost = new ArgDecl(ArgDecl.NoValue, "localhost", "local") ; + private static ArgDecl argTimeout = new ArgDecl(ArgDecl.HasValue, "timeout") ; + private static ArgDecl argFusekiConfig = new ArgDecl(ArgDecl.HasValue, "config", "conf") ; + private static ArgDecl argJettyConfig = new ArgDecl(ArgDecl.HasValue, "jetty-config") ; + private static ArgDecl argGZip = new ArgDecl(ArgDecl.HasValue, "gzip") ; + + // Deprecated. Use shiro. + private static ArgDecl argBasicAuth = new ArgDecl(ArgDecl.HasValue, "basic-auth") ; + + // private static ModLocation modLocation = new ModLocation() ; + private static ModDatasetAssembler modDataset = new ModDatasetAssembler() ; + + // fuseki [--mem|--desc assembler.ttl] [--port PORT] **** /datasetURI + + static public void main(String... argv) { + // Just to make sure ... + ARQ.init() ; + TDB.init() ; + Fuseki.init() ; + new FusekiCmdInner(argv).mainRun() ; + } + + private JettyServerConfig jettyServerConfig = new JettyServerConfig() ; + { + jettyServerConfig.port = 3030 ; + jettyServerConfig.contextPath = "/" ; + jettyServerConfig.jettyConfigFile = null ; + jettyServerConfig.pages = Fuseki.PagesStatic ; + jettyServerConfig.enableCompression = true ; + jettyServerConfig.verboseLogging = false ; + } + + private ServerInitialConfig cmdLineDataset = new ServerInitialConfig() ; + + public FusekiCmdInner(String... argv) { + super(argv) ; + + if ( false ) + // Consider ... + TransactionManager.QueueBatchSize = TransactionManager.QueueBatchSize / 2 ; + + getUsage().startCategory("Fuseki") ; + addModule(modDataset) ; + add(argMem, "--mem", "Create an in-memory, non-persistent dataset for the server") ; + add(argFile, "--file=FILE", + "Create an in-memory, non-persistent dataset for the server, initialised with the contents of the file") ; + add(argTDB, "--loc=DIR", "Use an existing TDB database (or create if does not exist)") ; + add(argMemTDB, "--memTDB", "Create an in-memory, non-persistent dataset using TDB (testing only)") ; + add(argPort, "--port", "Listen on this port number") ; + add(argPages, "--pages=DIR", "Set of pages to serve as static content") ; + // Set via jetty config file. + add(argLocalhost, "--localhost", "Listen only on the localhost interface") ; + add(argTimeout, "--timeout=", "Global timeout applied to queries (value in ms) -- format is X[,Y] ") ; + add(argAllowUpdate, "--update", "Allow updates (via SPARQL Update and SPARQL HTTP Update)") ; + add(argFusekiConfig, "--config=", "Use a configuration file to determine the services") ; + add(argJettyConfig, "--jetty-config=FILE", "Set up the server (not services) with a Jetty XML file") ; + add(argBasicAuth) ; + //add(argMgt, "--mgt", "Enable the management commands") ; + add(argMgt) ; // Legacy + add(argMgtPort) ; // Legacy + //add(argMgtPort, "--mgtPort=port", "Port for management optations") ; + //add(argHome, "--home=DIR", "Root of Fuseki installation (overrides environment variable FUSEKI_HOME)") ; + add(argGZip, "--gzip=on|off", "Enable GZip compression (HTTP Accept-Encoding) if request header set") ; + + //add(argUber) ; + // add(argGSP) ; + + super.modVersion.addClass(TDB.class) ; + super.modVersion.addClass(Fuseki.class) ; + } + + static String argUsage = "[--config=FILE] [--mem|--desc=AssemblerFile|--file=FILE] [--port PORT] /DatasetPathName" ; + + @Override + protected String getSummary() { + return getCommandName() + " " + argUsage ; + } + + @Override + protected void processModulesAndArgs() { + int x = 0 ; + + Logger log = Fuseki.serverLog ; + + if ( contains(argFusekiConfig) ) + cmdLineDataset.fusekiConfigFile = getValue(argFusekiConfig) ; + + ArgDecl assemblerDescDecl = new ArgDecl(ArgDecl.HasValue, "desc", "dataset") ; + + // ---- Datasets + + if ( contains(argMem) ) + x++ ; + if ( contains(argFile) ) + x++ ; + if ( contains(assemblerDescDecl) ) + x++ ; + if ( contains(argTDB) ) + x++ ; + if ( contains(argMemTDB) ) + x++ ; + + if ( cmdLineDataset.fusekiConfigFile != null ) { + if ( x > 1 ) + throw new CmdException("Dataset specified on the command line and also a configuration file specified.") ; + } else { + // if ( x == 0 ) + // throw new CmdException("Required: either --config=FILE or one of --mem, --file, --loc or --desc") ; + } + + if ( contains(argMem) ) { + log.info("Dataset: in-memory") ; + cmdLineDataset = new ServerInitialConfig() ; + cmdLineDataset.templateFile = Template.templateMemFN ; + // + } + + if ( contains(argFile) ) { + String filename = getValue(argFile) ; + log.info("Dataset: in-memory: load file: " + filename) ; + if ( !FileOps.exists(filename) ) + throw new CmdException("File not found: " + filename) ; + + // Directly populate the dataset. + cmdLineDataset = new ServerInitialConfig() ; + cmdLineDataset.dsg = DatasetGraphFactory.createMem() ; + + // INITIAL DATA. + Lang language = RDFLanguages.filenameToLang(filename) ; + if ( language == null ) + throw new CmdException("Can't guess language for file: " + filename) ; + RDFDataMgr.read(cmdLineDataset.dsg, filename) ; + } + + if ( contains(argMemTDB) ) { + //log.info("TDB dataset: in-memory") ; + cmdLineDataset = new ServerInitialConfig() ; + cmdLineDataset.templateFile = Template.templateTDBMemFN ; + cmdLineDataset.params.put(Template.DIR, Names.memName) ; + } + + if ( contains(argTDB) ) { + cmdLineDataset = new ServerInitialConfig() ; + cmdLineDataset.templateFile = Template.templateTDBDirFN ; + + String dir = getValue(argTDB) ; + cmdLineDataset.params.put(Template.DIR, dir) ; + } + + // Otherwise + if ( contains(assemblerDescDecl) ) { + log.info("Dataset from assembler") ; + // Need to add service details. + Dataset ds = modDataset.createDataset() ; + //cmdLineDataset.dsg = ds.asDatasetGraph() ; + } + + if ( cmdLineDataset != null ) { + if ( getPositional().size() > 1 ) + throw new CmdException("Multiple dataset path names given") ; + if ( getPositional().size() != 0 ) { + cmdLineDataset.datasetPath = getPositionalArg(0) ; + if ( cmdLineDataset.datasetPath.length() > 0 && !cmdLineDataset.datasetPath.startsWith("/") ) + throw new CmdException("Dataset path name must begin with a /: " + cmdLineDataset.datasetPath) ; + cmdLineDataset.allowUpdate = contains(argAllowUpdate) ; + if ( ! cmdLineDataset.allowUpdate ) + Fuseki.serverLog.info("Running in read-only mode for "+cmdLineDataset.datasetPath) ; + // Include the dataset name as NAME for any templates. + cmdLineDataset.params.put(Template.NAME, cmdLineDataset.datasetPath) ; + } + } + + // ---- Jetty server + if ( contains(argBasicAuth) ) + Fuseki.configLog.warn("--basic-auth ignored: Use Apache Shiro security - see shiro.ini") ; + + if ( contains(argPort) ) { + String portStr = getValue(argPort) ; + try { + jettyServerConfig.port = Integer.parseInt(portStr) ; + } catch (NumberFormatException ex) { + throw new CmdException(argPort.getKeyName() + " : bad port number: " + portStr) ; + } + } + + if ( contains(argMgt) ) + Fuseki.configLog.warn("Fuseki v2: Management functions are always enabled. --mgt not needed.") ; + + if ( contains(argMgtPort) ) + Fuseki.configLog.warn("Fuseki v2: Management functions are always on the same port as the server. --mgtPort ignored.") ; + +// if ( contains(argMgt) ) { +// jettyServerConfig.mgtPort = 0 ; +// if ( contains(argMgtPort) ) { +// String mgtPortStr = getValue(argMgtPort) ; +// try { +// jettyServerConfig.mgtPort = Integer.parseInt(mgtPortStr) ; +// } catch (NumberFormatException ex) { +// throw new CmdException("--"+argMgtPort.getKeyName() + " : bad port number: " + mgtPortStr) ; +// } +// } +// } + + if ( contains(argLocalhost) ) + jettyServerConfig.loopback = true ; + + if ( contains(argTimeout) ) { + String str = getValue(argTimeout) ; + ARQ.getContext().set(ARQ.queryTimeout, str) ; + } + + if ( contains(argJettyConfig) ) { + jettyServerConfig.jettyConfigFile = getValue(argJettyConfig) ; + if ( !FileOps.exists(jettyServerConfig.jettyConfigFile) ) + throw new CmdException("No such file: " + jettyServerConfig.jettyConfigFile) ; + } + + if ( contains(argBasicAuth) ) { + jettyServerConfig.authConfigFile = getValue(argBasicAuth) ; + if ( !FileOps.exists(jettyServerConfig.authConfigFile) ) + throw new CmdException("No such file: " + jettyServerConfig.authConfigFile) ; + } + + if ( contains(argHome) ) { + Fuseki.configLog.warn("--home ignored (use enviroment variables $FUSEKI_HOME and $FUSEKI_BASE)") ; +// List<String> args = super.getValues(argHome) ; +// homeDir = args.get(args.size() - 1) ; + } + + if ( contains(argPages) ) { + List<String> args = super.getValues(argPages) ; + jettyServerConfig.pages = args.get(args.size() - 1) ; + } + + if ( contains(argGZip) ) { + if ( !hasValueOfTrue(argGZip) && !hasValueOfFalse(argGZip) ) + throw new CmdException(argGZip.getNames().get(0) + ": Not understood: " + getValue(argGZip)) ; + jettyServerConfig.enableCompression = super.hasValueOfTrue(argGZip) ; + } + } + + private static String sort_out_dir(String path) { + path.replace('\\', '/') ; + if ( !path.endsWith("/") ) + path = path + "/" ; + return path ; + } + + @Override + protected void exec() { + //************* + FusekiServletContextListener.initialSetup = cmdLineDataset ; + // For standalone, command line use ... + JettyFuseki.initializeServer(jettyServerConfig) ; + JettyFuseki.instance.start() ; + JettyFuseki.instance.join() ; + System.exit(0) ; + } + + @Override + protected String getCommandName() { + return "fuseki" ; + } + } +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiConfigException.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiConfigException.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiConfigException.java new file mode 100644 index 0000000..5e1b018 --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiConfigException.java @@ -0,0 +1,28 @@ +/* + * 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; + + +public class FusekiConfigException extends FusekiException +{ + public FusekiConfigException(String msg, Throwable cause) { super(msg, cause) ; } + public FusekiConfigException(String msg) { super(msg) ; } + public FusekiConfigException(Throwable cause) { super(cause) ; } + public FusekiConfigException() { super() ; } +} http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiException.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiException.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiException.java new file mode 100644 index 0000000..04953ce --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiException.java @@ -0,0 +1,29 @@ +/* + * 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; + +import com.hp.hpl.jena.sparql.ARQException ; + +public class FusekiException extends ARQException +{ + public FusekiException(String msg, Throwable cause) { super(msg, cause) ; } + public FusekiException(String msg) { super(msg) ; } + public FusekiException(Throwable cause) { super(cause) ; } + public FusekiException() { super() ; } +} http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiLib.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiLib.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiLib.java new file mode 100644 index 0000000..cd405b6 --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiLib.java @@ -0,0 +1,260 @@ +/* + * 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; + +import java.util.Iterator ; + +import javax.servlet.http.HttpServletRequest ; + +import org.apache.jena.atlas.lib.MultiMap ; +import org.apache.jena.atlas.lib.MultiMapToList ; +import org.apache.jena.atlas.web.ContentType ; +import org.apache.jena.fuseki.server.SystemState ; +import org.apache.jena.fuseki.servlets.HttpAction ; +import org.apache.jena.riot.Lang ; +import org.apache.jena.riot.RDFLanguages ; + +import com.hp.hpl.jena.graph.Graph ; +import com.hp.hpl.jena.graph.Node ; +import com.hp.hpl.jena.graph.Triple ; +import com.hp.hpl.jena.query.* ; +import com.hp.hpl.jena.rdf.model.Literal ; +import com.hp.hpl.jena.rdf.model.Model ; +import com.hp.hpl.jena.rdf.model.RDFNode ; +import com.hp.hpl.jena.rdf.model.Resource ; +import com.hp.hpl.jena.shared.PrefixMapping ; +import com.hp.hpl.jena.sparql.core.DatasetGraph ; +import com.hp.hpl.jena.sparql.core.Quad ; +import com.hp.hpl.jena.sparql.util.Convert ; +import com.hp.hpl.jena.vocabulary.RDFS ; + +public class FusekiLib { + // ==> ActionLib + + /** Get the content type of an action or return the default. + * @param action + * @return ContentType + */ + public static ContentType getContentType(HttpAction action) { + return getContentType(action.request) ; + } + + /** Get the content type of an action or return the default. + * @param request + * @return ContentType + */ + public static ContentType getContentType(HttpServletRequest request) { + String contentTypeHeader = request.getContentType() ; + if ( contentTypeHeader == null ) + return null ; + return ContentType.create(contentTypeHeader) ; + } + + /** Get the incoming Lang based on Content-Type of an action. + * @param action + * @param dft Default if no "Content-Type:" found. + * @return ContentType + */ + public static Lang getLangFromAction(HttpAction action, Lang dft) { + String contentTypeHeader = action.request.getContentType() ; + if ( contentTypeHeader == null ) + return dft ; + return RDFLanguages.contentTypeToLang(contentTypeHeader) ; + } + + static String fmtRequest(HttpServletRequest request) { + StringBuffer sbuff = new StringBuffer() ; + sbuff.append(request.getMethod()) ; + sbuff.append(" ") ; + sbuff.append(Convert.decWWWForm(request.getRequestURL())) ; + + String qs = request.getQueryString() ; + if ( qs != null ) { + String tmp = request.getQueryString() ; + tmp = Convert.decWWWForm(tmp) ; + tmp = tmp.replace('\n', ' ') ; + tmp = tmp.replace('\r', ' ') ; + sbuff.append("?").append(tmp) ; + } + return sbuff.toString() ; + } + + /** Parse the query string - do not process the body even for a form */ + public static MultiMap<String, String> parseQueryString(HttpServletRequest req) { + MultiMap<String, String> map = MultiMapToList.create() ; + + // Don't use ServletRequest.getParameter or getParamterNames + // as that reads form data. This code parses just the query string. + if ( req.getQueryString() != null ) { + String[] params = req.getQueryString().split("&") ; + for (int i = 0; i < params.length; i++) { + String p = params[i] ; + String[] x = p.split("=", 2) ; + String name = null ; + String value = null ; + + if ( x.length == 0 ) { // No "=" + name = p ; + value = "" ; + } else if ( x.length == 1 ) { // param= + name = x[0] ; + value = "" ; + } else { // param=value + name = x[0] ; + value = x[1] ; + } + map.put(name, value) ; + } + } + return map ; + } + + public static String safeParameter(HttpServletRequest request, String pName) { + String value = request.getParameter(pName) ; + value = value.replace("\r", "") ; + value = value.replace("\n", "") ; + return value ; + } + + // Do the addition directly on the dataset + public static void addDataInto(Graph data, DatasetGraph dsg, Node graphName) { + // Prefixes? + if ( graphName == null ) + graphName = Quad.defaultGraphNodeGenerated ; + + Iterator<Triple> iter = data.find(Node.ANY, Node.ANY, Node.ANY) ; + for (; iter.hasNext();) { + Triple t = iter.next() ; + dsg.add(graphName, t.getSubject(), t.getPredicate(), t.getObject()) ; + } + + PrefixMapping pmapSrc = data.getPrefixMapping() ; + PrefixMapping pmapDest = dsg.getDefaultGraph().getPrefixMapping() ; + pmapDest.setNsPrefixes(pmapSrc) ; + } + + public static void addDataInto(DatasetGraph src, DatasetGraph dest) { + Iterator<Quad> iter = src.find(Node.ANY, Node.ANY, Node.ANY, Node.ANY) ; + for (; iter.hasNext();) { + Quad q = iter.next() ; + dest.add(q) ; + } + + PrefixMapping pmapSrc = src.getDefaultGraph().getPrefixMapping() ; + PrefixMapping pmapDest = dest.getDefaultGraph().getPrefixMapping() ; + pmapDest.withDefaultMappings(pmapSrc) ; + } + + // ---- Helper code + public static ResultSet query(String string, Model m) { + return query(string, m, null, null) ; + } + + public static ResultSet query(String string, Dataset ds) { + return query(string, ds, null, null) ; + } + + public static ResultSet query(String string, Model m, String varName, RDFNode value) { + Query query = QueryFactory.create(SystemState.PREFIXES + string) ; + QuerySolutionMap initValues = null ; + if ( varName != null ) + initValues = querySolution(varName, value) ; + try ( QueryExecution qExec = QueryExecutionFactory.create(query, m, initValues) ) { + return ResultSetFactory.copyResults(qExec.execSelect()) ; + } + } + + public static ResultSet query(String string, Dataset ds, String varName, RDFNode value) { + Query query = QueryFactory.create(SystemState.PREFIXES + string) ; + QuerySolutionMap initValues = null ; + if ( varName != null ) + initValues = querySolution(varName, value) ; + try ( QueryExecution qExec = QueryExecutionFactory.create(query, ds, initValues) ) { + return ResultSetFactory.copyResults(qExec.execSelect()) ; + } + } + + private static QuerySolutionMap querySolution(String varName, RDFNode value) { + QuerySolutionMap qsm = new QuerySolutionMap() ; + querySolution(qsm, varName, value) ; + return qsm ; + } + + public static QuerySolutionMap querySolution(QuerySolutionMap qsm, String varName, RDFNode value) { + qsm.add(varName, value) ; + return qsm ; + } + + public static RDFNode getOne(Resource svc, String property) { + String localName = property.substring(property.indexOf(':') + 1) ; + ResultSet rs = FusekiLib.query("SELECT * { ?svc " + property + " ?x}", svc.getModel(), "svc", svc) ; + if ( !rs.hasNext() ) + throw new FusekiConfigException("No " + localName + " for service " + FusekiLib.nodeLabel(svc)) ; + RDFNode x = rs.next().get("x") ; + if ( rs.hasNext() ) + throw new FusekiConfigException("Multiple " + localName + " for service " + FusekiLib.nodeLabel(svc)) ; + return x ; + } + + // Node presentation + public 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() ; + } + + // XXX Lib + public static String strForResource(Resource r) { + return strForResource(r, r.getModel()) ; + } + + // XXX Lib + public 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) ; + } + + public static String strForURI(String uri, PrefixMapping pm) { + if ( pm != null ) { + String x = pm.shortForm(uri) ; + + if ( !x.equals(uri) ) + return x ; + } + return "<" + uri + ">" ; + } + +} http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiLogging.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiLogging.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiLogging.java new file mode 100644 index 0000000..38400c5 --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiLogging.java @@ -0,0 +1,112 @@ +/** + * 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; + +import java.io.File ; +import java.net.URL ; + +import org.apache.jena.atlas.lib.StrUtils ; +import org.apache.jena.atlas.logging.LogCtl ; +import org.apache.jena.riot.SysRIOT ; +import org.apache.log4j.PropertyConfigurator ; +import org.apache.log4j.helpers.Loader ; + +public class FusekiLogging +{ + private static String log4Jsetup = StrUtils.strjoinNL + ("## Plain output to stdout", + "log4j.appender.jena.plainstdout=org.apache.log4j.ConsoleAppender", + "log4j.appender.jena.plainstdout.target=System.out", + "log4j.appender.jena.plainstdout.layout=org.apache.log4j.PatternLayout", + "log4j.appender.jena.plainstdout.layout.ConversionPattern=%d{HH:mm:ss} %-10c{1} %-5p %m%n", + + "## Plain output to stderr", + "log4j.appender.jena.plainstderr=org.apache.log4j.ConsoleAppender", + "log4j.appender.jena.plainstderr.target=System.err", + "log4j.appender.jena.plainstderr.layout=org.apache.log4j.PatternLayout", + "log4j.appender.jena.plainstderr.layout.ConversionPattern=%d{HH:mm:ss} %-10c{1} %-5p %m%n", + + "## Everything", + "log4j.rootLogger=INFO, jena.plainstdout", + "log4j.logger.com.hp.hpl.jena=WARN", + "log4j.logger.org.openjena=WARN", + "log4j.logger.org.apache.jena=WARN", + + "log4j.logger.org.apache.jena=WARN", + + "# System logs.", + "log4j.logger." + Fuseki.serverLogName + "=INFO", + "log4j.logger." + Fuseki.actionLogName + "=INFO", + "log4j.logger." + Fuseki.adminLogName + "=INFO", + "log4j.logger." + Fuseki.validationLogName + "=INFO", + "log4j.logger." + Fuseki.configLogName + "=INFO", + + "log4j.logger.org.apache.jena.tdb.loader=INFO", + "log4j.logger.org.eclipse.jetty=WARN" , + "log4j.logger.org.apache.shiro=WARN", + + "## Parser output", + "log4j.additivity" + SysRIOT.riotLoggerName + "=false", + "log4j.logger." + SysRIOT.riotLoggerName + "=INFO, jena.plainstdout" + ) ; + + // Set logging. + // 1/ Use log4j.configuration is defined. + // 2/ Use file:log4j.properties + // 3/ Use log4j.properties on the classpath. + // 4/ Use Built in. + + public static void setLogging() { + // No loggers have been created but configuration may have been set up. + String x = System.getProperty("log4j.configuration", null) ; + + if ( x != null && !x.equals("set") ) + // "set" indicates that logging was set before. + return ; + + // Look for a log4j.properties in the current working directory for easy customization. + try { + String fn = "log4j.properties" ; + File f = new File(fn) ; + if ( f.exists() ) { + PropertyConfigurator.configure(fn) ; + System.setProperty("log4j.configuration", "file:" + fn) ; + return ; + } + } catch (Throwable th) {} + + // Try log4j.properties + + // The log4j general is initialization done in a class static + // in LogManager so it can't be called again in any sensible manner. + // Instead, we include the same basic mechanism ... + URL url = Loader.getResource("log4j.properties") ; + if ( url != null ) { + PropertyConfigurator.configure(url); + System.setProperty("log4j.configuration", url.toString()) ; + return ; + } + + // Use builtin. + LogCtl.resetLogging(log4Jsetup) ; + // Stop anything attempting to do it again. + System.setProperty("log4j.configuration", "set") ; + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java new file mode 100644 index 0000000..be9be90 --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java @@ -0,0 +1,26 @@ +/* + * 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; + +import org.apache.jena.web.HttpSC ; + +public class FusekiNotFoundException extends FusekiRequestException +{ + public FusekiNotFoundException(String msg) { super(HttpSC.NOT_FOUND_404, msg) ; } +} http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java new file mode 100644 index 0000000..e197be2 --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java @@ -0,0 +1,57 @@ +/* + * 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; + +import org.apache.jena.web.HttpSC ; + + +public class FusekiRequestException extends FusekiException +{ + public static FusekiRequestException create(int code, String msg) + { + if ( code == HttpSC.NOT_FOUND_404 ) + return new FusekiNotFoundException(msg) ; + return new FusekiRequestException(code, msg) ; + } + + private final int statusCode ; + private final String responseMessage ; + protected FusekiRequestException(int code, String msg) + { + super(msg) ; + this.statusCode = code ; + responseMessage = msg ; + } + + public int getStatusCode() + { + return statusCode ; + } + + public String getResponseMessage() + { + return responseMessage ; + } + + @Override + public String toString() + { + return "HTTP: "+statusCode+" "+getMessage() ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/async/AsyncPool.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/async/AsyncPool.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/async/AsyncPool.java new file mode 100644 index 0000000..1d98ff2 --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/async/AsyncPool.java @@ -0,0 +1,97 @@ +/** + * 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.async; + +import static java.lang.String.format ; + +import java.util.* ; +import java.util.concurrent.* ; + +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.fuseki.server.DataService ; + +/** The set of currently active async tasks */ +public class AsyncPool +{ + private static int nMaxThreads = 4 ; + private static int MAX_FINISHED = 20 ; + + // See Executors.newCachedThreadPool and Executors.newFixedThreadPool + private ExecutorService executor = new ThreadPoolExecutor(0, nMaxThreads, + 120L, TimeUnit.SECONDS, + new LinkedBlockingQueue<Runnable>()) ; + private final Object mutex = new Object() ; + private long counter = 0 ; + private Map<String, AsyncTask> runningTasks = new LinkedHashMap<>() ; + private Map<String, AsyncTask> finishedTasks = new LinkedHashMap<>() ; + + private static AsyncPool instance = new AsyncPool() ; + public static AsyncPool get() + { return instance ; } + + private AsyncPool() { } + + public AsyncTask submit(Runnable task, String displayName, DataService dataService) { + synchronized(mutex) { + String taskId = Long.toString(++counter) ; + Fuseki.serverLog.info(format("Task : %s : %s",taskId, displayName)) ; + Callable<Object> c = Executors.callable(task) ; + AsyncTask asyncTask = new AsyncTask(c, this, taskId, displayName, dataService) ; + Future<Object> x = executor.submit(asyncTask) ; + runningTasks.put(taskId, asyncTask) ; + return asyncTask ; + } + } + + public Collection<AsyncTask> tasks() { + synchronized(mutex) { + List<AsyncTask> x = new ArrayList<>(runningTasks.size()+finishedTasks.size()) ; + x.addAll(runningTasks.values()) ; + x.addAll(finishedTasks.values()) ; + return x ; + } + } + + public void finished(AsyncTask task) { + synchronized(mutex) { + String id = task.getTaskId() ; + runningTasks.remove(id) ; + while ( finishedTasks.size() >= MAX_FINISHED ) + finishedTasks.remove(task.getTaskId()) ; + finishedTasks.put(id, task) ; + } + } + + public AsyncTask getRunningTask(String taskId) { + synchronized(mutex) { + return runningTasks.get(taskId) ; + } + } + + /** Get for any state */ + public AsyncTask getTask(String taskId) { + synchronized(mutex) { + AsyncTask task = runningTasks.get(taskId) ; + if ( task != null ) + return task ; + return finishedTasks.get(taskId) ; + } + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/async/AsyncTask.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/async/AsyncTask.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/async/AsyncTask.java new file mode 100644 index 0000000..0b73f7e --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/async/AsyncTask.java @@ -0,0 +1,114 @@ +/** + * 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.async; + +import static java.lang.String.format ; + +import java.util.concurrent.Callable ; + +import com.hp.hpl.jena.sparql.util.Utils ; + +import org.apache.jena.atlas.lib.InternalErrorException ; +import org.apache.jena.atlas.logging.Log ; +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.fuseki.server.DataService ; +import org.slf4j.Logger ; + +/** An asynchronous task */ +public class AsyncTask implements Callable<Object> +{ + private static Logger log = Fuseki.serverLog ; + + private final Callable<Object> callable ; + private final AsyncPool pool ; + + private final String displayName ; + private final DataService dataService ; + + private String startPoint = null ; + private String finishPoint = null ; + + private final String taskId ; + + /*package*/ AsyncTask(Callable<Object> callable, + AsyncPool pool, + String taskId, + String displayName, + DataService dataService ) { + this.callable = callable ; + this.pool = pool ; + this.taskId = taskId ; + this.displayName = displayName ; + this.dataService = dataService ; + } + + /** Unique task id */ + public String getTaskId() { return taskId ; } + + /** Display name - no newlines */ + public String displayName() { return displayName ; } + + public DataService getDataService() { return dataService ; } + + private void start() { + if ( startPoint != null ) { + String msg = format("[Task %s] Async task has already been started", taskId) ; + Log.warn(Fuseki.serverLog, msg) ; + throw new InternalErrorException("Finish has already been called ["+getTaskId()+"]") ; + } + + Fuseki.serverLog.info(format("[Task %s] starts : %s",taskId, displayName)) ; + startPoint = Utils.nowAsXSDDateTimeString() ; + } + + public void finish() { + if ( finishPoint != null ) { + String msg = format("[Task %s] Async task has already been finished", taskId) ; + Log.warn(Fuseki.serverLog, msg) ; + throw new InternalErrorException("Finish has already been called ["+getTaskId()+"]") ; + } + finishPoint = Utils.nowAsXSDDateTimeString() ; + Fuseki.serverLog.info(format("[Task %s] finishes : %s",taskId, displayName)) ; + } + + @Override + public Object call() { + try { + start() ; + return callable.call() ; + } + catch (Exception ex) { + log.error("Async task threw an expection", ex) ; + return null ; + } + finally { + finish() ; + pool.finished(this) ; + } + } + + public String getStartPoint() { + return startPoint ; + } + + public String getFinishPoint() { + return finishPoint ; + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/authz/AuthorizationFilter403.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/authz/AuthorizationFilter403.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/authz/AuthorizationFilter403.java new file mode 100644 index 0000000..ca4cd2e --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/authz/AuthorizationFilter403.java @@ -0,0 +1,59 @@ +/** + * 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.authz; + +import java.io.IOException ; + +import javax.servlet.ServletRequest ; +import javax.servlet.ServletResponse ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.web.HttpSC ; +import org.apache.shiro.web.filter.authz.AuthorizationFilter ; +import org.apache.shiro.web.util.WebUtils ; + +/** Specialise AuthorizationFilter to yield HTTP 403 on access denied */ +public abstract class AuthorizationFilter403 extends AuthorizationFilter +{ + private String message ; + + protected AuthorizationFilter403(String text) { setMessage(text) ; } + protected AuthorizationFilter403() { this(null) ; } + + /** Set the message used in HTTP 403 responses */ + public void setMessage(String msg) { message = msg ; } + + public String getMessage() { return message ; } + + @Override + protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException { + HttpServletResponse httpResponse ; + try { httpResponse = WebUtils.toHttp(response); } + catch (ClassCastException ex) { + // Not a HTTP Servlet operation + return super.onAccessDenied(request, response) ; + } + if ( message == null ) + httpResponse.sendError(HttpSC.FORBIDDEN_403) ; + else + httpResponse.sendError(HttpSC.FORBIDDEN_403, message) ; + return false ; // No further processing. + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/authz/DenyFilter.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/authz/DenyFilter.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/authz/DenyFilter.java new file mode 100644 index 0000000..aac7ecd --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/authz/DenyFilter.java @@ -0,0 +1,33 @@ +/** + * 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.authz; + +import javax.servlet.ServletRequest ; +import javax.servlet.ServletResponse ; + +/** An authorization filter that always denies access and sends back HTTP 403 */ +public class DenyFilter extends AuthorizationFilter403 { + + public DenyFilter() { super("Access denied") ; } + + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { + return false ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/authz/LocalhostFilter.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/authz/LocalhostFilter.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/authz/LocalhostFilter.java new file mode 100644 index 0000000..71de761 --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/authz/LocalhostFilter.java @@ -0,0 +1,62 @@ +/** + * 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.authz; + +import javax.servlet.ServletRequest ; +import javax.servlet.ServletResponse ; + +import org.apache.shiro.web.filter.authz.PortFilter ; + +/** + * A Filter that can allow or deny access based on whether the + * the host that sent the request is the loopback address (AKA localhost). + * Use of the external IP address of the local machine does not permit access, + * only the loopback interface is authorized. + * Responds with HTTP 403 on any denied request. + * + * Example: + * <pre> + * [main] + * localhost=org.apache.jena.fuseki.authz.LocalhostFilter + * ... + * [urls] + * /LocalFilesForLocalPeople/** = localhost + * </pre> + * @see PortFilter + */ + +public class LocalhostFilter extends AuthorizationFilter403 { + + private static final String message = "Access denied : only localhost access allowed" ; + + public LocalhostFilter() { super(message); } + + private static String LOCALHOST_IpV6 = "0:0:0:0:0:0:0:1" ; + private static String LOCALHOST_IpV4 = "127.0.0.1" ; // Strictly, 127.*.*.* + + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { + String remoteAddr = request.getRemoteAddr() ; + if ( LOCALHOST_IpV6.equals(remoteAddr) || LOCALHOST_IpV4.equals(remoteAddr) ) + return true ; + return false ; + } +} + + http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/Builder.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/Builder.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/Builder.java new file mode 100644 index 0000000..f8ec3cc --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/Builder.java @@ -0,0 +1,149 @@ +/** + * 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 java.lang.String.format ; +import static org.apache.jena.fuseki.FusekiLib.nodeLabel ; +import static org.apache.jena.fuseki.FusekiLib.query ; +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.fuseki.FusekiConfigException ; +import org.apache.jena.fuseki.FusekiLib ; +import org.apache.jena.fuseki.server.DataAccessPoint ; +import org.apache.jena.fuseki.server.DataService ; +import org.apache.jena.fuseki.server.Endpoint ; +import org.apache.jena.fuseki.server.OperationName ; +import org.slf4j.Logger ; + +import com.hp.hpl.jena.assembler.Assembler ; +import com.hp.hpl.jena.datatypes.xsd.XSDDatatype ; +import com.hp.hpl.jena.query.Dataset ; +import com.hp.hpl.jena.query.QuerySolution ; +import com.hp.hpl.jena.query.ResultSet ; +import com.hp.hpl.jena.rdf.model.Literal ; +import com.hp.hpl.jena.rdf.model.RDFNode ; +import com.hp.hpl.jena.rdf.model.Resource ; +import com.hp.hpl.jena.sparql.core.DatasetGraph ; +import com.hp.hpl.jena.sparql.util.FmtUtils ; +import com.hp.hpl.jena.tdb.TDB ; +import com.hp.hpl.jena.vocabulary.RDF ; +public class Builder +{ + private static Logger log = Fuseki.builderLog ; + + /** Build a DataAccessPoint, including DataServiceat Resource svc */ + public static DataAccessPoint buildDataAccessPoint(Resource svc) { + RDFNode n = FusekiLib.getOne(svc, "fu:name") ; + if ( ! n.isLiteral() ) + throw new FusekiConfigException("Not a literal for access point name: "+FmtUtils.stringForRDFNode(n)); + Literal object = n.asLiteral() ; + + if ( object.getDatatype() != null && ! object.getDatatype().equals(XSDDatatype.XSDstring) ) + Fuseki.configLog.error(format("Service name '%s' is not a string", FmtUtils.stringForRDFNode(object))); + String name = object.getLexicalForm() ; + name = DataAccessPoint.canonical(name) ; + + DataService dataService = Builder.buildDataService(svc) ; + DataAccessPoint dataAccess = new DataAccessPoint(name) ; + dataAccess.setDataService(dataService) ; + return dataAccess ; + } + + /** Build a DatasetRef starting at Resource svc */ + public static DataService buildDataService(Resource svc) { + log.info("Service: " + nodeLabel(svc)) ; + // DO REAL WORK + 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) ; + // In case the assembler included ja:contents + TDB.sync(ds) ; + DataService dataService = new DataService(null, ds.asDatasetGraph()) ; + addServiceEP(dataService, OperationName.Query, svc, "fu:serviceQuery") ; + addServiceEP(dataService, OperationName.Update, svc, "fu:serviceUpdate") ; + addServiceEP(dataService, OperationName.Upload, svc, "fu:serviceUpload") ; + addServiceEP(dataService, OperationName.GSP_R, svc, "fu:serviceReadGraphStore") ; + addServiceEP(dataService, OperationName.GSP, svc, "fu:serviceReadWriteGraphStore") ; + + if ( ! dataService.getOperation(OperationName.GSP).isEmpty() ) + dataService.addEndpoint(OperationName.Quads, "") ; + else if ( ! dataService.getOperation(OperationName.GSP_R).isEmpty() ) + dataService.addEndpoint(OperationName.Quads, "") ; + + // XXX +// // 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) ; +// } +// } + + return dataService ; + } + + /** Build a DataService starting at Resource svc */ + public static DataService buildDataService(DatasetGraph dsg, boolean allowUpdate) { + DataService dataService = new DataService(null, dsg) ; + addServiceEP(dataService, OperationName.Query, "query") ; + addServiceEP(dataService, OperationName.Query, "sparql") ; + if ( ! allowUpdate ) { + addServiceEP(dataService, OperationName.Quads, "quads") ; + addServiceEP(dataService, OperationName.GSP_R, "data") ; + return dataService ; + } + addServiceEP(dataService, OperationName.GSP, "data") ; + addServiceEP(dataService, OperationName.Update, "update") ; + addServiceEP(dataService, OperationName.Upload, "upload") ; + addServiceEP(dataService, OperationName.Quads, "") ; + return dataService ; + } + + private static void addServiceEP(DataService dataService, OperationName opName, String epName) { + dataService.addEndpoint(opName, epName) ; + } + + public static RDFNode getOne(Resource svc, String property) { + String ln = property.substring(property.indexOf(':') + 1) ; + ResultSet rs = FusekiLib.query("SELECT * { ?svc " + property + " ?x}", svc.getModel(), "svc", svc) ; + if ( !rs.hasNext() ) + throw new FusekiConfigException("No " + ln + " for service " + FusekiLib.nodeLabel(svc)) ; + RDFNode x = rs.next().get("x") ; + if ( rs.hasNext() ) + throw new FusekiConfigException("Multiple " + ln + " for service " + FusekiLib.nodeLabel(svc)) ; + return x ; + } + + + private static void addServiceEP(DataService dataService, OperationName opName, 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() ; + Endpoint operation = new Endpoint(opName, epName) ; + addServiceEP(dataService, opName, epName); + //log.info(" " + opName.name + " = " + dataAccessPoint.getName() + "/" + epName) ; + } + } + + +} + http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/DataServiceDesc.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/DataServiceDesc.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/DataServiceDesc.java new file mode 100644 index 0000000..2b85c99 --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/DataServiceDesc.java @@ -0,0 +1,107 @@ +/** + * 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 java.io.StringReader ; +import java.util.HashMap ; +import java.util.Map ; + +import org.apache.jena.fuseki.FusekiConfigException ; +import org.apache.jena.fuseki.server.DataService ; +import org.apache.jena.fuseki.server.FusekiServer ; +import org.apache.jena.fuseki.server.FusekiVocab ; +import org.apache.jena.riot.Lang ; +import org.apache.jena.riot.RDFDataMgr ; +import org.apache.jena.riot.RDFLanguages ; + +import com.hp.hpl.jena.rdf.model.Model ; +import com.hp.hpl.jena.rdf.model.ModelFactory ; +import com.hp.hpl.jena.rdf.model.Resource ; +import com.hp.hpl.jena.sparql.util.FmtUtils ; +import com.hp.hpl.jena.sparql.util.TypeNotUniqueException ; +import com.hp.hpl.jena.sparql.util.graph.GraphUtils ; + +// Check whether this is used or needed +public abstract class DataServiceDesc +{ + public static DataServiceDesc createFromTemplate(String templateFile, String dbName) { + Map<String, String> params = new HashMap<>() ; + params.put(Template.NAME, dbName) ; + FusekiServer.addGlobals(params); + String template = TemplateFunctions.templateFile(templateFile, params) ; + Lang lang = RDFLanguages.filenameToLang(templateFile, Lang.TTL) ; + StringReader sr = new StringReader(template) ; + return create(sr, lang) ; + } + + public static DataServiceDesc create(StringReader strReader, Lang lang ) { + Model model = ModelFactory.createDefaultModel() ; + RDFDataMgr.read(model, strReader, "http://base/", lang) ; + Resource root ; + try { + root = GraphUtils.findRootByType(model, FusekiVocab.fusekiService) ; + if ( root == null ) + throw new FusekiConfigException("No root of type " + + FmtUtils.stringForResource(FusekiVocab.fusekiService) + "found") ; + } catch (TypeNotUniqueException ex) { + throw new FusekiConfigException("Multiple items of type: " + FusekiVocab.fusekiService) ; + } + return new DataServiceDescResource(root) ; + } + + public static DataServiceDesc create(DataService dataService) { + return new DataServiceDescPrebuilt(dataService) ; + } + + //public abstract Resource getResource() ; + + public abstract DataService build() ; +// public abstract void unbuild() ; + + + private static class DataServiceDescResource extends DataServiceDesc { + protected Resource resource ; + + protected DataServiceDescResource(Resource resource) { + this.resource = resource ; + } + + public Resource getResource() { return resource ; } + + @Override + public DataService build() { + return Builder.buildDataService(resource) ; + } + } + + private static class DataServiceDescPrebuilt extends DataServiceDesc { + + private DataService dataService ; + + protected DataServiceDescPrebuilt(DataService dataService) { + this.dataService = dataService ; + } + + @Override + public DataService build() { + return dataService ; + } + } + +} http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java new file mode 100644 index 0000000..10319a4 --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java @@ -0,0 +1,261 @@ +/* + * 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 java.io.File ; +import java.io.FilenameFilter ; +import java.lang.reflect.Method ; +import java.util.ArrayList ; +import java.util.Collections ; +import java.util.List ; + +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.FusekiLib ; +import org.apache.jena.fuseki.server.DataAccessPoint ; +import org.apache.jena.fuseki.server.DatasetStatus ; +import org.apache.jena.fuseki.server.FusekiVocab ; +import org.apache.jena.fuseki.server.SystemState ; +import org.apache.jena.riot.RDFDataMgr ; +import org.slf4j.Logger ; + +import com.hp.hpl.jena.assembler.JA ; +import com.hp.hpl.jena.query.Dataset ; +import com.hp.hpl.jena.query.QuerySolution ; +import com.hp.hpl.jena.query.ResultSet ; +import com.hp.hpl.jena.rdf.model.* ; +import com.hp.hpl.jena.sparql.core.assembler.AssemblerUtils ; +import com.hp.hpl.jena.update.UpdateAction ; +import com.hp.hpl.jena.update.UpdateFactory ; +import com.hp.hpl.jena.update.UpdateRequest ; +import com.hp.hpl.jena.vocabulary.RDF ; + +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> readConfigFile(String filename) { + // Old-style config file. + Model model = RDFDataMgr.loadModel(filename) ; + if ( model.size() == 0 ) + return Collections.emptyList() ; + additionalRDF(model) ; + 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. + // ---- Services + ResultSet rs = FusekiLib.query("SELECT * { ?s fu:services [ list:member ?member ] }", model) ; + // If the old config.ttl file becomes just the server configuration file, + // then don't warn here. +// if ( !rs.hasNext() ) +// log.warn("No services found") ; + + List<DataAccessPoint> accessPoints = new ArrayList<>() ; + + for ( ; rs.hasNext() ; ) { + QuerySolution soln = rs.next() ; + Resource svc = soln.getResource("member") ; + DataAccessPoint acc = Builder.buildDataAccessPoint(svc) ; + 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) ; + } + } + + // XXX Move to utils + private static Model additionalRDF(Model m) { + SystemState.init$(); // Why? mvn jetty:run-war + String x1 = StrUtils.strjoinNL + ( SystemState.PREFIXES, + "INSERT { [] ja:loadClass 'com.hp.hpl.jena.tdb.TDB' }", + "WHERE { FILTER NOT EXISTS { [] ja:loadClass 'com.hp.hpl.jena.tdb.TDB' } }" + ) ; + String x2 = StrUtils.strjoinNL + (SystemState.PREFIXES, + "INSERT DATA {", + " tdb:DatasetTDB rdfs:subClassOf ja:RDFDataset .", + " tdb:GraphTDB rdfs:subClassOf ja:Model .", + "}" + ) ; + execute(m, x1) ; + execute(m, x2) ; + return m ; + } + + 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) { + List<DataAccessPoint> dataServiceRef = new ArrayList<>() ; + File d = new File(dir) ; + String[] aFiles = d.list(visibleFiles) ; + if ( aFiles == null ) { + log.warn("Not found: directory for assembler files for services: '"+dir+"'") ; + return Collections.emptyList() ; + } + for ( String assemFile : aFiles ) { + Model m = RDFDataMgr.loadModel(assemFile) ; + DataAccessPoint acc = readConfiguration(m) ; + dataServiceRef.add(acc) ; + } + + return dataServiceRef ; + } + + private static DataAccessPoint readConfiguration(Model m) { + additionalRDF(m) ; + List<Resource> services = getByType(FusekiVocab.fusekiService, m) ; + + if ( services.size() == 0 ) { + log.error("No services found") ; + throw new FusekiConfigException() ; + } + + // Remove? + if ( services.size() > 1 ) { + log.error("Multiple services found") ; + throw new FusekiConfigException() ; + } + + Resource service = services.get(0) ; + DataAccessPoint acc = Builder.buildDataAccessPoint(service) ; + return acc ; + } + + // ---- System database + /** Read the system database */ + public static List<DataAccessPoint> readSystemDatabase(Dataset ds) { + String qs = StrUtils.strjoinNL + (SystemState.PREFIXES , + "SELECT * {" , + " GRAPH ?g {", + " ?s fu:name ?name ;" , + " fu:status ?status ." , + " }", + "}" + ) ; + + List<DataAccessPoint> refs = new ArrayList<>() ; + + 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 resoure of the service description to the containing graph. + Resource svc = m.wrapAsResource(s.asNode()) ; + DataAccessPoint ref = Builder.buildDataAccessPoint(svc) ; + refs.add(ref) ; + } + return refs ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/Template.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/Template.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/Template.java new file mode 100644 index 0000000..f0b6ede --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/Template.java @@ -0,0 +1,52 @@ +/** + * 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 java.nio.file.Path ; + +import org.apache.jena.fuseki.server.FusekiServer ; + +public class Template +{ + public static Path getPath(String templateName) { + return FusekiServer.FUSEKI_BASE.resolve(templateName) ; + } + + public static final String templateDir = "templates" ; + public static final String templateMemFN = templateDir+"/config-mem" ; + public static final String templateTDBFN = templateDir+"/config-tdb" ; + public static final String templateTDBMemFN = templateDir+"/config-tdb-mem" ; + public static final String templateTDBDirFN = templateDir+"/config-tdb-dir" ; + public static final String templateServiceFN = templateDir+"/config-service" ; // Dummy used by dataset-less service. + + // Template may be in a resources area of a jar file so you can't do a directory listing. + public static final String[] templateNames = { + templateMemFN , + templateTDBFN , + templateTDBMemFN , + templateTDBDirFN , + templateServiceFN + } ; + + public static final String NAME = "NAME" ; + public static final String DATA = "DATA" ; + public static final String DIR = "DIR" ; + +} + http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/TemplateFunctions.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/TemplateFunctions.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/TemplateFunctions.java new file mode 100644 index 0000000..41c21c5 --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/build/TemplateFunctions.java @@ -0,0 +1,68 @@ +/** + * 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 java.io.IOException ; +import java.io.InputStream ; +import java.util.Map ; +import java.util.Map.Entry ; + +import org.apache.jena.atlas.io.IO ; +import org.apache.jena.fuseki.Fuseki ; + +import com.hp.hpl.jena.util.FileUtils ; + +public class TemplateFunctions +{ + /** Read in a template from a file, substitute for {NAME} and return the string. */ + public static String templateFile(String templateName, Map<String, String> params) { + String templateFilename = Template.getPath(templateName).toString() ; + String template ; + try { template = FileUtils.readWholeFileAsUTF8(templateFilename) ; } + catch (IOException ex) { + Fuseki.serverLog.error("File not found: "+templateFilename); + IO.exception(ex); return null ; + } + return templateString(template, params) ; + } + + /** Read a template file, substitute for {NAME} and return the model. */ + public static String templateResource(String resourceName, Map<String, String> params) { + String template ; + try { + InputStream in = TemplateFunctions.class.getClassLoader().getResourceAsStream(resourceName) ; + if ( in == null ) + Fuseki.serverLog.error("Resource not found: "+resourceName); + template = FileUtils.readWholeFileAsUTF8(in) ; + } + catch (IOException ex) { + Fuseki.serverLog.error("Error reading resource: "+resourceName); + IO.exception(ex); return null ; + } + return templateString(template, params) ; + } + + /** Create a template from a String */ + public static String templateString(String template, Map<String, String> params) { + for ( Entry<String, String> e : params.entrySet() ) { + template = template.replaceAll("\\{"+e.getKey()+"\\}", e.getValue()) ; + } + return template ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/conneg/ConNeg.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/src/main/java/org/apache/jena/fuseki/conneg/ConNeg.java b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/conneg/ConNeg.java new file mode 100644 index 0000000..41cfd07 --- /dev/null +++ b/jena-fuseki2/src/main/java/org/apache/jena/fuseki/conneg/ConNeg.java @@ -0,0 +1,123 @@ +/* + * 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.conneg; + +import static org.apache.jena.riot.web.HttpNames.hAcceptCharset ; + +import javax.servlet.http.HttpServletRequest ; + +import org.apache.jena.atlas.web.AcceptList ; +import org.apache.jena.atlas.web.MediaRange ; +import org.apache.jena.atlas.web.MediaType ; +import org.slf4j.Logger ; +import org.slf4j.LoggerFactory ; + +public class ConNeg +{ + private static Logger log = LoggerFactory.getLogger(ConNeg.class) ; + // See riot.ContentNeg (client side). + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 + + static public MediaType parse(String contentType) + { + try { + return MediaType.create(contentType) ; + } catch (RuntimeException ex) { return null ; } + } + + static public MediaType match(String headerString, AcceptList offerList) + { + AcceptList l = new AcceptList(headerString) ; + return AcceptList.match(l, offerList) ; + } + + /** Match a single media type against a header string */ + public static String match(String headerString, String mediaRangeStr) + { + AcceptList l = new AcceptList(headerString) ; + MediaRange aItem = new MediaRange(mediaRangeStr) ; // MediaType + MediaType m = l.match(aItem) ; + if ( m == null ) + return null ; + return m.toHeaderString() ; + } + + /*package*/ static String[] split(String s, String splitStr) + { + String[] x = s.split(splitStr,2) ; + for ( int i = 0 ; i < x.length ; i++ ) + { + x[i] = x[i].trim() ; + } + return x ; + } + + public static MediaType chooseCharset(HttpServletRequest httpRequest, + AcceptList myPrefs, + MediaType defaultMediaType) + { + String a = httpRequest.getHeader(hAcceptCharset) ; + if ( log.isDebugEnabled() ) + log.debug("Accept-Charset request: "+a) ; + + MediaType item = choose(a, myPrefs, defaultMediaType) ; + + if ( log.isDebugEnabled() ) + log.debug("Charset chosen: "+item) ; + + return item ; + } + + public static MediaType chooseContentType(HttpServletRequest httpRequest, + AcceptList myPrefs, + MediaType defaultMediaType) + { + String a = WebLib.getAccept(httpRequest) ; + if ( log.isDebugEnabled() ) + log.debug("Accept request: "+a) ; + + MediaType item = choose(a, myPrefs, defaultMediaType) ; + + if ( log.isDebugEnabled() ) + log.debug("Content type chosen: "+item) ; + + return item ; + } + + private static MediaType choose(String headerString, AcceptList myPrefs, + MediaType defaultMediaType) + { + if ( headerString == null ) + return defaultMediaType ; + + AcceptList headerList = new AcceptList(headerString) ; + + if ( myPrefs == null ) + { + MediaType i = headerList.first() ; + if ( i == null ) return defaultMediaType ; + return i ; + } + + MediaType i = AcceptList.match(headerList, myPrefs) ; + if ( i == null ) + return defaultMediaType ; + return i ; + } +}
