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     */
019    package org.apache.isis.viewer.restfulobjects.applib;
020    
021    import java.util.Map;
022    
023    import javax.ws.rs.core.MediaType;
024    import javax.ws.rs.core.MultivaluedMap;
025    import javax.ws.rs.core.UriBuilder;
026    
027    import org.jboss.resteasy.client.ClientExecutor;
028    import org.jboss.resteasy.client.ClientRequest;
029    import org.jboss.resteasy.specimpl.UriBuilderImpl;
030    
031    import org.apache.isis.viewer.restfulobjects.applib.RestfulRequest.RequestParameter;
032    import org.apache.isis.viewer.restfulobjects.applib.links.LinkRepresentation;
033    import org.apache.isis.viewer.restfulobjects.applib.util.UrlEncodingUtils;
034    
035    /**
036     * Configures the body, query string etc of a {@link ClientRequest}.
037     * 
038     * <p>
039     * Needed because, unfortunately, {@link ClientRequest} does not seem to allow
040     * the query string to be set directly (only
041     * {@link ClientRequest#getQueryParameters() query parameters}). Instead, it is
042     * necessary to {@link UriBuilderImpl#replaceQuery(String) use} its underlying
043     * {@link UriBuilderImpl}.
044     */
045    public class ClientRequestConfigurer {
046    
047        public static ClientRequestConfigurer create(final ClientExecutor executor, final String uriTemplate) {
048            final UriBuilder uriBuilder = new UriBuilderImpl().uriTemplate(uriTemplate);
049            final ClientRequest clientRequest = executor.createRequest(uriBuilder);
050            return new ClientRequestConfigurer(clientRequest, uriBuilder);
051        }
052    
053        private final ClientRequest clientRequest;
054        private final UriBuilder uriBuilder;
055    
056        ClientRequestConfigurer(final ClientRequest clientRequest, final UriBuilder uriBuilder) {
057            this.clientRequest = clientRequest;
058            this.uriBuilder = uriBuilder;
059        }
060    
061        public ClientRequestConfigurer accept(final MediaType mediaType) {
062            clientRequest.accept(mediaType);
063            return this;
064        }
065    
066        public ClientRequestConfigurer header(final String name, final String value) {
067            clientRequest.header(name, value);
068            return this;
069        }
070    
071        /**
072         * Prerequisite to {@link #configureArgs(JsonRepresentation)} or
073         * {@link #configureArgs(Map)}.
074         */
075        public ClientRequestConfigurer setHttpMethod(final HttpMethod httpMethod) {
076            clientRequest.setHttpMethod(httpMethod.getJavaxRsMethod());
077            return this;
078        }
079    
080        /**
081         * Used when creating a request with arguments to execute.
082         * 
083         * <p>
084         * Typical flow is:
085         * <ul>
086         * <li> {@link RestfulClient#createRequest(HttpMethod, String)}
087         * <li> {@link RestfulRequest#withArg(RequestParameter, Object)} for each arg
088         * <li> {@link RestfulRequest#execute()} - which calls this method.
089         * </ul>
090         */
091        public ClientRequestConfigurer configureArgs(final Map<RequestParameter<?>, Object> args) {
092            if (clientRequest.getHttpMethod() == null) {
093                throw new IllegalStateException("Must set up http method first");
094            }
095    
096            final JsonRepresentation argsAsMap = JsonRepresentation.newMap();
097            for (final RequestParameter<?> requestParam : args.keySet()) {
098                put(args, requestParam, argsAsMap);
099            }
100            getHttpMethod().setUpArgs(this, argsAsMap);
101            return this;
102        }
103    
104        private <P> void put(final Map<RequestParameter<?>, Object> args, final RequestParameter<P> requestParam, final JsonRepresentation argsAsMap) {
105            @SuppressWarnings("unchecked")
106            final P value = (P) args.get(requestParam);
107            final String valueStr = requestParam.getParser().asString(value);
108            argsAsMap.mapPut(requestParam.getName(), valueStr);
109        }
110    
111        /**
112         * Used when following links (
113         * {@link RestfulClient#follow(LinkRepresentation)}).
114         */
115        public ClientRequestConfigurer configureArgs(final JsonRepresentation requestArgs) {
116            if (clientRequest.getHttpMethod() == null) {
117                throw new IllegalStateException("Must set up http method first");
118            }
119    
120            getHttpMethod().setUpArgs(this, requestArgs);
121            return this;
122        }
123    
124        /**
125         * Called back from
126         * {@link HttpMethod#setUpArgs(ClientRequestConfigurer, JsonRepresentation)}
127         */
128        ClientRequestConfigurer body(final JsonRepresentation requestArgs) {
129            clientRequest.body(MediaType.APPLICATION_JSON_TYPE, requestArgs.toString());
130            return this;
131        }
132    
133        /**
134         * Called back from
135         * {@link HttpMethod#setUpArgs(ClientRequestConfigurer, JsonRepresentation)}
136         */
137        ClientRequestConfigurer queryString(final JsonRepresentation requestArgs) {
138            if (requestArgs.size() == 0) {
139                return this;
140            }
141            final String queryString = UrlEncodingUtils.urlEncode(requestArgs.toString());
142            uriBuilder.replaceQuery(queryString);
143            return this;
144        }
145    
146        /**
147         * Called back from
148         * {@link HttpMethod#setUpArgs(ClientRequestConfigurer, JsonRepresentation)}
149         */
150        ClientRequestConfigurer queryArgs(final JsonRepresentation requestArgs) {
151            final MultivaluedMap<String, String> queryParameters = clientRequest.getQueryParameters();
152            for (final Map.Entry<String, JsonRepresentation> entry : requestArgs.mapIterable()) {
153                final String param = entry.getKey();
154                final JsonRepresentation argRepr = entry.getValue();
155                final String arg = UrlEncodingUtils.urlEncode(argRepr.asArg());
156                queryParameters.add(param, arg);
157            }
158            return this;
159        }
160    
161        /**
162         * For testing.
163         */
164        ClientRequest getClientRequest() {
165            return clientRequest;
166        }
167    
168        HttpMethod getHttpMethod() {
169            final String httpMethod = clientRequest.getHttpMethod();
170            return HttpMethod.valueOf(httpMethod);
171        }
172    
173    }