org.wrml.core.www.WebClient.java Source code

Java tutorial

Introduction

Here is the source code for org.wrml.core.www.WebClient.java

Source

/**
 * Copyright (C) 2011 WRML.org <mark@wrml.org>
 *
 * Licensed 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.wrml.core.www;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.Map;
import java.util.Set;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;

import org.wrml.core.Model;
import org.wrml.core.model.format.Format;
import org.wrml.core.runtime.Context;
import org.wrml.core.service.ServiceMap;
import org.wrml.core.transformer.PassthroughTransformer;
import org.wrml.core.transformer.Transformer;
import org.wrml.core.transformer.Transformers;
import org.wrml.core.www.http.AcceptHeader;
import org.wrml.core.www.http.Body;
import org.wrml.core.www.http.DefaultFormatter;
import org.wrml.core.www.http.Entity;
import org.wrml.core.www.http.Formatter;
import org.wrml.core.www.http.Headers;
import org.wrml.core.www.http.HttpVersion;
import org.wrml.core.www.http.Message;
import org.wrml.core.www.http.MessageType;
import org.wrml.core.www.http.Method;
import org.wrml.core.www.http.RequestLine;
import org.wrml.core.www.http.StartLine;
import org.wrml.core.www.http.Status;
import org.wrml.core.www.http.StatusLine;

/*
 * Implements the service interface with an HTTP client.
 */
public class WebClient extends ServiceMap {

    private final HttpClient _HttpClient;
    private MediaType _DefaultFormatMediaType;
    private final Formatter _DefaultFormatter;

    public final static PassthroughTransformer<URI> ID_TRANSFORMER = new PassthroughTransformer<URI>();

    public WebClient(Context context) {
        super(context);

        final ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager();

        // TODO: Make this configurable
        connectionManager.setMaxTotal(100);

        _HttpClient = new DefaultHttpClient(connectionManager);

        // TODO: Make this configurable
        _DefaultFormatter = new DefaultFormatter();
    }

    @Override
    public void clear() {

        /*
         * TODO: Design question - Does clear make any sense for a REST API? If
         * not, should we throw an exception?
         */

        super.clear();
    }

