ingo@1: /*
ingo@1: * Copyright (c) 2010 by Intevation GmbH
ingo@1: *
ingo@1: * This program is free software under the LGPL (>=v2.1)
ingo@1: * Read the file LGPL.txt coming with the software for details
ingo@1: * or visit http://www.gnu.org/licenses/ if it does not exist.
ingo@1: */
ingo@1: package de.intevation.artifacts.httpclient.http;
ingo@1:
ingo@1: import java.io.InputStream;
ingo@1: import java.io.IOException;
ingo@1: import java.io.OutputStream;
ingo@10: import java.util.ArrayList;
ingo@10: import java.util.List;
ingo@1:
ingo@1: import org.apache.log4j.Logger;
ingo@1:
ingo@1: import org.restlet.Client;
ingo@1: import org.restlet.Request;
ingo@1: import org.restlet.Response;
ingo@10: import org.restlet.data.ClientInfo;
ingo@10: import org.restlet.data.Language;
ingo@1: import org.restlet.data.MediaType;
ingo@1: import org.restlet.data.Method;
ingo@10: import org.restlet.data.Preference;
ingo@1: import org.restlet.data.Protocol;
ingo@1: import org.restlet.data.Status;
ingo@1: import org.restlet.ext.xml.DomRepresentation;
ingo@1: import org.restlet.representation.Representation;
ingo@1:
ingo@1: import org.w3c.dom.Document;
ingo@1:
ingo@1: import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
ingo@1: import de.intevation.artifacts.httpclient.http.response.DocumentResponseHandler;
ingo@1: import de.intevation.artifacts.httpclient.http.response.ResponseHandler;
ingo@1: import de.intevation.artifacts.httpclient.http.response.StreamResponseHandler;
ingo@1: import de.intevation.artifacts.httpclient.objects.Artifact;
ingo@1: import de.intevation.artifacts.httpclient.objects.ArtifactFactory;
ingo@1: import de.intevation.artifacts.httpclient.utils.ArtifactProtocolUtils;
ingo@2: import de.intevation.artifacts.httpclient.utils.ArtifactCreator;
ingo@2:
ingo@1:
ingo@1: /**
ingo@1: * @author Ingo Weinzierl
ingo@1: */
ingo@1: public class HttpClientImpl implements HttpClient {
ingo@1:
ingo@1: private static final Logger logger = Logger.getLogger(HttpClient.class);
ingo@1:
ingo@4: /** The URL part of the resource to list the existing users of the server.*/
ingo@4: public static final String PATH_LIST_USERS = "/list-users";
ingo@4:
ingo@7: /** The URL part of the resource to list the Collections owned by a specific
ingo@7: * user.*/
ingo@7: public static final String PATH_USER_COLLECTIONS = "/list-collections";
ingo@7:
ingo@5: /** The URL part of the resource to call a specific service.*/
ingo@5: public static final String PATH_SERVICE = "/service";
ingo@5:
ingo@3: /** The URL path of the resource to create new artifact collections.*/
ingo@3: public static final String PATH_CREATE_COLLECTION = "/create-collection";
ingo@3:
ingo@6: /** The URL path of the resource to work with an artifact collections.*/
ingo@6: public static final String PATH_ACTION_COLLECTION = "/collection";
ingo@6:
ingo@9: /** The URL path of the resource to work with an artifact collections.*/
ingo@9: public static final String PATH_OUT_COLLECTION = "/collection";
ingo@9:
ingo@1: private String serverUrl;
ingo@1:
ingo@10: private String localeString;
ingo@10:
ingo@1:
ingo@1: public HttpClientImpl(String serverUrl) {
ingo@1: this.serverUrl = serverUrl;
ingo@1: }
ingo@1:
ingo@1:
ingo@10: /**
ingo@10: * This constructor might be used to modify the request's locale manually.
ingo@10: * E.g. the localization should not be based on the configured browser
ingo@10: * locale, but site specific configuration - than you are able to set the
ingo@10: * locale in this constructor.
ingo@10: *
ingo@10: * @param serverUrl The url that is used for the request.
ingo@10: * @param localeString The string representation of the desired locale.
ingo@10: */
ingo@10: public HttpClientImpl(String serverUrl, String localeString) {
ingo@10: this(serverUrl);
ingo@10:
ingo@10: this.localeString = localeString;
ingo@10: }
ingo@10:
ingo@10:
ingo@1: @Override
ingo@1: public ArtifactFactory[] getArtifactFactories()
ingo@1: throws ConnectionException
ingo@1: {
ingo@1: ResponseHandler handler = new DocumentResponseHandler();
ingo@1:
ingo@1: try {
ingo@1: String url = serverUrl + "/factories";
ingo@1: Document result = (Document) handler.handle(doGet(url));
ingo@1:
ingo@1: return ArtifactProtocolUtils.extractArtifactFactories(result);
ingo@1: }
ingo@1: catch (IOException ioe) {
ingo@1: throw new ConnectionException(
ingo@1: "Connection to server failed. No Factories recieved.");
ingo@1: }
ingo@1: }
ingo@1:
ingo@1:
ingo@2: /**
ingo@2: * This method creates a new artifact in the artifact server and returns
ingo@2: * this artifact. The new artifact is created using creator. If no
ingo@2: * {@link ArtifactCreator} is given (null), an {@link Artifact} is returned.
ingo@2: *
ingo@2: * @param doc The CREATE document.
ingo@2: * @param creator The {@link ArtifactCreator} that is used to extract the
ingo@2: * new artifact from response document of the server.
ingo@2: *
ingo@2: * @return the new artifact.
ingo@2: */
ingo@1: @Override
ingo@2: public Object create(Document doc, ArtifactCreator creator)
ingo@2: throws ConnectionException
ingo@2: {
ingo@1: ResponseHandler handler = new DocumentResponseHandler();
ingo@1:
ingo@1: try {
ingo@1: String url = serverUrl + "/create";
ingo@1: Document result = (Document) handler.handle(doPost(url, doc));
ingo@1:
ingo@2: return creator == null
ingo@2: ? ArtifactProtocolUtils.extractArtifact(result)
ingo@2: : creator.create(result);
ingo@1: }
ingo@1: catch (IOException ioe) {
ingo@1: throw new ConnectionException(
ingo@1: "Connection to server failed. No Artifact created.");
ingo@1: }
ingo@1: }
ingo@1:
ingo@1:
ingo@1: @Override
ingo@1: public Object describe(
ingo@1: Artifact artifact,
ingo@1: Document doc,
ingo@1: ResponseHandler handler)
ingo@1: throws ConnectionException
ingo@1: {
ingo@1: try {
ingo@1: String url = serverUrl + "/artifact/" + artifact.getUuid();
ingo@1: return handler.handle(doPost(url, doc));
ingo@1: }
ingo@1: catch (IOException ioe) {
ingo@1: throw new ConnectionException(
ingo@1: "Connection to server failed: " + ioe.getMessage());
ingo@1: }
ingo@1: }
ingo@1:
ingo@1:
ingo@1: @Override
ingo@1: public Object feed(Artifact artifact, Document doc, ResponseHandler handler)
ingo@1: throws ConnectionException
ingo@1: {
ingo@1: try {
ingo@1: String url = serverUrl + "/artifact/" + artifact.getUuid();
ingo@1: Document result = (Document) handler.handle(doPost(url, doc));
ingo@1:
ingo@1: return result;
ingo@1: }
ingo@1: catch (IOException ioe) {
ingo@1: throw new ConnectionException(
ingo@1: "Connection to server failed: " + ioe.getMessage());
ingo@1: }
ingo@1: }
ingo@1:
ingo@1:
ingo@1: @Override
ingo@1: public Object advance(Artifact artifact, Document doc, ResponseHandler handler)
ingo@1: throws ConnectionException
ingo@1: {
ingo@1: try {
ingo@1: String url = serverUrl + "/artifact/" + artifact.getUuid();
ingo@1: Document result = (Document) handler.handle(doPost(url, doc));
ingo@1:
ingo@1: return result;
ingo@1: }
ingo@1: catch (IOException ioe) {
ingo@1: throw new ConnectionException(
ingo@1: "Connection to server failed: " + ioe.getMessage());
ingo@1: }
ingo@1: }
ingo@1:
ingo@1:
ingo@1: @Override
ingo@1: public void out(
ingo@1: Artifact artifact,
ingo@1: Document doc,
ingo@1: String target,
ingo@1: OutputStream out)
ingo@1: throws ConnectionException
ingo@1: {
ingo@1: try {
ingo@1: String url =
ingo@1: serverUrl
ingo@1: + "/artifact/"
ingo@1: + artifact.getUuid()
ingo@1: + "/" + target;
ingo@1:
ingo@1: ResponseHandler handler = new StreamResponseHandler();
ingo@1:
ingo@1: InputStream stream = (InputStream) handler.handle(doPost(url, doc));
ingo@1:
ingo@1: byte[] b = new byte[4096];
ingo@1: int i = -1;
ingo@1: while ((i = stream.read(b)) > 0) {
ingo@1: out.write(b, 0, i);
ingo@1: }
ingo@1: }
ingo@1: catch (IOException ioe) {
ingo@1: throw new ConnectionException(
ingo@1: "Connection to server failed: " + ioe.getMessage());
ingo@1: }
ingo@1: }
ingo@1:
ingo@1:
ingo@10: //==============================
ingo@10: // HTTP specific methods
ingo@10: //==============================
ingo@10:
ingo@1: private Response doPost(String url, Document body) throws IOException {
ingo@1: logger.info("Start HTTP-POST request to: "+ url);
ingo@1:
ingo@1: Client client = new Client(Protocol.HTTP);
ingo@10: Request request = prepareRequest(Method.POST, url);
ingo@1:
ingo@1: Representation representation = new DomRepresentation(
ingo@1: MediaType.APPLICATION_XML,
ingo@1: body);
ingo@1:
ingo@1: request.setEntity(representation);
ingo@1: Response response = client.handle(request);
ingo@1:
ingo@1: Status status = response.getStatus();
ingo@1: if (status.getCode() != 200) {
ingo@1: logger.error("Response status: " + status.getCode());
ingo@1: throw new IOException(status.getDescription());
ingo@1: }
ingo@1:
ingo@1: return response;
ingo@1: }
ingo@1:
ingo@1:
ingo@1: private Response doGet(String url) throws IOException {
ingo@1: logger.info("Start HTTP-POST request to: "+ url);
ingo@1:
ingo@1: Client client = new Client(Protocol.HTTP);
ingo@10: Request request = prepareRequest(Method.GET, url);
ingo@1:
ingo@1: Response response = client.handle(request);
ingo@1:
ingo@1: Status status = response.getStatus();
ingo@1: if (status.getCode() != 200) {
ingo@1: logger.error("Response status: " + status.getCode());
ingo@1: throw new IOException(status.getDescription());
ingo@1: }
ingo@1:
ingo@1: return response;
ingo@1: }
ingo@4:
ingo@4:
ingo@10: /**
ingo@10: * This method prepares the request object.
ingo@10: *
ingo@10: * @param method The HTTP method (GET,POST).
ingo@10: * @param url The URL used for the request.
ingo@10: *
ingo@10: * @return the request object.
ingo@10: */
ingo@10: private Request prepareRequest(Method method, String url) {
ingo@10: Request request = new Request(method, url);
ingo@10:
ingo@10: ClientInfo info = request.getClientInfo();
ingo@10:
ingo@10: setLocale(info);
ingo@10:
ingo@10: request.setClientInfo(info);
ingo@10:
ingo@10: return request;
ingo@10: }
ingo@10:
ingo@10:
ingo@10: /**
ingo@10: * This method is called to set the request's locale.
ingo@10: *
ingo@10: * @param info The ClientInfo that is used to provide request information.
ingo@10: */
ingo@10: private void setLocale(ClientInfo info) {
ingo@11: if (localeString == null) {
ingo@10: return;
ingo@10: }
ingo@10:
ingo@10: List> accepted =
ingo@10: new ArrayList>();
ingo@10:
ingo@10: Language lang = Language.valueOf(localeString);
ingo@10:
ingo@10: if (lang != null) {
ingo@11: logger.info("Set locale of the request object: " + lang.toString());
ingo@11:
ingo@10: Preference pref = new Preference();
ingo@10: pref.setMetadata(lang);
ingo@10: accepted.add(pref);
ingo@10:
ingo@10: info.setAcceptedLanguages(accepted);
ingo@10: }
ingo@10: }
ingo@10:
ingo@10:
ingo@6: //==============================
ingo@6: // Collection API
ingo@6: //==============================
ingo@6:
ingo@6: /**
ingo@6: * This method triggers the artifact servers resource to create a new
ingo@6: * artifact collection.
ingo@6: *
ingo@6: * @param create The CREATE document for the collection.
ingo@6: * @param ownerId The uuid of the creator.
ingo@6: * @param handler The handler that is used to create the result object.
ingo@6: *
ingo@6: * @return a result object created by handler.
ingo@6: */
ingo@6: public Object createCollection(
ingo@6: Document create,
ingo@6: String ownerId,
ingo@6: ResponseHandler handler)
ingo@6: throws ConnectionException
ingo@6: {
ingo@6: String url = serverUrl + PATH_CREATE_COLLECTION + "/" + ownerId;
ingo@6:
ingo@6: try {
ingo@6: return handler.handle(doPost(url, create));
ingo@6: }
ingo@6: catch (IOException ioe) {
ingo@6: throw new ConnectionException(ioe.getMessage(), ioe);
ingo@6: }
ingo@6: }
ingo@6:
ingo@6:
ingo@6: /**
ingo@6: * This method might be used to trigger a collection specific action. The
ingo@6: * action that is executed depends on the document actionDoc.
ingo@6: *
ingo@6: * @param actionDoc The document that describes the action to be executed.
ingo@6: * @param uuid The uuid of the collection.
ingo@6: * @param handler The handler that is used to create the result object.
ingo@6: *
ingo@6: * @return a result object created by handler.
ingo@6: */
ingo@6: public Object doCollectionAction(
ingo@6: Document actionDoc,
ingo@6: String uuid,
ingo@6: ResponseHandler handler)
ingo@6: throws ConnectionException
ingo@6: {
ingo@6: String url = serverUrl + PATH_ACTION_COLLECTION + "/" + uuid;
ingo@6:
ingo@6: try {
ingo@6: return handler.handle(doPost(url, actionDoc));
ingo@6: }
ingo@6: catch (IOException ioe) {
ingo@6: throw new ConnectionException(ioe.getMessage(), ioe);
ingo@6: }
ingo@6: }
ingo@6:
ingo@6:
ingo@9: /**
ingo@9: * This method triggers the out() operation of a Collection. The result of
ingo@9: * this operation is written to out directly - there is no return
ingo@9: * value.
ingo@9: *
ingo@9: * @param doc The request document for the out() operation.
ingo@9: * @param uuid The identifier of the Collection.
ingo@9: * @param type The name of the output type.
ingo@9: * @param out The OutputStream.
ingo@9: */
ingo@9: public void collectionOut(
ingo@9: Document doc,
ingo@9: String uuid,
ingo@9: String type,
ingo@9: OutputStream out)
ingo@9: throws ConnectionException
ingo@9: {
ingo@9: try {
ingo@12: InputStream stream = collectionOut(doc, uuid, type);
ingo@9:
ingo@9: byte[] b = new byte[4096];
ingo@9: int i = -1;
ingo@9: while ((i = stream.read(b)) > 0) {
ingo@9: out.write(b, 0, i);
ingo@9: }
ingo@9: }
ingo@9: catch (IOException ioe) {
ingo@9: throw new ConnectionException(ioe.getMessage(), ioe);
ingo@9: }
ingo@9: }
ingo@9:
ingo@9:
ingo@12: /**
ingo@12: * This method triggers the out() operation of a Collection. The result of
ingo@12: * this operation is returned as an InputStream.
ingo@12: *
ingo@12: * @param doc The request document for the out() operation.
ingo@12: * @param uuid The identifier of the Collection.
ingo@12: * @param type The name of the output type.
ingo@12: *
ingo@12: * @return an InputStream.
ingo@12: */
ingo@12: public InputStream collectionOut(
ingo@12: Document doc,
ingo@12: String uuid,
ingo@12: String type)
ingo@12: throws ConnectionException
ingo@12: {
ingo@12: String url = serverUrl + PATH_OUT_COLLECTION + "/" + uuid + "/" + type;
ingo@12:
ingo@12: ResponseHandler handler = new StreamResponseHandler();
ingo@12:
ingo@12: try {
ingo@12: return (InputStream) handler.handle(doPost(url, doc));
ingo@12: }
ingo@12: catch (IOException ioe) {
ingo@12: throw new ConnectionException(ioe.getMessage(), ioe);
ingo@12: }
ingo@12: }
ingo@12:
ingo@12:
ingo@4: /*******************************
ingo@5: * Service API
ingo@5: *******************************/
ingo@5:
ingo@5: public Document callService(String url, String service, Document input)
ingo@5: throws ConnectionException
ingo@5: {
ingo@5: logger.info("Start service call to '" + service + "'");
ingo@5:
ingo@5: DocumentResponseHandler handler = new DocumentResponseHandler();
ingo@5:
ingo@5: try {
ingo@5: String serverUrl = url + PATH_SERVICE + "/" + service;
ingo@5: return (Document) handler.handle(doPost(serverUrl, input));
ingo@5: }
ingo@5: catch (IOException ioe) {
ingo@5: throw new ConnectionException(
ingo@5: "Connection to server failed: " + ioe.getMessage());
ingo@5: }
ingo@5: }
ingo@5:
ingo@5:
ingo@5: /*******************************
ingo@4: * Users API
ingo@4: *******************************/
ingo@5:
ingo@4: public Document listUsers()
ingo@4: throws ConnectionException
ingo@4: {
ingo@4: ResponseHandler handler = new DocumentResponseHandler();
ingo@4: String url = serverUrl + PATH_LIST_USERS;
ingo@4:
ingo@4: try {
ingo@4: return (Document) handler.handle(doGet(url));
ingo@4: }
ingo@4: catch (IOException ioe) {
ingo@4: throw new ConnectionException(ioe.getMessage(), ioe);
ingo@4: }
ingo@4: }
ingo@7:
ingo@7:
ingo@7: public Document listUserCollections(String userid)
ingo@7: throws ConnectionException
ingo@7: {
ingo@7: ResponseHandler handler = new DocumentResponseHandler();
ingo@7:
ingo@7: String url = serverUrl + PATH_USER_COLLECTIONS + "/" + userid;
ingo@7:
ingo@7: try {
ingo@7: return (Document) handler.handle(doGet(url));
ingo@7: }
ingo@7: catch (IOException ioe) {
ingo@7: throw new ConnectionException(ioe.getMessage(), ioe);
ingo@7: }
ingo@7: }
ingo@1: }
ingo@1: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: