001package org.apache.commons.jcs.auxiliary.remote.http.client;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.IOException;
023import java.io.UnsupportedEncodingException;
024import java.net.URLEncoder;
025
026import org.apache.commons.httpclient.HttpException;
027import org.apache.commons.httpclient.HttpMethod;
028import org.apache.commons.httpclient.HttpState;
029import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
030import org.apache.commons.httpclient.methods.PostMethod;
031import org.apache.commons.httpclient.methods.RequestEntity;
032import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheDispatcher;
033import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheRequest;
034import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheResponse;
035import org.apache.commons.jcs.utils.serialization.StandardSerializer;
036import org.apache.commons.logging.Log;
037import org.apache.commons.logging.LogFactory;
038
039/** Calls the service. */
040public class RemoteHttpCacheDispatcher
041    extends AbstractHttpClient
042    implements IRemoteCacheDispatcher
043{
044    /** Parameter encoding */
045    private static final String DEFAULT_ENCODING = "UTF-8";
046
047    /** Named of the parameter */
048    private static final String PARAMETER_REQUEST_TYPE = "RequestType";
049
050    /** Named of the parameter */
051    private static final String PARAMETER_KEY = "Key";
052
053    /** Named of the parameter */
054    private static final String PARAMETER_CACHE_NAME = "CacheName";
055
056    /** The Logger. */
057    private static final Log log = LogFactory.getLog( RemoteHttpCacheDispatcher.class );
058
059    /** This needs to be standard, since the other side is standard */
060    private StandardSerializer serializer = new StandardSerializer();
061
062    /**
063     * @param remoteHttpCacheAttributes
064     */
065    public RemoteHttpCacheDispatcher( RemoteHttpCacheAttributes remoteHttpCacheAttributes )
066    {
067        super( remoteHttpCacheAttributes );
068    }
069
070    /**
071     * All requests will go through this method.
072     * <p>
073     * TODO consider taking in a URL instead of using the one in the configuration.
074     * <p>
075     * @param remoteCacheRequest
076     * @return RemoteCacheResponse
077     * @throws IOException
078     */
079    @Override
080    public <K, V, T>
081        RemoteCacheResponse<T> dispatchRequest( RemoteCacheRequest<K, V> remoteCacheRequest )
082        throws IOException
083    {
084        try
085        {
086            byte[] requestAsByteArray = serializer.serialize( remoteCacheRequest );
087
088            String url = addParameters( remoteCacheRequest, getRemoteHttpCacheAttributes().getUrl() );
089
090            byte[] responseAsByteArray = processRequest( requestAsByteArray, url );
091
092            RemoteCacheResponse<T> remoteCacheResponse = null;
093            try
094            {
095                remoteCacheResponse = serializer.deSerialize( responseAsByteArray, null );
096            }
097            catch ( ClassNotFoundException e )
098            {
099                log.error( "Couldn't deserialize the response.", e );
100            }
101            return remoteCacheResponse;
102        }
103        catch ( Exception e )
104        {
105            throw new IOException("Problem dispatching request.", e);
106        }
107    }
108
109    /**
110     * @param requestAsByteArray
111     * @param url
112     * @return byte[] - the response
113     * @throws IOException
114     * @throws HttpException
115     */
116    protected byte[] processRequest( byte[] requestAsByteArray, String url )
117        throws IOException, HttpException
118    {
119        PostMethod post = new PostMethod( url );
120        RequestEntity requestEntity = new ByteArrayRequestEntity( requestAsByteArray );
121        post.setRequestEntity( requestEntity );
122        doWebserviceCall( post );
123        byte[] response = post.getResponseBody();
124        return response;
125    }
126
127    /**
128     * @param remoteCacheRequest
129     * @param baseUrl
130     * @return String
131     */
132    protected <K, V> String addParameters( RemoteCacheRequest<K, V> remoteCacheRequest, String baseUrl )
133    {
134        StringBuilder url = new StringBuilder( baseUrl == null ? "" : baseUrl );
135
136        try
137        {
138            if ( baseUrl != null && baseUrl.indexOf( "?" ) == -1 )
139            {
140                url.append( "?" );
141            }
142            else
143            {
144                url.append( "&" );
145            }
146
147            if ( getRemoteHttpCacheAttributes().isIncludeCacheNameAsParameter() )
148            {
149                if ( remoteCacheRequest.getCacheName() != null )
150                {
151                    url.append( PARAMETER_CACHE_NAME + "="
152                        + URLEncoder.encode( remoteCacheRequest.getCacheName(), DEFAULT_ENCODING ) );
153                }
154            }
155            if ( getRemoteHttpCacheAttributes().isIncludeKeysAndPatternsAsParameter() )
156            {
157                String keyValue = "";
158                switch ( remoteCacheRequest.getRequestType() )
159                {
160                    case GET:
161                    case REMOVE:
162                    case GET_KEYSET:
163                        keyValue = remoteCacheRequest.getKey() + "";
164                        break;
165                    case GET_MATCHING:
166                        keyValue = remoteCacheRequest.getPattern();
167                        break;
168                    case GET_MULTIPLE:
169                        keyValue = remoteCacheRequest.getKeySet() + "";
170                        break;
171                    case UPDATE:
172                        keyValue = remoteCacheRequest.getCacheElement().getKey() + "";
173                        break;
174                    default:
175                        break;
176                }
177                String encodedKeyValue = URLEncoder.encode( keyValue, DEFAULT_ENCODING );
178                url.append( "&" + PARAMETER_KEY + "=" + encodedKeyValue );
179            }
180            if ( getRemoteHttpCacheAttributes().isIncludeRequestTypeasAsParameter() )
181            {
182                url.append( "&"
183                    + PARAMETER_REQUEST_TYPE
184                    + "="
185                    + URLEncoder.encode( remoteCacheRequest.getRequestType().toString(), DEFAULT_ENCODING ) );
186            }
187        }
188        catch ( UnsupportedEncodingException e )
189        {
190            log.error( "Couldn't encode URL.", e );
191        }
192
193        if ( log.isDebugEnabled() )
194        {
195            log.debug( "Url: " + url.toString() );
196        }
197
198        return url.toString();
199    }
200
201    /**
202     * Called before the executeMethod on the client.
203     * <p>
204     * @param post http method
205     * @return HttpState
206     * @throws IOException
207     */
208    @Override
209    protected HttpState preProcessWebserviceCall( HttpMethod post )
210        throws IOException
211    {
212        // do nothing. Child can override.
213        return null;
214    }
215
216    /**
217     * Called after the executeMethod on the client.
218     * <p>
219     * @param post http method
220     * @param httpState state
221     * @throws IOException
222     */
223    @Override
224    protected void postProcessWebserviceCall( HttpMethod post, HttpState httpState )
225        throws IOException
226    {
227        // do nothing. Child can override.
228    }
229}