    @Override
    public boolean containsKey(Object key) {

        /*
         * TODO: Design proposal - A HEAD request and check for 200 success?
         */

        return super.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {

        /*
         * TODO: Design proposal - A HEAD request and check for 200 success?
         * Using the Model's (Document's) id?
         */

        return super.containsValue(value);
    }

    public Object create(URI collectionId, Object requestEntity, MediaType responseType, Model referrer) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Set<java.util.Map.Entry<URI, Object>> entrySet() {

        /*
         * TODO: Not really possible without a URI. Should we throw an
         * exception?
         */

        return super.entrySet();
    }

    /**
     * HTTP GET
     */
    public Object get(URI resourceId, Object cachedEntity, MediaType responseType, Model referrer) {

        System.out.println("WebClient: WWW GET request for: " + resourceId + " as: " + responseType);

        final Context context = getContext();

        // Create the request message
        final Message requestMessage = createRequestMessage(resourceId, cachedEntity, responseType, referrer,
                Method.GET);

        // Send the request message to get the response message
        final Message responseMessage = sendRequestMessage(requestMessage);

        final MediaType actualResponseType = responseMessage.getEntity().getHeaders().getContentType();

        final Entity responseEntity = responseMessage.getEntity();
        final Body responseBody = responseEntity.getBody();
        final InputStream responseInputStream = responseBody.getInputStream();

        if (actualResponseType.isWrml()) {

            // Default the format if need be
            final MediaType formatMediaType = (actualResponseType != null) ? actualResponseType
                    : getDefaultFormatMediaType();
            final Formatter formatter = getFormatter(formatMediaType);

            Model model = null;
            try {
                model = formatter.read(context, requestMessage, responseMessage);
            } catch (final Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            return model;
        } else {

            // TODO: Handle this better? (to allow for reading JSON and passing it through)

            // TODO: Allow for caching of responses?

            // If responseType is *NOT* a WRML type, then return the response message body's InputStream.     
            return responseInputStream;
        }

    }

    public MediaType getDefaultFormatMediaType() {
        return _DefaultFormatMediaType;
    }

    public Format getFormat(MediaType mediaType) {
        return getContext().getSystemTransformers().getMediaTypeToFormatTransformer().aToB(mediaType);
    }

    public Formatter getFormatter(MediaType mediaType) {

        // Get the Format associated with the MediaType
        //Format format = getFormat(mediaType);

        // TODO: Get all formatter Code-On-Demand

        // TODO: Find the right formatter for Java

        // TODO: Download and return the formatter

        return _DefaultFormatter;
    }

    public Transformer<URI, URI> getIdTransformer() {
        return ID_TRANSFORMER;
    }

    @Override
    public boolean isEmpty() {

        /*
         * TODO: Not really possible without a URI. Should we throw an
         * exception?
         */

        return super.isEmpty();
    }

    @Override
    public Set<URI> keySet() {

        /*
         * TODO: Not really possible without a URI. Should we throw an
         * exception?
         */

        return super.keySet();
    }

    public Object put(URI resourceId, Object requestEntity, MediaType responseType, Model referrer) {
        /*
         * TODO: HTTP PUT
         */

        return null;
    }

    @Override
    public void putAll(Map<? extends URI, ? extends Object> map) {

        /*
         * TODO: HTTP PUT (batch/transactional?)
         */

        super.putAll(map);
    }

    public Object remove(URI resourceId, MediaType responseType, Model referrer) {
        /*
         * TODO: HTTP DELETE
         */
        return null;
    }

    public void setDefaultFormatMediaType(MediaType defaultFormatMediaType) {
        _DefaultFormatMediaType = defaultFormatMediaType;
    }

    @Override
    public int size() {
        /*
         * TODO: Not really possible without a URI. Should we throw an
         * exception?
         */
        return super.size();
    }

    @Override
    public java.util.Collection<Object> values() {
        /*
         * TODO: Not really possible without a URI. Should we throw an
         * exception?
         */
        return super.values();
    }

    @Override
    protected void finalize() throws Throwable {

        // When HttpClient instance is no longer needed,
        // shut down the connection manager to ensure
        // immediate deallocation of all system resources
        _HttpClient.getConnectionManager().shutdown();

        super.finalize();
    }

    private Message createMessage(final MessageType messageType, final StartLine startLine) {

        // Create the message model
        final Message message = new DefaultMessage();

        // HTTP-message   = Request | Response
        message.setType(messageType);

        // start-line     = Request-Line | Status-Line
        message.setStartLine(startLine);

        // Create the entity with headers and a body
        final Entity entity = new DefaultEntity();
        message.setEntity(entity);

        final Headers headers = new DefaultHeaders();
        entity.setHeaders(headers);

        final Body body = new DefaultBody();
        entity.setBody(body);

        return message;
    }

    private Message createRequestMessage(final URI resourceId, final Object cachedEntity,
            final MediaType responseType, final Model referrer, final Method method) {

        final RequestLine requestLine = new DefaultRequestLine();
        // e.g. GET /pub/WWW/TheProject.html HTTP/1.1
        requestLine.setMethod(method);
        requestLine.setUri(resourceId);
        requestLine.setHttpVersion(HttpVersion.HTTP_1_1);

        // Create the request message
        final Message requestMessage = createMessage(MessageType.REQUEST, requestLine);

        return requestMessage;
    }

    private Message sendRequestMessage(Message requestMessage) {

        HttpUriRequest request = null;

        final RequestLine requestLine = (RequestLine) requestMessage.getStartLine();
        final Method method = requestLine.getMethod();
        final URI uri = requestLine.getUri();

        switch (method) {

        case GET:
            request = new HttpGet(uri);
            break;

        case HEAD:
            request = new HttpHead(uri);
            break;

        case POST:
            request = new HttpPost(uri);
            break;

        case PUT:
            request = new HttpPut(uri);
            break;

        case DELETE:
            request = new HttpDelete(uri);
            break;

        case OPTIONS:
            request = new HttpOptions(uri);
            break;

        case TRACE:
            request = new HttpTrace(uri);
            break;

        case CONNECT:
        default:
            break;
        }

        InputStream inputStream = null;

        MediaType responseMediaType = null;
        try {

            final HttpResponse response = _HttpClient.execute(request);
            final HttpEntity entity = response.getEntity();
            final Transformers<String> stringTransformers = getContext().getStringTransformers();
            responseMediaType = stringTransformers.getTransformer(MediaType.class)
                    .bToA(entity.getContentType().getValue());

            if (entity != null) {
                inputStream = entity.getContent();
            }
        } catch (final ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (final IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        final StatusLine statusLine = new DefaultStatusLine();

        // TODO: Implement transformers for the string values to enums
        //statusLine.setHTTPVersion(httpVersion);
        //statusLine.setStatus(status);

        // Create the response message
        final Message responseMessage = createMessage(MessageType.RESPONSE, statusLine);
        final Entity responseEntity = responseMessage.getEntity();

        responseEntity.getHeaders().setContentType(responseMediaType);
        responseEntity.getBody().setInputStream(inputStream);

        return responseMessage;
    }
}

abstract class AbstractStartLine implements StartLine {

    private HttpVersion _HttpVersion;

    public HttpVersion getHttpVersion() {
        return _HttpVersion;
    }

    public void setHttpVersion(HttpVersion httpVersion) {
        _HttpVersion = httpVersion;
    }

}

class DefaultAcceptHeader implements AcceptHeader {

}

class DefaultBody implements Body {

    private InputStream _InputStream;
    private OutputStream _OutputStream;

    public InputStream getInputStream() {
        return _InputStream;
    }

    public OutputStream getOutputStream() {
        return _OutputStream;
    }

    public void setInputStream(InputStream inputStream) {
        _InputStream = inputStream;
    }

    public void setOutputStream(OutputStream outputStream) {
        _OutputStream = outputStream;
    }

}

class DefaultEntity implements Entity {

    private Body _Body;
    private Headers _Headers;

    public Body getBody() {
        return _Body;
    }

    public Headers getHeaders() {
        return _Headers;
    }

    public void setBody(Body body) {
        _Body = body;
    }

    public void setHeaders(Headers headers) {
        _Headers = headers;
    }

}

class DefaultHeaders implements Headers {

    private AcceptHeader _AcceptHeader;
    private MediaType _ContentType;

    public AcceptHeader getAcceptHeader() {
        return _AcceptHeader;
    }

    public MediaType getContentType() {
        return _ContentType;
    }

    public void setAcceptHeader(AcceptHeader acceptHeader) {
        _AcceptHeader = acceptHeader;
    }

    public void setContentType(MediaType contentType) {
        _ContentType = contentType;
    }

}

class DefaultMessage implements Message {

    private Entity _Entity;
    private StartLine _StartLine;
    private MessageType _Type;

    public Entity getEntity() {
        return _Entity;
    }

    public StartLine getStartLine() {
        return _StartLine;
    }

    public MessageType getType() {
        return _Type;
    }

    public void setEntity(Entity entity) {
        _Entity = entity;
    }

    public void setStartLine(StartLine startLine) {
        _StartLine = startLine;
    }

    public void setType(MessageType type) {
        _Type = type;
    }

}

class DefaultRequestLine extends AbstractStartLine implements RequestLine {

    private Method _Method;
    private URI _RequestUri;

    public Method getMethod() {
        return _Method;
    }

    public URI getUri() {
        return _RequestUri;
    }

    public void setMethod(Method method) {
        _Method = method;
    }

    public void setUri(URI requestUri) {
        _RequestUri = requestUri;
    }

}

class DefaultStatusLine extends AbstractStartLine implements StatusLine {

    private Status _Status;

    public Status getStatus() {
        return _Status;
    }

    public void setStatus(Status status) {
        _Status = status;
    }

}