You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by rv...@apache.org on 2013/06/28 00:14:10 UTC

svn commit: r1497583 [1/2] - in /jena/trunk/jena-arq/src: main/java/com/hp/hpl/jena/sparql/engine/http/ main/java/com/hp/hpl/jena/sparql/modify/ main/java/org/apache/jena/atlas/web/ main/java/org/apache/jena/atlas/web/auth/ main/java/org/apache/jena/ri...

Author: rvesse
Date: Thu Jun 27 22:14:09 2013
New Revision: 1497583

URL: http://svn.apache.org/r1497583
Log:
Finish converting SPARQL Query code to new unified HTTP framework and support broader authentication for SPARQL Queries (JENA-480)

Started work on adding support for Forms based authentication

Added:
    jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/ApacheModAuthFormLogin.java
    jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/FormLogin.java
    jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/FormsAuthenticator.java
Modified:
    jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/HttpQuery.java
    jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/QueryEngineHTTP.java
    jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/QueryExceptionHTTP.java
    jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/modify/UpdateProcessRemote.java
    jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/HttpException.java
    jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/ServiceAuthenticator.java
    jena/trunk/jena-arq/src/main/java/org/apache/jena/riot/web/HttpOp.java
    jena/trunk/jena-arq/src/main/java/org/apache/jena/web/DatasetGraphAccessorHTTP.java
    jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/engine/http/TestService.java

Modified: jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/HttpQuery.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/HttpQuery.java?rev=1497583&r1=1497582&r2=1497583&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/HttpQuery.java (original)
+++ jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/HttpQuery.java Thu Jun 27 22:14:09 2013
@@ -18,534 +18,384 @@
 
 package com.hp.hpl.jena.sparql.engine.http;
 
