http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/soh
----------------------------------------------------------------------
diff --git a/jena-fuseki/soh b/jena-fuseki/soh
new file mode 100755
index 0000000..4f6590d
--- /dev/null
+++ b/jena-fuseki/soh
@@ -0,0 +1,713 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+
+# 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.
+
+# SPARQL HTTP Update, client.
+
+require 'optparse'
+require 'net/http'
+require 'uri'
+require 'cgi'
+require 'pp'
+require 'ostruct'
+
+# ToDo
+#  Allow a choice of media type for GET
+#   --accept "content-type" (and abbreviations)
+#   --header "Add:this"
+#   --user, --password
+#  Basic authentication: request.basic_auth("username", "password")
+#  Follow redirects => 301:  puts response["location"] # All headers are 
lowercase?
+
+SOH_NAME="SOH"
+SOH_VERSION="0.0.0"
+
+$proxy = ENV['http_proxy'] ? URI.parse(ENV['http_proxy']) : OpenStruct.new  
+
+# What about direct naming?
+
+# Names
+$mtTurtle           = 'text/turtle;charset=utf-8'
+$mtRDF              = 'application/rdf+xml'
+$mtText             = 'text/plain'
+$mtNTriples         = 'application/n-triples'
+$mtNQuads           = 'application/n-quads'
+$mtJSONLD           = 'application/ld+json'
+$mtTriG             = 'application/trig'
+$mtSparqlResultsX   = 'application/sparql-results+xml'
+$mtSparqlResultsJ   = 'application/sparql-results+json'
+$mtAppJSON          = 'application/json'
+$mtAppXML           = 'application/xml'
+$mtSparqlResultsTSV = 'application/sparql-results+tsv'
+$mtSparqlResultsCSV = 'application/sparql-results+csv'
+$mtSparqlUpdate     = 'application/sparql-update'
+$mtWWWForm          = 'application/x-www-form-urlencoded'
+$mtSparqlQuery      = "application/sparql-query" ;
+
+# Global media type table.
+$fileMediaTypes = {}
+$fileMediaTypes['ttl']   = $mtTurtle
+$fileMediaTypes['n3']    = 'text/n3; charset=utf-8'
+$fileMediaTypes['nt']    = $mtText
+$fileMediaTypes['rdf']   = $mtRDF
+$fileMediaTypes['owl']   = $mtRDF
+$fileMediaTypes['nq']    = $mtNQuads
+$fileMediaTypes['trig']  = $mtTriG
+$fileMediaTypes['json-ld']  = $mtJSONLD
+$fileMediaTypes['jsonld']  = $mtJSONLD
+
+# Global charset : no entry means "don't set"
+$charsetUTF8      = 'utf-8'
+$charset = {}
+$charset[$mtTurtle]   = 'utf-8'
+$charset[$mtText]     = 'ascii'
+$charset[$mtTriG]     = 'utf-8'
+$charset[$mtNQuads]   = 'utf-8'
+
+# Headers
+
+$hContentType         = 'Content-Type'
+# $hContentEncoding     = 'Content-Encoding'
+$hContentLength       = 'Content-Length'
+# $hContentLocation     = 'Content-Location'
+# $hContentRange        = 'Content-Range'
+
+$hAccept              = 'Accept'
+$hAcceptCharset       = 'Accept-Charset'
+$hAcceptEncoding      = 'Accept-Encoding'
+$hAcceptRanges        = 'Accept-Ranges' 
+
+$headers = { "User-Agent" => "#{SOH_NAME}/Fuseki #{SOH_VERSION}"}
+$print_http = false
+
+# Default for GET
+# At least allow anything (and hope!)
+$accept_rdf="#{$mtTurtle} , #{$mtNTriples};q=0.9 , #{$mtRDF};q=0.8 , 
#{$mtJSONLD};q=0.5"
+# Datasets
+$accept_ds="#{$mtTrig} , #{$mtNQuads};q=0.9 , #{$mtJSONLD};q=0.5"
+# For SPARQL query
+$accept_results="#{$mtSparqlResultsJ} , #{$mtSparqlResultsX};q=0.9 , 
#{$accept_rdf}"
+
+# Accept any in case of trouble.
+$accept_rdf="#{$accept_rdf} , */*;q=0.1"
+$accept_results="#{$accept_results} , */*;q=0.1" 
+
+# The media type usually forces the charset.
+$accept_charset=nil
+
+## Who we are.
+## Two styles:
+##    s-query .....
+##    soh query .....
+
+$cmd = File.basename($0)
+if $cmd == 'soh'
+then
+  $cmd = (ARGV.size == 0) ? 'soh' : ARGV.shift
+end
+
+if ! $cmd.start_with?('s-') && $cmd != 'soh'
+  $cmd = 's-'+$cmd
+end
+
+## -------- 
+
+def GET(dataset, graph)
+  print "GET #{dataset} #{graph}\n" if $verbose
+  requestURI = target(dataset, graph)
+  headers = {}
+  headers.merge!($headers)
+  headers[$hAccept] = $accept_rdf
+  headers[$hAcceptCharset] = $accept_charset unless $accept_charset.nil?
+  get_worker(requestURI, headers)
+end
+
+def get_worker(requestURI, headers)
+  uri = URI.parse(requestURI)
+  request = Net::HTTP::Get.new(uri.request_uri)
+  request.initialize_http_header(headers)
+  print_http_request(uri, request)
+  response_print_body(uri, request)
+end
+
+def HEAD(dataset, graph)
+  print "HEAD #{dataset} #{graph}\n" if $verbose
+  requestURI = target(dataset, graph)
+  headers = {}
+  headers.merge!($headers)
+  headers[$hAccept] = $accept_rdf
+  headers[$hAcceptCharset] = $accept_charset unless $accept_charset.nil?
+  uri = URI.parse(requestURI)
+  request = Net::HTTP::Head.new(uri.request_uri)
+  request.initialize_http_header(headers)
+  print_http_request(uri, request)
+  response_no_body(uri, request)
+end
+
+def PUT(dataset, graph, file)
+  print "PUT #{dataset} #{graph} #{file}\n" if $verbose
+  send_body(dataset, graph, file, Net::HTTP::Put)
+end
+
+def POST(dataset, graph, file)
+  print "POST #{dataset} #{graph} #{file}\n" if $verbose
+  send_body(dataset, graph, file, Net::HTTP::Post)
+end
+
+def DELETE(dataset, graph)
+  print "DELETE #{dataset} #{graph}\n" if $verbose
+  requestURI = target(dataset, graph)
+  uri = URI.parse(requestURI)
+  request = Net::HTTP::Delete.new(uri.request_uri)
+  headers = {}
+  headers.merge!($headers)
+  request.initialize_http_header(headers)
+  print_http_request(uri, request)
+  response_no_body(uri, request)
+end
+
+def uri_escape(string)
+  CGI.escape(string)
+end
+
+def target(dataset, graph)
+  return dataset+"?default" if graph == "default"
+  return dataset+"?graph="+uri_escape(graph)
+end
+
+def send_body(dataset, graph, file, method)
+  mt = content_type(file)
+  headers = {}
+  headers.merge!($headers)
+  headers[$hContentType] = mt
+  headers[$hContentLength] = File.size(file).to_s
+  ## p headers
+
+  requestURI = target(dataset, graph)
+  uri = URI.parse(requestURI)
+  
+  request = method.new(uri.request_uri)
+  request.initialize_http_header(headers)
+  print_http_request(uri, request)
+  request.body_stream = File.open(file)
+  response_no_body(uri, request)
+end
+
+def response_no_body(uri, request)
+  http = Net::HTTP::Proxy($proxy.host,$proxy.port).new(uri.host, uri.port)
+  http.read_timeout = nil
+  # check we can connect.
+  begin http.start
+  rescue Exception => e  
+    # puts e.message  
+    #puts e.backtrace.inspect  
+    warn_exit "Failed to connect: #{uri.host}:#{uri.port}: #{e.message}", 3
+  end
+  response = http.request(request)
+  print_http_response(response)
+  case response
+  when Net::HTTPSuccess, Net::HTTPRedirection
+    # OK
+  when Net::HTTPNotFound
+    warn_exit "404 Not found: #{uri}", 9
+    #print response.body
+  else
+    warn_exit "#{response.code} #{response.message} #{uri}", 9
+    # Unreachable
+      response.error!
+  end
+  # NO BODY IN RESPONSE
+end
+
+def response_print_body(uri, request)
+  http = Net::HTTP::Proxy($proxy.host,$proxy.port).new(uri.host, uri.port)
+  http.read_timeout = nil
+  # check we can connect.
+  begin http.start
+  rescue => e  
+    #puts e.backtrace.inspect  
+    #print e.class
+    warn_exit "Failed to connect: #{uri.host}:#{uri.port}: #{e.message}", 3
+  end
+
+  # Add a blank line if headers were output.
+  print "\n" if $http_print ;
+
+  begin
+    response = http.request(request) { |res| 
+      print_http_response(res)
+      #puts res.code
+      res.read_body do |segment|
+        print segment
+      end
+    }
+    case response
+    when Net::HTTPSuccess, Net::HTTPRedirection
+      # OK
+    when Net::HTTPNotFound
+      warn_exit "404 Not found: #{uri}", 9
+      #print response.body
+    else
+      warn_exit "#{response.code}: #{uri}", 9
+      # Unreachable
+      response.error!
+    end
+  rescue EOFError => e
+    warn_exit "IO Error: "+e.message, 3
+  end
+end
+
+def print_http_request(uri, request)
+  return unless $print_http
+  #print "Request\n"
+  print request.method," ",uri, "\n"
+  print_headers("  ",request)
+end
+
+def print_http_response(response)
+  return unless $print_http
+  #print "Response\n"
+  print response.code, " ", response.message, "\n"
+  print_headers("  ",response)
+end
+
+def print_headers(marker, headers)
+  headers.each do |k,v| 
+    k = k.split('-').map{|w| w.capitalize}.join('-')+':'
+    printf "%s%-20s %s\n",marker,k,v
+  end
+end
+
+def content_type(file)
+  file =~ /\.([^.]*)$/
+  ext = $1
+  mt = $fileMediaTypes[ext]
+  cs = $charset[mt]
+  mt = mt+';charset='+cs if ! cs.nil?
+  return mt
+end
+
+def charset(content_type)
+  return $charset[content_type]
+end
+
+def warn_exit(msg, rc)
+    warn msg
+    exit rc ;
+end
+
+def parseURI(uri_string)
+  begin
+    return URI.parse(uri_string).to_s
+  rescue URI::InvalidURIError => err
+    warn_exit "Bad URI: <#{uri_string}>", 2
+  end
+end
+
+## ---- Command
+
+def cmd_soh(command=nil)
+  ## Command line
+  options = {}
+  optparse = OptionParser.new do |opts|
+    # Set a banner, displayed at the top
+    # of the help screen.
+    case $cmd
+    when "s-http", "sparql-http", "soh"
+      banner="$cmd [get|post|put|delete] datasetURI graph [file]"
+    when "s-get", "s-head", "s-delete"
+      banner="$cmd  datasetURI graph"
+    end
+
+    opts.banner = $banner
+    # Define the options, and what they do
+    
+    options[:verbose] = false
+    opts.on( '-v', '--verbose', 'Verbose' ) do
+      options[:verbose] = true
+    end
+    
+    options[:version] = false
+    opts.on( '--version', 'Print version and exit' ) do
+      print "#{SOH_NAME} #{SOH_VERSION}\n"
+      exit
+    end
+    
+    # This displays the help screen, all programs are
+    # assumed to have this option.
+    opts.on( '-h', '--help', 'Display this screen and exit' ) do
+      puts opts
+      exit
+    end
+  end
+
+  begin optparse.parse!    
+  rescue OptionParser::InvalidArgument => e
+    warn e
+    exit
+  end
+
+  $verbose = options[:verbose]
+  $print_http = $verbose
+
+  if command.nil?
+    if ARGV.size == 0
+      warn "No command given: expected one of 'get', 'put', 'post', 'delete', 
'query' or 'update'"
+      exit 1
+    end
+    cmdPrint=ARGV.shift
+    command=cmdPrint.upcase
+  else
+    cmdPrint=command
+  end
+
+  case command
+  when "HEAD", "GET", "DELETE"
+    requiredFile=false
+  when "PUT", "POST"
+    requiredFile=true
+  when "QUERY"
+    cmd_sparql_query
+  when "UPDATE"
+    cmd_sparql_update
+  else
+    warn_exit "Unknown command: #{command}", 2
+  end
+
+  if requiredFile 
+  then
+    if ARGV.size != 3
+      warn_exit "Required: dataset URI, graph URI (or 'default') and file", 1 
+    end
+  else
+    if ARGV.size != 2
+      warn_exit "Required: dataset URI and graph URI (or 'default')", 1 
+    end
+  end
+
+  dataset=parseURI(ARGV.shift)
+  # Relative URI?
+  graph=parseURI(ARGV.shift)
+  file=""
+  if requiredFile
+  then
+    file = ARGV.shift if requiredFile
+    if ! File.exist?(file)
+      warn_exit "No such file: "+file, 3
+    end
+    if File.directory?(file)
+      warn_exit "File is a directory: "+file, 3
+    end
+  end
+
+  case command
+  when "GET"
+    GET(dataset, graph)
+  when "HEAD"
+    HEAD(dataset, graph)
+  when "PUT"
+    PUT(dataset, graph, file)
+  when "DELETE"
+    DELETE(dataset, graph)
+  when "POST"
+    POST(dataset, graph, file)
+  else
+    warn_exit "Internal error: Unknown command: #{cmd}", 2
+  end
+  exit 0
+end
+
+## --------
+def string_or_file(arg)
+  return arg if ! arg.match(/^@/)
+  a=(arg[1..-1])
+  open(a, 'rb'){|f| f.read}
+end
+
+## -------- SPARQL Query
+
+## Choose method
+def SPARQL_query(service, query, query_file, forcePOST=false, args2={})
+   if ! query_file.nil?
+    query = open(query_file, 'rb'){|f| f.read}
+  end
+  if forcePOST || query.length >= 2*1024 
+    SPARQL_query_POST(service, query, args2)
+  else
+    SPARQL_query_GET(service, query, args2)
+  end
+end
+
+## By GET
+
+def SPARQL_query_GET(service, query, args2)
+  args = { "query" => query }
+  args.merge!(args2)
+  qs=args.collect { |k,v| "#{k}=#{uri_escape(v)}" }.join('&')
+  action="#{service}?#{qs}"
+  headers={}
+  headers.merge!($headers)
+  headers[$hAccept]=$accept_results
+  get_worker(action, headers)
+end
+
+## By POST
+
+def SPARQL_query_POST(service, query, args2)
+  # DRY - body/no body for each of request and response.
+  post_params={ "query" => query }
+  post_params.merge!(args2)
+  uri = URI.parse(service)
+  headers={}
+  headers.merge!($headers)
+  headers[$hAccept]=$accept_results
+  execute_post_form_body(uri, headers, post_params)
+end
+
+def execute_post_form_body(uri, headers, post_params)
+  request = Net::HTTP::Post.new(uri.request_uri)
+  qs=post_params.collect { |k,v| "#{k}=#{uri_escape(v)}" }.join('&')
+  headers[$hContentType] = $mtWWWForm
+  headers[$hContentLength] = qs.length.to_s
+  request.initialize_http_header(headers)
+  request.body = qs
+  print_http_request(uri, request)
+  response_print_body(uri, request)
+end
+
+# Usage: -v --help --file= --query=
+def cmd_sparql_query
+  options={}
+  optparse = OptionParser.new do |opts|
+    opts.banner = "Usage: #{$cmd} [--query QUERY] [--service URI] [--post] 
'query' | @file"
+    opts.on('--service=URI', '--server=URI', 'SPARQL endpoint') do |uri|
+      options[:service]=uri
+    end
+    opts.on('--query=FILE','--file=FILE', 'Take query from a file') do |file|
+      options[:file]=file
+    end
+    opts.on('--output=TYPE', [:json,:xml,:text,:csv,:tsv],
+            'Set the output argument') do |type|
+      options[:output]=type
+    end
+    opts.on('--accept=TYPE', [:json,:xml,:text,:csv,:tsv], 
+            'Set the accept header type') do |type|
+      options[:accept]=type
+    end
+    options[:verbose] = false
+    opts.on( '--post', 'Force use of POST' ) do
+      options[:post] = true
+    end
+    opts.on( '-v', '--verbose', 'Verbose' ) do
+      options[:verbose] = true
+    end
+    opts.on( '--version', 'Print version and exit' ) do
+      print "#{SOH_NAME} #{SOH_VERSION}\n"
+      exit
+    end 
+    opts.on( '-h', '--help', 'Display this screen and exit' ) do
+      puts opts
+      exit
+    end
+  end
+
+  begin optparse.parse!    
+  rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e
+    warn e
+    exit 1
+  end
+
+  $verbose = options[:verbose]
+  $print_http = $verbose
+  usePOST = options[:post]
+
+  service = options[:service]
+  warn_exit 'No service specified.  Required --service=URI',1 if service.nil?
+
+  # Query
+  query=nil
+  query_file=options[:file]
+  if query_file.nil? && ARGV.size == 0
+  then
+    warn_exit 'No query specified.',1
+    end
+  if query_file.nil?
+    query = ARGV.shift
+    if query.match(/^@/)
+      query_file = query[1..-1]
+      query = nil
+    end
+  end
+
+  # --output ==> output= (non-standard)
+  args={}
+  case options[:output]
+  when nil
+  when  "json","xml","text","csv","tsv"
+    args['output'] = options[:output]
+  when :json,:xml,:text,:csv,:tsv
+    args['output'] = options[:output].to_s
+  else
+    warn_exit "Unrecognized output type: "+options[:output],2
+  end
+
+  # --accept
+  # options[:accept]
+
+  print "SPARQL #{service}\n" if $verbose
+  #args={"output"=>"text"}
+  SPARQL_query(service, query, query_file, usePOST, args)
+  exit(0)
+end
+
+## -------- SPARQL Update
+
+# Update sent as a WWW form.
+def SPARQL_update_by_form(service, update, args2={})
+  args = {}
+  args.merge!(args2)
+  headers={}
+  headers.merge!($headers)
+  # args? encode?
+  body="update="+uri_escape(update)
+  headers[$hContentType] = $mtWWWForm
+  headers[$hContentLength] = body.length.to_s
+  uri = URI.parse(service)
+  execute_post_form(uri, headers, body)
+end
+
+# DRY - query form.
+def execute_post_form(uri, headers, body)
+  request = Net::HTTP::Post.new(uri.request_uri)
+  request.initialize_http_header(headers)
+  request.body = body
+  print_http_request(uri, request)
+  response_no_body(uri, request)
+end
+
+def SPARQL_update(service, update, args2={})
+  args = {}
+  args.merge!(args2)
+  headers={}
+  headers.merge!($headers)
+  headers[$hContentType] = $mtSparqlUpdate
+  uri = URI.parse(service)
+  request = Net::HTTP::Post.new(uri.request_uri)
+  request.initialize_http_header(headers)
+  request.body = update
+  print_http_request(uri, request)
+  response_no_body(uri, request)
+end
+
+def cmd_sparql_update(by_raw_post=true)
+  # Share with cmd_sparql_query
+  options={}
+  optparse = OptionParser.new do |opts|
+    opts.banner = "Usage: #{$cmd} [--file REQUEST] [--service URI] 'request' | 
@file"
+    opts.on('--service=URI', '--server=URI', 'SPARQL endpoint') do |uri|
+      options[:service]=uri
+    end
+    opts.on('--update=FILE', '--file=FILE', 'Take update from a file') do 
|file|
+      options[:file]=file
+    end
+    options[:verbose] = false
+    opts.on( '-v', '--verbose', 'Verbose' ) do
+      options[:verbose] = true
+    end
+    opts.on( '--version', 'Print version and exit' ) do
+      print "#{SOH_NAME} #{SOH_VERSION}\n"
+      exit
+    end 
+    opts.on( '-h', '--help', 'Display this screen and exit' ) do
+      puts opts
+      exit
+    end
+  end
+
+  begin optparse.parse!    
+  rescue OptionParser::InvalidArgument => e
+    warn e
+    exit
+  end
+
+  $verbose = options[:verbose]
+  $print_http = $verbose
+
+  service = options[:service]
+  warn_exit 'No service specified. Required --service=URI',1   if service.nil?
+  
+  update=nil
+  update_file=options[:file]
+
+  if update_file.nil? && ARGV.size == 0
+  then
+    warn_exit 'No update specified.',1
+    end
+  if update_file.nil?
+    update = ARGV.shift
+    if update.match(/^@/)
+      update_file = update[1..-1]
+      update = nil
+    end
+  end
+  
+  print "SPARQL-Update #{service}\n" if $verbose
+  args={}
+
+  # Reads in the file :-(
+  if update.nil?
+  then
+    update = open(update_file, 'rb'){|f| f.read}
+  else
+    update = string_or_file(update)
+  end
+
+  if by_raw_post
+    SPARQL_update(service, update, args)
+  else
+    SPARQL_update_by_form(service, update, args)
+  end
+  exit(0)
+end
+
+## -------
+
+case $cmd
+when "s-http", "sparql-http", "soh"
+  $banner="#{$cmd} [get|post|put|delete] datasetURI graph [file]"
+  cmd_soh
+when "s-get", "s-head", "s-put", "s-delete", "s-post"
+
+  case $cmd
+  when "s-get", "s-head", "s-delete"
+    $banner="#{$cmd} datasetURI graph"
+  when "s-put", "s-post"
+    $banner="#{$cmd} datasetURI graph file"
+  end
+  cmd2 = $cmd.sub(/^s-/, '').upcase
+  cmd_soh cmd2
+
+when "s-query", "sparql-query"
+  cmd_sparql_query
+when "s-update", "sparql-update"
+  cmd_sparql_update true
+when "s-update-form", "sparql-update-form"
+  cmd_sparql_update false
+else 
+  warn_exit "Unknown: "+$cmd, 1
+end

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src-dev/dev/BackwardForwardDescribeFactory.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src-dev/dev/BackwardForwardDescribeFactory.java 
b/jena-fuseki/src-dev/dev/BackwardForwardDescribeFactory.java
new file mode 100644
index 0000000..2322929
--- /dev/null
+++ b/jena-fuseki/src-dev/dev/BackwardForwardDescribeFactory.java
@@ -0,0 +1,95 @@
+/*
+ * 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 dev;
+
+import com.hp.hpl.jena.query.Dataset;
+import com.hp.hpl.jena.rdf.model.Model;
+import com.hp.hpl.jena.rdf.model.ModelFactory;
+import com.hp.hpl.jena.rdf.model.RDFNode;
+import com.hp.hpl.jena.rdf.model.Resource;
+import com.hp.hpl.jena.sparql.ARQConstants;
+import com.hp.hpl.jena.sparql.core.Quad;
+import com.hp.hpl.jena.sparql.core.describe.DescribeHandler;
+import com.hp.hpl.jena.sparql.core.describe.DescribeHandlerFactory;
+import com.hp.hpl.jena.sparql.core.describe.DescribeHandlerRegistry;
+import com.hp.hpl.jena.sparql.util.Context;
+import com.hp.hpl.jena.tdb.TDB ;
+import com.hp.hpl.jena.util.iterator.ExtendedIterator;
+import com.hp.hpl.jena.vocabulary.RDFS;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BackwardForwardDescribeFactory implements DescribeHandlerFactory {
+
+    final static Logger log = 
LoggerFactory.getLogger(BackwardForwardDescribeFactory.class);
+
+    static {
+        TDB.init();
+        log.info("Attaching replacement describe handler");
+        DescribeHandlerRegistry reg = DescribeHandlerRegistry.get();
+        log.info("Clearing existing describe handlers");
+        reg.clear();
+        reg.add(new BackwardForwardDescribeFactory());
+        log.info("Attached");
+    }
+
+    @Override
+    public DescribeHandler create() {
+        return new BackwardForwardDescribe();
+    }
+
+    public static class BackwardForwardDescribe implements DescribeHandler {
+
+        private Dataset dataset;
+        private Model result;
+        private Model defaultModel;
+        private Model unionModel;
+
+        @Override
+        public void start(Model accumulateResultModel, Context qContext) {
+            this.result = accumulateResultModel;
+            this.dataset = (Dataset) 
qContext.get(ARQConstants.sysCurrentDataset);
+            this.defaultModel = dataset.getDefaultModel();
+            this.unionModel = dataset.getNamedModel(Quad.unionGraph.getURI());
+        }
+
+        @Override
+        public void describe(Resource resource) {
+            result.add(defaultModel.listStatements(resource, null, (RDFNode) 
null));
+            result.add(defaultModel.listStatements(null, null, resource));
+            result.add(unionModel.listStatements(resource, null, (RDFNode) 
null));
+            result.add(unionModel.listStatements(null, null, resource));
+
+            // Gather labels for dangling refs
+            Model labels = ModelFactory.createDefaultModel();
+            ExtendedIterator<RDFNode> it = 
result.listObjects().andThen(result.listSubjects());
+            while (it.hasNext()) {
+                RDFNode node = it.next();
+                if (node.isLiteral() || resource.equals(node)) continue;
+                labels.add(defaultModel.listStatements((Resource) node, 
RDFS.label, (RDFNode) null));
+                labels.add(unionModel.listStatements((Resource) node, 
RDFS.label, (RDFNode) null));
+            }
+            result.add(labels);
+        }
+
+        @Override
+        public void finish() {
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src-dev/dev/DevFuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src-dev/dev/DevFuseki.java 
b/jena-fuseki/src-dev/dev/DevFuseki.java
new file mode 100644
index 0000000..70bbd7b
--- /dev/null
+++ b/jena-fuseki/src-dev/dev/DevFuseki.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev;
+
+public class DevFuseki
+{
+    // SPARQL_QueryDataset.vialidate query -- but FROM etc is important for 
TDB. 
+    // DatasetDescription from protocol
+    //   createDataset from the ARQ.
+    //   SPARQL_QueryGeneral.datasetFromDescription
+    
+    // Config:
+    //   fuseki:name ==> fuseki:serviceName of fuseki:endpointBase 
+    //   rdfs:label for log files.
+    
+    // sparql.jsp needs to switch on presence and name of service endpoints.
+    // --accept for to soh for construct queries (check can get CONSTRUCT in 
TTL).
+    
+    // application/json for application/sparql-results+json. 
+    // application/xml for application/sparql-results+xml. 
+    
+    // LimitingGraph, LimitingBulkUpdateHandler --> change to use a limiting 
Sink<>
+    // Finish: SPARQL_QueryGeneral
+    //    Parse errors and etc need to be passed out.
+    // --jetty-config documentation
+    
+    // Rework arguments.
+    //   Explicit install pages.
+    //   Pages for read-only.
+    
+    // RDF/XML_ABBREV in ResponseModel
+    // Config Jetty from a file?
+    //   Alternatibe way to run Fuseki
+    
+       // Flint?
+       // Pages for publish mode.
+
+       // Multiple Accept headers
+    // WebContent and ContentType clean up.
+    
+       // SOH default to not needing 'default'
+       // More error handling.
+
+    // Migrate ContentType to RIOT
+    // use in WebContent.
+    
+    //soh : --accept application/turtle for CONSTRUCT queries.
+    
+    // Direct naming.
+    // Use absence/presence of a query string to switch.
+    
+    // sparql.jsp ==> if no dataset, go to choosing page or error page linking 
to.
+    
+    // Better handling of bad URI name for a graph http:/example/X => 
stacktrace.
+    
+    // FUSEKI_ROOT
+
+    // Documentation:
+    //   Plan documentation.
+    //   MIME types supported
+    // Include a data file in the distribution.
+    // curl as commandline
+    
+    // Dataset servers - bulk loader.
+    // Access the bulk loader via web. [later]
+    
+    // Structure pages, different static content servers
+    // /Main - index.html = fuseki.html
+    // /validate
+    // /admin
+    
+    // Server-local graph naming
+    
+    // Plain text error pages
+    
+    // Deploy to sparql.org -- need query form for read-only mode.
+    
+    // + LARQ
+    
+    // Testing:
+    //   No file -> error.
+    
+    // Remove from package conneg - use code from ARQ/Atlas.
+    // TypedStream TypedInoputStrea, TypedOutputStream, MediaType, MediaRange
+    
+    // Bundle tdb scripts with Fuseki.
+
+    // ParserFor - share between SPARQL_REST and SPARQL_Upload
+    // UploadTo dataset (TriG, N-Quads)
+   
+    // populate forms with prefixes (later)
+    
+    // Tests
+    //   TestProtocol (HTTP update, query, update), inc status codes.
+    //   SPARQL Query servlet / SPARQL Update servlet
+    //   TestContentNegotiation - is coveage enough?
+    
+    // ?? Slug header:
+    // http://bitworking.org/projects/atom/rfc5023.html#rfc.section.9.7
+    
+    // ETags.
+    
+    // Authentication
+    
+    // SOH
+    //   Refactor into body/no_body send & body/no_body receive
+    // All:
+    // -v --help --accept --user/--password ( or --auth user:pass) 
+    // Drop --service.
+    // Local config file - read to get service settings. 
+    
+    //   --accept line/shortname : s-get, s-query
+    //   Basic authentication: --user --password
+    
+    // Argument names: --service naming seems inconsistent.
+    
+    // Plug-ins:
+    //   Dataset (query, Update), HttpInternalIF?
+    //   "Connection"
+    // Locking => transaction support (via default model?)
+    //   HttpAction.beginRead() etc.
+    
+    // Java clients:
+    //   DatasetAccessor : don't serialise to byte[] and then send. 
+    //   DatasetAccessor : check existence of endpoint. 
+
+    // Content-Length: SHOULD
+    //   Transfer-Encoding: identity
+    // "chunked" encoding
+    // gzip
+    
+    // Code examples
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src-dev/dev/RunFuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src-dev/dev/RunFuseki.java 
b/jena-fuseki/src-dev/dev/RunFuseki.java
new file mode 100644
index 0000000..74f678c
--- /dev/null
+++ b/jena-fuseki/src-dev/dev/RunFuseki.java
@@ -0,0 +1,90 @@
+/*
+ * 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 dev;
+
+import org.apache.jena.fuseki.FusekiCmd ;
+
+public class RunFuseki
+{
+    public static void main(String[] args)
+    {
+        //demo() ;
+        //FusekiCmd.main("--config=config.ttl") ; System.exit(0) ;
+        main1() ;
+    }
+    
+    public static void demo()
+    {
+        String DIR="DemoServer" ;
+        FusekiCmd.main("--config="+name(DIR,"config.ttl"), 
"--pages="+name(DIR, "demo-pages")) ;
+        System.exit(0) ;
+    }
+    
+    public static String name(String DIR, String filename)
+    {
+        StringBuilder sb = new StringBuilder() ;
+        if ( ! filename.startsWith("/") )
+        {
+            sb.append(DIR) ;
+            if ( ! DIR.endsWith("/") )
+                sb.append("/") ;
+        }
+        sb.append(filename) ;
+        return sb.toString() ;
+    }
+    
+    private static void main1()
+    {
+        String tmpdir = System.getenv("TMPDIR") ;
+        if ( tmpdir == null )
+            tmpdir = System.getenv("TMP") ;
+        if ( tmpdir == null )
+            tmpdir = System.getenv("HOME")+"/tmp" ;
+        if ( ! tmpdir.endsWith("/") )
+            tmpdir = tmpdir+"/" ;
+        
+        FusekiCmd.main(
+                     //  "-v",
+                     "--mem", "--update", "/ds"
+                     //"--update", "--loc="+tmpdir+"DB", 
"--set=tdb:unionDefaultGraph=true", "/ds"
+                     //"--update", "--mem", "/ds"
+
+                     //"--update", "--memtdb", 
"--set=tdb:unionDefaultGraph=true", "/ds"
+                     
+                    //"--debug",
+                    //"--update",
+                    //"--timeout=1000,5000",
+                    //"--set=arq:queryTimeout=1000",
+                    //"--port=3030",
+                    //"--mgtPort=3031",
+                    //"--mem",
+                    //"--home=/home/afs/Projects/Fuseki",
+                    //"--loc=DB",
+                    //"--file=D.nt",
+                    //"--gzip=no",
+                    //"--desc=desc.ttl",
+                    //--pages=
+                    //"--jetty-config=jetty-fuseki.xml",
+                    //"--config=config-tdb.ttl"
+                    // "/ds"
+                    ) ;
+        System.exit(0) ;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/DEF.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/DEF.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/DEF.java
new file mode 100644
index 0000000..c24f7e3
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/DEF.java
@@ -0,0 +1,66 @@
+/*
+ * 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.riot.WebContent.* ;
+import org.apache.jena.atlas.web.AcceptList ;
+import org.apache.jena.atlas.web.MediaType ;
+
+public class DEF
+{
+    public static final MediaType acceptRDFXML        = 
MediaType.create(contentTypeRDFXML) ;
+    public static final MediaType acceptNQuads        = 
MediaType.create(contentTypeNQuads) ;
+    public static final MediaType acceptRSXML         = 
MediaType.create(contentTypeResultsXML) ;
+
+    public static final AcceptList rdfOffer           = 
AcceptList.create(contentTypeTurtle, 
+                                                                          
contentTypeTurtleAlt1,
+                                                                          
contentTypeTurtleAlt2,
+                                                                          
contentTypeNTriples,
+                                                                          
contentTypeNTriplesAlt,
+                                                                          
contentTypeRDFXML,
+                                                                          
contentTypeJSONLD,
+                                                                          
contentTypeRDFJSON,
+                                                                          
contentTypeRDFThrift
+                                                                          ) ;
+    
+    public static final AcceptList quadsOffer         = 
AcceptList.create(contentTypeTriG,
+                                                                          
contentTypeTriGAlt1,
+                                                                          
contentTypeTriGAlt2,
+                                                                          
contentTypeJSONLD,
+                                                                          
contentTypeNQuads,
+                                                                          
contentTypeNQuadsAlt1,
+                                                                          
contentTypeNQuadsAlt2 
+                                                                          ) ;
+    
+    public static final AcceptList rsOfferTable            = 
AcceptList.create(contentTypeResultsJSON,
+                                                                          
contentTypeTextCSV,
+                                                                          
contentTypeTextTSV,
+                                                                          
contentTypeResultsXML,
+                                                                          
contentTypeResultsThrift,
+                                                                          
contentTypeTextPlain
+                                                                          ) ;
+    
+    public static final AcceptList rsOfferBoolean            = 
AcceptList.create(contentTypeResultsJSON,
+                                                                          
contentTypeTextCSV,
+                                                                          
contentTypeTextTSV,
+                                                                          
contentTypeResultsXML,
+                                                                          
contentTypeTextPlain
+                                                                          ) ;
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/EmbeddedFusekiServer.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/EmbeddedFusekiServer.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/EmbeddedFusekiServer.java
new file mode 100644
index 0000000..0261b67
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/EmbeddedFusekiServer.java
@@ -0,0 +1,86 @@
+/*
+ * 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.atlas.lib.FileOps ;
+import org.apache.jena.fuseki.server.FusekiConfig ;
+import org.apache.jena.fuseki.server.SPARQLServer ;
+import org.apache.jena.fuseki.server.ServerConfig ;
+
+import com.hp.hpl.jena.sparql.core.DatasetGraph ;
+import com.hp.hpl.jena.sparql.core.DatasetGraphFactory ;
+import com.hp.hpl.jena.tdb.TDBFactory ;
+
+/** Embedded (same JVM) server.
+ * <p>Example for one server per test suite: 
+ * <pre>
+    private static EmbeddedFusekiServer server = null ;
+    \@BeforeClass public static void beforeClass() { 
+        server = EmbeddedFusekiServer.createMemByPath(3030, "/test") ;
+        server.start() ;
+    \@AfterClass  public static void afterClass()  { 
+        server.stop() ;
+    }
+    </pre>
+ */
+public class EmbeddedFusekiServer
+{
+    
+    public static EmbeddedFusekiServer mem(int port, String datasetPath) {
+        DatasetGraph dsg = DatasetGraphFactory.createMem() ;
+        return EmbeddedFusekiServer.create(port, dsg, datasetPath) ;
+    }
+    
+    public static EmbeddedFusekiServer memTDB(int port, String datasetPath) {
+        DatasetGraph dsg = TDBFactory.createDatasetGraph() ;
+        return EmbeddedFusekiServer.create(port, dsg, datasetPath) ;
+    }
+
+    public static EmbeddedFusekiServer create(int port, DatasetGraph dsg, 
String datasetPath) {
+        ServerConfig conf = FusekiConfig.defaultConfiguration(datasetPath, 
dsg, true, true) ;
+        conf.port = port ;
+        conf.pagesPort = port ;
+        if ( ! FileOps.exists(conf.pages) )
+            conf.pages = null ;
+        return new EmbeddedFusekiServer(conf) ;
+    }
+        
+    public static EmbeddedFusekiServer configure(int port, String fileConfig) {
+        ServerConfig conf = FusekiConfig.configure(fileConfig) ;
+        conf.port = port ;
+        conf.pagesPort = port ;
+        if ( ! FileOps.exists(conf.pages) )
+            conf.pages = null ;
+        return new EmbeddedFusekiServer(conf) ;
+    }
+    
+    private SPARQLServer server = null ;
+    
+    public EmbeddedFusekiServer(ServerConfig conf) {
+        server = new SPARQLServer(conf) ;
+    }
+    
+    public void start() {
+        server.start() ;
+    }
+    
+    public void stop() {
+        server.stop() ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/Fuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/Fuseki.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/Fuseki.java
new file mode 100644
index 0000000..c62ba1f
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/Fuseki.java
@@ -0,0 +1,174 @@
+/*
+ * 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.fuseki.server.SPARQLServer ;
+import org.apache.jena.riot.RIOT ;
+import org.apache.jena.riot.system.stream.LocatorFTP ;
+import org.apache.jena.riot.system.stream.LocatorHTTP ;
+import org.apache.jena.riot.system.stream.StreamManager ;
+import org.slf4j.Logger ;
+import org.slf4j.LoggerFactory ;
+
+import com.hp.hpl.jena.query.ARQ ;
+import com.hp.hpl.jena.sparql.SystemARQ ;
+import com.hp.hpl.jena.sparql.lib.Metadata ;
+import com.hp.hpl.jena.sparql.mgt.SystemInfo ;
+import com.hp.hpl.jena.sparql.util.Context ;
+import com.hp.hpl.jena.sparql.util.MappingRegistry ;
+import com.hp.hpl.jena.tdb.TDB ;
+import com.hp.hpl.jena.tdb.transaction.TransactionManager ;
+
+/**
+ * <p>The main class enabling us to:</p> 
+ * <ol>
+ * <li>create instances of a Fuseki server e.g.
+ * the ARQ, RIOT and TDB server stack</li>
+ * <li>get server global {@link com.hp.hpl.jena.sparql.util.Context} e.g. 
+ * named values used to pass implementation-specific parameters across 
+ * general interfaces.</li>
+ * <li>get the {@link org.apache.jena.fuseki.server.SPARQLServer} 
instance.</li>
+ * <li>set the {@link org.apache.jena.fuseki.server.SPARQLServer} 
instance.</li>
+ *
+ */
+public class Fuseki
+{
+    /** Path to ???*/
+    static public String PATH = "org.apache.jena.fuseki" ;
+    
+    /** a unique IRI for the Fuseki namespace*/
+    static public String FusekiIRI = "http://jena.apache.org/Fuseki"; ;
+    
+    /** Fuseki home environment, usually set to $FUSEKI_HOME */
+    static public String FusekiHomeEnv = "FUSEKI_HOME" ;
+    
+    /** a unique IRI including the symbol notation for which properties should 
be appended */
+    static public String FusekiSymbolIRI = "http://jena.apache.org/fuseki#"; ;
+    
+    /** ??? */
+    static public String PagesStatic = "pages" ;
+    
+    /** 
+     * TEMPORARY - this enables POST of triples to the dataset URI causing a 
graph to be created.
+     * POSTing to /dataset?graph=uri is preferred 
+     */
+    static public boolean graphStoreProtocolPostCreate = false ;
+    
+    /** an relative path to the location of <code>fuseki-properties.xml</code> 
file */
+    static private String metadataLocation = 
"org/apache/jena/fuseki/fuseki-properties.xml" ;
+    
+    /** Object which holds metadata specified within {@link 
Fuseki#metadataLocation} */
+    static private Metadata metadata = initMetadata() ;
+    
+    private static Metadata initMetadata()
+    {
+        Metadata m = new Metadata() ;
+        //m.addMetadata(metadataDevLocation) ;
+        m.addMetadata(metadataLocation) ;
+        return m ;
+    }
+    
+    /** The name of the Fuseki server. Set to the string <code>Fuseki</code> 
by default.*/
+    static public final String NAME             = "Fuseki" ;
+    
+    /** Version of this Fuseki instance */
+    static public final String VERSION          = 
metadata.get(PATH+".version", "development");
+    
+    /** Date when Fuseki was built */
+    static public final String BUILD_DATE       = 
metadata.get(PATH+".build.datetime", "unknown") ; // call Date if unavailable.
+    
+    /** An identifier for the HTTP Fuseki server instance*/
+    static public final String serverHttpName   = NAME+" ("+VERSION+")" ;    
+    
+    /** Actual log file for operations */
+    public static final String requestLogName   = PATH+".Fuseki" ;
+    
+    /** Instance of log for operations */
+    public static final Logger requestLog       = 
LoggerFactory.getLogger(requestLogName) ;
+    
+    /** Actual log file for general server messages.*/
+    public static final String serverLogName    = PATH+".Server" ;
+    
+    /** Instance of log for general server messages */
+    public static final Logger serverLog        = 
LoggerFactory.getLogger(serverLogName) ;
+    
+    /** Actual log file for config server messages. */
+    public static final String configLogName    = PATH+".Config" ;
+    
+    /** Instance of log for config server message s*/
+    public static final Logger configLog        = 
LoggerFactory.getLogger(configLogName) ;
+    
+    /** Instance of log for config server message s*/
+    public static boolean verboseLogging        = false ;
+    
+    /** An instance of management for stream opening, including redirecting 
through a 
+     * location mapper whereby a name (e.g. URL) is redirected to another name 
(e.g. local file).
+     * */
+    public static final StreamManager webStreamManager ;
+    static {
+        webStreamManager = new StreamManager() ;
+        // Only know how to handle http and ftp URLs - nothing local.
+        webStreamManager.addLocator(new LocatorHTTP()) ;
+        webStreamManager.addLocator(new LocatorFTP()) ;
+    }
+    
+    private static boolean initialized = false ;
+    
+    /**
+     * Initialize an instance of the Fuseki server stack.
+     */
+    public synchronized static void init()
+    {
+        if ( initialized )
+            return ;
+        initialized = true ;
+        ARQ.init() ;
+        SystemInfo sysInfo = new SystemInfo(FusekiIRI, PATH, VERSION, 
BUILD_DATE) ;
+        SystemARQ.registerSubSystem(sysInfo) ;
+        RIOT.init() ;
+        TDB.init() ;
+        MappingRegistry.addPrefixMapping("fuseki", FusekiSymbolIRI) ;
+        
+        TDB.setOptimizerWarningFlag(false) ;
+        // Don't set TDB batch commits.
+        // This can be slower, but it less memory hungry and more predictable. 
+        TransactionManager.QueueBatchSize = 0 ;
+    }
+  
+    /**
+     * Get server global {@link com.hp.hpl.jena.sparql.util.Context}.
+     * @return {@link com.hp.hpl.jena.query.ARQ#getContext()}
+     */
+    public static Context getContext()
+    {
+        return ARQ.getContext() ;
+    }
+    
+    // Temporary ...
+    private static SPARQLServer server ;
+    
+    /** set/specify the {@link org.apache.jena.fuseki.server.SPARQLServer} 
instance.*/
+    public static void setServer(SPARQLServer _server)      { server = _server 
; }
+    
+    /** get the {@link org.apache.jena.fuseki.server.SPARQLServer} instance. */
+    public static SPARQLServer getServer()                  { return server ; }
+
+    // Force a call to init.
+    static { init() ; }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiCmd.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiCmd.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiCmd.java
new file mode 100644
index 0000000..b076871
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiCmd.java
@@ -0,0 +1,505 @@
+/*
+ * 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() ) {
+            System.out.println("File") ;
+            // 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 specificed on the command line 
and also a configuration file specificed.") ;
+        }
+        else
+        {
+            if ( x == 0 )
+                throw new CmdException("Required: either --config=FILE or one 
of --mem, --file, --loc or --desc") ;
+        }
+        
+        if ( contains(argMem) )
+        {
+            log.info("Dataset: in-memory") ;
+            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("Dataset specificed on the command line 
and also a configuration file specificed.") ;
+            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/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiConfigException.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiConfigException.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiConfigException.java
new file mode 100644
index 0000000..5e1b018
--- /dev/null
+++ 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiConfigException.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki;
+
+
+public class FusekiConfigException extends FusekiException
+{
+    public FusekiConfigException(String msg, Throwable cause)    { super(msg, 
cause) ; }
+    public FusekiConfigException(String msg)                     { super(msg) 
; }
+    public FusekiConfigException(Throwable cause)                { 
super(cause) ; }
+    public FusekiConfigException()                               { super() ; }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiException.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiException.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiException.java
new file mode 100644
index 0000000..04953ce
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiException.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki;
+
+import com.hp.hpl.jena.sparql.ARQException ;
+
+public class FusekiException extends ARQException
+{
+    public FusekiException(String msg, Throwable cause)    { super(msg, cause) 
; }
+    public FusekiException(String msg)                     { super(msg) ; }
+    public FusekiException(Throwable cause)                { super(cause) ; }
+    public FusekiException()                               { super() ; }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiLib.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiLib.java
new file mode 100644
index 0000000..5324793
--- /dev/null
+++ b/jena-fuseki/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/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java
new file mode 100644
index 0000000..be9be90
--- /dev/null
+++ 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki;
+
+import org.apache.jena.web.HttpSC ;
+
+public class FusekiNotFoundException extends FusekiRequestException
+{
+    public FusekiNotFoundException(String msg)    { 
super(HttpSC.NOT_FOUND_404, msg) ; }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java
new file mode 100644
index 0000000..e197be2
--- /dev/null
+++ 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki;
+
+import org.apache.jena.web.HttpSC ;
+
+
+public class FusekiRequestException extends FusekiException
+{
+    public static FusekiRequestException create(int code, String msg)
+    {
+        if ( code == HttpSC.NOT_FOUND_404 )
+            return new FusekiNotFoundException(msg) ;
+        return new FusekiRequestException(code, msg) ;
+    }
+    
+    private final int statusCode ;
+    private final String responseMessage ;
+    protected FusekiRequestException(int code, String msg)
+    {
+        super(msg) ;
+        this.statusCode = code ;
+        responseMessage = msg ;
+    }
+    
+    public int getStatusCode()
+    {
+        return statusCode ;
+    }
+
+    public String getResponseMessage()
+    {
+        return responseMessage ;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "HTTP: "+statusCode+" "+getMessage() ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/HttpNames.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/HttpNames.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/HttpNames.java
new file mode 100644
index 0000000..f6cee56
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/HttpNames.java
@@ -0,0 +1,93 @@
+/*
+ * 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" ;
+    
+    // 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/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/Test.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/Test.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/Test.java
new file mode 100644
index 0000000..b4f5fed
--- /dev/null
+++ b/jena-fuseki/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") ; }
+}
+

Reply via email to