http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiCmd.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiCmd.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiCmd.java new file mode 100644 index 0000000..77bafe8 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiCmd.java @@ -0,0 +1,508 @@ +/* + * 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 static org.apache.jena.fuseki.Fuseki.serverLog ; + +import java.io.File ; +import java.io.InputStream ; +import java.util.List ; + +import org.apache.jena.atlas.io.IO ; +import org.apache.jena.atlas.lib.FileOps ; +import org.apache.jena.atlas.lib.Lib ; +import org.apache.jena.atlas.lib.StrUtils ; +import org.apache.jena.atlas.logging.LogCtl ; +import org.apache.jena.fuseki.mgt.ManagementServer ; +import org.apache.jena.fuseki.server.FusekiConfig ; +import org.apache.jena.fuseki.server.SPARQLServer ; +import org.apache.jena.fuseki.server.ServerConfig ; +import org.apache.jena.riot.Lang ; +import org.apache.jena.riot.RDFDataMgr ; +import org.apache.jena.riot.RDFLanguages ; +import org.apache.jena.riot.SysRIOT ; +import org.eclipse.jetty.server.Server ; +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.DatasetGraph ; +import com.hp.hpl.jena.sparql.core.DatasetGraphFactory ; +import com.hp.hpl.jena.tdb.TDB ; +import com.hp.hpl.jena.tdb.TDBFactory ; +import com.hp.hpl.jena.tdb.sys.Names ; +import com.hp.hpl.jena.tdb.transaction.TransactionManager ; + +public class FusekiCmd extends CmdARQ +{ + private static String log4Jsetup = StrUtils.strjoinNL( + "## Plain output to stdout" + , "log4j.appender.jena.plain=org.apache.log4j.ConsoleAppender" + , "log4j.appender.jena.plain.target=System.out" + , "log4j.appender.jena.plain.layout=org.apache.log4j.PatternLayout" + , "log4j.appender.jena.plain.layout.ConversionPattern=%d{HH:mm:ss} %-5p %m%n" + + , "## Plain output with level, to stderr" + , "log4j.appender.jena.plainlevel=org.apache.log4j.ConsoleAppender" + , "log4j.appender.jena.plainlevel.target=System.err" + , "log4j.appender.jena.plainlevel.layout=org.apache.log4j.PatternLayout" + , "log4j.appender.jena.plainlevel.layout.ConversionPattern=%d{HH:mm:ss} %-5p %m%n" + + , "## Everything" + , "log4j.rootLogger=INFO, jena.plain" + , "log4j.logger.com.hp.hpl.jena=WARN" + , "log4j.logger.org.openjena=WARN" + , "log4j.logger.org.apache.jena=WARN" + + , "# Server log." + , "log4j.logger.org.apache.jena.fuseki.Server=INFO" + , "# Request log." + , "log4j.logger.org.apache.jena.fuseki.Fuseki=INFO" + , "log4j.logger.org.apache.jena.tdb.loader=INFO" + , "log4j.logger.org.eclipse.jetty=ERROR" + + , "## Parser output" + , "log4j.additivity."+SysRIOT.riotLoggerName+"=false" + , "log4j.logger."+SysRIOT.riotLoggerName+"=INFO, jena.plainlevel " + ) ; + + + // Set logging. + // 1/ Use log4j.configuration is defined. + // 2/ Use file:log4j.properties + // 3/ Use Built in. + + 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 CmdMain set logging. + // Use standard log4j initialization. + return ; + } + + String fn = "log4j.properties" ; + File f = new File(fn) ; + if ( f.exists() ) { + // Use file log4j.properties + System.setProperty("log4j.configuration", "file:"+fn) ; + return ; + } + // Use built-in for Fuseki. + LogCtl.resetLogging(log4Jsetup) ; + } + + static { setLogging() ; } + + // Arguments: + // --update + + // Specific switches: + + // --admin=on/off + + // --http-update + // --http-get + + // --sparql-query + // --sparql-update + + // pages/validators/ + // pages/control/ + // pages/query/ or /pages/sparql/ + + private static ArgDecl argMgtPort = new ArgDecl(ArgDecl.HasValue, "mgtPort", "mgtport") ; + 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") ; + private static ArgDecl argUber = new ArgDecl(ArgDecl.NoValue, "uber", "über") ; // Use the überservlet (experimental) + private static ArgDecl argBasicAuth = new ArgDecl(ArgDecl.HasValue, "basic-auth") ; + + private static ArgDecl argGSP = new ArgDecl(ArgDecl.NoValue, "gsp") ; // GSP compliance mode + + private static ArgDecl argHome = new ArgDecl(ArgDecl.HasValue, "home") ; + private static ArgDecl argPages = new ArgDecl(ArgDecl.HasValue, "pages") ; + + //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 FusekiCmd(argv).mainRun() ; + } + + private int port = 3030 ; + private int mgtPort = -1 ; + private boolean listenLocal = false ; + + private DatasetGraph dsg = null ; + private String datasetPath = null ; + private boolean allowUpdate = false ; + + private String fusekiConfigFile = null ; + private boolean enableCompression = true ; + private String jettyConfigFile = null ; + private String authConfigFile = null ; + private String homeDir = null ; + private String pagesDir = null ; + + public FusekiCmd(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, "--basic-auth=FILE", "Configure basic auth using provided Jetty realm file, ignored if --jetty-config is used") ; + add(argMgtPort, "--mgtPort=port", "Enable the management commands on the given port") ; + 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) ) + fusekiConfigFile = getValue(argFusekiConfig) ; + + ArgDecl assemblerDescDecl = new ArgDecl(ArgDecl.HasValue, "desc", "dataset") ; + if ( contains(argMem) ) x++ ; + if ( contains(argFile) ) x++ ; + if ( contains(assemblerDescDecl) ) x++ ; + if ( contains(argTDB) ) x++ ; + if ( contains(argMemTDB) ) x++ ; + + if ( fusekiConfigFile != null ) + { + if ( x >= 1 ) + throw new CmdException("Dataset specified on the command line but a configuration file also given.") ; + } + else + { + if ( x != 1 ) + throw new CmdException("Required: either --config=FILE or one of --mem, --file, --loc or --desc") ; + } + + // One of: + // argMem, argFile, argMemTDB, argTDB, + + + if ( contains(argMem) ) + { + log.info("Dataset: in-memory") ; + dsg = DatasetGraphFactory.createMem() ; + } + if ( contains(argFile) ) + { + dsg = DatasetGraphFactory.createMem() ; + // replace by RiotLoader after ARQ refresh. + String filename = getValue(argFile) ; + log.info("Dataset: in-memory: load file: "+filename) ; + if ( ! FileOps.exists(filename) ) + throw new CmdException("File not found: "+filename) ; + + Lang language = RDFLanguages.filenameToLang(filename) ; + if ( language == null ) + throw new CmdException("Can't guess language for file: "+filename) ; + InputStream input = IO.openFile(filename) ; + + if ( RDFLanguages.isQuads(language) ) + RDFDataMgr.read(dsg, filename) ; + else + RDFDataMgr.read(dsg.getDefaultGraph(), filename) ; + } + + if ( contains(argMemTDB) ) + { + log.info("TDB dataset: in-memory") ; + dsg = TDBFactory.createDatasetGraph() ; + } + + if ( contains(argTDB) ) + { + String dir = getValue(argTDB) ; + + if ( Lib.equal(dir, Names.memName) ) { + log.info("TDB dataset: in-memory") ; + } else { + if ( ! FileOps.exists(dir) ) + throw new CmdException("Directory not found: "+dir) ; + log.info("TDB dataset: directory="+dir) ; + } + dsg = TDBFactory.createDatasetGraph(dir) ; + } + + // Otherwise + if ( contains(assemblerDescDecl) ) + { + log.info("Dataset from assembler") ; + Dataset ds = modDataset.createDataset() ; + if ( ds != null ) + dsg = ds.asDatasetGraph() ; + } + + if ( contains(argFusekiConfig) ) + { + if ( dsg != null ) + throw new CmdException("(internal error) Dataset specificed on the command line but a a configuration file also given.") ; + fusekiConfigFile = getValue(argFusekiConfig) ; + } + + if ( contains(argPort) ) + { + String portStr = getValue(argPort) ; + try { + port = Integer.parseInt(portStr) ; + } catch (NumberFormatException ex) + { + throw new CmdException(argPort.getKeyName()+" : bad port number: "+portStr) ; + } + } + + if ( contains(argMgtPort) ) + { + String mgtPortStr = getValue(argMgtPort) ; + try { + mgtPort = Integer.parseInt(mgtPortStr) ; + } catch (NumberFormatException ex) + { + throw new CmdException(argMgtPort.getKeyName()+" : bad port number: "+mgtPortStr) ; + } + } + + if ( contains(argLocalhost) ) + listenLocal = true ; + + if ( fusekiConfigFile == null && dsg == null ) + throw new CmdException("No dataset defined and no configuration file: "+argUsage) ; + + if ( dsg != null ) + { + if ( getPositional().size() == 0 ) + throw new CmdException("No dataset path name given") ; + if ( getPositional().size() > 1 ) + throw new CmdException("Multiple dataset path names given") ; + datasetPath = getPositionalArg(0) ; + if ( datasetPath.length() > 0 && ! datasetPath.startsWith("/") ) + throw new CmdException("Dataset path name must begin with a /: "+datasetPath) ; + + allowUpdate = contains(argAllowUpdate) ; + } + + if ( contains(argTimeout) ) + { + String str = getValue(argTimeout) ; + ARQ.getContext().set(ARQ.queryTimeout, str) ; + } + + if ( contains(argJettyConfig) ) + { + jettyConfigFile = getValue(argJettyConfig) ; + if ( !FileOps.exists(jettyConfigFile) ) + throw new CmdException("No such file: "+jettyConfigFile) ; + } + + if ( contains(argBasicAuth) ) + { + authConfigFile = getValue(argBasicAuth) ; + if ( !FileOps.exists(authConfigFile) ) + throw new CmdException("No such file: " + authConfigFile) ; + } + + if ( contains(argHome) ) + { + List<String> args = super.getValues(argHome) ; + homeDir = args.get(args.size()-1) ; + } + + if ( contains(argPages) ) + { + List<String> args = super.getValues(argPages) ; + pagesDir = args.get(args.size()-1) ; + } + + if ( contains(argGZip) ) + { + if ( ! hasValueOfTrue(argGZip) && ! hasValueOfFalse(argGZip) ) + throw new CmdException(argGZip.getNames().get(0)+": Not understood: "+getValue(argGZip)) ; + enableCompression = super.hasValueOfTrue(argGZip) ; + } + + if ( contains(argUber) ) + SPARQLServer.überServlet = true ; + + if ( contains(argGSP) ) + { + SPARQLServer.überServlet = true ; + Fuseki.graphStoreProtocolPostCreate = true ; + } + + } + + private static String sort_out_dir(String path) + { + path.replace('\\', '/') ; + if ( ! path.endsWith("/")) + path = path +"/" ; + return path ; + } + + @Override + protected void exec() + { + if ( homeDir == null ) + { + if ( System.getenv(Fuseki.FusekiHomeEnv) != null ) + homeDir = System.getenv(Fuseki.FusekiHomeEnv) ; + else + homeDir = "." ; + } + + homeDir = sort_out_dir(homeDir) ; + Fuseki.configLog.info("Home Directory: " + FileOps.fullDirectoryPath(homeDir)); + if ( ! FileOps.exists(homeDir) ) + Fuseki.configLog.warn("No such directory for Fuseki home: "+homeDir) ; + + String staticContentDir = pagesDir ; + if ( staticContentDir == null ) + staticContentDir = homeDir+Fuseki.PagesStatic ; + + Fuseki.configLog.debug("Static Content Directory: "+ FileOps.fullDirectoryPath(staticContentDir)) ; + + if ( ! FileOps.exists(staticContentDir) ) { + Fuseki.configLog.warn("No such directory for static content: " + FileOps.fullDirectoryPath(staticContentDir)) ; + Fuseki.configLog.warn("You may need to set the --pages or --home option to configure static content correctly"); + } + + if ( jettyConfigFile != null ) + Fuseki.configLog.info("Jetty configuration: "+jettyConfigFile) ; + + ServerConfig serverConfig ; + + if ( fusekiConfigFile != null ) + { + Fuseki.configLog.info("Configuration file: "+fusekiConfigFile) ; + serverConfig = FusekiConfig.configure(fusekiConfigFile) ; + } + else + { + serverConfig = FusekiConfig.defaultConfiguration(datasetPath, dsg, allowUpdate, listenLocal) ; + if ( ! allowUpdate ) + Fuseki.serverLog.info("Running in read-only mode."); + } + + // TODO Get from parsing config file. + serverConfig.port = port ; + serverConfig.pages = staticContentDir ; + serverConfig.mgtPort = mgtPort ; + serverConfig.pagesPort = port ; + serverConfig.loopback = listenLocal ; + serverConfig.enableCompression = enableCompression ; + serverConfig.jettyConfigFile = jettyConfigFile ; + serverConfig.authConfigFile = authConfigFile ; + serverConfig.verboseLogging = ( super.isVerbose() || super.isDebug() ) ; + + SPARQLServer server = new SPARQLServer(serverConfig) ; + + // Temporary + Fuseki.setServer(server) ; + + Server mgtServer = null ; + + if ( mgtPort > 0 ) + { + Fuseki.configLog.info("Management services on port "+mgtPort) ; + mgtServer = ManagementServer.createManagementServer(mgtPort) ; + try { mgtServer.start() ; } + catch (java.net.BindException ex) + { serverLog.error("SPARQLServer: Failed to start management server: " + ex.getMessage()) ; System.exit(1) ; } + catch (Exception ex) + { serverLog.error("SPARQLServer: Failed to start management server: " + ex.getMessage(), ex) ; System.exit(1) ; } + } + + server.start() ; + try { server.getServer().join() ; } catch (Exception ex) {} + + if ( mgtServer != null ) + { + try { mgtServer.stop() ; } + catch (Exception e) { serverLog.warn("Failed to cleanly stop the management server", e) ; } + } + System.exit(0) ; + } + + + @Override + protected String getCommandName() + { + return "fuseki" ; + } +}
http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiConfigException.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiConfigException.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiConfigException.java new file mode 100644 index 0000000..5e1b018 --- /dev/null +++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiException.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiException.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiException.java new file mode 100644 index 0000000..04953ce --- /dev/null +++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiLib.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiLib.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiLib.java new file mode 100644 index 0000000..5324793 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiLib.java @@ -0,0 +1,148 @@ +/* + * 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.commons.lang.StringUtils ; +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.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.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 ; + +public class FusekiLib { + /** 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) { + StringBuilder sbuff = new StringBuilder() ; + 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 ( String p : params ) + { + 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 = StringUtils.replaceChars(value, "\r", "") ; + value = StringUtils.replaceChars(value, "\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) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java new file mode 100644 index 0000000..be9be90 --- /dev/null +++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java new file mode 100644 index 0000000..e197be2 --- /dev/null +++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/HttpNames.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/HttpNames.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/HttpNames.java new file mode 100644 index 0000000..65d73c7 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/HttpNames.java @@ -0,0 +1,94 @@ +/* + * 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 HttpNames +{ + // Request + public static final String hAccept = "Accept" ; + public static final String hAcceptEncoding = "Accept-Encoding" ; + public static final String hAcceptCharset = "Accept-Charset" ; + //public static final String hAcceptLanguage = "Accept-Language" ; + + // Response + public static final String xhAcceptRanges = "Accept-Ranges" ; + public static final String hAllow = "Allow" ; + public static final String hContentEncoding = "Content-Encoding" ; + public static final String hContentLengh = "Content-Length" ; + public static final String hContentLocation = "Content-Location" ; + public static final String hContentRange = "Content-Range" ; + public static final String hContentType = "Content-Type" ; + public static final String hRetryAfter = "Retry-After" ; + public static final String hServer = "Server" ; + public static final String hLocation = "Location" ; + public static final String hVary = "Vary" ; + public static final String charset = "charset" ; + + // CORS: + // http://www.w3.org/TR/cors/ http://esw.w3.org/CORS_Enabled + public static final String hAccessControlAllowOrigin = "Access-Control-Allow-Origin" ; + public static final String hAccessControlAllowHeaders = "Access-Control-Allow-Headers" ; + + // Fuseki parameter names + public static final String paramGraph = "graph" ; + public static final String paramGraphDefault = "default" ; + + public static final String paramQuery = "query" ; + public static final String paramQueryRef = "query-ref" ; + public static final String paramDefaultGraphURI = "default-graph-uri" ; + public static final String paramNamedGraphURI = "named-graph-uri" ; + + public static final String paramStyleSheet = "stylesheet" ; + public static final String paramAccept = "accept" ; + public static final String paramOutput1 = "output" ; // See Yahoo! developer: http://developer.yahoo.net/common/json.html + public static final String paramOutput2 = "format" ; // Alternative name + public static final String paramCallback = "callback" ; + public static final String paramForceAccept = "force-accept" ; // Force the accept header at the last moment + public static final String paramTimeout = "timeout" ; + + public static final String paramUpdate = "update" ; + public static final String paramRequest = "request" ; + public static final String paramUsingGraphURI = "using-graph-uri" ; + public static final String paramUsingNamedGraphURI = "using-named-graph-uri" ; + + public static final String METHOD_DELETE = "DELETE"; + public static final String METHOD_HEAD = "HEAD"; + public static final String METHOD_GET = "GET"; + public static final String METHOD_OPTIONS = "OPTIONS"; + public static final String METHOD_PATCH = "PATCH" ; + public static final String METHOD_POST = "POST"; + public static final String METHOD_PUT = "PUT"; + public static final String METHOD_TRACE = "TRACE"; + + public static final String HEADER_IFMODSINCE = "If-Modified-Since"; + public static final String HEADER_LASTMOD = "Last-Modified"; + + // Names for services in the default configuration + public static final String ServiceQuery = "query" ; + public static final String ServiceQueryAlt = "sparql" ; + public static final String ServiceUpdate = "update" ; + public static final String ServiceData = "data" ; + public static final String ServiceUpload = "upload" ; + public static final String ServiceGeneralQuery = "/sparql" ; + + // Posisble values of fields. + // TODO Pull in from results writer. + public static final String valueDefault = "default" ; + +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/Test.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/Test.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/Test.java new file mode 100644 index 0000000..b4f5fed --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/Test.java @@ -0,0 +1,25 @@ +/* + * 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 Test +{ + public static void init() { System.out.println("INIT called") ; } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/conneg/ConNeg.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/conneg/ConNeg.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/conneg/ConNeg.java new file mode 100644 index 0000000..b448c6c --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/conneg/ConNeg.java @@ -0,0 +1,205 @@ +/* + * 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.fuseki.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 ; + +/** + * <p>Content negotiation is a mechanism defined in the HTTP specification + * that makes it possible to serve different versions of a document + * (or more generally, a resource representation) at the same URI, so that + * user agents can specify which version fit their capabilities the best.</p> + * + * <p>ConNeg is used in Fuseki to help matching the content media type requested + * by the user, against the list of offered media types.</p> + * + * @see <a href="http://en.wikipedia.org/wiki/Content_negotiation">http://en.wikipedia.org/wiki/Content_negotiation</a> + * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1">http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1</a> + */ +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 + + /** + * Parses the content type. It splits the string by semi-colon and finds the + * other features such as the "q" quality factor. For a complete documentation + * on how the parsing happens, see + * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1">http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1</a>. + * + * @param contentType content type string + * @return parsed media type + */ + static public MediaType parse(String contentType) + { + try { + return MediaType.create(contentType) ; + } catch (RuntimeException ex) { return null ; } + } + + /** + * <p>Creates a {@link AcceptList} with the given HTTP header string and + * uses the {@link AcceptList#match(MediaType)} method to decide which + * media type matches the HTTP header string.</p> + * + * <p>The <em>q</em> quality factor is used to decide which choice is the best + * match.</p> + * + * @param headerString HTTP header string + * @param offerList accept list + * @return matched media type + */ + 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. + * + * @param headerString HTTP header string + * @param mediaRangeStr Semi-colon separated list of media types + * @return the matched media type or <code>null</code> if there was no match + */ + 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() ; + } + + /** + * Split and trims a string using a given regex. + * + * @param s string + * @param splitStr given regex + * @return an array with the trimmed strings found + */ + /*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 ; + } + + /** + * <p>Chooses the charset by using the Accept-Charset HTTP header.</p> + * + * <p>See {@link ConNeg#choose(String, AcceptList, MediaType)}.</p> + * + * @param httpRequest HTTP request + * @param myPrefs accept list + * @param defaultMediaType default media type + * @return media type chosen + */ + 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 ; + } + + /** + * <p>Choose the content media type by extracting the Accept HTTP header from + * the HTTP request and choosing + * (see {@link ConNeg#choose(String, AcceptList, MediaType)}) a content media + * type that matches the header.</p> + * + * @param httpRequest HTTP request + * @param myPrefs accept list + * @param defaultMediaType default media type + * @return media type chosen + */ + 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 ; + } + + /** + * <p>This method receives a HTTP header string, an {@link AcceptList} and a + * default {@link MediaType}.</p> + * + * <p>If the header string is null, it returns the given default MediaType.</p> + * + * <p>Otherwise it builds an {@link AcceptList} object with the header string + * and uses it to match against the given MediaType.</p> + * + * @param headerString HTTP header string + * @param myPrefs accept list + * @param defaultMediaType default media type + * @return a media type or <code>null</code> if none matched or if the + * HTTP header string and the default media type are <code>null</code>. + */ + 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 ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/conneg/WebLib.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/conneg/WebLib.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/conneg/WebLib.java new file mode 100644 index 0000000..fdeb139 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/conneg/WebLib.java @@ -0,0 +1,60 @@ +/* + * 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 java.util.Enumeration ; + +import javax.servlet.http.HttpServletRequest ; + +import org.apache.jena.fuseki.HttpNames ; + +public class WebLib +{ + /** Split a string, removing whitespace around the split string. + * e.g. Use in splitting HTTP accept/content-type headers. + */ + public 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 ; + } + + /** Migrate to WebLib */ + public static String getAccept(HttpServletRequest httpRequest) + { + // There can be multiple accept headers -- note many tools don't allow these to be this way (e.g. wget, curl) + Enumeration<String> en = httpRequest.getHeaders(HttpNames.hAccept) ; + if ( ! en.hasMoreElements() ) + return null ; + StringBuilder sb = new StringBuilder() ; + String sep = "" ; + for ( ; en.hasMoreElements() ; ) + { + String x = en.nextElement() ; + sb.append(sep) ; + sep = ", " ; + sb.append(x) ; + } + return sb.toString() ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/ActionBackup.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/ActionBackup.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/ActionBackup.java new file mode 100644 index 0000000..c36e8be --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/ActionBackup.java @@ -0,0 +1,196 @@ +/** + * 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.mgt ; + +import static java.lang.String.format ; + +import java.io.* ; +import java.util.concurrent.Callable ; +import java.util.concurrent.ExecutorService ; +import java.util.concurrent.Executors ; +import java.util.zip.GZIPOutputStream ; + +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.atlas.io.IO ; +import org.apache.jena.atlas.lib.FileOps ; +import org.apache.jena.atlas.logging.Log ; +import org.apache.jena.fuseki.FusekiException ; +import org.apache.jena.fuseki.FusekiLib ; +import org.apache.jena.fuseki.server.DatasetRef ; +import org.apache.jena.fuseki.server.DatasetRegistry ; +import org.apache.jena.fuseki.servlets.HttpAction ; +import org.apache.jena.fuseki.servlets.ServletBase ; +import org.apache.jena.riot.Lang ; +import org.apache.jena.riot.RDFDataMgr ; +import org.apache.jena.web.HttpSC ; + +import com.hp.hpl.jena.sparql.core.DatasetGraph ; +import com.hp.hpl.jena.sparql.util.Utils ; + +public class ActionBackup extends ServletBase +{ + public ActionBackup() { super() ; } + + // Limit to one backup at a time. + public static final ExecutorService backupService = Executors.newFixedThreadPool(1) ; + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException + { + String dataset = FusekiLib.safeParameter(request, "dataset") ; + if ( dataset == null ) + { + response.sendError(HttpSC.BAD_REQUEST_400, "Required parameter missing: ?dataset=") ; + return ; + } + + if ( ! dataset.startsWith("/") ) + dataset="/"+dataset ; + + // HttpSession session = request.getSession(true) ; + // session.setAttribute("dataset", dataset) ; + // session.setMaxInactiveInterval(15*60) ; // 10 mins + + boolean known = DatasetRegistry.get().isRegistered(dataset) ; + if (!known) + { + response.sendError(HttpSC.BAD_REQUEST_400, "No such dataset: " + dataset) ; + return ; + } + + long id = allocRequestId(request, response); + HttpAction action = new HttpAction(id, request, response, false) ; + DatasetRef ref = DatasetRegistry.get().get(dataset) ; + action.setDataset(ref); + scheduleBackup(action) ; + } + + static final String BackupArea = "backups" ; + + private void scheduleBackup(final HttpAction action) + { + String dsName = action.dsRef.name ; + final String ds = dsName.startsWith("/")? dsName : "/"+dsName ; + + String timestamp = Utils.nowAsString("yyyy-MM-dd_HH-mm-ss") ; + final String filename = BackupArea + ds + "_" + timestamp ; + FileOps.ensureDir(BackupArea) ; + + try { + final Callable<Boolean> task = new Callable<Boolean>() { + @Override + public Boolean call() throws Exception + { + log.info(format("[%d] Start backup %s to '%s'", action.id, ds, filename)) ; + action.beginRead() ; + try { + backup(action.getActiveDSG(), filename) ; + log.info(format("[%d] Finish backup %s to '%s'", action.id, ds, filename)) ; + } + catch ( RuntimeException ex ) + { + log.info(format("[%d] Exception during backup: ", action.id, ex.getMessage()), ex) ; + return Boolean.FALSE ; + } + finally { + action.endRead() ; + } + return Boolean.TRUE ; + }} ; + + log.info(format("[%d] Schedule backup %s to '%s'", action.id, ds, filename)) ; + backupService.submit(task) ; + } + //catch (FusekiException ex) + catch (RuntimeException ex) + { + log.warn("Unanticipated exception", ex) ; + try { action.response.sendError(HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage()) ; } + catch (IOException e) { IO.exception(e) ; } + return ; + } + + successPage(action, "Backup scheduled - see server log for details") ; + } + + // Share with new ServletBase. + protected static void successPage(HttpAction action, String message) + { + try { + action.response.setContentType("text/html"); + action.response.setStatus(HttpSC.OK_200); + PrintWriter out = action.response.getWriter() ; + out.println("<html>") ; + out.println("<head>") ; + out.println("</head>") ; + out.println("<body>") ; + out.println("<h1>Success</h1>"); + if ( message != null ) + { + out.println("<p>") ; + out.println(message) ; + out.println("</p>") ; + } + out.println("</body>") ; + out.println("</html>") ; + out.flush() ; + } catch (IOException ex) { IO.exception(ex) ; } + } + + public static void backup(DatasetGraph dsg, String backupfile) + { + if ( ! backupfile.endsWith(".nq") ) + backupfile = backupfile+".nq" ; + + OutputStream out = null ; + try + { + if ( true ) + { + // This seems to achive about the same as "gzip -6" + // It's not too expensive in elapsed time but it's not zero cost. + // GZip, large buffer. + out = new FileOutputStream(backupfile+".gz") ; + out = new GZIPOutputStream(out, 8*1024) ; + out = new BufferedOutputStream(out) ; + } + else + { + out = new FileOutputStream(backupfile) ; + out = new BufferedOutputStream(out) ; + } + + RDFDataMgr.write(out, dsg,Lang.NQUADS) ; + out.close() ; + out = null ; + } + catch (FileNotFoundException e) + { + Log.warn(ActionBackup.class, "File not found: "+backupfile) ; + throw new FusekiException("File not found: "+backupfile) ; + } + catch (IOException e) { IO.exception(e) ; } + finally { + try { if (out != null) out.close() ; } + catch (IOException e) { /* ignore */ } + } + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/ActionDataset.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/ActionDataset.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/ActionDataset.java new file mode 100644 index 0000000..fc3d395 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/ActionDataset.java @@ -0,0 +1,121 @@ +/* + * 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.mgt; + +import java.io.IOException ; +import java.io.UnsupportedEncodingException ; + +import javax.servlet.ServletOutputStream ; +import javax.servlet.http.HttpServlet ; +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; +import javax.servlet.http.HttpSession ; + +import org.apache.commons.codec.binary.Base64 ; +import org.apache.jena.fuseki.FusekiLib ; +import org.apache.jena.fuseki.HttpNames ; +import org.apache.jena.fuseki.server.DatasetRegistry ; +import org.apache.jena.web.HttpSC ; + +/** Log-in and choose dataset */ +public class ActionDataset extends HttpServlet +{ + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException + { +// request.getRemoteUser() ; +// request.getUserPrincipal() ; + + String dataset = FusekiLib.safeParameter(request, "dataset") ; + HttpSession session = request.getSession(true) ; + session.setAttribute("dataset", dataset) ; + session.setMaxInactiveInterval(15*60) ; // 10 mins + + boolean known = DatasetRegistry.get().isRegistered(dataset) ; + if ( !known ) + { + response.sendError(HttpSC.BAD_REQUEST_400, "No such dataset: "+dataset) ; + return ; + } + + if ( true ) + { + // Redirect to GET page. + response.setHeader(HttpNames.hLocation, PageNames.pageAfterLogin) ; + response.setStatus(HttpSC.SEE_OTHER_303) ; + } + else + { + // Welcome style - but HTML inline :-( + response.setContentType("text/html"); + response.setStatus(HttpSC.OK_200) ; + ServletOutputStream out = response.getOutputStream() ; + out.print("<p>"+dataset+"("+known+")</p>") ; + + for ( String name : DatasetRegistry.get().keys() ) { + out.print("<li>") ; + out.print(name) ; + out.println("</li>") ; + } + out.println("</ul>") ; + out.println("<p><a href=\"info\">Next</a></p>") ; + } + +// Cookie cookie = new Cookie("org.apache.jena.fuseki.session", dataset) ; +// // 24 hours. +// cookie.setMaxAge(24*60*60) ; + + } + + /** + * This method returns true if the HttpServletRequest contains a valid + * authorisation header + * @param req The HttpServletRequest to test + * @return true if the Authorisation header is valid + */ + + private boolean authenticate(HttpServletRequest req) + { + String authhead=req.getHeader("Authorization"); + + if(authhead!=null) + { + byte[] up = Base64.decodeBase64(authhead.substring(6)) ; + // Decode the authorisation String + String usernpass ; + try + { + usernpass = new String(up, "ascii") ; + } catch (UnsupportedEncodingException e) + { + e.printStackTrace(); + usernpass = null ; + } + // Split the username from the password + String user=usernpass.substring(0,usernpass.indexOf(":")); + String password=usernpass.substring(usernpass.indexOf(":")+1); + + if (user.equals("user") && password.equals("pass")) + return true; + } + + return false; + } + +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/ManagementServer.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/ManagementServer.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/ManagementServer.java new file mode 100644 index 0000000..5e9dca9 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/ManagementServer.java @@ -0,0 +1,99 @@ +/** + * 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.mgt; + +import static org.apache.jena.fuseki.Fuseki.serverLog ; + +import java.util.List ; + +import javax.servlet.http.HttpServlet ; + +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.fuseki.server.FusekiErrorHandler ; +import org.apache.jena.fuseki.servlets.DumpServlet ; +import org.eclipse.jetty.server.Connector ; +import org.eclipse.jetty.server.Server ; +import org.eclipse.jetty.server.nio.SelectChannelConnector ; +import org.eclipse.jetty.servlet.ServletContextHandler ; +import org.eclipse.jetty.servlet.ServletHolder ; + +public class ManagementServer +{ + public static Server createManagementServer(int mgtPort) + { + Fuseki.serverLog.info("Adding management functions") ; + + // Separate Jetty server + Server server = new Server() ; + +// BlockingChannelConnector bcConnector = new BlockingChannelConnector() ; +// bcConnector.setUseDirectBuffers(false) ; +// Connector connector = bcConnector ; + + Connector connector = new SelectChannelConnector() ; + // Ignore idle time. + // If set, then if this goes off, it keeps going off and you get a lot of log messages. + connector.setMaxIdleTime(0) ; // Jetty outputs a lot of messages if this goes off. + connector.setPort(mgtPort); + server.addConnector(connector) ; + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setErrorHandler(new FusekiErrorHandler()) ; + server.setHandler(context); + + // Add the server control servlet + addServlet(context, new MgtCmdServlet(), "/mgt") ; + addServlet(context, new DumpServlet(), "/dump") ; + addServlet(context, new StatsServlet(), "/stats") ; + addServlet(context, new PingServlet(), "/ping") ; + + return server ; + // Old plan +// // Development : server control panel. +// addServlet(context, new ServerServlet(), "/server") ; +// addServlet(context, new ActionBackup(), "/backup") ; + } + + // SHARE + private static void addServlet(ServletContextHandler context, String datasetPath, HttpServlet servlet, List<String> pathSpecs) + { + for ( String pathSpec : pathSpecs ) + { + if ( pathSpec.endsWith("/") ) + pathSpec = pathSpec.substring(0, pathSpec.length()-1) ; + if ( pathSpec.startsWith("/") ) + pathSpec = pathSpec.substring(1, pathSpec.length()) ; + addServlet(context, servlet, datasetPath+"/"+pathSpec) ; + } + } + + private static void addServlet(ServletContextHandler context, HttpServlet servlet, String pathSpec) + { + ServletHolder holder = new ServletHolder(servlet) ; + addServlet(context, holder, pathSpec) ; + } + + private static void addServlet(ServletContextHandler context, ServletHolder holder, String pathSpec) + { + serverLog.debug("Add servlet @ "+pathSpec) ; + context.addServlet(holder, pathSpec) ; + } + +} + http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/MgtCmdServlet.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/MgtCmdServlet.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/MgtCmdServlet.java new file mode 100644 index 0000000..5385e75 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/MgtCmdServlet.java @@ -0,0 +1,169 @@ +/* + * 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. + */ + +/** A servlet that dumps its request + */ + +// Could be neater - much, much neater! + +package org.apache.jena.fuseki.mgt ; + +import java.io.IOException ; +import java.io.PrintWriter ; + +import javax.servlet.http.HttpServlet ; +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.fuseki.server.DatasetRef ; +import org.apache.jena.fuseki.server.SPARQLServer ; +import org.apache.jena.fuseki.server.ServiceRef ; +import org.apache.jena.web.HttpSC ; +import org.slf4j.Logger ; + +import com.hp.hpl.jena.Jena ; +import com.hp.hpl.jena.query.ARQ ; +import com.hp.hpl.jena.tdb.TDB ; + +/** Control functions for a Fuskei server */ + +public class MgtCmdServlet extends HttpServlet +{ + // Experimental - likely to change. + private static Logger log = Fuseki.serverLog ; + + public MgtCmdServlet() + { + + } + + @Override + public void init() + { + return ; + } + + public static String paramCmd = "cmd" ; + public static String cmdBackup = "backup" ; // &dataset=/datasetname + public static String cmdRestart = "restart" ; // Not implemented. + public static String cmdShutdown = "shutdown" ; // Server stops, no questions asked. (Not implemented) + + ActionBackup actionBackup = new ActionBackup() ; + + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + // Commands format: + // ?cmd=backup&<other args per command> + + String[] args = req.getParameterValues(paramCmd) ; + if ( args == null ) { + resp.setContentType("text/plain") ; + resp.setStatus(HttpSC.BAD_REQUEST_400) ; + + return ; + } + for ( String cmd : args ) { + if ( log.isInfoEnabled() ) + log.info("Management command: " + cmd) ; + + if ( cmd.equalsIgnoreCase(cmdBackup) ) { + actionBackup.doPost(req, resp) ; + continue ; + } + if ( cmd.equalsIgnoreCase(cmdRestart) ) { + + continue ; + } + if ( cmd.equalsIgnoreCase(cmdShutdown) ) { + Fuseki.getServer().stop() ; + continue ; + } + log.warn("Unrecognized command : " + cmd) ; + + } + } + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) + { + try { + // serverLog.info("Fuseki Server Config servlet") ; + + PrintWriter out = resp.getWriter() ; + resp.setContentType("text/plain") ; + SPARQLServer server = Fuseki.getServer() ; + + out.println("Software:") ; + String fusekiVersion = Fuseki.VERSION ; + if ( fusekiVersion.equals("${project.version}") ) + fusekiVersion = "(development)" ; + + out.printf(" %s %s\n", Fuseki.NAME, fusekiVersion) ; + out.printf(" %s %s\n", TDB.NAME, TDB.VERSION) ; + out.printf(" %s %s\n", ARQ.NAME, ARQ.VERSION) ; + out.printf(" %s %s\n", Jena.NAME, Jena.VERSION) ; + + // out.printf("Port: %s\n", + // server.getServer().getConnectors()[0].getPort()) ; + out.println() ; + + for ( DatasetRef dsRef : server.getDatasets() ) { + datasetRefDetails(out, dsRef) ; + out.println() ; + } + } + catch (IOException ex) {} + } + + private static void datasetRefDetails(PrintWriter out, DatasetRef dsRef) + { + if ( dsRef.name != null ) + out.println("Name = " + dsRef.name) ; + else + out.println("Name = <unset>") ; + + endpointDetail(out, "Query", dsRef, dsRef.query) ; + endpointDetail(out, "Update", dsRef, dsRef.update) ; + endpointDetail(out, "Upload", dsRef, dsRef.upload) ; + endpointDetail(out, "Graphs(Read)", dsRef, dsRef.readGraphStore) ; + endpointDetail(out, "Graphs(RW)", dsRef, dsRef.readWriteGraphStore) ; + } + + private static void endpointDetail(PrintWriter out, String label, DatasetRef dsRef, ServiceRef service) + { + boolean first = true ; + out.printf(" %-15s :: ", label) ; + + for ( String s : service.endpoints ) { + if ( !first ) + out.print(" , ") ; + first = false ; + s = "/" + dsRef.name + "/" + s ; + out.print(s) ; + } + out.println() ; + } + + @Override + public String getServletInfo() + { + return "Fuseki Control Servlet" ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/MgtFunctions.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/MgtFunctions.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/MgtFunctions.java new file mode 100644 index 0000000..e43b1e2 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/MgtFunctions.java @@ -0,0 +1,180 @@ +/* + * 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.mgt; + +import java.util.List ; + +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpSession ; + +import org.apache.jena.atlas.io.IndentedLineBuffer ; +import org.apache.jena.atlas.iterator.Iter ; +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.fuseki.server.DatasetRef ; +import org.apache.jena.fuseki.server.DatasetRegistry ; +import org.apache.jena.fuseki.server.ServiceRef ; + +import com.hp.hpl.jena.shared.PrefixMapping ; +import com.hp.hpl.jena.sparql.core.DatasetGraph ; +import com.hp.hpl.jena.sparql.core.Prologue ; +import com.hp.hpl.jena.sparql.serializer.PrologueSerializer ; +import com.hp.hpl.jena.tdb.store.DatasetGraphTDB ; + +/** Avoid code in JSPs */ +public class MgtFunctions +{ + /** Return the name of the current dataset */ + public static String dataset(HttpServletRequest request, String dftValue) + { + String ds = dataset(request) ; + if ( ds == null ) + return dftValue ; + return ds ; + } + + /** Return the name of the current dataset */ + public static String dataset(HttpServletRequest request) + { + HttpSession session = request.getSession(false) ; + if ( session == null ) + return "No session"; + String ds = (String)session.getAttribute("dataset") ; + return ds ; + } + + /** Return the dataset description reference for currnet dataset */ + public static DatasetRef datasetDesc(HttpServletRequest request) + { + HttpSession session = request.getSession(false) ; + if ( session == null ) + return null ; + String ds = (String)session.getAttribute("dataset") ; + return DatasetRegistry.get().get(ds) ; + } + + /** Return lists of datasets */ + public static List<String> datasets(HttpServletRequest request) + { + return Iter.toList(DatasetRegistry.get().keys()) ; + } + + /** Return name of */ + public static String actionDataset(HttpServletRequest request) + { + return PageNames.actionDatasetNames ; + } + + // Service name getters ... + + /** Return a SPARQL query service name for the dataset */ + public static String serviceQuery(String dataset) + { + String dft = "sparql" ; + DatasetRef ref = getFromRegistry(dataset) ; + if ( ref == null ) + return dft ; + return serviceNameOrDefault(ref.query, dft) ; + } + + /** Return a SPARQL update service name for the dataset */ + public static String serviceUpdate(String dataset) + { + String dft = "update" ; + DatasetRef ref = getFromRegistry(dataset) ; + if ( ref == null ) + return dft ; + return serviceNameOrDefault(ref.update, dft) ; + } + + /** Return a SPARQL upload service name for the dataset */ + public static String serviceUpload(String dataset) + { + String dft = "upload" ; + DatasetRef ref = getFromRegistry(dataset) ; + if ( ref == null ) + return dft ; + return serviceNameOrDefault(ref.upload, dft) ; + } + + /** Return a SPARQL Graph Store Protocol (Read) service name for the dataset */ + public static String serviceGraphRead(String dataset) + { + String dft = "get" ; + DatasetRef ref = getFromRegistry(dataset) ; + if ( ref == null ) + return dft ; + return serviceNameOrDefault(ref.readGraphStore, dft) ; + } + + /** Return a SPARQL Graph Store Protocol (Read-Write) service name for the dataset */ + public static String serviceGraphReadWrite(String dataset) + { + String dft = "data" ; + DatasetRef ref = getFromRegistry(dataset) ; + if ( ref == null ) + return dft ; + return serviceNameOrDefault(ref.readWriteGraphStore, dft) ; + } + + private static DatasetRef getFromRegistry(String dataset) + { + DatasetRegistry registry = DatasetRegistry.get() ; + if ( registry == null ) + { + Fuseki.serverLog.warn("No dataset registry") ; + return null ; + } + + DatasetRef ref = registry.get(dataset) ; + if ( ref == null ) + Fuseki.serverLog.warn("Dataset not found: "+dataset) ; + return ref ; + } + + private static String serviceNameOrDefault(ServiceRef service, String defaultValue) + { + if ( service.endpoints.isEmpty() ) + return defaultValue ; + String x = service.endpoints.get(0) ; + if ( x.startsWith("/") ) + x = x.substring(1) ; + return x ; + } + + /** Return prefixes for the datasets, SPARQL syntax. */ + public static String prefixes(HttpServletRequest request) + { + String dsName = dataset(request) ; + DatasetRef desc = getFromRegistry(dsName) ; + if ( desc == null ) + return "<not found>" ; + DatasetGraph dsg = desc.dataset ; + + if ( dsg instanceof DatasetGraphTDB ) + { + PrefixMapping pmap = ((DatasetGraphTDB)dsg).getPrefixes().getPrefixMapping() ; + Prologue prologue = new Prologue(pmap) ; + IndentedLineBuffer buff = new IndentedLineBuffer() ; + PrologueSerializer.output(buff, prologue) ; + buff.append("\n") ; + return buff.asString() ; + } + return "" ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/PageNames.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/PageNames.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/PageNames.java new file mode 100644 index 0000000..4dc315b --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/PageNames.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.mgt; + +public class PageNames +{ + //public static final String pageControlPanel = "/control-panel.tpl" ; + + /** The dispatch URL for the place to go after login (0.2.1 - login = choose dataset) */ + public static final String pageAfterLogin = "/sparql.tpl" ; + + /** This is the full web dispatch URL: control-panel.tpl knowns + * the name indirectly because it gets it via velocity + * calling mgt.actionDataset. + */ + public static final String actionDatasetNames = "/$/datasets" ; +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/PingServlet.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/PingServlet.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/PingServlet.java new file mode 100644 index 0000000..5d4ca53 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/PingServlet.java @@ -0,0 +1,75 @@ +/** + * 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.mgt; + +import static org.apache.jena.riot.WebContent.charsetUTF8 ; +import static org.apache.jena.riot.WebContent.contentTypeTextPlain ; + +import java.io.IOException ; + +import javax.servlet.ServletOutputStream ; +import javax.servlet.http.HttpServlet ; +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.riot.web.HttpNames ; +import org.apache.jena.web.HttpSC ; + +import com.hp.hpl.jena.sparql.util.Utils ; + +/** + * The ping servlet provides a low costy, uncached endpoint that can be used + * to determine if this component is running and responding. For example, + * a nagios check should use this endpoint. + */ +public class PingServlet extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + doCommon(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) { + doCommon(req, resp); + } + + + @Override + protected void doHead(HttpServletRequest req, HttpServletResponse resp) { + doCommon(req, resp); + } + + protected void doCommon(HttpServletRequest request, HttpServletResponse response) { + try { + response.setHeader(HttpNames.hCacheControl, "must-revalidate,no-cache,no-store"); + response.setHeader(HttpNames.hPragma, "no-cache"); + response.setContentType(contentTypeTextPlain); + response.setCharacterEncoding(charsetUTF8) ; + response.setStatus(HttpSC.OK_200); + ServletOutputStream out = response.getOutputStream() ; + out.println(Utils.nowAsXSDDateTimeString()); + } catch (IOException ex) { + Fuseki.serverLog.warn("ping :: IOException :: "+ex.getMessage()); + } + } +} + +