-import java.io.* ;
-import java.net.HttpURLConnection ;
-import java.net.MalformedURLException ;
-import java.net.SocketTimeoutException ;
-import java.net.URL ;
-import java.util.ArrayList ;
-import java.util.Iterator ;
-import java.util.List ;
-import java.util.Map ;
-import java.util.regex.Pattern ;
-import java.util.zip.DeflaterInputStream ;
-import java.util.zip.GZIPInputStream ;
-
-import org.apache.commons.codec.binary.Base64 ;
-import org.apache.jena.atlas.lib.StrUtils ;
-import org.apache.jena.riot.WebContent ;
-import org.slf4j.Logger ;
-import org.slf4j.LoggerFactory ;
-
-import com.hp.hpl.jena.query.ARQ ;
-import com.hp.hpl.jena.query.QueryExecException ;
-import com.hp.hpl.jena.shared.JenaException ;
-import com.hp.hpl.jena.sparql.ARQInternalErrorException ;
-import com.hp.hpl.jena.sparql.util.Convert ;
-import com.hp.hpl.jena.util.FileUtils ;
-
-/** Create an execution object for performing a query on a model
- *  over HTTP.  This is the main protocol engine for HTTP query.
- *  There are higher level classes for doing a query and presenting
- *  the results in an API fashion. 
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.zip.DeflaterInputStream;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.AbstractHttpClient;
+import org.apache.http.impl.client.DecompressingHttpClient;
+import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.jena.atlas.AtlasException;
+import org.apache.jena.atlas.lib.StrUtils;
+import org.apache.jena.atlas.web.HttpException;
+import org.apache.jena.atlas.web.TypedInputStream;
+import org.apache.jena.atlas.web.auth.HttpAuthenticator;
+import org.apache.jena.atlas.web.auth.SimpleAuthenticator;
+import org.apache.jena.riot.WebContent;
+import org.apache.jena.riot.web.HttpOp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.hp.hpl.jena.query.ARQ;
+import com.hp.hpl.jena.query.QueryExecException;
+import com.hp.hpl.jena.shared.JenaException;
+import com.hp.hpl.jena.sparql.ARQInternalErrorException;
+import com.hp.hpl.jena.sparql.util.Convert;
+import com.hp.hpl.jena.util.FileUtils;
+
+/**
+ * Create an execution object for performing a query on a model over HTTP. This
+ * is the main protocol engine for HTTP query. There are higher level classes
+ * for doing a query and presenting the results in an API fashion.
  * 
- *  If the query string is large, then HTTP POST is used. */
-public class HttpQuery extends Params
-{
-    static final Logger log = LoggerFactory.getLogger(HttpQuery.class.getName()) ;
-    
+ * If the query string is large, then HTTP POST is used.
+ */
+public class HttpQuery extends Params {
+    static final Logger log = LoggerFactory.getLogger(HttpQuery.class.getName());
+
     /** The definition of "large" queries */
     // Not final so that other code can change it.
-    static public /*final*/ int urlLimit = 2*1024 ;
-    
-    String serviceURL ;
-    
-    String contentTypeResult = WebContent.contentTypeResultsXML ;
-    HttpURLConnection httpConnection = null ;
-    
-    // An object indicate no value associated with parameter name 
-    final static Object noValue = new Object() ;
-    String user = null ;
-    char[] password = null ;
-    
-    int responseCode = 0;
-    String responseMessage = null ;
-    boolean forcePOST = false ;
-    String queryString = null ;
-    boolean serviceParams = false ;
-    private final Pattern queryParamPattern = Pattern.compile(".+[&|\\?]query=.*") ;
-    
-    int connectTimeout = 0;
-    int readTimeout = 0;
-    
-    private boolean allowGZip = false ;
+    static public/* final */int urlLimit = 2 * 1024;
+
+    String serviceURL;
+    String contentTypeResult = WebContent.contentTypeResultsXML;
+
+    // An object indicate no value associated with parameter name
+    final static Object noValue = new Object();
+
+    private HttpAuthenticator authenticator = null;
+    private int responseCode = 0;
+    private String responseMessage = null;
+    private boolean forcePOST = false;
+    private String queryString = null;
+    private boolean serviceParams = false;
+    private final Pattern queryParamPattern = Pattern.compile(".+[&|\\?]query=.*");
+    private int connectTimeout = 0, readTimeout = 0;
+    private boolean allowGZip = false;
     private boolean allowDeflate = false;
-    
-    //static final String ENC_UTF8 = "UTF-8" ;
-    
-    /** Create a execution object for a whole model GET
-     * @param serviceURL     The model
+
+    // static final String ENC_UTF8 = "UTF-8" ;
+
+    /**
+     * Create a execution object for a whole model GET
+     * 
+     * @param serviceURL
+     *            The model
      */
-    
-    public HttpQuery(String serviceURL)
-    {
-        init(serviceURL) ;
+    public HttpQuery(String serviceURL) {
+        init(serviceURL);
     }
-        
 
-    /** Create a execution object for a whole model GET
-     * @param url           The model
+    /**
+     * Create a execution object for a whole model GET
+     * 
+     * @param url
+     *            The model
      */
-    
-    public HttpQuery(URL url)
-    {
-        init(url.toString()) ;
+    public HttpQuery(URL url) {
+        init(url.toString());
     }
-        
 
-    private void init(String serviceURL)
-    {
-        if ( log.isTraceEnabled())
-            log.trace("URL: "+serviceURL) ;
- 
-        if ( serviceURL.indexOf('?') >= 0 )
-            serviceParams = true ;
+    private void init(String serviceURL) {
+        if (log.isTraceEnabled())
+            log.trace("URL: " + serviceURL);
 
-        if ( queryParamPattern.matcher(serviceURL).matches() )
-            throw new QueryExecException("SERVICE URL overrides the 'query' SPARQL protocol parameter") ;
+        if (serviceURL.indexOf('?') >= 0)
+            serviceParams = true;
 
-        this.serviceURL = serviceURL ;
+        if (queryParamPattern.matcher(serviceURL).matches())
+            throw new QueryExecException("SERVICE URL overrides the 'query' SPARQL protocol parameter");
+
+        this.serviceURL = serviceURL;
     }
-    
-    private String getQueryString()
-    {
-        if ( queryString == null )
-            queryString = super.httpString() ;
-        return queryString ;
+
+    private String getQueryString() {
+        if (queryString == null)
+            queryString = super.httpString();
+        return queryString;
     }
 
-    public HttpURLConnection getConnection() { return httpConnection ; }
-    
-    /** Set the content type (Accept header) for the results
+    /**
+     * Set the content type (Accept header) for the results
+     * 
+     * @param contentType
+     *            Accept content type
      */
-    public void setAccept(String contentType)
-    {
-        contentTypeResult = contentType ;
+    public void setAccept(String contentType) {
+        contentTypeResult = contentType;
     }
-    
+
     /**
-     * Gets the Content Type, if the query has been made this reflects the Content-Type header returns, if it has not been made this reflects only the Accept header that will be sent (as set via the {@link #setAccept(String)} method)
+     * Gets the Content Type
+     * <p>
+     * If the query has been made this reflects the Content-Type header returns,
+     * if it has not been made this reflects only the Accept header that will be
+     * sent (as set via the {@link #setAccept(String)} method)
+     * </p>
+     * 
+     * @return Content Type
      */
-    public String getContentType()
-    {
-    	return contentTypeResult;
+    public String getContentType() {
+        return contentTypeResult;
     }
-    
+
     /**
-     * Gets the HTTP Response Code returned by the request (returns 0 if request has yet to be made)
+     * Gets the HTTP Response Code returned by the request (returns 0 if request
+     * has yet to be made)
+     * 
+     * @return Response Code
      */
-    public int getResponseCode()
-    {
-    	return responseCode;
+    public int getResponseCode() {
+        return responseCode;
     }
-    
+
     /**
      * Sets whether the HTTP request will include a Accept-Encoding: gzip header
+     * 
+     * @param allow
+     *            Whether to allow GZip encoding
      */
-    public void setAllowGZip(boolean allow)
-    {
-    	allowGZip = allow;
+    public void setAllowGZip(boolean allow) {
+        allowGZip = allow;
     }
-    
+
     /**
-     * Sets whether the HTTP request will include a Accept-Encoding: deflate header
+     * Sets whether the HTTP request will include a Accept-Encoding: deflate
+     * header
+     * 
+     * @param allow
+     *            Whether to allow Deflate encoding
      */
-    public void setAllowDeflate(boolean allow)
-    {
-    	allowDeflate = allow;
+    public void setAllowDeflate(boolean allow) {
+        allowDeflate = allow;
     }
-    
+
     /**
-     * Sets basic authentication
-     * @param user Username
-     * @param password Password
+     * Sets basic authentication. It may be preferable to use the
+     * {@link #setAuthenticator(HttpAuthenticator)} method since that provides
+     * more flexibility in the type of authentication supported.
+     * 
+     * @param user
+     *            User name
+     * @param password
+     *            Password
      */
-    public void setBasicAuthentication(String user, char[] password)
-    {
-        this.user = user ;
-        this.password = password ;
+    public void setBasicAuthentication(String user, char[] password) {
+        this.setAuthenticator(new SimpleAuthenticator(user, password));
     }
-    
-    /** Return whether this request will go by GET or POST
-     *  @return boolean
+
+    /**
+     * Sets the authenticator to use
+     * @param authenticator Authenticator
      */
-    public boolean usesPOST()
-    {
-        if ( forcePOST )
-            return true ;
-        String s = getQueryString() ;
-        
-        return serviceURL.length()+s.length() >= urlLimit ;
+    public void setAuthenticator(HttpAuthenticator authenticator) {
+        this.authenticator = authenticator;
+    }
+
+    /**
+     * Return whether this request will go by GET or POST
+     * 
+     * @return boolean
+     */
+    public boolean usesPOST() {
+        if (forcePOST)
+            return true;
+        String s = getQueryString();
+
+        return serviceURL.length() + s.length() >= urlLimit;
     }
 
-    /** Force the use of HTTP POST for the query operation
+    /**
+     * Force the use of HTTP POST for the query operation
      */
 
-    public void setForcePOST()
-    {
-        forcePOST = true ;
+    public void setForcePOST() {
+        forcePOST = true;
     }
-    
+
     /**
      * Sets HTTP Connection timeout, any value <= 0 is taken to mean no timeout
+     * 
+     * @param timeout
+     *            Connection Timeout
      */
-    public void setConnectTimeout(int timeout)
-    {
-    	connectTimeout = timeout;
+    public void setConnectTimeout(int timeout) {
+        connectTimeout = timeout;
     }
-    
+
     /**
      * Gets the HTTP Connection timeout
+     * 
+     * @return Connection Timeout
      */
-    public int getConnectTimeout()
-    {
-    	return connectTimeout;
+    public int getConnectTimeout() {
+        return connectTimeout;
     }
-    
+
     /**
      * Sets HTTP Read timeout, any value <= 0 is taken to mean no timeout
+     * 
+     * @param timeout
+     *            Read Timeout
      */
-    public void setReadTimeout(int timeout)
-    {
-    	readTimeout = timeout;
+    public void setReadTimeout(int timeout) {
+        readTimeout = timeout;
     }
-    
+
     /**
      * Gets the HTTP Read timeout
+     * 
+     * @return Read Timeout
      */
-    public int getReadTimeout()
-    {
-    	return readTimeout;
+    public int getReadTimeout() {
+        return readTimeout;
     }
 
-    /** Execute the operation
-     * @return Model    The resulting model
+    /**
+     * Execute the operation
+     * 
+     * @return Model The resulting model
      * @throws QueryExceptionHTTP
      */
-    public InputStream exec() throws QueryExceptionHTTP
-    {
+    public InputStream exec() throws QueryExceptionHTTP {
         try {
             if (usesPOST())
                 return execPost();
             return execGet();
-        } catch (QueryExceptionHTTP httpEx)
-        {
+        } catch (QueryExceptionHTTP httpEx) {
             log.trace("Exception in exec", httpEx);
             throw httpEx;
-        }
-        catch (JenaException jEx)
-        {
+        } catch (JenaException jEx) {
             log.trace("JenaException in exec", jEx);
-            throw jEx ;
+            throw jEx;
         }
     }
 
-    private InputStream execGet() throws QueryExceptionHTTP
-    {
-        URL target = null ;
-        String qs = getQueryString() ;
-        
-        ARQ.getHttpRequestLogger().trace(qs) ;
-        
+    private InputStream execGet() throws QueryExceptionHTTP {
+        URL target = null;
+        String qs = getQueryString();
+
+        ARQ.getHttpRequestLogger().trace(qs);
+
         try {
-            if ( count() == 0 )
-                target = new URL(serviceURL) ; 
+            if (count() == 0)
+                target = new URL(serviceURL);
             else
-                target = new URL(serviceURL+(serviceParams ? "&" : "?")+qs) ;
+                target = new URL(serviceURL + (serviceParams ? "&" : "?") + qs);
+        } catch (MalformedURLException malEx) {
+            throw new QueryExceptionHTTP(0, "Malformed URL: " + malEx);
         }
-        catch (MalformedURLException malEx)
-        { throw new QueryExceptionHTTP(0, "Malformed URL: "+malEx) ; }
-        log.trace("GET "+target.toExternalForm()) ;
-        
-        try
-        {
-            httpConnection = (HttpURLConnection) target.openConnection();
-            // This is the default setting - but be clear about it.
-            httpConnection.setInstanceFollowRedirects(true) ;       
-            httpConnection.setRequestProperty("Accept", contentTypeResult) ;
-            
-            int x = httpConnection.getReadTimeout() ;
-            
-            // By default, following 3xx redirects is true
-            //conn.setFollowRedirects(true) ;
-            basicAuthentication(httpConnection) ;
-            applyTimeouts(httpConnection);
-            applyEncodings(httpConnection);
-            
-            httpConnection.setDoInput(true);
-            httpConnection.connect();
-            try
-            {
-                return execCommon();
-            }
-            catch (QueryExceptionHTTP qEx)
-            {
+        log.trace("GET " + target.toExternalForm());
+
+        try {
+            try {
+                HttpClient client = new SystemDefaultHttpClient();
+                if (this.connectTimeout > 0)
+                    client.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, this.connectTimeout);
+                if (this.readTimeout > 0)
+                    client.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, this.readTimeout);
+                HttpContext context = new BasicHttpContext();
+                if (allowGZip || allowDeflate) {
+                    // Apply auth early as the decompressing client we're about
+                    // to add will block this being applied later
+                    HttpOp.applyAuthentication((AbstractHttpClient) client, serviceURL, context, authenticator);
+                    client = new DecompressingHttpClient(client);
+                }
+                TypedInputStream stream = HttpOp.execHttpGet(target.toString(), contentTypeResult, client, context,
+                        this.authenticator);
+                if (stream == null)
+                    throw new QueryExceptionHTTP(404);
+                return execCommon(stream);
+            } catch (HttpException httpEx) {
                 // Back-off and try POST if something complain about long URIs
-                // Broken 
-                if (qEx.getResponseCode() == 414 /*HttpServletResponse.SC_REQUEST_URI_TOO_LONG*/ )
+                if (httpEx.getResponseCode() == 414)
                     return execPost();
-                throw qEx;
+                throw httpEx;
             }
+        } catch (HttpException httpEx) {
+            // Unwrap and re-wrap the HTTP exception
+            responseCode = httpEx.getResponseCode();
+            throw new QueryExceptionHTTP(responseCode, "Error making the query, see cause for details", httpEx.getCause());
         }
-        catch (java.net.ConnectException connEx)
-        { throw new QueryExceptionHTTP(QueryExceptionHTTP.NoServer, "Failed to connect to remote server"); }
-        catch (IOException ioEx)
-        { throw new QueryExceptionHTTP(ioEx); }
-    }
-    
-    private InputStream execPost() throws QueryExceptionHTTP
-    {
+    }
+
+    private InputStream execPost() throws QueryExceptionHTTP {
         URL target = null;
-        try { target = new URL(serviceURL); }
-        catch (MalformedURLException malEx)
-        { throw new QueryExceptionHTTP(0, "Malformed URL: " + malEx); }
-        log.trace("POST "+target.toExternalForm()) ;
-        
-        ARQ.getHttpRequestLogger().trace(target.toExternalForm()) ;
-
-        try
-        {
-            httpConnection = (HttpURLConnection) target.openConnection();
-            httpConnection.setRequestMethod("POST") ;
-            httpConnection.setRequestProperty("Accept", contentTypeResult) ;
-            httpConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded") ;
-            basicAuthentication(httpConnection) ;
-            applyTimeouts(httpConnection);
-            applyEncodings(httpConnection);
-            httpConnection.setDoOutput(true) ;
-            
-            boolean first = true ;
-            OutputStream out = httpConnection.getOutputStream() ;
-            for ( Iterator<Pair> iter = pairs().listIterator() ; iter.hasNext() ; )
-            {
-                if ( ! first )
-                    out.write('&') ;
-                first = false ;
-                Pair p = iter.next() ;
-                out.write(p.getName().getBytes()) ;
-                out.write('=') ;
-                String x = p.getValue() ;
-                x = Convert.encWWWForm(x) ;
-                out.write(x.getBytes()) ;
-                ARQ.getHttpRequestLogger().trace("Param: "+x) ;
-            }
-            out.flush() ;
-            httpConnection.connect() ;
-            return execCommon() ;
-        }
-        catch (java.net.ConnectException connEx)
-        { throw new QueryExceptionHTTP(-1, "Failed to connect to remote server"); }
-        catch (SocketTimeoutException timeoutEx)
-        { throw new QueryExceptionHTTP(-1, "Failed to connect to remove server within specified timeout"); }
-        catch (IOException ioEx)
-        { throw new QueryExceptionHTTP(ioEx); }
-    }
-    
-    private void basicAuthentication(HttpURLConnection httpConnection2)
-    {
-        // Do basic authentication : do directly, not via an Authenticator, because it 
-        // avoids an extra round trip (Java normally does the request without authetication,
-        // then reties with)
-
-        if ( user != null || password != null)
-        {
-            try
-            {
-                if ( user == null || password == null )
-                    log.warn("Only one of user/password is set") ;
-                // We want: "Basic user:password" except user:password is base 64 encoded.
-                // Build string, get as UTF-8, bytes, translate to base 64. 
-                StringBuffer x = new StringBuffer() ;
-                byte b[] = x.append(user).append(":").append(password).toString().getBytes("UTF-8") ;
-                String y = Base64.encodeBase64String(b) ;
-                httpConnection.setRequestProperty("Authorization", "Basic "+y) ;
-                // Overwrite any password details we copied.
-                // Still leaves the copy in the HTTP connection.  But this only basic auth. 
-                for ( int i = 0 ; i < x.length() ; i++ ) x.setCharAt(i, '*') ;
-                for ( int i = 0 ; i < b.length ; i++ ) b[i] = (byte)0 ; 
-            } catch (UnsupportedEncodingException ex)
-            {
-                // Can't happen - UTF-8 is required of all Java platforms. 
-                throw new ARQInternalErrorException("UTF-8 is broken on this platform", ex) ;
-            }
+        try {
+            target = new URL(serviceURL);
+        } catch (MalformedURLException malEx) {
+            throw new QueryExceptionHTTP(0, "Malformed URL: " + malEx);
         }
-    }
+        log.trace("POST " + target.toExternalForm());
 
-    private void applyTimeouts(HttpURLConnection conn)
-    {
-    	if (connectTimeout > 0)
-    	{
-    		conn.setConnectTimeout(connectTimeout);
-    	}
-    	if (readTimeout > 0)
-    	{
-    		conn.setReadTimeout(readTimeout);
-    	}
-    }
-    
-    private void applyEncodings(HttpURLConnection conn)
-    {
-    	List<String> encodings = new ArrayList<String>();
-    	if (allowGZip) encodings.add("gzip");
-    	if (allowDeflate) encodings.add("deflate");
-    	if (encodings.size() > 0)
-    	{
-    		//Apply the Accept-Encoding header if at least one encoding has been selected
-    		conn.setRequestProperty("Accept-Encoding", StrUtils.strjoin(", ", encodings));
-    	}
-    }
+        ARQ.getHttpRequestLogger().trace(target.toExternalForm());
 
-    private InputStream execCommon() throws QueryExceptionHTTP
-    {
         try {
-            try {
-                responseCode = httpConnection.getResponseCode() ;
-                responseMessage = Convert.decWWWForm(httpConnection.getResponseMessage()) ;
-            } catch (NullPointerException ex) {
-                // This happens if you talk to a non-HTTP port.
-                // e.g. memcached!
-                throw new QueryExceptionHTTP("Problems with HTTP response (was it an HTTP server?)", ex) ;
+            HttpClient client = new SystemDefaultHttpClient();
+            if (this.connectTimeout > 0)
+                client.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, this.connectTimeout);
+            if (this.readTimeout > 0)
+                client.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, this.readTimeout);
+            HttpContext context = new BasicHttpContext();
+            if (allowGZip || allowDeflate) {
+                // Apply auth early as the decompressing client we're about
+                // to add will block this being applied later
+                HttpOp.applyAuthentication((AbstractHttpClient) client, serviceURL, context, authenticator);
+                client = new DecompressingHttpClient(client);
             }
-            // 1xx: Informational 
-            // 2xx: Success 
-            // 3xx: Redirection 
-            // 4xx: Client Error 
-            // 5xx: Server Error 
-            
-            if ( 300 <= responseCode && responseCode < 400 )
-                throw new QueryExceptionHTTP(responseCode, responseMessage) ;
-            
-            // Other 400 and 500 - errors 
-            
-            if ( responseCode >= 400 )
-            {
-                
-                InputStream x = httpConnection.getErrorStream() ;
-                String str = null ;
-                if ( x != null )
-                {
-                    //String ct = httpConnection.getContentType() ;
-                    //httpConnection.getContentEncoding() ;
-                    
-                    try { str = FileUtils.readWholeFileAsUTF8(x) ; }
-                    catch (Throwable ex) {}
-                }
-                if ( str != null )
-                    throw new QueryExceptionHTTP(responseCode, responseMessage+"\n"+str) ;
-                else
-                    throw new QueryExceptionHTTP(responseCode, responseMessage) ;
-            }
-            
-            // Request succeeded
-            //httpConnection.setReadTimeout(10) ;
-            InputStream in = httpConnection.getInputStream() ;
-
-            //Get the returned content type so we can expose this later via the getContentType() method
-            //We strip any parameters off the returned content type e.g. ;charset=UTF-8 since code that
-            //consumes our getContentType() method will expect a bare MIME type
-            contentTypeResult = httpConnection.getContentType() ;
-            if (contentTypeResult.contains(";"))
-            {
-            	contentTypeResult = contentTypeResult.substring(0, contentTypeResult.indexOf(';'));
-            }
-            
-            
-            //If compression was enabled and we got a compressed response as indicated by the presence of
-            //a Content-Encoding header we need to ensure the input stream is appropriately wrapped in
-            //the relevant stream type but checking that the JVM hasn't been clever enough to do
-            //this for us already
-            String contentEnc = httpConnection.getContentEncoding() ;
-            
-            if (contentEnc != null)
-            {
-            	if (contentEnc.equalsIgnoreCase("gzip"))
-            	{
-            		if (!(in instanceof GZIPInputStream))
-            		{
-            			in = new GZIPInputStream(in);
-            		}
-            	}
-            	else if (contentEnc.equalsIgnoreCase("deflate"))
-            	{
-            		if (!(in instanceof DeflaterInputStream))
-            		{
-            			in = new DeflaterInputStream(in);
-            		}
-            	}
-            }
-            
-            if ( false )
-            {
-                // Dump the header
-                Map<String,List<String>> map = httpConnection.getHeaderFields() ;
-                for ( Iterator<String> iter = map.keySet().iterator() ; iter.hasNext() ; )
-                {
-                    String k = iter.next();
-                    List<String> v = map.get(k) ;
-                    System.out.println(k+" = "+v) ;
-                }
-            }
-            
-            // Dump response body
-            if ( false )
-            {
-                StringBuffer b = new StringBuffer(1000) ;
-                byte[] chars = new byte[1000] ;
-                while(true)
-                {
-                    int x = in.read(chars) ;
-                    if ( x < 0 ) break ;
-                    b.append(new String(chars, 0, x, FileUtils.encodingUTF8)) ;
-                }
-                System.out.println(b.toString()) ;
-                System.out.flush() ;
-                // Reset
-                in = new ByteArrayInputStream(b.toString().getBytes(FileUtils.encodingUTF8)) ;
-            }
-            
-            
-            // +++ WORKAROUND for badly behaved apps.
-            // Apps sometimes call QueryExecution.close straight after .execSelect.
-            // that results in some resuls being seen, not all of them => XMl parse errors.
-//            byte[] bytes = IO.readWholeFile(in) ;
-//            in.close()            
-//            in = new ByteArrayInputStream(bytes) ;
-            // +++ 
-           
-            return in ;
-        }
-        catch (IOException ioEx)
-        {
-            throw new QueryExceptionHTTP(ioEx) ;
-        } 
-        catch (QueryExceptionHTTP httpEx)
-        {
-        	//We want to throw this upwards and not catch it in the next block and inadvertently rewrap it
-        	//since that can hide the real error details from the user
-        	throw httpEx;
+
+            TypedInputStream stream = HttpOp.execHttpPostForm(serviceURL, contentTypeResult, (Params) this, client, context,
+                    this.authenticator);
+            if (stream == null)
+                throw new QueryExceptionHTTP(404);
+            return execCommon(stream);
+        } catch (HttpException httpEx) {
+            // Unwrap and re-wrap the HTTP Exception
+            responseCode = httpEx.getResponseCode();
+            throw new QueryExceptionHTTP(responseCode, "Error making the query, see cause for details", httpEx.getCause());
         }
-        catch (JenaException rdfEx)
-        {
-            throw new QueryExceptionHTTP(rdfEx) ;
+    }
+
+    private InputStream execCommon(TypedInputStream stream) throws QueryExceptionHTTP {
+        // Assume response code must be 200 if we got here
+        responseCode = 200;
+
+        // Get the returned content type so we can expose this later via the
+        // getContentType() method
+        // We strip any parameters off the returned content type e.g.
+        // ;charset=UTF-8 since code that
+        // consumes our getContentType() method will expect a bare MIME type
+        contentTypeResult = stream.getContentType();
+        if (contentTypeResult != null && contentTypeResult.contains(";")) {
+            contentTypeResult = contentTypeResult.substring(0, contentTypeResult.indexOf(';'));
         }
+
+        // NB - Content Encoding is now handled at a higher level
+        // so we don't have to worry about wrapping the stream at all
+
+        return stream;
     }
-    
+
     @Override
-    public String toString()
-    {
-        String s = httpString() ;
-        if ( s != null && s.length() > 0 )
-            return serviceURL+"?"+s ;
-        return serviceURL ;
+    public String toString() {
+        String s = httpString();
+        if (s != null && s.length() > 0)
+            return serviceURL + "?" + s;
+        return serviceURL;
     }
 }

Modified: jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/QueryEngineHTTP.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/QueryEngineHTTP.java?rev=1497583&r1=1497582&r2=1497583&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/QueryEngineHTTP.java (original)
+++ jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/QueryEngineHTTP.java Thu Jun 27 22:14:09 2013
@@ -18,134 +18,128 @@
 
 package com.hp.hpl.jena.sparql.engine.http;
 
-import java.io.ByteArrayInputStream ;
-import java.io.InputStream ;
-import java.util.ArrayList ;
-import java.util.Iterator ;
-import java.util.List ;
-import java.util.Map ;
-import java.util.concurrent.TimeUnit ;
-
-import org.apache.jena.atlas.io.IO ;
-import org.apache.jena.riot.* ;
-import org.slf4j.Logger ;
-import org.slf4j.LoggerFactory ;
-
-import com.hp.hpl.jena.graph.Triple ;
-import com.hp.hpl.jena.query.* ;
-import com.hp.hpl.jena.rdf.model.Model ;
-import com.hp.hpl.jena.sparql.ARQException ;
-import com.hp.hpl.jena.sparql.graph.GraphFactory ;
-import com.hp.hpl.jena.sparql.resultset.CSVInput ;
-import com.hp.hpl.jena.sparql.resultset.JSONInput ;
-import com.hp.hpl.jena.sparql.resultset.TSVInput ;
-import com.hp.hpl.jena.sparql.resultset.XMLInput ;
-import com.hp.hpl.jena.sparql.util.Context ;
-import com.hp.hpl.jena.util.FileManager ;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.jena.atlas.io.IO;
+import org.apache.jena.atlas.web.auth.HttpAuthenticator;
+import org.apache.jena.atlas.web.auth.SimpleAuthenticator;
+import org.apache.jena.riot.*;
+import org.apache.jena.riot.web.HttpOp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.hp.hpl.jena.graph.Triple;
+import com.hp.hpl.jena.query.*;
+import com.hp.hpl.jena.rdf.model.Model;
+import com.hp.hpl.jena.sparql.ARQException;
+import com.hp.hpl.jena.sparql.graph.GraphFactory;
+import com.hp.hpl.jena.sparql.resultset.CSVInput;
+import com.hp.hpl.jena.sparql.resultset.JSONInput;
+import com.hp.hpl.jena.sparql.resultset.TSVInput;
+import com.hp.hpl.jena.sparql.resultset.XMLInput;
+import com.hp.hpl.jena.sparql.util.Context;
+import com.hp.hpl.jena.util.FileManager;
 
 /**
- * A query execution implementation where queries are executed against a remote service
- *
+ * A query execution implementation where queries are executed against a remote
+ * service
+ * 
  */
-public class QueryEngineHTTP implements QueryExecution
-{
-    private static Logger log = LoggerFactory.getLogger(QueryEngineHTTP.class) ;
-    
-    public static final String QUERY_MIME_TYPE = WebContent.contentTypeSPARQLQuery ; // "application/sparql-query" ;
-    private final Query query ;
-    private final String queryString ;
-    private final String service ;
-    private final Context context ;
-    
-    //Params
-    Params params = null ;
-    
+public class QueryEngineHTTP implements QueryExecution {
+    private static Logger log = LoggerFactory.getLogger(QueryEngineHTTP.class);
+
+    public static final String QUERY_MIME_TYPE = WebContent.contentTypeSPARQLQuery; // "application/sparql-query"
+                                                                                    // ;
+    private final Query query;
+    private final String queryString;
+    private final String service;
+    private final Context context;
+
+    // Params
+    Params params = null;
+
     // Protocol
-    List<String> defaultGraphURIs = new ArrayList<String>() ;
-    List<String> namedGraphURIs  = new ArrayList<String>() ;
-    private String user = null ;
-    private char[] password = null ;
-    
-    private boolean finished = false ;
-    
-    //Timeouts
-    private long connectTimeout = -1 ;
+    List<String> defaultGraphURIs = new ArrayList<String>();
+    List<String> namedGraphURIs = new ArrayList<String>();
+    private HttpAuthenticator authenticator;
+
+    private boolean finished = false;
+
+    // Timeouts
+    private long connectTimeout = -1;
     private TimeUnit connectTimeoutUnit = TimeUnit.MILLISECONDS;
-    private long readTimeout = -1 ;
+    private long readTimeout = -1;
     private TimeUnit readTimeoutUnit = TimeUnit.MILLISECONDS;
-    
-    //Compression Support
-    private boolean allowGZip = true ;
-    private boolean	allowDeflate = true;
-    
-    //Content Types
+
+    // Compression Support
+    private boolean allowGZip = true;
+    private boolean allowDeflate = true;
+
+    // Content Types
     private String selectContentType = WebContent.contentTypeResultsXML;
     private String askContentType = WebContent.contentTypeResultsXML;
     private String modelContentType = WebContent.contentTypeRDFXML;
-    public static String[] supportedSelectContentTypes = new String []
-    		{
-    			WebContent.contentTypeResultsXML,
-    			WebContent.contentTypeResultsJSON,
-    			WebContent.contentTypeTextTSV,
-    			WebContent.contentTypeTextCSV
-    		};
-    public static String[] supportedAskContentTypes = new String []
-    		{
-    			WebContent.contentTypeResultsXML,
-    			WebContent.contentTypeJSON,
-    			WebContent.contentTypeTextTSV,
-    			WebContent.contentTypeTextCSV
-    		};
-    
+    public static String[] supportedSelectContentTypes = new String[] { WebContent.contentTypeResultsXML,
+            WebContent.contentTypeResultsJSON, WebContent.contentTypeTextTSV, WebContent.contentTypeTextCSV };
+    public static String[] supportedAskContentTypes = new String[] { WebContent.contentTypeResultsXML,
+            WebContent.contentTypeJSON, WebContent.contentTypeTextTSV, WebContent.contentTypeTextCSV };
+
     // Releasing HTTP input streams is important. We remember this for SELECT,
     // and will close when the engine is closed
     private InputStream retainedConnection = null;
-    
-    public QueryEngineHTTP(String serviceURI, Query query)
-    { 
-        this(serviceURI, query, query.toString()) ;
-    }
-    
-    public QueryEngineHTTP(String serviceURI, String queryString)
-    { 
-        this(serviceURI, null, queryString) ;
-    }
-
-    private QueryEngineHTTP(String serviceURI, Query query, String queryString)
-    { 
-        this.query = query ;
-        this.queryString = queryString ;
-        this.service = serviceURI ;
+
+    public QueryEngineHTTP(String serviceURI, Query query) {
+        this(serviceURI, query, query.toString());
+    }
+
+    public QueryEngineHTTP(String serviceURI, String queryString) {
+        this(serviceURI, null, queryString);
+    }
+
+    private QueryEngineHTTP(String serviceURI, Query query, String queryString) {
+        this.query = query;
+        this.queryString = queryString;
+        this.service = serviceURI;
         // Copy the global context to freeze it.
-        this.context = new Context(ARQ.getContext()) ;
-        
+        this.context = new Context(ARQ.getContext());
+
         // Apply service configuration if relevant
         QueryEngineHTTP.applyServiceConfig(serviceURI, this);
     }
-    
+
     /**
      * <p>
-     * Helper method which applies configuration from the Context to the query engine
-     * if a service context exists for the given URI
+     * Helper method which applies configuration from the Context to the query
+     * engine if a service context exists for the given URI
      * </p>
      * <p>
-     * Based off proposed patch for JENA-405 but modified to apply all relevant configuration, this is in part also
-     * based off of the private {@code configureQuery()} method of the {@link Service} class though it omits
-     * parameter merging since that will be done automatically whenever the {@link QueryEngineHTTP} instance
-     * makes a query for remote submission.
+     * Based off proposed patch for JENA-405 but modified to apply all relevant
+     * configuration, this is in part also based off of the private
+     * {@code configureQuery()} method of the {@link Service} class though it
+     * omits parameter merging since that will be done automatically whenever
+     * the {@link QueryEngineHTTP} instance makes a query for remote submission.
      * </p>
-     * @param serviceURI Service URI
+     * 
+     * @param serviceURI
+     *            Service URI
      */
     private static void applyServiceConfig(String serviceURI, QueryEngineHTTP engine) {
-        if (engine.context == null) return;
-        
+        if (engine.context == null)
+            return;
+
         @SuppressWarnings("unchecked")
         Map<String, Context> serviceContextMap = (Map<String, Context>) engine.context.get(Service.serviceContext);
         if (serviceContextMap != null && serviceContextMap.containsKey(serviceURI)) {
             Context serviceContext = serviceContextMap.get(serviceURI);
             if (log.isDebugEnabled())
                 log.debug("Endpoint URI {} has SERVICE Context: {} ", serviceURI, serviceContext);
-            
+
             // Apply behavioral options
             engine.setAllowGZip(serviceContext.isTrueOrUndef(Service.queryGzip));
             engine.setAllowDeflate(serviceContext.isTrueOrUndef(Service.queryDeflate));
@@ -164,491 +158,554 @@ public class QueryEngineHTTP implements 
             }
         }
     }
-    
+
     /**
      * Applies context provided timeouts to the given engine
-     * @param engine Engine
-     * @param context Context
+     * 
+     * @param engine
+     *            Engine
+     * @param context
+     *            Context
      */
     private static void applyServiceTimeouts(QueryEngineHTTP engine, Context context) {
-        if (context.isDefined(Service.queryTimeout)) 
-        {
+        if (context.isDefined(Service.queryTimeout)) {
             Object obj = context.get(Service.queryTimeout);
-            if (obj instanceof Number) 
-            {
+            if (obj instanceof Number) {
                 int x = ((Number) obj).intValue();
                 engine.setTimeout(-1, x);
-            } 
-            else if (obj instanceof String)
-            {
+            } else if (obj instanceof String) {
                 try {
                     String str = obj.toString();
                     if (str.contains(",")) {
-                        
+
                         String[] a = str.split(",");
                         int connect = Integer.parseInt(a[0]);
                         int read = Integer.parseInt(a[1]);
                         engine.setTimeout(read, connect);
-                    } 
-                    else 
-                    {
+                    } else {
                         int x = Integer.parseInt(str);
                         engine.setTimeout(-1, x);
                     }
-                } 
-                catch (NumberFormatException ex) 
-                {
+                } catch (NumberFormatException ex) {
                     throw new QueryExecException("Can't interpret string for timeout: " + obj);
                 }
-            } 
-            else
-            {
+            } else {
                 throw new QueryExecException("Can't interpret timeout: " + obj);
             }
         }
     }
-    
-//    public void setParams(Params params)
-//    { this.params = params ; }
-    
+
+    // public void setParams(Params params)
+    // { this.params = params ; }
+
     // Meaning-less
     @Override
-    public void setFileManager(FileManager fm)
-    { throw new UnsupportedOperationException("FileManagers do not apply to remote query execution") ; }  
+    public void setFileManager(FileManager fm) {
+        throw new UnsupportedOperationException("FileManagers do not apply to remote query execution");
+    }
 
     @Override
-    public void setInitialBinding(QuerySolution binding)
-    { throw new UnsupportedOperationException("Initial bindings not supported for remote queries, consider using a ParameterizedSparqlString to prepare a query for remote execution") ; }
-    
-    public void setInitialBindings(ResultSet table)
-    { throw new UnsupportedOperationException("Initial bindings not supported for remote queries, consider using a ParameterizedSparqlString to prepare a query for remote execution") ; }
-    
-    /**  @param defaultGraphURIs The defaultGraphURIs to set. */
-    public void setDefaultGraphURIs(List<String> defaultGraphURIs)
-    {
-        this.defaultGraphURIs = defaultGraphURIs ;
-    }
-    
-    /**  @param namedGraphURIs The namedGraphURIs to set. */
-    public void setNamedGraphURIs(List<String> namedGraphURIs)
-    {
-        this.namedGraphURIs = namedGraphURIs ;
+    public void setInitialBinding(QuerySolution binding) {
+        throw new UnsupportedOperationException(
+                "Initial bindings not supported for remote queries, consider using a ParameterizedSparqlString to prepare a query for remote execution");
     }
-    
+
+    public void setInitialBindings(ResultSet table) {
+        throw new UnsupportedOperationException(
+                "Initial bindings not supported for remote queries, consider using a ParameterizedSparqlString to prepare a query for remote execution");
+    }
+
+    /**
+     * @param defaultGraphURIs
+     *            The defaultGraphURIs to set.
+     */
+    public void setDefaultGraphURIs(List<String> defaultGraphURIs) {
+        this.defaultGraphURIs = defaultGraphURIs;
+    }
+
+    /**
+     * @param namedGraphURIs
+     *            The namedGraphURIs to set.
+     */
+    public void setNamedGraphURIs(List<String> namedGraphURIs) {
+        this.namedGraphURIs = namedGraphURIs;
+    }
+
     /**
      * Sets whether the HTTP request will specify Accept-Encoding: gzip
      */
-    public void setAllowGZip(boolean allowed)
-    {
-    	allowGZip = allowed;
+    public void setAllowGZip(boolean allowed) {
+        allowGZip = allowed;
     }
-    
+
     /**
      * Sets whether the HTTP requests will specify Accept-Encoding: deflate
      */
-    public void setAllowDeflate(boolean allowed)
-    {
-    	allowDeflate = allowed;
-    }
-
-    public void addParam(String field, String value)
-    {
-        if ( params == null )
-            params = new Params() ;
-        params.addParam(field, value) ;
-    }
-    
-    /** @param defaultGraph The defaultGraph to add. */
-    public void addDefaultGraph(String defaultGraph)
-    {
-        if ( defaultGraphURIs == null )
-            defaultGraphURIs = new ArrayList<String>() ;
-        defaultGraphURIs.add(defaultGraph) ;
-    }
-
-    /** @param name The URI to add. */
-    public void addNamedGraph(String name)
-    {
-        if ( namedGraphURIs == null )
-            namedGraphURIs = new ArrayList<String>() ;
-        namedGraphURIs.add(name) ;
+    public void setAllowDeflate(boolean allowed) {
+        allowDeflate = allowed;
     }
-    
+
+    public void addParam(String field, String value) {
+        if (params == null)
+            params = new Params();
+        params.addParam(field, value);
+    }
+
     /**
-     * Gets whether basic authentication credentials have been provided
-     * @return True if basic authentication credentials have been provided
+     * @param defaultGraph
+     *            The defaultGraph to add.
+     */
+    public void addDefaultGraph(String defaultGraph) {
+        if (defaultGraphURIs == null)
+            defaultGraphURIs = new ArrayList<String>();
+        defaultGraphURIs.add(defaultGraph);
+    }
+
+    /**
+     * @param name
+     *            The URI to add.
+     */
+    public void addNamedGraph(String name) {
+        if (namedGraphURIs == null)
+            namedGraphURIs = new ArrayList<String>();
+        namedGraphURIs.add(name);
+    }
+
+    /**
+     * Gets whether an authentication mechanism has been provided.
+     * <p>
+     * Even if this returns false authentication may still be used if the
+     * default authenticator applies, this is controlled via the
+     * {@link HttpOp#setDefaultAuthenticator(HttpAuthenticator)} method
+     * </p>
+     * 
+     * @return True if an authenticator has been provided
      */
     public boolean isUsingBasicAuthentication() {
-        return this.user != null || this.password != null;
+        return this.authenticator != null;
     }
-    
-    /** Set user and password for basic authentication.
-     *  After the request is made (one of the exec calls), the application
-     *  can overwrite the password array to remove details of the secret.
+
+    /**
+     * Set user and password for basic authentication. After the request is made
+     * (one of the exec calls), the application can overwrite the password array
+     * to remove details of the secret.
+     * <p>
+     * Note that it may be more flexible to
+     * </p>
+     * 
      * @param user
      * @param password
      */
-    public void setBasicAuthentication(String user, char[] password)
-    {
-        this.user = user ;
-        this.password = password ;
-    }
-    
-    @Override
-    public ResultSet execSelect()
-    {
-        HttpQuery httpQuery = makeHttpQuery() ;
-        httpQuery.setAccept(selectContentType) ;
-        InputStream in = httpQuery.exec() ;
-        
-        if ( false )
-        {
-            byte b[] = IO.readWholeFile(in) ;
-            String str = new String(b) ;
-            System.out.println(str) ;
-            in = new ByteArrayInputStream(b) ; 
+    public void setBasicAuthentication(String user, char[] password) {
+        this.authenticator = new SimpleAuthenticator(user, password);
+    }
+
+    /**
+     * Sets the HTTP authenticator to use, if none is set then the default
+     * authenticator is used. This may be configured via the
+     * {@link HttpOp#setDefaultAuthenticator(HttpAuthenticator)} method.
+     * 
+     * @param authenticator
+     *            HTTP authenticator
+     */
+    public void setAuthenticator(HttpAuthenticator authenticator) {
+        this.authenticator = authenticator;
+    }
+
+    @Override
+    public ResultSet execSelect() {
+        HttpQuery httpQuery = makeHttpQuery();
+        httpQuery.setAccept(selectContentType);
+        InputStream in = httpQuery.exec();
+
+        if (false) {
+            byte b[] = IO.readWholeFile(in);
+            String str = new String(b);
+            System.out.println(str);
+            in = new ByteArrayInputStream(b);
         }
-        
+
         retainedConnection = in; // This will be closed on close()
-        
-        //TODO: Find a way to auto-detect how to create the ResultSet based on the content type in use
-        
-        //Don't assume the endpoint actually gives back the content type we asked for
+
+        // TODO: Find a way to auto-detect how to create the ResultSet based on
+        // the content type in use
+
+        // Don't assume the endpoint actually gives back the content type we
+        // asked for
         String actualContentType = httpQuery.getContentType();
-        
-        //If the server fails to return a Content-Type then we will assume
-        //the server returned the type we asked for
-        if (actualContentType == null || actualContentType.equals(""))
-        {
-        	actualContentType = selectContentType;
+
+        // If the server fails to return a Content-Type then we will assume
+        // the server returned the type we asked for
+        if (actualContentType == null || actualContentType.equals("")) {
+            actualContentType = selectContentType;
         }
-        
+
         if (actualContentType.equals(WebContent.contentTypeResultsXML))
             return ResultSetFactory.fromXML(in);
         if (actualContentType.equals(WebContent.contentTypeResultsJSON))
-            return ResultSetFactory.fromJSON(in); 
+            return ResultSetFactory.fromJSON(in);
         if (actualContentType.equals(WebContent.contentTypeTextTSV))
             return ResultSetFactory.fromTSV(in);
         if (actualContentType.equals(WebContent.contentTypeTextCSV))
             return CSVInput.fromCSV(in);
-        throw new QueryException("Endpoint returned Content-Type: " + actualContentType + " which is not currently supported for SELECT queries");
+        throw new QueryException("Endpoint returned Content-Type: " + actualContentType
+                + " which is not currently supported for SELECT queries");
     }
 
     @Override
-    public Model execConstruct()                   { return execConstruct(GraphFactory.makeJenaDefaultModel()) ; }
-    
+    public Model execConstruct() {
+        return execConstruct(GraphFactory.makeJenaDefaultModel());
+    }
+
     @Override
-    public Model execConstruct(Model model)        { return execModel(model) ; }
-    
+    public Model execConstruct(Model model) {
+        return execModel(model);
+    }
+
     @Override
-    public Iterator<Triple> execConstructTriples() { return execTriples() ; }
+    public Iterator<Triple> execConstructTriples() {
+        return execTriples();
+    }
 
     @Override
-    public Model execDescribe()                    { return execDescribe(GraphFactory.makeJenaDefaultModel()) ; }
-    
+    public Model execDescribe() {
+        return execDescribe(GraphFactory.makeJenaDefaultModel());
+    }
+
     @Override
-    public Model execDescribe(Model model)         { return execModel(model) ; }
-    
+    public Model execDescribe(Model model) {
+        return execModel(model);
+    }
+
     @Override
-    public Iterator<Triple> execDescribeTriples()  { return execTriples() ; }
+    public Iterator<Triple> execDescribeTriples() {
+        return execTriples();
+    }
 
-    private Model execModel(Model model)
-    {
-        HttpQuery httpQuery = makeHttpQuery() ;
-        httpQuery.setAccept(modelContentType) ;
-        InputStream in = httpQuery.exec() ;
-        
-        //Don't assume the endpoint actually gives back the content type we asked for
+    private Model execModel(Model model) {
+        HttpQuery httpQuery = makeHttpQuery();
+        httpQuery.setAccept(modelContentType);
+        InputStream in = httpQuery.exec();
+
+        // Don't assume the endpoint actually gives back the content type we
+        // asked for
         String actualContentType = httpQuery.getContentType();
-        
-        //If the server fails to return a Content-Type then we will assume
-        //the server returned the type we asked for
-        if (actualContentType == null || actualContentType.equals(""))
-        {
-        	actualContentType = modelContentType;
+
+        // If the server fails to return a Content-Type then we will assume
+        // the server returned the type we asked for
+        if (actualContentType == null || actualContentType.equals("")) {
+            actualContentType = modelContentType;
         }
-        
-        //Try to select language appropriately here based on the model content type
+
+        // Try to select language appropriately here based on the model content
+        // type
         Lang lang = WebContent.contentTypeToLang(actualContentType);
-        if (! RDFLanguages.isTriples(lang)) 
-           throw new QueryException("Endpoint returned Content Type: " + actualContentType + " which is not a valid RDF Graph syntax");
-        RDFDataMgr.read(model, in, lang) ;
-        this.close() ; 
-        return model ;
-    }
-    
-    private Iterator<Triple> execTriples()
-    {
-        HttpQuery httpQuery = makeHttpQuery() ;
-        httpQuery.setAccept(modelContentType) ;
-        InputStream in = httpQuery.exec() ;
-        
-        //Don't assume the endpoint actually gives back the content type we asked for
+        if (!RDFLanguages.isTriples(lang))
+            throw new QueryException("Endpoint returned Content Type: " + actualContentType
+                    + " which is not a valid RDF Graph syntax");
+        RDFDataMgr.read(model, in, lang);
+        this.close();
+        return model;
+    }
+
+    private Iterator<Triple> execTriples() {
+        HttpQuery httpQuery = makeHttpQuery();
+        httpQuery.setAccept(modelContentType);
+        InputStream in = httpQuery.exec();
+
+        // Don't assume the endpoint actually gives back the content type we
+        // asked for
         String actualContentType = httpQuery.getContentType();
-        
-        //If the server fails to return a Content-Type then we will assume
-        //the server returned the type we asked for
-        if (actualContentType == null || actualContentType.equals(""))
-        {
+
+        // If the server fails to return a Content-Type then we will assume
+        // the server returned the type we asked for
+        if (actualContentType == null || actualContentType.equals("")) {
             actualContentType = modelContentType;
         }
-        
-        //Try to select language appropriately here based on the model content type
+
+        // Try to select language appropriately here based on the model content
+        // type
         Lang lang = WebContent.contentTypeToLang(actualContentType);
-        if (! RDFLanguages.isTriples(lang)) 
-            throw new QueryException("Endpoint returned Content Type: " + actualContentType + " which is not a valid RDF Graph syntax");
-        
+        if (!RDFLanguages.isTriples(lang))
+            throw new QueryException("Endpoint returned Content Type: " + actualContentType
+                    + " which is not a valid RDF Graph syntax");
+
         return RiotReader.createIteratorTriples(in, lang, null);
     }
-    
+
     @Override
-    public boolean execAsk()
-    {
-        HttpQuery httpQuery = makeHttpQuery() ;
-        httpQuery.setAccept(askContentType) ;
-        InputStream in = httpQuery.exec() ;
+    public boolean execAsk() {
+        HttpQuery httpQuery = makeHttpQuery();
+        httpQuery.setAccept(askContentType);
+        InputStream in = httpQuery.exec();
 
         try {
-            //Don't assume the endpoint actually gives back the content type we asked for
+            // Don't assume the endpoint actually gives back the content type we
+            // asked for
             String actualContentType = httpQuery.getContentType();
-            
-            //If the server fails to return a Content-Type then we will assume
-            //the server returned the type we asked for
-            if (actualContentType == null || actualContentType.equals(""))
-            {
-            	actualContentType = askContentType;
+
+            // If the server fails to return a Content-Type then we will assume
+            // the server returned the type we asked for
+            if (actualContentType == null || actualContentType.equals("")) {
+                actualContentType = askContentType;
             }
             Lang lang = WebContent.contentTypeToLang(actualContentType);
-            if (! RDFLanguages.isTriples(lang)) 
+            if (!RDFLanguages.isTriples(lang))
 
-            //Parse the result appropriately depending on the selected content type
-            if (actualContentType.equals(WebContent.contentTypeResultsXML))
-                return XMLInput.booleanFromXML(in) ;
+                // Parse the result appropriately depending on the selected
+                // content type
+                if (actualContentType.equals(WebContent.contentTypeResultsXML))
+                    return XMLInput.booleanFromXML(in);
             if (actualContentType.equals(WebContent.contentTypeResultsJSON))
-                return JSONInput.booleanFromJSON(in) ;
+                return JSONInput.booleanFromJSON(in);
             if (actualContentType.equals(WebContent.contentTypeTextTSV))
-            	return TSVInput.booleanFromTSV(in);
+                return TSVInput.booleanFromTSV(in);
             if (actualContentType.equals(WebContent.contentTypeTextCSV))
-            	return CSVInput.booleanFromCSV(in);
-            throw new QueryException("Endpoint returned Content-Type: " + actualContentType + " which is not currently supported for ASK queries");
+                return CSVInput.booleanFromCSV(in);
+            throw new QueryException("Endpoint returned Content-Type: " + actualContentType
+                    + " which is not currently supported for ASK queries");
         } finally {
             // Ensure connection is released
-            try { in.close(); }
-            catch (java.io.IOException e) { log.warn("Failed to close connection", e); }
+            try {
+                in.close();
+            } catch (java.io.IOException e) {
+                log.warn("Failed to close connection", e);
+            }
         }
     }
 
     @Override
-    public Context getContext() { return context ; }
-    
-    @Override public Dataset getDataset()   { return null ; }
+    public Context getContext() {
+        return context;
+    }
+
+    @Override
+    public Dataset getDataset() {
+        return null;
+    }
 
-    // This may be null - if we were created form a query string, 
+    // This may be null - if we were created form a query string,
     // we don't guarantee to parse it so we let through non-SPARQL
-    // extensions to the far end. 
-    @Override public Query getQuery()       { return query ; }
-    
+    // extensions to the far end.
+    @Override
+    public Query getQuery() {
+        return query;
+    }
+
     @Override
-    public void setTimeout(long readTimeout)
-    {
+    public void setTimeout(long readTimeout) {
         this.readTimeout = readTimeout;
         this.readTimeoutUnit = TimeUnit.MILLISECONDS;
     }
 
     @Override
-    public void setTimeout(long readTimeout, long connectTimeout)
-    {
+    public void setTimeout(long readTimeout, long connectTimeout) {
         this.readTimeout = readTimeout;
         this.readTimeoutUnit = TimeUnit.MILLISECONDS;
         this.connectTimeout = connectTimeout;
         this.connectTimeoutUnit = TimeUnit.MILLISECONDS;
     }
 
-
     @Override
-    public void setTimeout(long readTimeout, TimeUnit timeoutUnits)
-    {
+    public void setTimeout(long readTimeout, TimeUnit timeoutUnits) {
         this.readTimeout = readTimeout;
         this.readTimeoutUnit = timeoutUnits;
     }
 
     @Override
-    public void setTimeout(long timeout1, TimeUnit timeUnit1, long timeout2, TimeUnit timeUnit2)
-    {
+    public void setTimeout(long timeout1, TimeUnit timeUnit1, long timeout2, TimeUnit timeUnit2) {
         this.readTimeout = timeout1;
         this.readTimeoutUnit = timeUnit1;
         this.connectTimeout = timeout2;
         this.connectTimeoutUnit = timeUnit2;
     }
-    
+
     @Override
-    public long getTimeout1() { return asMillis(readTimeout, readTimeoutUnit) ; } 
-    
+    public long getTimeout1() {
+        return asMillis(readTimeout, readTimeoutUnit);
+    }
+
     @Override
-    public long getTimeout2() { return asMillis(connectTimeout, connectTimeoutUnit) ; }
-    
+    public long getTimeout2() {
+        return asMillis(connectTimeout, connectTimeoutUnit);
+    }
+
     /**
-     * Gets whether HTTP requests will indicate to the remote server that GZip encoding of responses is accepted
+     * Gets whether HTTP requests will indicate to the remote server that GZip
+     * encoding of responses is accepted
+     * 
      * @return True if GZip encoding will be accepted
      */
-    public boolean getAllowGZip() { return allowGZip; }
-    
+    public boolean getAllowGZip() {
+        return allowGZip;
+    }
+
     /**
-     * Gets whether HTTP requests will indicate to the remote server that Deflate encoding of responses is accepted
+     * Gets whether HTTP requests will indicate to the remote server that
+     * Deflate encoding of responses is accepted
+     * 
      * @return True if Deflate encoding will be accepted
      */
-    public boolean getAllowDeflate() { return allowDeflate; }
+    public boolean getAllowDeflate() {
+        return allowDeflate;
+    }
+
+    private static long asMillis(long duration, TimeUnit timeUnit) {
+        return (duration < 0) ? duration : timeUnit.toMillis(duration);
+    }
+
+    private HttpQuery makeHttpQuery() {
+        if (finished)
+            throw new ARQException("HTTP execution already closed");
+
+        HttpQuery httpQuery = new HttpQuery(service);
+        httpQuery.merge(getServiceParams(service, context));
+        httpQuery.addParam(HttpParams.pQuery, queryString);
+
+        for (Iterator<String> iter = defaultGraphURIs.iterator(); iter.hasNext();) {
+            String dft = iter.next();
+            httpQuery.addParam(HttpParams.pDefaultGraph, dft);
+        }
+        for (Iterator<String> iter = namedGraphURIs.iterator(); iter.hasNext();) {
+            String name = iter.next();
+            httpQuery.addParam(HttpParams.pNamedGraph, name);
+        }
+
+        if (params != null)
+            httpQuery.merge(params);
 
-    private static long asMillis(long duration, TimeUnit timeUnit)
-    {
-        return (duration < 0 ) ? duration : timeUnit.toMillis(duration) ;
-    }
-    
-    private HttpQuery makeHttpQuery()
-    {
-        // Also need to tie to ResultSet returned which is streamed back if StAX.
-        if ( finished )
-            throw new ARQException("HTTP execution already closed") ;
-        
-        HttpQuery httpQuery = new HttpQuery(service) ;
-        httpQuery.merge(getServiceParams(service, context)) ;
-        httpQuery.addParam(HttpParams.pQuery, queryString );
-        
-        for ( Iterator<String> iter = defaultGraphURIs.iterator() ; iter.hasNext() ; )
-        {
-            String dft = iter.next() ;
-            httpQuery.addParam(HttpParams.pDefaultGraph, dft) ;
-        }
-        for ( Iterator<String> iter = namedGraphURIs.iterator() ; iter.hasNext() ; )
-        {
-            String name = iter.next() ;
-            httpQuery.addParam(HttpParams.pNamedGraph, name) ;
-        }
-        
-        if ( params != null )
-            httpQuery.merge(params) ;
-        
         if (allowGZip)
-        	httpQuery.setAllowGZip(true);
+            httpQuery.setAllowGZip(true);
 
         if (allowDeflate)
-        	httpQuery.setAllowDeflate(true);
-        
-        httpQuery.setBasicAuthentication(user, password) ;
-        
-        //Apply timeouts
-        if (connectTimeout > 0)
-        {
-        	httpQuery.setConnectTimeout((int)connectTimeoutUnit.toMillis(connectTimeout));
-        }
-        if (readTimeout > 0)
-        {
-        	httpQuery.setReadTimeout((int)readTimeoutUnit.toMillis(readTimeout));
-        }
-        
-        return httpQuery ;
-    }
-
-    
-    // This is to allow setting additional/optional query parameters on a per SERVICE level, see: JENA-195
-    protected static Params getServiceParams(String serviceURI, Context context) throws QueryExecException
-    {
+            httpQuery.setAllowDeflate(true);
+
+        httpQuery.setAuthenticator(this.authenticator);
+
+        // Apply timeouts
+        if (connectTimeout > 0) {
+            httpQuery.setConnectTimeout((int) connectTimeoutUnit.toMillis(connectTimeout));
+        }
+        if (readTimeout > 0) {
+            httpQuery.setReadTimeout((int) readTimeoutUnit.toMillis(readTimeout));
+        }
+
+        return httpQuery;
+    }
+
+    // This is to allow setting additional/optional query parameters on a per
+    // SERVICE level, see: JENA-195
+    protected static Params getServiceParams(String serviceURI, Context context) throws QueryExecException {
         Params params = new Params();
         @SuppressWarnings("unchecked")
-        Map<String, Map<String,List<String>>> serviceParams = (Map<String, Map<String,List<String>>>)context.get(ARQ.serviceParams) ;
-        if ( serviceParams != null ) 
-        {
-            Map<String,List<String>> paramsMap = serviceParams.get(serviceURI) ;
-            if ( paramsMap != null )
-            {
-                for (String param : paramsMap.keySet()) 
-                {   
-                    if ( HttpParams.pQuery.equals(param) ) 
-                        throw new QueryExecException("ARQ serviceParams overrides the 'query' SPARQL protocol parameter") ;
-
-                    List<String> values = paramsMap.get(param) ;
-                    for (String value : values) 
-                        params.addParam(param, value) ;
+        Map<String, Map<String, List<String>>> serviceParams = (Map<String, Map<String, List<String>>>) context
+                .get(ARQ.serviceParams);
+        if (serviceParams != null) {
+            Map<String, List<String>> paramsMap = serviceParams.get(serviceURI);
+            if (paramsMap != null) {
+                for (String param : paramsMap.keySet()) {
+                    if (HttpParams.pQuery.equals(param))
+                        throw new QueryExecException("ARQ serviceParams overrides the 'query' SPARQL protocol parameter");
+
+                    List<String> values = paramsMap.get(param);
+                    for (String value : values)
+                        params.addParam(param, value);
                 }
-            }            
+            }
         }
         return params;
     }
 
-    public void cancel() { finished = true ; }
-    
+    /**
+     * Cancel query evaluation
+     */
+    public void cancel() {
+        finished = true;
+    }
+
     @Override
-    public void abort() { try { close() ; } catch (Exception ex) {} }
+    public void abort() {
+        try {
+            close();
+        } catch (Exception ex) {
+            log.warn("Error during abort", ex);
+        }
+    }
 
     @Override
     public void close() {
-        finished = true ;
+        finished = true;
         if (retainedConnection != null) {
-            try { retainedConnection.close(); }
-            catch (java.io.IOException e) { log.warn("Failed to close connection", e); }
-            finally { retainedConnection = null; }
+            try {
+                retainedConnection.close();
+            } catch (java.io.IOException e) {
+                log.warn("Failed to close connection", e);
+            } finally {
+                retainedConnection = null;
+            }
         }
     }
 
-//    public boolean isActive() { return false ; }
-    
+    // public boolean isActive() { return false ; }
+
     @Override
-    public String toString()
-    {
-        HttpQuery httpQuery = makeHttpQuery() ;
-        return "GET "+httpQuery.toString() ;
+    public String toString() {
+        HttpQuery httpQuery = makeHttpQuery();
+        return "GET " + httpQuery.toString();
     }
-    
+
     /**
-     * Sets the Content Type for SELECT queries provided that the format is supported
+     * Sets the Content Type for SELECT queries provided that the format is
+     * supported
+     * 
      * @param contentType
      */
-    public void setSelectContentType(String contentType)
-    {
-    	boolean ok = false;
-    	for (String supportedType : supportedSelectContentTypes)
-    	{
-    		if (supportedType.equals(contentType))
-    		{
-    			ok = true;
-    			break;
-    		}
-    	}
-    	if (!ok) throw new IllegalArgumentException("Given Content Type '" + contentType + "' is not a supported SELECT results format");
-    	selectContentType = contentType;
+    public void setSelectContentType(String contentType) {
+        boolean ok = false;
+        for (String supportedType : supportedSelectContentTypes) {
+            if (supportedType.equals(contentType)) {
+                ok = true;
+                break;
+            }
+        }
+        if (!ok)
+            throw new IllegalArgumentException("Given Content Type '" + contentType
+                    + "' is not a supported SELECT results format");
+        selectContentType = contentType;
     }
-    
+
     /**
-     * Sets the Content Type for ASK queries provided that the format is supported
+     * Sets the Content Type for ASK queries provided that the format is
+     * supported
+     * 
      * @param contentType
      */
-    public void setAskContentType(String contentType)
-    {
-    	boolean ok = false;
-    	for (String supportedType : supportedAskContentTypes)
-    	{
-    		if (supportedType.equals(contentType))
-    		{
-    			ok = true;
-    			break;
-    		}
-    	}
-    	if (!ok) throw new IllegalArgumentException("Given Content Type '" + contentType + "' is not a supported ASK results format");
-    	askContentType = contentType;
+    public void setAskContentType(String contentType) {
+        boolean ok = false;
+        for (String supportedType : supportedAskContentTypes) {
+            if (supportedType.equals(contentType)) {
+                ok = true;
+                break;
+            }
+        }
+        if (!ok)
+            throw new IllegalArgumentException("Given Content Type '" + contentType + "' is not a supported ASK results format");
+        askContentType = contentType;
     }
-    
+
     /**
-     * Sets the Content Type for CONSTRUCT/DESCRIBE queries provided that the format is supported
+     * Sets the Content Type for CONSTRUCT/DESCRIBE queries provided that the
+     * format is supported
+     * 
      * @param contentType
      */
-    public void setModelContentType(String contentType)
-    {
+    public void setModelContentType(String contentType) {
         // Check that this is a valid setting
-        Lang lang = WebContent.contentTypeToLang(contentType) ;
-        if (lang == null) 
-            throw new IllegalArgumentException("Given Content Type '" + contentType + "' is not supported by RIOT") ;
-        if (!RDFLanguages.isTriples(lang)) 
-            throw new IllegalArgumentException("Given Content Type '" + contentType + "' is not a RDF Graph format") ;
-        modelContentType = contentType ;
+        Lang lang = WebContent.contentTypeToLang(contentType);
+        if (lang == null)
+            throw new IllegalArgumentException("Given Content Type '" + contentType + "' is not supported by RIOT");
+        if (!RDFLanguages.isTriples(lang))
+            throw new IllegalArgumentException("Given Content Type '" + contentType + "' is not a RDF Graph format");
+        modelContentType = contentType;
     }
 }

Modified: jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/QueryExceptionHTTP.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/QueryExceptionHTTP.java?rev=1497583&r1=1497582&r2=1497583&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/QueryExceptionHTTP.java (original)
+++ jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/http/QueryExceptionHTTP.java Thu Jun 27 22:14:09 2013
@@ -18,6 +18,8 @@
 
 package com.hp.hpl.jena.sparql.engine.http;
 
+import org.apache.jena.atlas.web.HttpException;
+
 import com.hp.hpl.jena.query.QueryException ;
 
 /** Exception class for all operations in the SPARQL client library.
@@ -86,6 +88,12 @@ public class QueryExceptionHTTP extends 
         this.responseMessage = msg ;
     }
     
+    public QueryExceptionHTTP(int responseCode, String message, Throwable cause) {
+        super(message, cause);
+        this.responseCode = responseCode;
+    }
+
+
     @Override
     public String toString()
     {

Modified: jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/modify/UpdateProcessRemote.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/modify/UpdateProcessRemote.java?rev=1497583&r1=1497582&r2=1497583&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/modify/UpdateProcessRemote.java (original)
+++ jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/modify/UpdateProcessRemote.java Thu Jun 27 22:14:09 2013
@@ -54,7 +54,9 @@ public class UpdateProcessRemote extends
         // Build endpoint URL
         String endpoint = this.getEndpoint();
         String querystring = this.getQueryString();
-        endpoint = endpoint.contains("?") ? endpoint + "&" + querystring : endpoint + "?" + querystring;
+        if (querystring != null && !querystring.equals("")) {
+            endpoint = endpoint.contains("?") ? endpoint + "&" + querystring : endpoint + "?" + querystring;
+        }
         
         // Execution
         String reqStr = this.getUpdateRequest().toString() ;

Modified: jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/HttpException.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/HttpException.java?rev=1497583&r1=1497582&r2=1497583&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/HttpException.java (original)
+++ jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/HttpException.java Thu Jun 27 22:14:09 2013
@@ -39,6 +39,10 @@ public class HttpException extends Runti
         super(message, cause);
     }
     
+    public HttpException(Throwable cause) {
+        super(cause);
+    }
+    
     /**
      * Gets the response code, may be -1 if unknown
      * @return Response Code if known, -1 otherwise

Added: jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/ApacheModAuthFormLogin.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/ApacheModAuthFormLogin.java?rev=1497583&view=auto
==============================================================================
--- jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/ApacheModAuthFormLogin.java (added)
+++ jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/ApacheModAuthFormLogin.java Thu Jun 27 22:14:09 2013
@@ -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.atlas.web.auth;
+
+/**
+ * Represents form login credentials where form logins are against Apache
+ * mod_auth_form secured servers using default form field configuration
+ * 
+ */
+public class ApacheModAuthFormLogin extends FormLogin {
+
+    private static final String APACHE_MOD_AUTH_FORM_USER_FIELD = "httpd_username";
+    private static final String APACHE_MOD_AUTH_FORM_PASSWORD_FIELD = "httpd_password";
+
+    /**
+     * Creates new form login credentials
+     * 
+     * @param loginFormURL
+     *            Login Form URL
+     * @param username
+     *            User name
+     * @param password
+     *            Password
+     */
+    public ApacheModAuthFormLogin(String loginFormURL, String username, char[] password) {
+        super(loginFormURL, APACHE_MOD_AUTH_FORM_USER_FIELD, APACHE_MOD_AUTH_FORM_PASSWORD_FIELD, username, password);
+    }
+}

Added: jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/FormLogin.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/FormLogin.java?rev=1497583&view=auto
==============================================================================
--- jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/FormLogin.java (added)
+++ jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/FormLogin.java Thu Jun 27 22:14:09 2013
@@ -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.atlas.web.auth;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.message.BasicNameValuePair;
+
+/**
+ * Represents form login credentials
+ * 
+ */
+public class FormLogin {
+
+    private String loginForm, loginUserField, loginPasswordField, username;
+    private char[] password;
+
+    /**
+     * Creates new form login credentials
+     * 
+     * @param loginFormURL
+     *            Login Form URL
+     * @param loginUserField
+     *            Login Form User field name
+     * @param loginPasswordField
+     *            Login Form Password field name
+     * @param username
+     *            User name
+     * @param password
+     *            Password
+     */
+    public FormLogin(String loginFormURL, String loginUserField, String loginPasswordField, String username, char[] password) {
+        this.loginForm = loginFormURL;
+        this.loginUserField = loginUserField;
+        this.loginPasswordField = loginPasswordField;
+        this.username = username;
+        this.password = password;
+    }
+
+    /**
+     * Gets the login form URL
+     * 
+     * @return Login Form URL
+     */
+    public String getLoginFormURL() {
+        return this.loginForm;
+    }
+
+    /**
+     * Gets the HTTP Entity for the Login request
+     * 
+     * @return Login request entity
+     * @throws UnsupportedEncodingException
+     *             Thrown if the platform does not support UTF-8
+     */
+    public HttpEntity getLoginEntity() throws UnsupportedEncodingException {
+        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
+        nvps.add(new BasicNameValuePair(this.loginUserField, this.username));
+        nvps.add(new BasicNameValuePair(this.loginPasswordField, new String(this.password)));
+
+        return new UrlEncodedFormEntity(nvps, "UTF-8");
+    }
+}

Added: jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/FormsAuthenticator.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/FormsAuthenticator.java?rev=1497583&view=auto
==============================================================================
--- jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/FormsAuthenticator.java (added)
+++ jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/FormsAuthenticator.java Thu Jun 27 22:14:09 2013
@@ -0,0 +1,124 @@
+/*
+ * 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.atlas.web.auth;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.AbstractHttpClient;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.protocol.HttpContext;
+import org.apache.jena.atlas.web.HttpException;
+
+/**
+ * An authenticator capable of making Form based logins and using cookies to
+ * maintain authentication state
+ * 
+ */
+public class FormsAuthenticator implements HttpAuthenticator {
+
+    private Map<URI, CookieStore> cookies = new HashMap<URI, CookieStore>();
+    private Map<URI, FormLogin> logins = new HashMap<URI, FormLogin>();
+    
+    /**
+     * Creates a new authenticator with the given login
+     * @param target Target URI
+     * @param login Login
+     */
+    public FormsAuthenticator(URI target, FormLogin login) {
+        if (target == null) throw new IllegalArgumentException("Target URI cannot be null");
+        this.logins.put(target, login);
+    }
+    
+    /**
+     * Creates a new authenticator with the given logins
+     * @param logins Logins
+     */
+    public FormsAuthenticator(Map<URI, FormLogin> logins) {
+        this.logins.putAll(logins);
+    }
+
+    @Override
+    public synchronized void apply(AbstractHttpClient client, HttpContext httpContext, URI target) {
+        if (client == null)
+            return;
+
+        synchronized (this.cookies) {
+            if (this.cookies.containsKey(target)) {
+                // Use existing cookies
+                CookieStore store = this.cookies.get(target);
+                if (store != null)
+                    client.setCookieStore(store);
+                return;
+            }
+        }
+        
+        // Do we have a login available for the target server?
+        FormLogin login = this.logins.get(target);
+        if (login == null)
+            return;
+        
+        // Use a fresh Cookie Store for new login attempts
+        CookieStore store = new BasicCookieStore();
+        client.setCookieStore(store);
+
+        try {
+            // Try to login
+            HttpPost post = new HttpPost(login.getLoginFormURL());
+            post.setEntity(login.getLoginEntity());
+            HttpResponse response = client.execute(post, httpContext);
+
+            // Check for successful login
+            if (response.getStatusLine().getStatusCode() >= 400) {
+                throw new HttpException(response.getStatusLine().getStatusCode(), "Login attempt failed - "
+                        + response.getStatusLine().getReasonPhrase());
+            }
+
+            // Otherwise assume a successful login
+            synchronized (this.cookies) {
+                this.cookies.put(target, client.getCookieStore());
+            }
+
+        } catch (UnsupportedEncodingException e) {
+            throw new HttpException("UTF-8 encoding not supported on your platform", e);
+        } catch (IOException e) {
+            throw new HttpException("Error making login request", e);
+        }
+    }
+    
+    /**
+     * Adds a login to the authenticator, if the authenticator had previously logged into the given URI cookies for that URI are discarded
+     * @param target Target URI
+     * @param login Login
+     */
+    public void addLogin(URI target, FormLogin login) {
+        if (target == null) throw new IllegalArgumentException("Target URI cannot be null");
+        this.logins.put(target, login);
+        synchronized (this.cookies) {
+            this.cookies.remove(target);
+        }
+    }
+
+}