/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2014. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.bigdata.rdf.sail.webapp.client; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.message.BasicNameValuePair; import org.openrdf.query.resultio.BooleanQueryResultFormat; import org.openrdf.query.resultio.TupleQueryResultFormat; import org.openrdf.rio.RDFFormat; public class AbstractConnectOptions implements IMimeTypes { /** The URL of the remote REST service. */ public final String serviceURL; /** The HTTP method (GET, POST, etc). */ public String method = "POST"; /** The Request Header value */ public static final transient String ACCEPT_HEADER = "Accept"; /** * When <code>true</code>, the request is a non-idempotent operation (an * UPDATE request or some kind) and must be directed to the leader for HA. * When <code>false</code>, the request is an idempotent operation and * should be load balanced over the available service for HA. This option * is ignored for non-HA requests. */ public boolean update; /** * Used for {@link RDFFormat} responses. */ public static final String DEFAULT_GRAPH_ACCEPT_HEADER; /** * Used for {@link TupleQueryResultFormat} responses. */ public static final String DEFAULT_SOLUTIONS_ACCEPT_HEADER; /** * Used for {@link BooleanQueryResultFormat} responses. */ public static final String DEFAULT_BOOLEAN_ACCEPT_HEADER; // /** // * Used for NSS mutation operation responses. // */ // public static final String MIME_APPLICATION_XML = "application/xml"; // /** // * Used to interchange {@link Properties} objects. // */ // public static final String MIME_PROPERTIES_XML = "application/xml"; static { /** * Note: This has been commented out. If it is included, then a lot of * the total code base gets dragged into the bigdata-client JAR. If this * creates a problem for clients, then we will need to examine the * bigdata RDF model and bigdata RDF parser packages carefully and * relayer them in order to decouple them from the rest of the code * base. * * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/628" > * Create a bigdata-client jar for the NSS REST API </a> */ // ServiceProviderHook.forceLoad(); /* * FIXME We really need to know whether we are talking to a triple or * quads mode end point before we can set this to [true]. The preference * winds up as BINARY, which happens to support context so we are not * forcing bigdata (at least) into a choice that it can not make. We * might run into problems with non-bigdata end points. If they support * SPARQL Service Description, then we could figure out whether the end * point is triples or quads and setup the Accept header appropriately. * Otherwise we might have to raise this into the application (or the * ServiceFactory). */ final boolean requireContext = false; DEFAULT_GRAPH_ACCEPT_HEADER = AcceptHeaderFactory .getDefaultGraphAcceptHeader(requireContext); DEFAULT_SOLUTIONS_ACCEPT_HEADER = AcceptHeaderFactory .getDefaultSolutionsAcceptHeader(); DEFAULT_BOOLEAN_ACCEPT_HEADER = AcceptHeaderFactory .getDefaultBooleanAcceptHeader(); } /** * Request parameters to be formatted as URL query parameters. */ public Map<String, String[]> requestParams; /** * Optional request headers. */ public Map<String, String> requestHeaders; @Override public String toString() { return getClass().getName()// + "{method=" + method// + ",serviceURL=" + serviceURL// + ",update=" + update// + ",requestParams=" + requestParams// + ",requestHeaders=" + requestHeaders// + ",requestHeaders=" + requestHeaders// + "}";// } public AbstractConnectOptions(final String serviceURL) { if (serviceURL == null) throw new IllegalArgumentException(); this.serviceURL = serviceURL; } public void addRequestParam(final String name, final String[] vals) { if (requestParams == null) { requestParams = new LinkedHashMap<String, String[]>(); } requestParams.put(name, vals); } public void addRequestParam(final String name, final String val) { addRequestParam(name, new String[] { val }); } public void addRequestParam(final String name) { addRequestParam(name, (String[]) null); } public String getRequestParam(final String name) { return (requestParams != null && requestParams.containsKey(name)) ? requestParams .get(name)[0] : null; } public void setHeader(final String name, final String val) { if (requestHeaders == null) { requestHeaders = new LinkedHashMap<String, String>(); } requestHeaders.put(name, val); } public void setAcceptHeader(final String value) { setHeader(ACCEPT_HEADER, value); } public String getAcceptHeader() { return getHeader(ACCEPT_HEADER); } public String getHeader(final String name) { if (requestHeaders == null) return null; return requestHeaders.get(name); } /** * Add any URL query parameters (for a GET request). * * @see #1097 (Extension REST API does not support multiple context values) * * @see #getFormEntity(Map) */ static public void addQueryParams(final StringBuilder urlString, final Map<String, String[]> requestParams) throws UnsupportedEncodingException { if (requestParams == null) return; boolean first = true; for (Map.Entry<String, String[]> e : requestParams.entrySet()) { final String name = e.getKey(); final String[] vals = e.getValue(); if (vals == null) { urlString.append(first ? "?" : "&"); first = false; urlString.append(URLEncoder.encode(name, RemoteRepository.UTF8)); } else { for (String val : vals) { urlString.append(first ? "?" : "&"); first = false; urlString.append(URLEncoder.encode(name, RemoteRepository.UTF8)); urlString.append("="); if(val!=null) urlString.append(URLEncoder.encode(val, RemoteRepository.UTF8)); } } } // next Map.Entry } /** * Variant of {@link #addQueryParams(StringBuilder, Map)} that returns an * entity for a POST request. * <p> * Add query params to an {@link IMimeTypes#MIME_APPLICATION_URL_ENCODED} * entity. * * @see #addQueryParams(StringBuilder, Map) * * TODO Rename either this or {@link #addQueryParams(StringBuilder, Map)} * to align the method names since they serve the same purpose but for * GET vs POST. */ public static HttpEntity getFormEntity( final Map<String, String[]> requestParams) throws Exception { final List<NameValuePair> formparams = new ArrayList<NameValuePair>(); if (requestParams != null) { for (Map.Entry<String, String[]> e : requestParams.entrySet()) { final String name = e.getKey(); final String[] vals = e.getValue(); if (vals == null) { formparams.add(new BasicNameValuePair(name, null)); } else { for (String val : vals) { formparams.add(new BasicNameValuePair(name, val)); } } } // next Map.Entry } return new UrlEncodedFormEntity(formparams, RemoteRepository.UTF8); } /** * Apply a UTF8 encoding to a component of a URL. * * @param in * The text to be encoded. * * @return The UTF8 encoding of that text. * * @throws RuntimeException * if the {@link RemoteRepository#UTF8} encoding is not * available. * @throws NullPointerException * if the argument is <code>null</code>. */ public static String urlEncode(final String in) { try { final String out = URLEncoder.encode(in, RemoteRepository.UTF8); return out; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } }