001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.component.gae.task;
018    
019    import javax.servlet.http.HttpServletRequest;
020    import javax.servlet.http.HttpServletResponse;
021    
022    import com.google.appengine.api.taskqueue.TaskOptions;
023    
024    import org.apache.camel.Exchange;
025    import org.apache.camel.Message;
026    import org.apache.camel.component.gae.bind.InboundBinding;
027    import org.apache.camel.component.gae.bind.OutboundBinding;
028    import org.apache.camel.component.http.DefaultHttpBinding;
029    import org.apache.camel.component.http.HttpMessage;
030    import org.apache.camel.spi.HeaderFilterStrategy;
031    
032    /**
033     * Binds the {@link TaskOptions} of the task queueing service to a Camel
034     * {@link Exchange} for outbound communication. For inbound communication a
035     * {@link HttpMessage} is bound to {@link Exchange}.
036     */
037    public class GTaskBinding implements 
038        OutboundBinding <GTaskEndpoint, TaskOptions, Void>,
039        InboundBinding  <GTaskEndpoint, HttpServletRequest, HttpServletResponse> { 
040    
041        /**
042         * Camel header name corresponding to <code>X-AppEngine-QueueName</code>
043         * header created by task queueing service.
044         */
045        public static final String GTASK_QUEUE_NAME = "CamelGtaskQueueName";
046    
047        /**
048         * Camel header name corresponding to <code>X-AppEngine-TaskName</code>
049         * header created by task queueing service.
050         */
051        public static final String GTASK_TASK_NAME = "CamelGtaskTaskName";
052        
053        /**
054         * Camel header name corresponding to <code>X-AppEngine-TaskRetryCount</code>
055         * header created by task queueing service.
056         */
057        public static final String GTASK_RETRY_COUNT = "CamelGtaskRetryCount";
058    
059        static final String GAE_QUEUE_NAME = "X-AppEngine-QueueName";
060        static final String GAE_TASK_NAME = "X-AppEngine-TaskName";
061        static final String GAE_RETRY_COUNT = "X-AppEngine-TaskRetryCount";
062        
063        // ----------------------------------------------------------------
064        //  Outbound binding
065        // ----------------------------------------------------------------
066        
067        /**
068         * Reads data from <code>exchange</code> and writes it to a newly created
069         * {@link TaskOptions} instance. The <code>request</code> parameter is
070         * ignored.
071         * 
072         * @param endpoint
073         * @param exchange
074         * @param request
075         *            ignored.
076         * @return a newly created {@link TaskOptions} instance containing data from
077         *         <code>exchange</code>.
078         */
079        public TaskOptions writeRequest(GTaskEndpoint endpoint, Exchange exchange, TaskOptions request) {
080            TaskOptions answer = TaskOptions.Builder.withUrl(getWorkerRoot(endpoint) + endpoint.getPath());
081            writeRequestHeaders(endpoint, exchange, answer);
082            writeRequestBody(endpoint, exchange, answer);
083            // TODO: consider TaskOptions method (POST, GET, ...)
084            return answer;
085        }
086        
087        /**
088         * @throws UnsupportedOperationException
089         */
090        public Exchange readResponse(GTaskEndpoint endpoint, Exchange exchange, Void response) {
091            throw new UnsupportedOperationException("gtask responses not supported");
092        }
093    
094        // ----------------------------------------------------------------
095        //  Inbound binding
096        // ----------------------------------------------------------------
097        
098        /**
099         * Replaces the task service-specific headers (<code>X-AppEngine-*</code>)
100         * with Camel-specific headers.
101         * 
102         * @see GTaskBinding#GTASK_QUEUE_NAME
103         * @see GTaskBinding#GTASK_TASK_NAME
104         * @see GTaskBinding#GTASK_RETRY_COUNT
105         * @see DefaultHttpBinding#readRequest(HttpServletRequest, HttpMessage)
106         */
107        public Exchange readRequest(GTaskEndpoint endpoint, Exchange exchange, HttpServletRequest request) {
108            readRequestHeaders(endpoint, exchange, request);
109            return exchange;
110        }
111    
112        public HttpServletResponse writeResponse(GTaskEndpoint endpoint, Exchange exchange, HttpServletResponse response) {
113            return response;
114        }
115    
116        // ----------------------------------------------------------------
117        //  Customization points
118        // ----------------------------------------------------------------
119        
120        protected void writeRequestHeaders(GTaskEndpoint endpoint, Exchange exchange, TaskOptions request) {
121            HeaderFilterStrategy strategy = endpoint.getHeaderFilterStrategy();
122            for (String headerName : exchange.getIn().getHeaders().keySet()) {
123                String headerValue = exchange.getIn().getHeader(headerName, String.class);
124                if (strategy != null && !strategy.applyFilterToCamelHeaders(headerName, headerValue, exchange)) {
125                    request.header(headerName, headerValue);
126                }
127            }
128        }
129    
130        protected void readRequestHeaders(GTaskEndpoint endpoint, Exchange exchange, HttpServletRequest request) {
131            Message message = exchange.getIn();
132            String key = GAE_QUEUE_NAME;
133            Object val = message.getHeader(key);
134            if (val != null) {
135                message.getHeaders().put(GTASK_QUEUE_NAME, val);
136                message.getHeaders().remove(key);
137            }
138            key = GAE_TASK_NAME;
139            val = message.getHeader(key);
140            if (val != null) {
141                message.getHeaders().put(GTASK_TASK_NAME, val);
142                message.getHeaders().remove(key);
143            }
144            key = GAE_RETRY_COUNT;
145            val = message.getHeader(key);
146            if (val != null) {
147                message.getHeaders().put(GTASK_RETRY_COUNT, Integer.parseInt(val.toString()));
148                message.getHeaders().remove(key);
149            }
150            // EXPERIMENTAL // TODO: resolve gzip encoding issues
151            exchange.getIn().removeHeader("Accept-Encoding");
152            exchange.getIn().removeHeader("Content-Encoding");
153        }
154        
155        protected void writeRequestBody(GTaskEndpoint endpoint, Exchange exchange, TaskOptions request) {
156            // TODO: allow message header or endpoint uri to configure character encoding and content type
157            request.payload(exchange.getIn().getBody(byte[].class), "application/octet-stream");
158        }
159        
160        protected String getWorkerRoot(GTaskEndpoint endpoint) {
161            return "/" + endpoint.getWorkerRoot();
162        }
163    
164    }