http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/StatsServlet.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/StatsServlet.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/StatsServlet.java new file mode 100644 index 0000000..e165355 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/StatsServlet.java @@ -0,0 +1,171 @@ +/** + * 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.util.Iterator ; + +import javax.servlet.ServletOutputStream ; +import javax.servlet.http.HttpServlet ; +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.atlas.json.JSON ; +import org.apache.jena.atlas.json.JsonArray ; +import org.apache.jena.atlas.json.JsonObject ; +import org.apache.jena.fuseki.server.* ; +import org.apache.jena.riot.WebContent ; + +public class StatsServlet extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + //throws ServletException, IOException + { + try { + // Conneg etc. + statsJSON(req, resp) ; + } catch (IOException e) + { } + } + + private void statsJSON(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + ServletOutputStream out = resp.getOutputStream() ; + resp.setContentType(WebContent.contentTypeJSON); + resp.setCharacterEncoding(WebContent.charsetUTF8) ; + + /* + * { "server" : .... + * "datasets" : { + * "ds1": { counters... } + * GSP stucture? + * + */ + + JsonObject obj = new JsonObject() ; + JsonObject datasets = new JsonObject() ; + JsonObject server = new JsonObject() ; + server.put("host", req.getLocalName()+":"+req.getLocalPort()) ; + + for ( String ds : DatasetRegistry.get().keys() ) + statsJSON(datasets, ds) ; + + obj.put("server", server) ; + obj.put("datasets", datasets) ; + + JSON.write(out, obj) ; + out.flush() ; + } + + private void statsJSON(JsonObject datasets, String ds) { + DatasetRef desc = DatasetRegistry.get().get(ds) ; + JsonObject stats = new JsonObject() ; + datasets.put(ds, stats) ; + stats.put(CounterName.Requests.name(), desc.getCounters().value(CounterName.Requests)) ; + stats.put(CounterName.RequestsGood.name(), desc.getCounters().value(CounterName.RequestsGood)) ; + stats.put(CounterName.RequestsBad.name(), desc.getCounters().value(CounterName.RequestsBad)) ; + JsonObject services = new JsonObject() ; + +// JsonArray endpoints = new JsonArray() ; +// services.put("endpoints", endpoints) ; +// JsonArray srvNames = new JsonArray() ; +// services.put("names", srvNames) ; + + // There can be several endpoints for one service. + for ( ServiceRef srvRef : desc.getServiceRefs() ) { + JsonObject epStats = new JsonObject() ; + statsJSON(epStats, srvRef) ; + services.put(srvRef.name, epStats) ; + JsonArray endpoints = new JsonArray() ; + epStats.put("endpoints", endpoints) ; + for ( String ep : srvRef.endpoints) { + endpoints.add(ep) ; + } + } + stats.put("services", services) ; + } + + private void statsJSON(JsonObject epStats, ServiceRef srvRef) { + for (CounterName cn : srvRef.getCounters().counters()) { + Counter c = srvRef.getCounters().get(cn) ; + epStats.put(cn.name(), c.value()) ; + } + } + + private void statsTxt(HttpServletResponse resp) throws IOException + { + ServletOutputStream out = resp.getOutputStream() ; + resp.setContentType(WebContent.contentTypeTextPlain); + resp.setCharacterEncoding(WebContent.charsetUTF8) ; + + Iterator<String> iter = DatasetRegistry.get().keys().iterator() ; + while(iter.hasNext()) + { + String ds = iter.next() ; + DatasetRef desc = DatasetRegistry.get().get(ds) ; + statsTxt(out, desc) ; + if ( iter.hasNext() ) + out.println() ; + } + out.flush() ; + } + private void statsTxt(ServletOutputStream out, DatasetRef desc) throws IOException + { + out.println("Dataset: "+desc.name) ; + out.println(" Requests = "+desc.getCounters().value(CounterName.Requests)) ; + out.println(" Good = "+desc.getCounters().value(CounterName.RequestsGood)) ; + out.println(" Bad = "+desc.getCounters().value(CounterName.RequestsBad)) ; + + out.println(" SPARQL Query:") ; + out.println(" Request = "+desc.query.getCounters().value(CounterName.Requests)) ; + out.println(" Good = "+desc.query.getCounters().value(CounterName.RequestsGood)) ; + out.println(" Bad requests = "+desc.query.getCounters().value(CounterName.RequestsBad)) ; + out.println(" Timeouts = "+desc.query.getCounters().value(CounterName.QueryTimeouts)) ; + out.println(" Bad exec = "+desc.query.getCounters().value(CounterName.QueryExecErrors)) ; + + out.println(" SPARQL Update:") ; + out.println(" Request = "+desc.update.getCounters().value(CounterName.Requests)) ; + out.println(" Good = "+desc.update.getCounters().value(CounterName.RequestsGood)) ; + out.println(" Bad requests = "+desc.update.getCounters().value(CounterName.RequestsBad)) ; + out.println(" Bad exec = "+desc.update.getCounters().value(CounterName.UpdateExecErrors)) ; + + out.println(" Upload:") ; + out.println(" Requests = "+desc.upload.getCounters().value(CounterName.Requests)) ; + out.println(" Good = "+desc.upload.getCounters().value(CounterName.RequestsGood)) ; + out.println(" Bad = "+desc.upload.getCounters().value(CounterName.RequestsBad)) ; + + out.println(" SPARQL Graph Store Protocol:") ; + out.println(" GETs = "+gspValue(desc, CounterName.GSPget)+ " (good="+gspValue(desc, CounterName.GSPgetGood)+"/bad="+gspValue(desc, CounterName.GSPgetBad)+")") ; + out.println(" PUTs = "+gspValue(desc, CounterName.GSPput)+ " (good="+gspValue(desc, CounterName.GSPputGood)+"/bad="+gspValue(desc, CounterName.GSPputBad)+")") ; + out.println(" POSTs = "+gspValue(desc, CounterName.GSPpost)+ " (good="+gspValue(desc, CounterName.GSPpostGood)+"/bad="+gspValue(desc, CounterName.GSPpostBad)+")") ; + out.println(" DELETEs = "+gspValue(desc, CounterName.GSPdelete)+ " (good="+gspValue(desc, CounterName.GSPdeleteGood)+"/bad="+gspValue(desc, CounterName.GSPdeleteBad)+")") ; + out.println(" HEADs = "+gspValue(desc, CounterName.GSPhead)+ " (good="+gspValue(desc, CounterName.GSPheadGood)+"/bad="+gspValue(desc, CounterName.GSPheadBad)+")") ; + } + + private long gspValue(DatasetRef desc, CounterName cn) { + long x1 = desc.readGraphStore.getCounters().value(cn) ; + long x2 = desc.readWriteGraphStore.getCounters().value(cn) ; + return x1+x2 ; + } + + +} + +
http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/GraphLoadUtils.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/GraphLoadUtils.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/GraphLoadUtils.java new file mode 100644 index 0000000..da16863 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/GraphLoadUtils.java @@ -0,0 +1,76 @@ +/* + * 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.migrate; + +import org.apache.jena.atlas.web.TypedInputStream ; +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.riot.RDFDataMgr ; +import org.apache.jena.riot.system.StreamRDF ; +import org.apache.jena.riot.system.StreamRDFLib ; + +import com.hp.hpl.jena.graph.Factory ; +import com.hp.hpl.jena.graph.Graph ; +import com.hp.hpl.jena.rdf.model.Model ; +import com.hp.hpl.jena.rdf.model.ModelFactory ; + +/** A packaging of code to do a controlled read of a graph or model */ + +public class GraphLoadUtils +{ + // ---- Model level + + public static Model readModel(String uri, int limit) + { + Graph g = Factory.createGraphMem() ; + readUtil(g, uri, limit) ; + return ModelFactory.createModelForGraph(g) ; + } + + public static void loadModel(Model model, String uri, int limit) + { + Graph g = model.getGraph() ; + readUtil(g, uri, limit) ; + } + + // ---- Graph level + + public static Graph readGraph(String uri, int limit) + { + Graph g = Factory.createGraphMem() ; + readUtil(g, uri, limit) ; + return g ; + } + + public static void loadGraph(Graph g, String uri, int limit) + { + readUtil(g, uri, limit) ; + } + + // ** Worker. + private static void readUtil(Graph graph, String uri, int limit) + { + // We need to do this ourselves, not via riot, to use the webStreamManager + StreamRDF sink = StreamRDFLib.graph(graph) ; + sink = new SinkRDFLimited(sink, limit) ; + + TypedInputStream input = Fuseki.webStreamManager.open(uri) ; + RDFDataMgr.parse(sink, input, uri) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/Registry.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/Registry.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/Registry.java new file mode 100644 index 0000000..ca30be1 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/Registry.java @@ -0,0 +1,42 @@ +/* + * 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.migrate; + +import java.util.Collection ; +import java.util.HashMap ; +import java.util.Map ; + +public class Registry<T> +{ + protected Map<String, T> registry = new HashMap<String, T>() ; + + public Registry() {} + + public void put(String key, T value) { registry.put(key, value) ; } + + public T get(String key) { return registry.get(key) ; } + + public boolean isRegistered(String key) { return registry.containsKey(key) ; } + public void remove(String key) { registry.remove(key) ; } + public Collection<String> keys() { return registry.keySet() ; } + //public Iterator<String> keys() { return registry.keySet().iterator() ; } + + public int size() { return registry.size() ; } + public boolean isEmpty() { return registry.isEmpty() ; } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/SinkRDFLimited.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/SinkRDFLimited.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/SinkRDFLimited.java new file mode 100644 index 0000000..514d756 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/SinkRDFLimited.java @@ -0,0 +1,55 @@ +/* + * 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.migrate; + +import org.apache.jena.riot.system.StreamRDF ; +import org.apache.jena.riot.system.StreamRDFWrapper ; + +import com.hp.hpl.jena.graph.Triple ; +import com.hp.hpl.jena.sparql.core.Quad ; + +public class SinkRDFLimited extends StreamRDFWrapper +{ + private long count = 0 ; + private final long limit ; + + public SinkRDFLimited(StreamRDF output, long limit) + { + super(output) ; + this.limit = limit ; + } + + @Override + public void triple(Triple triple) + { + count++ ; + super.triple(triple) ; + } + + @Override + public void quad(Quad quad) + { + count++ ; + super.quad(quad) ; + } + + public long getCount() { return count ; } + public long getLimit() { return limit ; } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counter.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counter.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counter.java new file mode 100644 index 0000000..88d4d37 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counter.java @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.fuseki.server; + +import java.util.concurrent.atomic.AtomicLong ; + +/** A statistics counter */ +public class Counter +{ + private AtomicLong counter = new AtomicLong(0) ; + + public Counter() {} + + public void inc() { counter.incrementAndGet() ; } + public void dec() { counter.decrementAndGet() ; } + public long value() { return counter.get() ; } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterMXBean.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterMXBean.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterMXBean.java new file mode 100644 index 0000000..2de7658 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterMXBean.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.server; + +public interface CounterMXBean +{ + long getValue() ; +} + http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterName.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterName.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterName.java new file mode 100644 index 0000000..2952aa8 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterName.java @@ -0,0 +1,83 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.fuseki.server; + +/** Names for all counters */ +public enum CounterName { + // There are generic names - apply to all services and datasets + // and specific ones. + + + // Total request received + Requests("requests"), + // .. of which some and "good" and some are "bad". + // #"good" + #"bad" roughly equals #"requests" + // except that the total is incremented at the start, and the outcome at the end. + // There may also be short term consistency issues. + RequestsGood("requests.good"), + RequestsBad("requests.bad") , + + // SPARQL Protocol - query and update - together with upload. + + // Query - standard and ... + QueryTimeouts("query.timeouts") , + QueryExecErrors("query.execerrors") , + + // Update - standard and ... + UpdateExecErrors("update.execerrors"), + + // Upload ... standard counters + + // Graph Store Protocol. + + // For each HTTP method + GSPget("gsp.get.requests") , + GSPgetGood("gsp.get.requests.good") , + GSPgetBad("gsp.get.requests.bad") , + + GSPpost("gsp.post.requests") , + GSPpostGood("gsp.post.requests.good") , + GSPpostBad("gsp.post.requests.bad") , + + GSPdelete("gsp.delete.requests") , + GSPdeleteGood("gsp.delete.requests.good") , + GSPdeleteBad("gsp.delete.requests.bad") , + + GSPput("gsp.put.requests") , + GSPputGood("gsp.put.requests.good") , + GSPputBad("gsp.put.requests.bad") , + + GSPhead("gsp.head.requests") , + GSPheadGood("gsp.head.requests.good") , + GSPheadBad("gsp.head.requests.bad") , + + GSPpatch("gsp.patch.requests") , + GSPpatchGood("gsp.patch.requests.good") , + GSPpatchBad("gsp.patch.requests.bad") , + + GSPoptions("gsp.options.requests") , + GSPoptionsGood("gsp.options.requests.good") , + GSPoptionsBad("gsp.options.requests.bad") , + + ; + + private String name ; + private CounterName(String name) { this.name = name ; } + +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterSet.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterSet.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterSet.java new file mode 100644 index 0000000..7d0d622 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterSet.java @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.fuseki.server ; + +import java.util.Collection ; +import java.util.HashMap ; +import java.util.Map ; + +import org.slf4j.Logger ; +import org.slf4j.LoggerFactory ; + +/** A collection of counters */ +public class CounterSet { + private static Logger log = LoggerFactory.getLogger(CounterSet.class) ; + + private Map<CounterName, Counter> counters = new HashMap<CounterName, Counter>() ; + + public CounterSet() {} + + public Collection<CounterName> counters() { + return counters.keySet() ; + } + + public void inc(CounterName c) { + get(c).inc() ; + } + + public void dec(CounterName c) { + get(c).dec() ; + } + + public long value(CounterName c) { + return get(c).value() ; + } + + public void add(CounterName counterName) { + if ( counters.containsKey(counterName) ) { + log.warn("Duplicate counter in counter set: " + counterName) ; + return ; + } + counters.put(counterName, new Counter()) ; + } + + public boolean contains(CounterName cn) { + return counters.containsKey(cn) ; + } + + public Counter get(CounterName cn) { + Counter c = counters.get(cn) ; + if ( c == null ) + log.warn("No counter in counter set: " + cn) ; + return c ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counters.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counters.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counters.java new file mode 100644 index 0000000..4e5ca4b --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counters.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.server; + +/** Objects that have a counter set */ +public interface Counters { + public CounterSet getCounters() ; +} + http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetMXBean.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetMXBean.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetMXBean.java new file mode 100644 index 0000000..bf38229 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetMXBean.java @@ -0,0 +1,35 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.fuseki.server; + +public interface DatasetMXBean +{ + String getName() ; + + long getRequests() ; + long getRequestsGood() ; + long getRequestsBad() ; + +// void enable() ; +// void disable() ; +// void setReadOnly() ; +// boolean getReadOnly() ; + +} + http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRef.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRef.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRef.java new file mode 100644 index 0000000..040c759 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRef.java @@ -0,0 +1,241 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.fuseki.server; + +import java.util.* ; +import java.util.concurrent.atomic.AtomicLong ; + +import org.apache.jena.fuseki.Fuseki ; + +import com.hp.hpl.jena.query.ReadWrite ; +import com.hp.hpl.jena.sparql.core.DatasetGraph ; + +public class DatasetRef implements DatasetMXBean, Counters +{ + public String name = null ; + public DatasetGraph dataset = null ; + + public ServiceRef query = new ServiceRef("query") ; + public ServiceRef update = new ServiceRef("update") ; + public ServiceRef upload = new ServiceRef("upload") ; + public ServiceRef readGraphStore = new ServiceRef("gspRead") ; + public ServiceRef readWriteGraphStore = new ServiceRef("gspReadWrite") ; + + // Dataset-level counters. + private final CounterSet counters = new CounterSet() ; + @Override + public CounterSet getCounters() { return counters ; } + + private Map<String, ServiceRef> endpoints = new HashMap<String, ServiceRef>() ; + private List<ServiceRef> serviceRefs = new ArrayList<ServiceRef>() ; + private boolean initialized = false ; + + // Two step initiation (c.f. Builder pattern) + // Create object - incrementally set state - call init to calculate internal datastructures. + public DatasetRef() {} + public void init() { + if ( initialized ) + Fuseki.serverLog.warn("Already initialized: dataset = "+name) ; + initialized = true ; + initServices() ; + } + + @Override public String toString() { return "DatasetRef:'"+name+"'" ; } + + private void initServices() { + add(query) ; + add(update) ; + add(upload) ; + add(readGraphStore) ; + add(readWriteGraphStore) ; + addCounters() ; + } + + private void add(ServiceRef srvRef) { + serviceRefs.add(srvRef) ; + for ( String ep : srvRef.endpoints ) + endpoints.put(ep, srvRef) ; + } + + public ServiceRef getServiceRef(String service) { + if ( ! initialized ) + Fuseki.serverLog.error("Not initialized: dataset = "+name) ; + if ( service.startsWith("/") ) + service = service.substring(1, service.length()) ; + return endpoints.get(service) ; + } + + public Collection<ServiceRef> getServiceRefs() { + return serviceRefs ; + } + + /** Counter of active read transactions */ + public AtomicLong activeReadTxn = new AtomicLong(0) ; + + /** Counter of active write transactions */ + public AtomicLong activeWriteTxn = new AtomicLong(0) ; + + /** Cumulative counter of read transactions */ + public AtomicLong totalReadTxn = new AtomicLong(0) ; + + /** Cumulative counter of writer transactions */ + public AtomicLong totalWriteTxn = new AtomicLong(0) ; + +// /** Count of requests received - anyzservice */ +// public AtomicLong countServiceRequests = new AtomicLong(0) ; +// /** Count of requests received that fail in some way */ +// public AtomicLong countServiceRequestsBad = new AtomicLong(0) ; +// /** Count of requests received that fail in some way */ +// public AtomicLong countServiceRequestsOK = new AtomicLong(0) ; +// +// // SPARQL Query +// +// /** Count of SPARQL Queries successfully executed */ +// public AtomicLong countQueryOK = new AtomicLong(0) ; +// /** Count of SPARQL Queries with syntax errors */ +// public AtomicLong countQueryBadSyntax = new AtomicLong(0) ; +// /** Count of SPARQL Queries with timeout on execution */ +// public AtomicLong countQueryTimeout = new AtomicLong(0) ; +// /** Count of SPARQL Queries with execution errors (not timeouts) */ +// public AtomicLong countQueryBadExecution = new AtomicLong(0) ; + + public void startTxn(ReadWrite mode) + { + switch(mode) + { + case READ: + activeReadTxn.getAndIncrement() ; + totalReadTxn.getAndIncrement() ; + break ; + case WRITE: + activeWriteTxn.getAndIncrement() ; + totalWriteTxn.getAndIncrement() ; + break ; + } + } + + public void finishTxn(ReadWrite mode) + { + switch(mode) + { + case READ: + activeReadTxn.decrementAndGet() ; + break ; + case WRITE: + activeWriteTxn.decrementAndGet() ; + break ; + } + } + + //TODO Need to be able to set this from the config file. + public boolean allowDatasetUpdate = false; + + public boolean allowTimeoutOverride = false; + public long maximumTimeoutOverride = Long.MAX_VALUE; + + public boolean isReadOnly() + { + return ! allowDatasetUpdate && + ! update.isActive() && + ! upload.isActive() && + ! readWriteGraphStore.isActive() + ; + } + + // MBean + + @Override + public String getName() { return name ; } + + @Override public long getRequests() { + return counters.value(CounterName.Requests) ; + } + + @Override + public long getRequestsGood() { + return counters.value(CounterName.RequestsGood) ; + } + @Override + public long getRequestsBad() { + return counters.value(CounterName.RequestsBad) ; + } + + private void addCounters() { + getCounters().add(CounterName.Requests) ; + getCounters().add(CounterName.RequestsGood) ; + getCounters().add(CounterName.RequestsBad) ; + + query.getCounters().add(CounterName.Requests) ; + query.getCounters().add(CounterName.RequestsGood) ; + query.getCounters().add(CounterName.RequestsBad) ; + query.getCounters().add(CounterName.QueryTimeouts) ; + query.getCounters().add(CounterName.QueryExecErrors) ; + + update.getCounters().add(CounterName.Requests) ; + update.getCounters().add(CounterName.RequestsGood) ; + update.getCounters().add(CounterName.RequestsBad) ; + update.getCounters().add(CounterName.UpdateExecErrors) ; + + upload.getCounters().add(CounterName.Requests) ; + upload.getCounters().add(CounterName.RequestsGood) ; + upload.getCounters().add(CounterName.RequestsBad) ; + + addCountersForGSP(readWriteGraphStore.getCounters(), false) ; + if ( readGraphStore != readWriteGraphStore ) + addCountersForGSP(readGraphStore.getCounters(), true) ; + } + + private void addCountersForGSP(CounterSet cs, boolean readWrite) { + cs.add(CounterName.Requests) ; + cs.add(CounterName.RequestsGood) ; + cs.add(CounterName.RequestsBad) ; + + cs.add(CounterName.GSPget) ; + cs.add(CounterName.GSPgetGood) ; + cs.add(CounterName.GSPgetBad) ; + + cs.add(CounterName.GSPhead) ; + cs.add(CounterName.GSPheadGood) ; + cs.add(CounterName.GSPheadBad) ; + + // Add anyway. + // if ( ! readWrite ) + // return ; + + cs.add(CounterName.GSPput) ; + cs.add(CounterName.GSPputGood) ; + cs.add(CounterName.GSPputBad) ; + + cs.add(CounterName.GSPpost) ; + cs.add(CounterName.GSPpostGood) ; + cs.add(CounterName.GSPpostBad) ; + + cs.add(CounterName.GSPdelete) ; + cs.add(CounterName.GSPdeleteGood) ; + cs.add(CounterName.GSPdeleteBad) ; + + cs.add(CounterName.GSPpatch) ; + cs.add(CounterName.GSPpatchGood) ; + cs.add(CounterName.GSPpatchBad) ; + + cs.add(CounterName.GSPoptions) ; + cs.add(CounterName.GSPoptionsGood) ; + cs.add(CounterName.GSPoptionsBad) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRegistry.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRegistry.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRegistry.java new file mode 100644 index 0000000..152e8cd --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRegistry.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.fuseki.server; + +import org.apache.jena.fuseki.migrate.Registry ; + +public class DatasetRegistry extends Registry<DatasetRef> +{ + private static DatasetRegistry singleton = new DatasetRegistry() ; + + public static DatasetRegistry get() { return singleton ; } + + private DatasetRegistry() {} +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiConfig.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiConfig.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiConfig.java new file mode 100644 index 0000000..9c36a7c --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiConfig.java @@ -0,0 +1,374 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.fuseki.server; + +import java.lang.reflect.Method ; +import java.util.ArrayList ; +import java.util.Arrays ; +import java.util.List ; + +import org.apache.jena.atlas.iterator.Iter ; +import org.apache.jena.atlas.lib.StrUtils ; +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.fuseki.FusekiConfigException ; +import org.apache.jena.fuseki.HttpNames ; +import org.slf4j.Logger ; + +import com.hp.hpl.jena.assembler.Assembler ; +import com.hp.hpl.jena.assembler.JA ; +import com.hp.hpl.jena.query.ARQ ; +import com.hp.hpl.jena.query.Dataset ; +import com.hp.hpl.jena.query.Query ; +import com.hp.hpl.jena.query.QueryExecution ; +import com.hp.hpl.jena.query.QueryExecutionFactory ; +import com.hp.hpl.jena.query.QueryFactory ; +import com.hp.hpl.jena.query.QuerySolution ; +import com.hp.hpl.jena.query.QuerySolutionMap ; +import com.hp.hpl.jena.query.ResultSet ; +import com.hp.hpl.jena.query.ResultSetFactory ; +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.ResIterator ; +import com.hp.hpl.jena.rdf.model.Resource ; +import com.hp.hpl.jena.rdf.model.Statement ; +import com.hp.hpl.jena.rdf.model.StmtIterator ; +import com.hp.hpl.jena.shared.PrefixMapping ; +import com.hp.hpl.jena.sparql.core.DatasetGraph ; +import com.hp.hpl.jena.sparql.core.DatasetGraphFactory ; +import com.hp.hpl.jena.sparql.core.DatasetGraphReadOnly ; +import com.hp.hpl.jena.sparql.core.assembler.AssemblerUtils ; +import com.hp.hpl.jena.tdb.TDB ; +import com.hp.hpl.jena.util.FileManager ; +import com.hp.hpl.jena.vocabulary.RDF ; +import com.hp.hpl.jena.vocabulary.RDFS ; + +public class FusekiConfig +{ + static { Fuseki.init(); } + + // The datastructure that captures a servers configuration. + + // Server port + int port ; + // Management command port - -1 for none. + int mgtPort ; + List<DatasetRef> datasets = null ; + + + private static Logger log = Fuseki.configLog ; + + private static String prefixes = StrUtils.strjoinNL( + "PREFIX fu: <http://jena.apache.org/fuseki#>" , + "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>", + "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>", + "PREFIX tdb: <http://jena.hpl.hp.com/2008/tdb#>", + "PREFIX list: <http://jena.hpl.hp.com/ARQ/list#>", + "PREFIX list: <http://jena.hpl.hp.com/ARQ/list#>", + "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>", + "PREFIX apf: <http://jena.hpl.hp.com/ARQ/property#>", + "PREFIX afn: <http://jena.hpl.hp.com/ARQ/function#>" , + "") ; + + public static ServerConfig defaultConfiguration(String datasetPath, DatasetGraph dsg, boolean allowUpdate, boolean listenLocal) + { + DatasetRef dbDesc = new DatasetRef() ; + dbDesc.name = datasetPath ; + dbDesc.dataset = dsg ; + dbDesc.query.endpoints.add(HttpNames.ServiceQuery) ; + dbDesc.query.endpoints.add(HttpNames.ServiceQueryAlt) ; + + if ( allowUpdate ) + { + dbDesc.update.endpoints.add(HttpNames.ServiceUpdate) ; + dbDesc.upload.endpoints.add(HttpNames.ServiceUpload) ; + dbDesc.readWriteGraphStore.endpoints.add(HttpNames.ServiceData) ; + dbDesc.allowDatasetUpdate = true ; + } + else + dbDesc.readGraphStore.endpoints.add(HttpNames.ServiceData) ; + ServerConfig config = new ServerConfig() ; + config.datasets = Arrays.asList(dbDesc) ; + config.port = 3030 ; + config.mgtPort = 3031 ; + config.pagesPort = config.port ; + config.loopback = listenLocal ; + config.jettyConfigFile = null ; + config.pages = Fuseki.PagesStatic ; + config.enableCompression = true ; + config.verboseLogging = false ; + return config ; + } + + public static ServerConfig configure(String filename) + { + // Be absolutely sure everything has initialized. + // Some initialization registers assemblers and sets abbreviation vocabulary. + ARQ.init(); + TDB.init() ; + Fuseki.init() ; + Model m = FileManager.get().loadModel(filename) ; + + // Find one server. + List<Resource> servers = getByType(FusekiVocab.tServer, m) ; + if ( servers.size() == 0 ) + throw new FusekiConfigException("No server found (no resource with type "+strForResource(FusekiVocab.tServer)) ; + if ( servers.size() > 1 ) + throw new FusekiConfigException(servers.size()+" servers found (must be exactly one in a configuration file)") ; + + // ---- Server + Resource server = servers.get(0) ; + processServer(server) ; + + // ---- Services + ResultSet rs = query("SELECT * { ?s fu:services [ list:member ?member ] }", m) ; + if ( ! rs.hasNext() ) + log.warn("No services found") ; + + List<DatasetRef> services = new ArrayList<DatasetRef>() ; + + for ( ; rs.hasNext() ; ) + { + QuerySolution soln = rs.next() ; + Resource svc = soln.getResource("member") ; + DatasetRef sd = processService(svc) ; + services.add(sd) ; + } + + // TODO Properties for the other fields. + ServerConfig config = new ServerConfig() ; + config.datasets = services ; + config.port = 3030 ; + config.mgtPort = 3031 ; + config.pagesPort = config.port ; + config.jettyConfigFile = null ; + config.pages = Fuseki.PagesStatic ; + config.enableCompression = true ; + config.verboseLogging = false ; + return config ; + } + + + // DatasetRef used where there isn't a real Dataset e.g. the SPARQL processor. + + private static DatasetRef noDataset = new DatasetRef() ; + private static DatasetGraph dummyDSG = new DatasetGraphReadOnly(DatasetGraphFactory.createMemFixed()) ; + static { + noDataset.name = "" ; + noDataset.dataset = dummyDSG ; + noDataset.query.endpoints.add(HttpNames.ServiceQuery) ; + noDataset.query.endpoints.add(HttpNames.ServiceQueryAlt) ; + noDataset.allowDatasetUpdate = false ; + noDataset.init(); + // Don't register it. + // This is used as a placeholder and shoudl not be found by "all datasets" + // DatasetRegistry.get().put("", noDataset) ; + } + + /** Return the DatasetRef (read-only) for when there is no dataset, just a SPARQL Query processor */ + public static DatasetRef serviceOnlyDatasetRef() { return noDataset ; } + + private static void processServer(Resource server) + { + // Global, currently. + AssemblerUtils.setContext(server, Fuseki.getContext()) ; + + StmtIterator sIter = server.listProperties(JA.loadClass) ; + for( ; sIter.hasNext(); ) + { + Statement s = sIter.nextStatement() ; + RDFNode rn = s.getObject() ; + String className = null ; + if ( rn instanceof Resource ) + { + String uri = ((Resource)rn).getURI() ; + if ( uri == null ) + { + log.warn("Blank node for class to load") ; + continue ; + } + String javaScheme = "java:" ; + if ( ! uri.startsWith(javaScheme) ) + { + log.warn("Class to load is not 'java:': "+uri) ; + continue ; + } + className = uri.substring(javaScheme.length()) ; + } + if ( rn instanceof Literal ) + className = ((Literal)rn).getLexicalForm() ; + /*Loader.*/loadAndInit(className) ; + } + // ---- + } + + private static void loadAndInit(String className) + { + try { + Class<?> classObj = Class.forName(className); + log.info("Loaded "+className) ; + Method initMethod = classObj.getMethod("init"); + initMethod.invoke(null); + } catch (ClassNotFoundException ex) + { + log.warn("Class not found: "+className); + } + catch (Exception e) { throw new FusekiConfigException(e) ; } + } + + private static DatasetRef processService(Resource svc) + { + log.info("Service: "+nodeLabel(svc)) ; + DatasetRef sDesc = new DatasetRef() ; + sDesc.name = ((Literal)getOne(svc, "fu:name")).getLexicalForm() ; + log.info(" name = "+sDesc.name) ; + + addServiceEP("query", sDesc.name, sDesc.query, svc, "fu:serviceQuery") ; + addServiceEP("update", sDesc.name, sDesc.update, svc, "fu:serviceUpdate") ; + addServiceEP("upload", sDesc.name, sDesc.upload, svc, "fu:serviceUpload") ; + addServiceEP("graphStore(RW)", sDesc.name, sDesc.readWriteGraphStore, svc, "fu:serviceReadWriteGraphStore") ; + addServiceEP("graphStore(R)", sDesc.name, sDesc.readGraphStore, svc, "fu:serviceReadGraphStore") ; + // Extract timeout overriding configuration if present. + if (svc.hasProperty(FusekiVocab.pAllowTimeoutOverride)) { + sDesc.allowTimeoutOverride = svc.getProperty(FusekiVocab.pAllowTimeoutOverride).getObject().asLiteral().getBoolean(); + if (svc.hasProperty(FusekiVocab.pMaximumTimeoutOverride)) { + sDesc.maximumTimeoutOverride = (int) (svc.getProperty(FusekiVocab.pMaximumTimeoutOverride).getObject().asLiteral().getFloat() * 1000); + } + } + + Resource datasetDesc = ((Resource)getOne(svc, "fu:dataset")) ; + + // Check if it is in the model. + if ( ! datasetDesc.hasProperty(RDF.type) ) + throw new FusekiConfigException("No rdf:type for dataset "+nodeLabel(datasetDesc)) ; + + Dataset ds = (Dataset)Assembler.general.open(datasetDesc) ; + sDesc.dataset = ds.asDatasetGraph() ; + return sDesc ; + + } + + private static RDFNode getOne(Resource svc, String property) + { + String ln = property.substring(property.indexOf(':')+1) ; + ResultSet rs = query("SELECT * { ?svc "+property+" ?x}", svc.getModel(), "svc", svc) ; + if ( ! rs.hasNext() ) + throw new FusekiConfigException("No "+ln+" for service "+nodeLabel(svc)) ; + RDFNode x = rs.next().get("x") ; + if ( rs.hasNext() ) + throw new FusekiConfigException("Multiple "+ln+" for service "+nodeLabel(svc)) ; + return x ; + } + + private static List<Resource> getByType(Resource type, Model m) + { + ResIterator rIter = m.listSubjectsWithProperty(RDF.type, type) ; + return Iter.toList(rIter) ; + } + + private static void addServiceEP(String label, String name, ServiceRef service, Resource svc, String property) + { + ResultSet rs = query("SELECT * { ?svc "+property+" ?ep}", svc.getModel(), "svc", svc) ; + for ( ; rs.hasNext() ; ) + { + QuerySolution soln = rs.next() ; + String epName = soln.getLiteral("ep").getLexicalForm() ; + service.endpoints.add(epName) ; + log.info(" "+label+" = /"+name+"/"+epName) ; + } + } + + + private static ResultSet query(String string, Model m) + { + return query(string, m, null, null) ; + } + + private static ResultSet query(String string, Model m, String varName, RDFNode value) + { + Query query = QueryFactory.create(prefixes+string) ; + QuerySolutionMap initValues = null ; + if ( varName != null ) + initValues = querySolution(varName, value) ; + try(QueryExecution qExec = QueryExecutionFactory.create(query, m, initValues)) { + ResultSet rs = ResultSetFactory.copyResults(qExec.execSelect()) ; + return rs ; + } + } + + private static QuerySolutionMap querySolution(String varName, RDFNode value) + { + QuerySolutionMap qsm = new QuerySolutionMap() ; + querySolution(qsm, varName, value) ; + return qsm ; + } + + private static QuerySolutionMap querySolution(QuerySolutionMap qsm, String varName, RDFNode value) + { + qsm.add(varName, value) ; + return qsm ; + } + + // Node presentation + private static String nodeLabel(RDFNode n) + { + if ( n == null ) + return "<null>" ; + if ( n instanceof Resource ) + return strForResource((Resource)n) ; + + Literal lit = (Literal)n ; + return lit.getLexicalForm() ; + } + + private static String strForResource(Resource r) { return strForResource(r, r.getModel()) ; } + + private static String strForResource(Resource r, PrefixMapping pm) + { + if ( r == null ) + return "NULL "; + if ( r.hasProperty(RDFS.label)) + { + RDFNode n = r.getProperty(RDFS.label).getObject() ; + if ( n instanceof Literal ) + return ((Literal)n).getString() ; + } + + if ( r.isAnon() ) + return "<<blank node>>" ; + + if ( pm == null ) + pm = r.getModel() ; + + return strForURI(r.getURI(), pm ) ; + } + + private static String strForURI(String uri, PrefixMapping pm) + { + if ( pm != null ) + { + String x = pm.shortForm(uri) ; + + if ( ! x.equals(uri) ) + return x ; + } + return "<"+uri+">" ; + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiErrorHandler.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiErrorHandler.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiErrorHandler.java new file mode 100644 index 0000000..d1660f5 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiErrorHandler.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.fuseki.server; + +import static java.lang.String.format ; + +import java.io.* ; + +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.web.HttpSC ; +import org.eclipse.jetty.http.HttpHeaders ; +import org.eclipse.jetty.http.HttpMethods ; +import org.eclipse.jetty.http.MimeTypes ; +import org.eclipse.jetty.server.AbstractHttpConnection ; +import org.eclipse.jetty.server.Request ; +import org.eclipse.jetty.server.handler.ErrorHandler ; + +public class FusekiErrorHandler extends ErrorHandler +{ + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException + { + AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection(); + connection.getRequest().setHandled(true); + String method = request.getMethod(); + + if(!method.equals(HttpMethods.GET) && !method.equals(HttpMethods.POST) && !method.equals(HttpMethods.HEAD)) + return; + + response.setContentType(MimeTypes.TEXT_PLAIN_UTF_8) ; + response.setHeader(HttpHeaders.CACHE_CONTROL, "must-revalidate,no-cache,no-store") ; + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024) ; + //String writer = IO.UTF8(null) ; + try(Writer writer = new OutputStreamWriter(bytes, "UTF-8")) { + + handleErrorPage(request, writer, connection.getResponse().getStatus(), connection.getResponse().getReason()); + + if ( ! Fuseki.VERSION.equalsIgnoreCase("development") ) + { + writer.write("\n") ; + writer.write("\n") ; + writer.write(format("Fuseki - version %s (Build date: %s)\n", Fuseki.VERSION, Fuseki.BUILD_DATE)) ; + } + writer.flush(); + } + response.setContentLength(bytes.size()) ; + // Copy + response.getOutputStream().write(bytes.toByteArray()) ; + } + + @Override + protected void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message) + throws IOException + { + if ( message == null ) + message = HttpSC.getMessage(code) ; + writer.write(format("Error %d: %s\n", code, message)) ; + + Throwable th = (Throwable)request.getAttribute("javax.servlet.error.exception"); + while(th!=null) + { + writer.write("\n"); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + th.printStackTrace(pw); + pw.flush(); + writer.write(sw.getBuffer().toString()); + writer.write("\n"); + th = th.getCause(); + } + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiServletContextListener.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiServletContextListener.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiServletContextListener.java new file mode 100644 index 0000000..f1c9642 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiServletContextListener.java @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.fuseki.server; + +import javax.servlet.ServletContextEvent ; +import javax.servlet.ServletContextListener ; + +public class FusekiServletContextListener implements ServletContextListener { + // This could do the initialization. + private final SPARQLServer sparqlServer ; + public FusekiServletContextListener(SPARQLServer sparqlServer) { + this.sparqlServer = sparqlServer ; + } + + @Override + public void contextInitialized(ServletContextEvent sce) { +// Fuseki.serverLog.info("contextInitialized") ; +// for ( DatasetRef dsRef : sparqlServer.getDatasets() ) +// Fuseki.serverLog.info("Dataset: "+dsRef.getName()) ; + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { +// Fuseki.serverLog.info("contextDestroyed") ; + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java new file mode 100644 index 0000000..d4d4e54 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.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.server; + +import org.apache.jena.fuseki.FusekiException ; +import org.apache.jena.iri.IRI ; +import org.apache.jena.riot.system.IRIResolver ; + +import com.hp.hpl.jena.rdf.model.Property ; +import com.hp.hpl.jena.rdf.model.Resource ; +import com.hp.hpl.jena.rdf.model.ResourceFactory ; + +public class FusekiVocab +{ + public static String NS = "http://jena.apache.org/fuseki#" ; + + public static final Resource tServer = resource("Server") ; + + public static final Property pServices = property("services") ; + public static final Property pServiceName = property("name") ; + + public static final Property pServiceQueryEP = property("serviceQuery") ; + public static final Property pServiceUpdateEP = property("serviceUpdate") ; + public static final Property pServiceUploadEP = property("serviceUpload") ; + public static final Property pServiceReadWriteGraphStoreEP = property("serviceReadWriteGraphStore") ; + public static final Property pServiceReadgraphStoreEP = property("serviceReadGraphStore") ; + + public static final Property pAllowTimeoutOverride = property("allowTimeoutOverride"); + public static final Property pMaximumTimeoutOverride = property("maximumTimeoutOverride"); + + private static Resource resource(String localname) { return ResourceFactory.createResource(iri(localname)) ; } + private static Property property(String localname) { return ResourceFactory.createProperty(iri(localname)) ; } + + private static String iri(String localname) + { + String uri = NS+localname ; + IRI iri = IRIResolver.parseIRI(uri) ; + if ( iri.hasViolation(true) ) + throw new FusekiException("Bad IRI: "+iri) ; + if ( ! iri.isAbsolute() ) + throw new FusekiException("Bad IRI: "+iri) ; + + return uri ; + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java new file mode 100644 index 0000000..7448eb2 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java @@ -0,0 +1,484 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.fuseki.server ; + +import static java.lang.String.format ; +import static org.apache.jena.fuseki.Fuseki.serverLog ; + +import java.io.FileInputStream ; +import java.util.* ; + +import javax.servlet.DispatcherType ; +import javax.servlet.http.HttpServlet ; + +import org.apache.jena.atlas.lib.FileOps ; +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.fuseki.FusekiException ; +import org.apache.jena.fuseki.HttpNames ; +import org.apache.jena.fuseki.mgt.ActionDataset ; +import org.apache.jena.fuseki.mgt.MgtFunctions ; +import org.apache.jena.fuseki.mgt.PageNames ; +import org.apache.jena.fuseki.servlets.* ; +import org.apache.jena.fuseki.validation.DataValidator ; +import org.apache.jena.fuseki.validation.IRIValidator ; +import org.apache.jena.fuseki.validation.QueryValidator ; +import org.apache.jena.fuseki.validation.UpdateValidator ; +import org.apache.jena.riot.WebContent ; +import org.eclipse.jetty.http.MimeTypes ; +import org.eclipse.jetty.security.* ; +import org.eclipse.jetty.security.authentication.BasicAuthenticator ; +import org.eclipse.jetty.server.Connector ; +import org.eclipse.jetty.server.Server ; +import org.eclipse.jetty.server.nio.BlockingChannelConnector ; +import org.eclipse.jetty.servlet.DefaultServlet ; +import org.eclipse.jetty.servlet.ServletContextHandler ; +import org.eclipse.jetty.servlet.ServletHolder ; +import org.eclipse.jetty.servlets.GzipFilter ; +import org.eclipse.jetty.util.security.Constraint ; +import org.eclipse.jetty.xml.XmlConfiguration ; + +import com.hp.hpl.jena.sparql.mgt.ARQMgt ; +import com.hp.hpl.jena.sparql.util.Utils ; + +/** + * SPARQLServer is the Jena server instance which wraps/utilizes + * {@link org.eclipse.jetty.server.Server}. This class provides + * immediate access to the {@link org.eclipse.jetty.server.Server#start()} and + * {@link org.eclipse.jetty.server.Server#stop()} commands as well as obtaining + * instances of the server and server configuration. Finally we can obtain + * instances of {@link org.apache.jena.fuseki.server.ServerConfig}. + * + */ +public class SPARQLServer { + static { + Fuseki.init() ; + } + + private ServerConfig serverConfig ; + + private Server server = null ; + private static List<String> epDataset = Arrays.asList("*") ; + + /** + * Default constructor which requires a {@link org.apache.jena.fuseki.server.ServerConfig} + * object as input. We use this config to specify (verbose) logging, enable compression + * etc. + * @param config + */ + public SPARQLServer(ServerConfig config) { + this.serverConfig = config ; + // Currently server-wide. + Fuseki.verboseLogging = config.verboseLogging ; + + // GZip compression + // Note that regardless of this setting we'll always leave it turned off + // for the servlets + // where it makes no sense to have it turned on e.g. update and upload + + ServletContextHandler context = buildServer(serverConfig.jettyConfigFile, config.enableCompression) ; + configureDatasets(context) ; + } + + private void configureDatasets(ServletContextHandler context) { + // Build them all. + for (DatasetRef dsDesc : serverConfig.datasets) + configureOneDataset(context, dsDesc, serverConfig.enableCompression) ; + + } + + /** + * Initialize the {@link SPARQLServer} instance. + */ + public void start() { + String now = Utils.nowAsString() ; + serverLog.info(format("%s %s %s", Fuseki.NAME, Fuseki.VERSION, Fuseki.BUILD_DATE)) ; + // This does not get set usefully for Jetty as we use it. + // String jettyVersion = org.eclipse.jetty.server.Server.getVersion() ; + // serverLog.info(format("Jetty %s",jettyVersion)) ; + String host = server.getConnectors()[0].getHost() ; + if ( host != null ) + serverLog.info("Incoming connections limited to " + host) ; + serverLog.info(format("Started %s on port %d", now, server.getConnectors()[0].getPort())) ; + + try { + server.start() ; + } catch (java.net.BindException ex) { + serverLog.error("SPARQLServer: Failed to start server: " + ex.getMessage()) ; + System.exit(1) ; + } catch (Exception ex) { + serverLog.error("SPARQLServer: Failed to start server: " + ex.getMessage(), ex) ; + System.exit(1) ; + } + + ServletContextHandler context = (ServletContextHandler)server.getHandler() ; + } + + /** + * Stop the {@link SPARQLServer} instance. + */ + public void stop() { + String now = Utils.nowAsString() ; + serverLog.info(format("Stopped %s on port %d", now, server.getConnectors()[0].getPort())) ; + try { + server.stop() ; + } catch (Exception ex) { + Fuseki.serverLog.warn("SPARQLServer: Exception while stopping server: " + ex.getMessage(), ex) ; + } + removeJMX() ; + } + + /** + * Get the Jetty instance. + * @return Server + */ + public Server getServer() { + return server ; + } + + /** + * Get the datasets associated with the server. + * @return returns the datasets via {@link org.apache.jena.fuseki.server.ServerConfig#datasets} + */ + public List<DatasetRef> getDatasets() { + return serverConfig.datasets ; + } + + /** + * Obtain the {@link org.apache.jena.fuseki.server.ServerConfig} + * @return ServerConfig + */ + public ServerConfig getServerConfig() { + return serverConfig ; + } + + // Later : private and in constructor. + private ServletContextHandler buildServer(String jettyConfig, boolean enableCompression) { + if ( jettyConfig != null ) { + // --jetty-config=jetty-fuseki.xml + // for detailed configuration of the server using Jetty features. + server = configServer(jettyConfig) ; + } else + server = defaultServerConfig(serverConfig.port, serverConfig.loopback) ; + // Keep the server to a maximum number of threads. + // server.setThreadPool(new QueuedThreadPool(ThreadPoolSize)) ; + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS) ; + context.setErrorHandler(new FusekiErrorHandler()) ; + context.addEventListener(new FusekiServletContextListener(this)); + + // Increase form size. + context.getServletContext().getContextHandler().setMaxFormContentSize(10 * 1000 * 1000) ; + + // Wire up authentication if appropriate + if ( jettyConfig == null && serverConfig.authConfigFile != null ) { + Constraint constraint = new Constraint() ; + constraint.setName(Constraint.__BASIC_AUTH) ; + constraint.setRoles(new String[]{"fuseki"}) ; + constraint.setAuthenticate(true) ; + + ConstraintMapping mapping = new ConstraintMapping() ; + mapping.setConstraint(constraint) ; + mapping.setPathSpec("/*") ; + + IdentityService identService = new DefaultIdentityService() ; + + ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler() ; + securityHandler.addConstraintMapping(mapping) ; + securityHandler.setIdentityService(identService) ; + + HashLoginService loginService = new HashLoginService("Fuseki Authentication", serverConfig.authConfigFile) ; + loginService.setIdentityService(identService) ; + + securityHandler.setLoginService(loginService) ; + securityHandler.setAuthenticator(new BasicAuthenticator()) ; + + context.setSecurityHandler(securityHandler) ; + + serverLog.debug("Basic Auth Configuration = " + serverConfig.authConfigFile) ; + } + + // Wire up context handler to server + server.setHandler(context) ; + + // Constants. Add RDF types. + MimeTypes mt = new MimeTypes() ; + mt.addMimeMapping("rdf", WebContent.contentTypeRDFXML + ";charset=utf-8") ; + mt.addMimeMapping("ttl", WebContent.contentTypeTurtle + ";charset=utf-8") ; + mt.addMimeMapping("nt", WebContent.contentTypeNTriples + ";charset=ascii") ; + mt.addMimeMapping("nq", WebContent.contentTypeNQuads + ";charset=ascii") ; + mt.addMimeMapping("trig", WebContent.contentTypeTriG + ";charset=utf-8") ; + + // mt.addMimeMapping("tpl", "text/html;charset=utf-8") ; + context.setMimeTypes(mt) ; + server.setHandler(context) ; + + serverLog.debug("Pages = " + serverConfig.pages) ; + + boolean installManager = true ; + boolean installServices = true ; + + String validationRoot = "/validate" ; + + // Should all services be /_/.... or some such? + + if ( installManager || installServices ) { + // TODO Respect port. + if ( serverConfig.pagesPort != serverConfig.port ) + serverLog.warn("Not supported yet - pages on a different port to services") ; + if ( serverConfig.pages != null ) { + if ( ! FileOps.exists(serverConfig.pages) ) + serverLog.warn("No pages directory - "+serverConfig.pages) ; + String base = serverConfig.pages ; + Map<String, Object> data = new HashMap<String, Object>() ; + data.put("mgt", new MgtFunctions()) ; + SimpleVelocityServlet templateEngine = new SimpleVelocityServlet(base, data) ; + addServlet(context, templateEngine, "*.tpl", false) ; + } + } + + if ( installManager ) { + // Action when control panel selects a dataset. + HttpServlet datasetChooser = new ActionDataset() ; + addServlet(context, datasetChooser, PageNames.actionDatasetNames, false) ; + } + + if ( installServices ) { + // Validators + HttpServlet validateQuery = new QueryValidator() ; + HttpServlet validateUpdate = new UpdateValidator() ; + HttpServlet validateData = new DataValidator() ; + HttpServlet validateIRI = new IRIValidator() ; + + HttpServlet dumpService = new DumpServlet() ; + HttpServlet generalQueryService = new SPARQL_QueryGeneral() ; + + addServlet(context, validateQuery, validationRoot + "/query", false) ; + addServlet(context, validateUpdate, validationRoot + "/update", false) ; + addServlet(context, validateData, validationRoot + "/data", false) ; + addServlet(context, validateIRI, validationRoot + "/iri", false) ; + + // general query processor. + addServlet(context, generalQueryService, HttpNames.ServiceGeneralQuery, enableCompression) ; + } + + if ( installManager || installServices ) { + String[] files = {"fuseki.html", "index.html"} ; + context.setWelcomeFiles(files) ; + addContent(context, "/", serverConfig.pages) ; + } + + return context ; + } + + /** Experimental - off by default. The überservlet sits on the dataset name and handles all requests. + * Includes direct naming and quad access to the dataset. + */ + public static boolean überServlet = false ; + + private static List<String> ListOfEmptyString = Arrays.asList("") ; + + private void configureOneDataset(ServletContextHandler context, DatasetRef dsDesc, boolean enableCompression) { + String datasetPath = dsDesc.name ; + if ( datasetPath.equals("/") ) + datasetPath = "" ; + else + if ( !datasetPath.startsWith("/") ) + datasetPath = "/" + datasetPath ; + + if ( datasetPath.endsWith("/") ) + datasetPath = datasetPath.substring(0, datasetPath.length() - 1) ; + + dsDesc.init() ; + + DatasetRegistry.get().put(datasetPath, dsDesc) ; + serverLog.info(format("Dataset path = %s", datasetPath)) ; + + HttpServlet sparqlQuery = new SPARQL_QueryDataset() ; + HttpServlet sparqlUpdate = new SPARQL_Update() ; + HttpServlet sparqlUpload = new SPARQL_Upload() ; + HttpServlet sparqlHttpR = new SPARQL_REST_R() ; + HttpServlet sparqlHttpRW = new SPARQL_REST_RW() ; + HttpServlet sparqlDataset = new SPARQL_UberServlet.AccessByConfig() ; + + if ( !überServlet ) { + // If uberserver, these are unnecessary but can be used. + // If just means the überservlet isn't handling these operations. + addServlet(context, datasetPath, sparqlQuery, dsDesc.query, enableCompression) ; + addServlet(context, datasetPath, sparqlUpdate, dsDesc.update, false) ; + addServlet(context, datasetPath, sparqlUpload, dsDesc.upload, false) ; // No point - no results of any size. + addServlet(context, datasetPath, sparqlHttpR, dsDesc.readGraphStore, enableCompression) ; + addServlet(context, datasetPath, sparqlHttpRW, dsDesc.readWriteGraphStore, enableCompression) ; + // This adds direct operations on the dataset itself. + // addServlet(context, datasetPath, sparqlDataset, + // ListOfEmptyString, enableCompression) ; + } else { + // This is the servlet that analyses requests and dispatches them to + // the appropriate servlet. + // SPARQL Query, SPARQL Update -- handles dataset?query= + // dataset?update= + // Graph Store Protocol (direct and indirect naming) if enabled. + // GET/PUT/POST on the dataset itself. + // It also checks for a request that looks like a service request + // and passes it + // on to the service (this takes precedence over direct naming). + addServlet(context, datasetPath, sparqlDataset, epDataset, enableCompression) ; + } + + // Add JMX beans to record daatset and it's services. + addJMX(dsDesc) ; + } + + private static Server configServer(String jettyConfig) { + try { + serverLog.info("Jetty server config file = " + jettyConfig) ; + Server server = new Server() ; + XmlConfiguration configuration = new XmlConfiguration(new FileInputStream(jettyConfig)) ; + configuration.configure(server) ; + return server ; + } catch (Exception ex) { + serverLog.error("SPARQLServer: Failed to configure server: " + ex.getMessage(), ex) ; + throw new FusekiException("Failed to configure a server using configuration file '" + jettyConfig + "'") ; + } + } + + private static Server defaultServerConfig(int port, boolean loopback) { + // Server, with one NIO-based connector, large input buffer size (for + // long URLs, POSTed forms (queries, updates)). + Server server = new Server() ; + + // Using "= new SelectChannelConnector() ;" on Darwin (OS/X) causes + // problems + // with initialization not seen (thread scheduling?) in Joseki. + + // BlockingChannelConnector is better for pumping large responses back + // but there have been observed problems with DirectMemory allocation + // (-XX:MaxDirectMemorySize=1G does not help) + // Connector connector = new SelectChannelConnector() ; + + // Connector and specific settings. + BlockingChannelConnector bcConnector = new BlockingChannelConnector() ; + // bcConnector.setUseDirectBuffers(false) ; + + Connector connector = bcConnector ; + // Ignore. 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. + if ( loopback ) + connector.setHost("localhost"); + connector.setPort(port) ; + // Some people do try very large operations ... + connector.setRequestHeaderSize(64 * 1024) ; + connector.setRequestBufferSize(5 * 1024 * 1024) ; + connector.setResponseBufferSize(5 * 1024 * 1024) ; + server.addConnector(connector) ; + return server ; + } + + private static void addContent(ServletContextHandler context, String pathSpec, String pages) { + DefaultServlet staticServlet = new DefaultServlet() ; + ServletHolder staticContent = new ServletHolder(staticServlet) ; + staticContent.setInitParameter("resourceBase", pages) ; + + // Note we set GZip to false for static content because the Jetty + // DefaultServlet has + // a built-in GZip capability that is better for static content than the + // mechanism the + // GzipFilter uses for dynamic content + addServlet(context, staticContent, pathSpec, false) ; + } + + private void addServlet(ServletContextHandler context, String datasetPath, HttpServlet servlet, + ServiceRef serviceRef, boolean enableCompression) { + addServlet(context, datasetPath, servlet, serviceRef.endpoints, enableCompression) ; + } + + // SHARE + private static void addServlet(ServletContextHandler context, String datasetPath, HttpServlet servlet, + List<String> pathSpecs, boolean enableCompression) { + for (String pathSpec : pathSpecs) { + if ( pathSpec.equals("") ) { + // "" is special -- add as "base" and "base/" + addServlet(context, servlet, datasetPath + "/", enableCompression) ; + addServlet(context, servlet, datasetPath, enableCompression) ; + continue ; + } + + if ( pathSpec.endsWith("/") ) + pathSpec = pathSpec.substring(0, pathSpec.length() - 1) ; + if ( pathSpec.startsWith("/") ) + pathSpec = pathSpec.substring(1, pathSpec.length()) ; + addServlet(context, servlet, datasetPath + "/" + pathSpec, enableCompression) ; + } + } + + private static void addServlet(ServletContextHandler context, HttpServlet servlet, String pathSpec, + boolean enableCompression) { + ServletHolder holder = new ServletHolder(servlet) ; + addServlet(context, holder, pathSpec, enableCompression) ; + } + + private static void addServlet(ServletContextHandler context, ServletHolder holder, String pathSpec, + boolean enableCompression) { + if ( serverLog.isDebugEnabled() ) { + if ( enableCompression ) + serverLog.debug("Add servlet @ " + pathSpec + " (with gzip)") ; + else + serverLog.debug("Add servlet @ " + pathSpec) ; + } + context.addServlet(holder, pathSpec) ; + + if ( enableCompression ) + context.addFilter(GzipFilter.class, pathSpec, EnumSet.allOf(DispatcherType.class)) ; + } + + private void addJMX() { + DatasetRegistry registry = DatasetRegistry.get() ; + for (String ds : registry.keys()) { + DatasetRef dsRef = registry.get(ds) ; + addJMX(dsRef) ; + } + } + + private void addJMX(DatasetRef dsRef) { + String x = dsRef.name ; + // if ( x.startsWith("/") ) + // x = x.substring(1) ; + ARQMgt.register(Fuseki.PATH + ".dataset:name=" + x, dsRef) ; + // For all endpoints + for (ServiceRef sRef : dsRef.getServiceRefs()) { + ARQMgt.register(Fuseki.PATH + ".dataset:name=" + x + "/" + sRef.name, sRef) ; + } + } + + private void removeJMX() { + DatasetRegistry registry = DatasetRegistry.get() ; + for (String ds : registry.keys()) { + DatasetRef ref = registry.get(ds) ; + } + } + + private void removeJMX(DatasetRef dsRef) { + String x = dsRef.getName() ; + ARQMgt.unregister(Fuseki.PATH + ".dataset:name=" + x) ; + for (ServiceRef sRef : dsRef.getServiceRefs()) { + ARQMgt.unregister(Fuseki.PATH + ".dataset:name=" + x + "/" + sRef.name) ; + } + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java new file mode 100644 index 0000000..4e0b865 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.fuseki.server; + +import java.util.List ; + +/** This represents a configuration of a SPARQL server. + */ + +public class ServerConfig +{ + public ServerConfig() {} + + /** Port to run the server service on */ + public int port ; + /** Port for the management interface : -1 for no management interface */ + public int mgtPort ; + /** Port for the pages UI : this can be the same as the services port. */ + public int pagesPort ; + /** Jetty config file - if null, use the built-in configuration of Jetty */ + public String jettyConfigFile = null ; + /** Listen only on the loopback (localhost) interface */ + public boolean loopback = false ; + /** The local directory for serving the static pages */ + public String pages ; + /** The list of datasets */ + public List<DatasetRef> datasets ; + /** Enable Accept-Encoding compression. Set to false by default.*/ + public boolean enableCompression = false ; + + /** Enable additional logging */ + public boolean verboseLogging = false ; + /** + * Authentication config file used to setup Jetty Basic auth, if a Jetty config file was set this is ignored since Jetty config allows much more complex auth methods to be implemented + */ + public String authConfigFile ; + +} +
