/* * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2007. * * Licensed under the Aduna BSD-style license. */ package org.openrdf.http.client; import static org.openrdf.http.protocol.Protocol.ACCEPT_PARAM_NAME; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HeaderElement; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpConnectionManager; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.DeleteMethod; import org.apache.commons.httpclient.methods.EntityEnclosingMethod; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.InputStreamRequestEntity; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.httpclient.methods.RequestEntity; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.httpclient.params.HttpConnectionManagerParams; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import info.aduna.io.IOUtil; import info.aduna.net.http.HttpClientUtil; import org.openrdf.OpenRDFUtil; import org.openrdf.http.protocol.Protocol; import org.openrdf.http.protocol.UnauthorizedException; import org.openrdf.http.protocol.error.ErrorInfo; import org.openrdf.http.protocol.error.ErrorType; import org.openrdf.http.protocol.transaction.TransactionWriter; import org.openrdf.http.protocol.transaction.operations.TransactionOperation; import org.openrdf.model.Resource; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.model.ValueFactory; import org.openrdf.model.impl.URIImpl; import org.openrdf.model.impl.ValueFactoryImpl; import org.openrdf.query.Binding; import org.openrdf.query.Dataset; import org.openrdf.query.GraphQueryResult; import org.openrdf.query.MalformedQueryException; import org.openrdf.query.QueryLanguage; import org.openrdf.query.TupleQueryResult; import org.openrdf.query.TupleQueryResultHandler; import org.openrdf.query.TupleQueryResultHandlerException; import org.openrdf.query.UnsupportedQueryLanguageException; import org.openrdf.query.impl.GraphQueryResultImpl; import org.openrdf.query.impl.TupleQueryResultBuilder; import org.openrdf.query.resultio.BooleanQueryResultFormat; import org.openrdf.query.resultio.BooleanQueryResultParser; import org.openrdf.query.resultio.BooleanQueryResultParserRegistry; import org.openrdf.query.resultio.QueryResultIO; import org.openrdf.query.resultio.QueryResultParseException; import org.openrdf.query.resultio.TupleQueryResultFormat; import org.openrdf.query.resultio.TupleQueryResultParser; import org.openrdf.query.resultio.TupleQueryResultParserRegistry; import org.openrdf.query.resultio.UnsupportedQueryResultFormatException; import org.openrdf.repository.RepositoryException; import org.openrdf.rio.RDFFormat; import org.openrdf.rio.RDFHandler; import org.openrdf.rio.RDFHandlerException; import org.openrdf.rio.RDFParseException; import org.openrdf.rio.RDFParser; import org.openrdf.rio.RDFParserRegistry; import org.openrdf.rio.Rio; import org.openrdf.rio.UnsupportedRDFormatException; import org.openrdf.rio.helpers.StatementCollector; /** * Low-level HTTP client for Sesame's HTTP protocol. Methods correspond directly * to the functionality offered by the protocol. * * @author Herko ter Horst * @author Arjohn Kampman */ public class HTTPClient { /*-----------* * Constants * *-----------*/ final Logger logger = LoggerFactory.getLogger(this.getClass()); /*-----------* * Variables * *-----------*/ private ValueFactory valueFactory; private String serverURL; private String repositoryURL; private HttpClient httpClient; private AuthScope authScope; private TupleQueryResultFormat preferredTQRFormat = TupleQueryResultFormat.BINARY; private BooleanQueryResultFormat preferredBQRFormat = BooleanQueryResultFormat.TEXT; private RDFFormat preferredRDFFormat = RDFFormat.TURTLE; /*--------------* * Constructors * *--------------*/ public HTTPClient() { valueFactory = ValueFactoryImpl.getInstance(); // Use MultiThreadedHttpConnectionManager to allow concurrent access on // HttpClient HttpConnectionManager manager = new MultiThreadedHttpConnectionManager(); // Allow 20 concurrent connections to the same host (default is 2) HttpConnectionManagerParams params = new HttpConnectionManagerParams(); params.setDefaultMaxConnectionsPerHost(20); manager.setParams(params); httpClient = new HttpClient(manager); } /*-----------------* * Get/set methods * *-----------------*/ protected final HttpClient getHttpClient() { return httpClient; } public void setValueFactory(ValueFactory valueFactory) { this.valueFactory = valueFactory; } public ValueFactory getValueFactory() { return valueFactory; } public void setServerURL(String serverURL) { if (serverURL == null) { throw new IllegalArgumentException("serverURL must not be null"); } this.serverURL = serverURL; } public String getServerURL() { return serverURL; } protected void checkServerURL() { if (serverURL == null) { throw new IllegalStateException("Server URL has not been set"); } } public void setRepositoryURL(final String repositoryURL) { if (repositoryURL == null) { throw new IllegalArgumentException("repositoryURL must not be null"); } this.repositoryURL = repositoryURL; // Try to parse the server URL from the repository URL Pattern urlPattern = Pattern.compile("(.*)/" + Protocol.REPOSITORIES + "/[^/]*/?"); Matcher matcher = urlPattern.matcher(repositoryURL); if (matcher.matches() && matcher.groupCount() == 1) { setServerURL(matcher.group(1)); } } public String getRepositoryURL() { return repositoryURL; } protected void checkRepositoryURL() { if (repositoryURL == null) { throw new IllegalStateException("Repository URL has not been set"); } } public void setRepositoryID(String repositoryID) { checkServerURL(); repositoryURL = Protocol.getRepositoryLocation(serverURL, repositoryID); } /** * Sets the preferred format for encoding tuple query results. The * {@link TupleQueryResultFormat#BINARY binary} format is preferred by * default. * * @param format * The preferred {@link TupleQueryResultFormat}, or <tt>null</tt> * to indicate no specific format is preferred. */ public void setPreferredTupleQueryResultFormat(TupleQueryResultFormat format) { preferredTQRFormat = format; } /** * Gets the preferred {@link TupleQueryResultFormat} for encoding tuple query * results. * * @return The preferred format, of <tt>null</tt> if no specific format is * preferred. */ public TupleQueryResultFormat getPreferredTupleQueryResultFormat() { return preferredTQRFormat; } /** * Sets the preferred format for encoding RDF documents. The * {@link RDFFormat#TURTLE Turtle} format is preferred by default. * * @param format * The preferred {@link RDFFormat}, or <tt>null</tt> to indicate no * specific format is preferred. */ public void setPreferredRDFFormat(RDFFormat format) { preferredRDFFormat = format; } /** * Gets the preferred {@link RDFFormat} for encoding RDF documents. * * @return The preferred format, of <tt>null</tt> if no specific format is * preferred. */ public RDFFormat getPreferredRDFFormat() { return preferredRDFFormat; } /** * Sets the preferred format for encoding boolean query results. The * {@link BooleanQueryResultFormat#TEXT binary} format is preferred by * default. * * @param format * The preferred {@link BooleanQueryResultFormat}, or <tt>null</tt> * to indicate no specific format is preferred. */ public void setPreferredBooleanQueryResultFormat(BooleanQueryResultFormat format) { preferredBQRFormat = format; } /** * Gets the preferred {@link BooleanQueryResultFormat} for encoding boolean * query results. * * @return The preferred format, of <tt>null</tt> if no specific format is * preferred. */ public BooleanQueryResultFormat getPreferredBooleanQueryResultFormat() { return preferredBQRFormat; } /** * Set the username and password for authenication with the remote server. * * @param username * the username * @param password * the password */ public void setUsernameAndPassword(String username, String password) { checkServerURL(); if (username != null && password != null) { try { URL server = new URL(serverURL); authScope = new AuthScope(server.getHost(), AuthScope.ANY_PORT); httpClient.getState().setCredentials(authScope, new UsernamePasswordCredentials(username, password)); } catch (MalformedURLException e) { logger.warn("Unable to set username and password for malformed URL {}", serverURL); } } else { httpClient.getState().clearCredentials(); } } /*------------------* * Protocol version * *------------------*/ public String getServerProtocol() throws IOException, RepositoryException, UnauthorizedException { checkServerURL(); GetMethod method = new GetMethod(Protocol.getProtocolLocation(serverURL)); setDoAuthentication(method); try { int httpCode = httpClient.executeMethod(method); if (httpCode == HttpURLConnection.HTTP_OK) { return method.getResponseBodyAsString(); } else if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new UnauthorizedException(); } else { ErrorInfo errInfo = getErrorInfo(method); throw new RepositoryException("Failed to get server protocol: " + errInfo); } } finally { method.releaseConnection(); } } /*-----------------* * Repository list * *-----------------*/ public TupleQueryResult getRepositoryList() throws IOException, RepositoryException, UnauthorizedException { try { TupleQueryResultBuilder builder = new TupleQueryResultBuilder(); getRepositoryList(builder); return builder.getQueryResult(); } catch (TupleQueryResultHandlerException e) { // Found a bug in TupleQueryResultBuilder? throw new RuntimeException(e); } } public void getRepositoryList(TupleQueryResultHandler handler) throws IOException, TupleQueryResultHandlerException, RepositoryException, UnauthorizedException { checkServerURL(); GetMethod method = new GetMethod(Protocol.getRepositoriesLocation(serverURL)); setDoAuthentication(method); try { getTupleQueryResult(method, handler); } catch (MalformedQueryException e) { // This shouldn't happen as no queries are involved logger.warn("Server reported unexpected malfored query error", e); throw new RepositoryException(e.getMessage(), e); } finally { releaseConnection(method); } } /*------------------* * Query evaluation * *------------------*/ public TupleQueryResult sendTupleQuery(QueryLanguage ql, String query, Dataset dataset, boolean includeInferred, Binding... bindings) throws IOException, RepositoryException, MalformedQueryException, UnauthorizedException { try { TupleQueryResultBuilder builder = new TupleQueryResultBuilder(); sendTupleQuery(ql, query, dataset, includeInferred, builder, bindings); return builder.getQueryResult(); } catch (TupleQueryResultHandlerException e) { // Found a bug in TupleQueryResultBuilder? throw new RuntimeException(e); } } public void sendTupleQuery(QueryLanguage ql, String query, Dataset dataset, boolean includeInferred, TupleQueryResultHandler handler, Binding... bindings) throws IOException, TupleQueryResultHandlerException, RepositoryException, MalformedQueryException, UnauthorizedException { HttpMethod method = getQueryMethod(ql, query, dataset, includeInferred, bindings); try { getTupleQueryResult(method, handler); } finally { releaseConnection(method); } } public GraphQueryResult sendGraphQuery(QueryLanguage ql, String query, Dataset dataset, boolean includeInferred, Binding... bindings) throws IOException, RepositoryException, MalformedQueryException, UnauthorizedException { try { StatementCollector collector = new StatementCollector(); sendGraphQuery(ql, query, dataset, includeInferred, collector, bindings); return new GraphQueryResultImpl(collector.getNamespaces(), collector.getStatements()); } catch (RDFHandlerException e) { // Found a bug in TupleQueryResultBuilder? throw new RuntimeException(e); } } public void sendGraphQuery(QueryLanguage ql, String query, Dataset dataset, boolean includeInferred, RDFHandler handler, Binding... bindings) throws IOException, RDFHandlerException, RepositoryException, MalformedQueryException, UnauthorizedException { HttpMethod method = getQueryMethod(ql, query, dataset, includeInferred, bindings); try { getRDF(method, handler, false); } finally { releaseConnection(method); } } public boolean sendBooleanQuery(QueryLanguage ql, String query, Dataset dataset, boolean includeInferred, Binding... bindings) throws IOException, RepositoryException, MalformedQueryException, UnauthorizedException { HttpMethod method = getQueryMethod(ql, query, dataset, includeInferred, bindings); try { return getBoolean(method); } finally { releaseConnection(method); } } protected HttpMethod getQueryMethod(QueryLanguage ql, String query, Dataset dataset, boolean includeInferred, Binding... bindings) { PostMethod method = new PostMethod(getRepositoryURL()); setDoAuthentication(method); method.setRequestHeader("Content-Type", Protocol.FORM_MIME_TYPE + "; charset=utf-8"); List<NameValuePair> queryParams = getQueryMethodParameters(ql, query, dataset, includeInferred, bindings); method.setRequestBody(queryParams.toArray(new NameValuePair[queryParams.size()])); return method; } protected List<NameValuePair> getQueryMethodParameters(QueryLanguage ql, String query, Dataset dataset, boolean includeInferred, Binding... bindings) { List<NameValuePair> queryParams = new ArrayList<NameValuePair>(bindings.length + 10); queryParams.add(new NameValuePair(Protocol.QUERY_LANGUAGE_PARAM_NAME, ql.getName())); queryParams.add(new NameValuePair(Protocol.QUERY_PARAM_NAME, query)); queryParams.add(new NameValuePair(Protocol.INCLUDE_INFERRED_PARAM_NAME, Boolean.toString(includeInferred))); if (dataset != null) { for (URI defaultGraphURI : dataset.getDefaultGraphs()) { queryParams.add(new NameValuePair(Protocol.DEFAULT_GRAPH_PARAM_NAME, defaultGraphURI.toString())); } for (URI namedGraphURI : dataset.getNamedGraphs()) { queryParams.add(new NameValuePair(Protocol.NAMED_GRAPH_PARAM_NAME, namedGraphURI.toString())); } } for (int i = 0; i < bindings.length; i++) { String paramName = Protocol.BINDING_PREFIX + bindings[i].getName(); String paramValue = Protocol.encodeValue(bindings[i].getValue()); queryParams.add(new NameValuePair(paramName, paramValue)); } return queryParams; } /*---------------------------* * Get/add/remove statements * *---------------------------*/ public void getStatements(Resource subj, URI pred, Value obj, boolean includeInferred, RDFHandler handler, Resource... contexts) throws IOException, RDFHandlerException, RepositoryException, UnauthorizedException { checkRepositoryURL(); GetMethod method = new GetMethod(Protocol.getStatementsLocation(getRepositoryURL())); setDoAuthentication(method); List<NameValuePair> params = new ArrayList<NameValuePair>(5); if (subj != null) { params.add(new NameValuePair(Protocol.SUBJECT_PARAM_NAME, Protocol.encodeValue(subj))); } if (pred != null) { params.add(new NameValuePair(Protocol.PREDICATE_PARAM_NAME, Protocol.encodeValue(pred))); } if (obj != null) { params.add(new NameValuePair(Protocol.OBJECT_PARAM_NAME, Protocol.encodeValue(obj))); } for (String encodedContext : Protocol.encodeContexts(contexts)) { params.add(new NameValuePair(Protocol.CONTEXT_PARAM_NAME, encodedContext)); } params.add(new NameValuePair(Protocol.INCLUDE_INFERRED_PARAM_NAME, Boolean.toString(includeInferred))); method.setQueryString(params.toArray(new NameValuePair[params.size()])); try { getRDF(method, handler, true); } catch (MalformedQueryException e) { logger.warn("Server reported unexpected malfored query error", e); throw new RepositoryException(e.getMessage(), e); } finally { releaseConnection(method); } } public void sendTransaction(final Iterable<? extends TransactionOperation> txn) throws IOException, RepositoryException, UnauthorizedException { checkRepositoryURL(); PostMethod method = new PostMethod(Protocol.getStatementsLocation(getRepositoryURL())); setDoAuthentication(method); // Create a RequestEntity for the transaction data method.setRequestEntity(new RequestEntity() { public long getContentLength() { return -1; // don't know } public String getContentType() { return Protocol.TXN_MIME_TYPE; } public boolean isRepeatable() { return true; } public void writeRequest(OutputStream out) throws IOException { TransactionWriter txnWriter = new TransactionWriter(); txnWriter.serialize(txn, out); } }); try { int httpCode = httpClient.executeMethod(method); if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new UnauthorizedException(); } else if (!HttpClientUtil.is2xx(httpCode)) { ErrorInfo errInfo = getErrorInfo(method); throw new RepositoryException("Transaction failed: " + errInfo + " (" + httpCode + ")"); } } finally { releaseConnection(method); } } public void upload(final Reader contents, String baseURI, final RDFFormat dataFormat, boolean overwrite, Resource... contexts) throws IOException, RDFParseException, RepositoryException, UnauthorizedException { final Charset charset = dataFormat.hasCharset() ? dataFormat.getCharset() : Charset.forName("UTF-8"); RequestEntity entity = new RequestEntity() { public long getContentLength() { return -1; // don't know } public String getContentType() { return dataFormat.getDefaultMIMEType() + "; charset=" + charset.name(); } public boolean isRepeatable() { return false; } public void writeRequest(OutputStream out) throws IOException { OutputStreamWriter writer = new OutputStreamWriter(out, charset); IOUtil.transfer(contents, writer); writer.flush(); } }; upload(entity, baseURI, overwrite, contexts); } public void upload(InputStream contents, String baseURI, RDFFormat dataFormat, boolean overwrite, Resource... contexts) throws IOException, RDFParseException, RepositoryException, UnauthorizedException { // Set Content-Length to -1 as we don't know it and we also don't want to // cache RequestEntity entity = new InputStreamRequestEntity(contents, -1, dataFormat.getDefaultMIMEType()); upload(entity, baseURI, overwrite, contexts); } protected void upload(RequestEntity reqEntity, String baseURI, boolean overwrite, Resource... contexts) throws IOException, RDFParseException, RepositoryException, UnauthorizedException { OpenRDFUtil.verifyContextNotNull(contexts); checkRepositoryURL(); String uploadURL = Protocol.getStatementsLocation(getRepositoryURL()); // Select appropriate HTTP method EntityEnclosingMethod method; if (overwrite) { method = new PutMethod(uploadURL); } else { method = new PostMethod(uploadURL); } setDoAuthentication(method); // Set relevant query parameters List<NameValuePair> params = new ArrayList<NameValuePair>(5); for (String encodedContext : Protocol.encodeContexts(contexts)) { params.add(new NameValuePair(Protocol.CONTEXT_PARAM_NAME, encodedContext)); } if (baseURI != null && baseURI.trim().length() != 0) { String encodedBaseURI = Protocol.encodeValue(new URIImpl(baseURI)); params.add(new NameValuePair(Protocol.BASEURI_PARAM_NAME, encodedBaseURI)); } method.setQueryString(params.toArray(new NameValuePair[params.size()])); // Set payload method.setRequestEntity(reqEntity); // Send request try { int httpCode = httpClient.executeMethod(method); if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new UnauthorizedException(); } else if (httpCode == HttpURLConnection.HTTP_UNSUPPORTED_TYPE) { throw new UnsupportedRDFormatException(method.getResponseBodyAsString()); } else if (!HttpClientUtil.is2xx(httpCode)) { ErrorInfo errInfo = ErrorInfo.parse(method.getResponseBodyAsString()); if (errInfo.getErrorType() == ErrorType.MALFORMED_DATA) { throw new RDFParseException(errInfo.getErrorMessage()); } else if (errInfo.getErrorType() == ErrorType.UNSUPPORTED_FILE_FORMAT) { throw new UnsupportedRDFormatException(errInfo.getErrorMessage()); } else { throw new RepositoryException("Failed to upload data: " + errInfo); } } } finally { releaseConnection(method); } } /*-------------* * Context IDs * *-------------*/ public TupleQueryResult getContextIDs() throws IOException, RepositoryException, UnauthorizedException { try { TupleQueryResultBuilder builder = new TupleQueryResultBuilder(); getContextIDs(builder); return builder.getQueryResult(); } catch (TupleQueryResultHandlerException e) { // Found a bug in TupleQueryResultBuilder? throw new RuntimeException(e); } } public void getContextIDs(TupleQueryResultHandler handler) throws IOException, TupleQueryResultHandlerException, RepositoryException, UnauthorizedException { checkRepositoryURL(); GetMethod method = new GetMethod(Protocol.getContextsLocation(getRepositoryURL())); setDoAuthentication(method); try { getTupleQueryResult(method, handler); } catch (MalformedQueryException e) { logger.warn("Server reported unexpected malfored query error", e); throw new RepositoryException(e.getMessage(), e); } finally { releaseConnection(method); } } /*---------------------------* * Get/add/remove namespaces * *---------------------------*/ public TupleQueryResult getNamespaces() throws IOException, RepositoryException, UnauthorizedException { try { TupleQueryResultBuilder builder = new TupleQueryResultBuilder(); getNamespaces(builder); return builder.getQueryResult(); } catch (TupleQueryResultHandlerException e) { // Found a bug in TupleQueryResultBuilder? throw new RuntimeException(e); } } public void getNamespaces(TupleQueryResultHandler handler) throws IOException, TupleQueryResultHandlerException, RepositoryException, UnauthorizedException { checkRepositoryURL(); HttpMethod method = new GetMethod(Protocol.getNamespacesLocation(repositoryURL)); setDoAuthentication(method); try { getTupleQueryResult(method, handler); } catch (MalformedQueryException e) { logger.warn("Server reported unexpected malfored query error", e); throw new RepositoryException(e.getMessage(), e); } finally { releaseConnection(method); } } public String getNamespace(String prefix) throws IOException, RepositoryException, UnauthorizedException { checkRepositoryURL(); HttpMethod method = new GetMethod(Protocol.getNamespacePrefixLocation(repositoryURL, prefix)); setDoAuthentication(method); try { int httpCode = httpClient.executeMethod(method); if (httpCode == HttpURLConnection.HTTP_OK) { return method.getResponseBodyAsString(); } else if (httpCode == HttpURLConnection.HTTP_NOT_FOUND) { return null; } else if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new UnauthorizedException(); } else { ErrorInfo errInfo = getErrorInfo(method); throw new RepositoryException("Failed to get namespace: " + errInfo + " (" + httpCode + ")"); } } finally { releaseConnection(method); } } public void setNamespacePrefix(String prefix, String name) throws IOException, RepositoryException, UnauthorizedException { checkRepositoryURL(); EntityEnclosingMethod method = new PutMethod(Protocol.getNamespacePrefixLocation(repositoryURL, prefix)); setDoAuthentication(method); method.setRequestEntity(new StringRequestEntity(name, "text/plain", "UTF-8")); try { int httpCode = httpClient.executeMethod(method); if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new UnauthorizedException(); } else if (!HttpClientUtil.is2xx(httpCode)) { ErrorInfo errInfo = getErrorInfo(method); throw new RepositoryException("Failed to set namespace: " + errInfo + " (" + httpCode + ")"); } } finally { releaseConnection(method); } } public void removeNamespacePrefix(String prefix) throws IOException, RepositoryException, UnauthorizedException { checkRepositoryURL(); HttpMethod method = new DeleteMethod(Protocol.getNamespacePrefixLocation(repositoryURL, prefix)); setDoAuthentication(method); try { int httpCode = httpClient.executeMethod(method); if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new UnauthorizedException(); } else if (!HttpClientUtil.is2xx(httpCode)) { ErrorInfo errInfo = getErrorInfo(method); throw new RepositoryException("Failed to remove namespace: " + errInfo + " (" + httpCode + ")"); } } finally { releaseConnection(method); } } public void clearNamespaces() throws IOException, RepositoryException, UnauthorizedException { checkRepositoryURL(); HttpMethod method = new DeleteMethod(Protocol.getNamespacesLocation(repositoryURL)); setDoAuthentication(method); try { int httpCode = httpClient.executeMethod(method); if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new UnauthorizedException(); } else if (!HttpClientUtil.is2xx(httpCode)) { ErrorInfo errInfo = getErrorInfo(method); throw new RepositoryException("Failed to clear namespaces: " + errInfo + " (" + httpCode + ")"); } } finally { releaseConnection(method); } } /*-------------------------* * Repository/context size * *-------------------------*/ public long size(Resource... contexts) throws IOException, RepositoryException, UnauthorizedException { checkRepositoryURL(); String[] encodedContexts = Protocol.encodeContexts(contexts); NameValuePair[] contextParams = new NameValuePair[encodedContexts.length]; for (int i = 0; i < encodedContexts.length; i++) { contextParams[i] = new NameValuePair(Protocol.CONTEXT_PARAM_NAME, encodedContexts[i]); } HttpMethod method = new GetMethod(Protocol.getSizeLocation(repositoryURL)); setDoAuthentication(method); method.setQueryString(contextParams); try { int httpCode = httpClient.executeMethod(method); if (httpCode == HttpURLConnection.HTTP_OK) { String response = method.getResponseBodyAsString(); try { return Long.parseLong(response); } catch (NumberFormatException e) { throw new RepositoryException("Server responded with invalid size value: " + response); } } else if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new UnauthorizedException(); } else { ErrorInfo errInfo = getErrorInfo(method); throw new RepositoryException(errInfo.toString()); } } finally { method.releaseConnection(); } } /*------------------* * Response parsing * *------------------*/ protected void getTupleQueryResult(HttpMethod method, TupleQueryResultHandler handler) throws IOException, TupleQueryResultHandlerException, RepositoryException, MalformedQueryException, UnauthorizedException { // Specify which formats we support using Accept headers Set<TupleQueryResultFormat> tqrFormats = TupleQueryResultParserRegistry.getInstance().getKeys(); if (tqrFormats.isEmpty()) { throw new RepositoryException("No tuple query result parsers have been registered"); } for (TupleQueryResultFormat format : tqrFormats) { // Determine a q-value that reflects the user specified preference int qValue = 10; if (preferredTQRFormat != null && !preferredTQRFormat.equals(format)) { // Prefer specified format over other formats qValue -= 2; } for (String mimeType : format.getMIMETypes()) { String acceptParam = mimeType; if (qValue < 10) { acceptParam += ";q=0." + qValue; } method.addRequestHeader(ACCEPT_PARAM_NAME, acceptParam); } } int httpCode = httpClient.executeMethod(method); if (httpCode == HttpURLConnection.HTTP_OK) { String mimeType = getResponseMIMEType(method); try { TupleQueryResultFormat format = TupleQueryResultFormat.matchMIMEType(mimeType, tqrFormats); TupleQueryResultParser parser = QueryResultIO.createParser(format, getValueFactory()); parser.setTupleQueryResultHandler(handler); parser.parse(method.getResponseBodyAsStream()); } catch (UnsupportedQueryResultFormatException e) { throw new RepositoryException("Server responded with an unsupported file format: " + mimeType); } catch (QueryResultParseException e) { throw new RepositoryException("Malformed query result from server", e); } } else if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new UnauthorizedException(); } else { ErrorInfo errInfo = getErrorInfo(method); // Throw appropriate exception if (errInfo.getErrorType() == ErrorType.MALFORMED_QUERY) { throw new MalformedQueryException(errInfo.getErrorMessage()); } else if (errInfo.getErrorType() == ErrorType.UNSUPPORTED_QUERY_LANGUAGE) { throw new UnsupportedQueryLanguageException(errInfo.getErrorMessage()); } else { throw new RepositoryException(errInfo.toString()); } } } protected void getRDF(HttpMethod method, RDFHandler handler, boolean requireContext) throws IOException, RDFHandlerException, RepositoryException, MalformedQueryException, UnauthorizedException { // Specify which formats we support using Accept headers Set<RDFFormat> rdfFormats = RDFParserRegistry.getInstance().getKeys(); if (rdfFormats.isEmpty()) { throw new RepositoryException("No tuple RDF parsers have been registered"); } for (RDFFormat format : rdfFormats) { // Determine a q-value that reflects the necessity of context // support and the user specified preference int qValue = 10; if (requireContext && !format.supportsContexts()) { // Prefer context-supporting formats over pure triple-formats qValue -= 5; } if (preferredRDFFormat != null && !preferredRDFFormat.equals(format)) { // Prefer specified format over other formats qValue -= 2; } if (!format.supportsNamespaces()) { // We like reusing namespace prefixes qValue -= 1; } for (String mimeType : format.getMIMETypes()) { String acceptParam = mimeType; if (qValue < 10) { acceptParam += ";q=0." + qValue; } method.addRequestHeader(ACCEPT_PARAM_NAME, acceptParam); } } int httpCode = httpClient.executeMethod(method); if (httpCode == HttpURLConnection.HTTP_OK) { String mimeType = getResponseMIMEType(method); try { RDFFormat format = RDFFormat.matchMIMEType(mimeType, rdfFormats); RDFParser parser = Rio.createParser(format, getValueFactory()); parser.setPreserveBNodeIDs(true); parser.setRDFHandler(handler); parser.parse(method.getResponseBodyAsStream(), method.getURI().getURI()); } catch (UnsupportedRDFormatException e) { throw new RepositoryException("Server responded with an unsupported file format: " + mimeType); } catch (RDFParseException e) { throw new RepositoryException("Malformed query result from server", e); } } else if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new UnauthorizedException(); } else { ErrorInfo errInfo = getErrorInfo(method); // Throw appropriate exception if (errInfo.getErrorType() == ErrorType.MALFORMED_QUERY) { throw new MalformedQueryException(errInfo.getErrorMessage()); } else if (errInfo.getErrorType() == ErrorType.UNSUPPORTED_QUERY_LANGUAGE) { throw new UnsupportedQueryLanguageException(errInfo.getErrorMessage()); } else { throw new RepositoryException(errInfo.toString()); } } } protected boolean getBoolean(HttpMethod method) throws IOException, RepositoryException, MalformedQueryException, UnauthorizedException { // Specify which formats we support using Accept headers Set<BooleanQueryResultFormat> booleanFormats = BooleanQueryResultParserRegistry.getInstance().getKeys(); if (booleanFormats.isEmpty()) { throw new RepositoryException("No boolean query result parsers have been registered"); } for (BooleanQueryResultFormat format : booleanFormats) { // Determine a q-value that reflects the user specified preference int qValue = 10; if (preferredBQRFormat != null && !preferredBQRFormat.equals(format)) { // Prefer specified format over other formats qValue -= 2; } for (String mimeType : format.getMIMETypes()) { String acceptParam = mimeType; if (qValue < 10) { acceptParam += ";q=0." + qValue; } method.addRequestHeader(ACCEPT_PARAM_NAME, acceptParam); } } int httpCode = httpClient.executeMethod(method); if (httpCode == HttpURLConnection.HTTP_OK) { String mimeType = getResponseMIMEType(method); try { BooleanQueryResultFormat format = BooleanQueryResultFormat.matchMIMEType(mimeType, booleanFormats); BooleanQueryResultParser parser = QueryResultIO.createParser(format); return parser.parse(method.getResponseBodyAsStream()); } catch (UnsupportedQueryResultFormatException e) { throw new RepositoryException("Server responded with an unsupported file format: " + mimeType); } catch (QueryResultParseException e) { throw new RepositoryException("Malformed query result from server", e); } } else if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED) { throw new UnauthorizedException(); } else { ErrorInfo errInfo = getErrorInfo(method); // Throw appropriate exception if (errInfo.getErrorType() == ErrorType.MALFORMED_QUERY) { throw new MalformedQueryException(errInfo.getErrorMessage()); } else if (errInfo.getErrorType() == ErrorType.UNSUPPORTED_QUERY_LANGUAGE) { throw new UnsupportedQueryLanguageException(errInfo.getErrorMessage()); } else { throw new RepositoryException(method.getStatusText()); } } } /*-------------------------* * General utility methods * *-------------------------*/ /** * Gets the MIME type specified in the response headers of the supplied * method, if any. For example, if the response headers contain * <tt>Content-Type: application/xml;charset=UTF-8</tt>, this method will * return <tt>application/xml</tt> as the MIME type. * * @param method * The method to get the reponse MIME type from. * @return The response MIME type, or <tt>null</tt> if not available. */ protected String getResponseMIMEType(HttpMethod method) throws IOException { Header[] headers = method.getResponseHeaders("Content-Type"); for (Header header : headers) { HeaderElement[] headerElements = header.getElements(); for (HeaderElement headerEl : headerElements) { String mimeType = headerEl.getName(); if (mimeType != null) { logger.debug("reponse MIME type is {}", mimeType); return mimeType; } } } return null; } protected ErrorInfo getErrorInfo(HttpMethod method) throws RepositoryException { try { ErrorInfo errInfo = ErrorInfo.parse(method.getResponseBodyAsString()); logger.warn("Server reports problem: {}", errInfo.getErrorMessage()); return errInfo; } catch (IOException e) { logger.warn("Unable to retrieve error info from server"); throw new RepositoryException("Unable to retrieve error info from server", e); } } protected final void setDoAuthentication(HttpMethod method) { if (authScope != null && httpClient.getState().getCredentials(authScope) != null) { method.setDoAuthentication(true); } else { method.setDoAuthentication(false); } } protected final void releaseConnection(HttpMethod method) { try { // Read the entire response body to enable the reuse of the connection InputStream responseStream = method.getResponseBodyAsStream(); if (responseStream != null) { while (responseStream.read() >= 0) { // do nothing } } method.releaseConnection(); } catch (IOException e) { logger.warn("I/O error upon releasing connection", e); } } }