001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019package org.apache.isis.viewer.restfulobjects.applib.client;
020
021import java.util.Map;
022
023import javax.ws.rs.core.MediaType;
024import javax.ws.rs.core.MultivaluedMap;
025import javax.ws.rs.core.UriBuilder;
026
027import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
028import org.apache.isis.viewer.restfulobjects.applib.LinkRepresentation;
029import org.apache.isis.viewer.restfulobjects.applib.RestfulHttpMethod;
030import org.apache.isis.viewer.restfulobjects.applib.client.RestfulRequest.RequestParameter;
031import org.apache.isis.viewer.restfulobjects.applib.util.UrlEncodingUtils;
032import org.jboss.resteasy.client.ClientExecutor;
033import org.jboss.resteasy.client.ClientRequest;
034import org.jboss.resteasy.specimpl.UriBuilderImpl;
035
036/**
037 * Configures the body, query string etc of a {@link ClientRequest}.
038 * 
039 * <p>
040 * Needed because, unfortunately, {@link ClientRequest} does not seem to allow
041 * the query string to be set directly (only
042 * {@link ClientRequest#getQueryParameters() query parameters}). Instead, it is
043 * necessary to {@link UriBuilderImpl#replaceQuery(String) use} its underlying
044 * {@link UriBuilderImpl}.
045 */
046public class ClientRequestConfigurer {
047
048    public static ClientRequestConfigurer create(final ClientExecutor executor, final String uriTemplate) {
049        final UriBuilder uriBuilder = new UriBuilderImpl().uriTemplate(uriTemplate);
050        final ClientRequest clientRequest = executor.createRequest(uriBuilder);
051        return new ClientRequestConfigurer(clientRequest, uriBuilder);
052    }
053
054    private final ClientRequest clientRequest;
055    private final UriBuilder uriBuilder;
056
057    ClientRequestConfigurer(final ClientRequest clientRequest, final UriBuilder uriBuilder) {
058        this.clientRequest = clientRequest;
059        this.uriBuilder = uriBuilder;
060    }
061
062    public ClientRequestConfigurer accept(final MediaType mediaType) {
063        clientRequest.accept(mediaType);
064        return this;
065    }
066
067    public ClientRequestConfigurer header(final String name, final String value) {
068        clientRequest.header(name, value);
069        return this;
070    }
071
072    /**
073     * Prerequisite to {@link #configureArgs(JsonRepresentation)} or
074     * {@link #configureArgs(Map)}.
075     */
076    public ClientRequestConfigurer setHttpMethod(final RestfulHttpMethod httpMethod) {
077        clientRequest.setHttpMethod(httpMethod.getJavaxRsMethod());
078        return this;
079    }
080
081    /**
082     * Used when creating a request with arguments to execute.
083     * 
084     * <p>
085     * Typical flow is:
086     * <ul>
087     * <li> {@link RestfulClient#createRequest(RestfulHttpMethod, String)}
088     * <li> {@link RestfulRequest#withArg(RequestParameter, Object)} for each arg
089     * <li> {@link RestfulRequest#execute()} - which calls this method.
090     * </ul>
091     */
092    public ClientRequestConfigurer configureArgs(final Map<RequestParameter<?>, Object> args) {
093        if (clientRequest.getHttpMethod() == null) {
094            throw new IllegalStateException("Must set up http method first");
095        }
096
097        final JsonRepresentation argsAsMap = JsonRepresentation.newMap();
098        for (final RequestParameter<?> requestParam : args.keySet()) {
099            put(args, requestParam, argsAsMap);
100        }
101        getHttpMethod().setUpArgs(this, argsAsMap);
102        return this;
103    }
104
105    private <P> void put(final Map<RequestParameter<?>, Object> args, final RequestParameter<P> requestParam, final JsonRepresentation argsAsMap) {
106        @SuppressWarnings("unchecked")
107        final P value = (P) args.get(requestParam);
108        final String valueStr = requestParam.getParser().asString(value);
109        argsAsMap.mapPut(requestParam.getName(), valueStr);
110    }
111
112    /**
113     * Used when following links (
114     * {@link RestfulClient#follow(LinkRepresentation)}).
115     */
116    public ClientRequestConfigurer configureArgs(final JsonRepresentation requestArgs) {
117        if (clientRequest.getHttpMethod() == null) {
118            throw new IllegalStateException("Must set up http method first");
119        }
120
121        getHttpMethod().setUpArgs(this, requestArgs);
122        return this;
123    }
124
125    /**
126     * Called back from
127     * {@link RestfulHttpMethod#setUpArgs(ClientRequestConfigurer, JsonRepresentation)}
128     */
129    public ClientRequestConfigurer body(final JsonRepresentation requestArgs) {
130        clientRequest.body(MediaType.APPLICATION_JSON_TYPE, requestArgs.toString());
131        return this;
132    }
133
134    /**
135     * Called back from
136     * {@link RestfulHttpMethod#setUpArgs(ClientRequestConfigurer, JsonRepresentation)}
137     */
138    public ClientRequestConfigurer queryString(final JsonRepresentation requestArgs) {
139        if (requestArgs.size() == 0) {
140            return this;
141        }
142        final String queryString = UrlEncodingUtils.urlEncode(requestArgs.toString());
143        uriBuilder.replaceQuery(queryString);
144        return this;
145    }
146
147    /**
148     * Called back from
149     * {@link RestfulHttpMethod#setUpArgs(ClientRequestConfigurer, JsonRepresentation)}
150     */
151    public ClientRequestConfigurer queryArgs(final JsonRepresentation requestArgs) {
152        final MultivaluedMap<String, String> queryParameters = clientRequest.getQueryParameters();
153        for (final Map.Entry<String, JsonRepresentation> entry : requestArgs.mapIterable()) {
154            final String param = entry.getKey();
155            final JsonRepresentation argRepr = entry.getValue();
156            final String arg = UrlEncodingUtils.urlEncode(argRepr.asArg());
157            queryParameters.add(param, arg);
158        }
159        return this;
160    }
161
162    /**
163     * For testing.
164     */
165    ClientRequest getClientRequest() {
166        return clientRequest;
167    }
168
169    RestfulHttpMethod getHttpMethod() {
170        final String httpMethod = clientRequest.getHttpMethod();
171        return RestfulHttpMethod.valueOf(httpMethod);
172    }
173
174}