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: sascha@21: private static final ThreadLocal CLIENT = sascha@21: new ThreadLocal() { sascha@21: @Override sascha@21: protected Client initialValue() { sascha@21: logger.debug("create new HTTP client"); sascha@21: return new Client(Protocol.HTTP); sascha@21: } sascha@21: }; 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)); sascha@17: try { sascha@17: byte[] b = new byte[4096]; sascha@17: int i; sascha@17: while ((i = stream.read(b)) >= 0) { sascha@17: out.write(b, 0, i); sascha@17: } sascha@17: } sascha@17: finally { sascha@17: stream.close(); 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 { sascha@21: if (logger.isDebugEnabled()) { sascha@21: logger.debug("Start HTTP-POST request to: " + url); sascha@21: } ingo@1: sascha@21: Client client = getClient(); 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: sascha@21: private static Client getClient() { sascha@21: return CLIENT.get(); sascha@21: } sascha@21: sascha@21: ingo@1: private Response doGet(String url) throws IOException { sascha@21: if (logger.isDebugEnabled()) { sascha@21: logger.debug("Start HTTP-POST request to: "+ url); sascha@21: } ingo@1: sascha@21: Client client = getClient(); 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) { sascha@21: if (logger.isDebugEnabled()) { sascha@21: logger.debug( sascha@21: "Set locale of the request object: " + lang.toString()); sascha@21: } 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]; sascha@17: try { sascha@17: int i; sascha@17: while ((i = stream.read(b)) >= 0) { sascha@17: out.write(b, 0, i); sascha@17: } sascha@17: } sascha@17: finally { sascha@17: stream.close(); 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: { sascha@21: if (logger.isDebugEnabled()) { sascha@21: logger.debug("Start service call to '" + service + "'"); sascha@21: } ingo@5: sascha@21: DocumentResponseHandler handler = new DocumentResponseHandler(); sascha@21: sascha@21: try { sascha@21: String serverUrl = url + PATH_SERVICE + "/" + service; sascha@21: return (Document) handler.handle(doPost(serverUrl, input)); sascha@21: } sascha@21: catch (IOException ioe) { sascha@21: throw new ConnectionException( sascha@21: "Connection to server failed: " + ioe.getMessage()); sascha@21: } 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: