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.restlet;
018    
019    import java.net.URI;
020    import java.util.HashMap;
021    import java.util.Map;
022    
023    import org.apache.camel.Endpoint;
024    import org.apache.camel.Exchange;
025    import org.apache.camel.HeaderFilterStrategyAware;
026    import org.apache.camel.impl.DefaultComponent;
027    import org.apache.camel.spi.HeaderFilterStrategy;
028    import org.apache.camel.util.CamelContextHelper;
029    import org.apache.camel.util.URISupport;
030    import org.apache.camel.util.UnsafeUriCharactersEncoder;
031    import org.apache.commons.logging.Log;
032    import org.apache.commons.logging.LogFactory;
033    import org.restlet.Component;
034    import org.restlet.Guard;
035    import org.restlet.Restlet;
036    import org.restlet.Server;
037    import org.restlet.data.ChallengeScheme;
038    import org.restlet.data.Method;
039    import org.restlet.data.Protocol;
040    
041    /**
042     * A Camel component embedded Restlet that produces and consumes exchanges.
043     *
044     * @version $Revision: 743835 $
045     */
046    public class RestletComponent extends DefaultComponent<Exchange> implements HeaderFilterStrategyAware {
047        private static final Log LOG = LogFactory.getLog(RestletComponent.class);
048    
049        private final Map<String, Server> servers = new HashMap<String, Server>();
050        private final Map<String, MethodBasedRouter> routers = new HashMap<String, MethodBasedRouter>();
051        private final Component component = new Component();
052        private HeaderFilterStrategy headerFilterStrategy = new RestletHeaderFilterStrategy();
053    
054        @Override
055        @SuppressWarnings("unchecked")
056        protected Endpoint createEndpoint(String uri, String remaining, Map parameters) throws Exception {
057            
058            RestletBinding restletBinding = null;
059            // lookup binding in registry if provided
060            String ref = getAndRemoveParameter(parameters, "restletBindingRef", String.class);
061            if (ref != null) {
062                restletBinding = CamelContextHelper.mandatoryLookup(getCamelContext(), ref, RestletBinding.class);
063            }
064            
065            if (restletBinding == null) {
066                restletBinding = new DefaultRestletBinding();
067            }
068            
069            if (restletBinding instanceof HeaderFilterStrategyAware) {
070                ((HeaderFilterStrategyAware)restletBinding).setHeaderFilterStrategy(headerFilterStrategy);
071            }
072            
073            Map<String, String> realm = null;
074            ref = getAndRemoveParameter(parameters, "restletRealmRef", String.class);
075            if (ref != null) {
076                realm = CamelContextHelper.mandatoryLookup(getCamelContext(), ref, Map.class);
077            }
078            
079            Method method = getAndRemoveParameter(parameters, "restletMethod", Method.class);
080    
081            // construct URI so we can use it to get the splitted information
082            URI u = new URI(UnsafeUriCharactersEncoder.encode(remaining));
083            String protocol = u.getScheme();
084    
085            String uriPattern = u.getPath();
086            if (parameters.size() > 0) {
087                uriPattern = uriPattern + "?" + URISupport.createQueryString(parameters);
088            }
089    
090            int port = 0;
091            String host = u.getHost();
092            if (u.getPort() > 0) {
093                port = u.getPort();
094            }
095    
096            RestletEndpoint result = new RestletEndpoint(this, remaining, restletBinding);
097            result.setProtocol(protocol);
098            result.setUriPattern(uriPattern);
099            result.setHost(host);
100            if (port > 0) {
101                result.setPort(port);
102            }
103            if (method != null) {
104                result.setRestletMethod(method);
105            }
106            if (realm != null) {
107                result.setRealm(realm);
108            }
109    
110            return result;
111        }
112        
113        @Override
114        protected void doStart() throws Exception {
115            super.doStart();
116            component.start();
117        }
118        
119        @Override
120        protected void doStop() throws Exception {
121            component.stop();
122            // just clear maps, component will stop the servers and routes
123            servers.clear();
124            routers.clear();
125            super.doStop();
126        }
127        
128        public HeaderFilterStrategy getHeaderFilterStrategy() {
129            return headerFilterStrategy;
130        }
131    
132        public void setHeaderFilterStrategy(HeaderFilterStrategy strategy) {
133            this.headerFilterStrategy = strategy;
134        }
135    
136        public void connect(RestletConsumer consumer) throws Exception {
137            RestletEndpoint endpoint = (RestletEndpoint)consumer.getEndpoint();
138            addServerIfNeccessary(endpoint);
139            MethodBasedRouter router = getMethodRouter(endpoint.getUriPattern());
140            
141            Map<String, String> realm = endpoint.getRealm();
142            Restlet target = consumer.getRestlet();
143            if (realm != null && realm.size() > 0) {
144                Guard guard = new Guard(component.getContext().createChildContext(), 
145                        ChallengeScheme.HTTP_BASIC, "Camel-Restlet Endpoint Realm");
146                for (Map.Entry<String, String> entry : realm.entrySet()) {
147                    guard.getSecrets().put(entry.getKey(), entry.getValue().toCharArray());
148                }
149                guard.setNext(target);
150                target = guard;
151                if (LOG.isDebugEnabled()) {
152                    LOG.debug("Target has been set to guard: " + guard);
153                }
154            }
155            
156            router.addRoute(endpoint.getRestletMethod(), target);
157            
158            if (!router.hasBeenAttached()) {
159                component.getDefaultHost().attach(endpoint.getUriPattern(), router);
160                if (LOG.isDebugEnabled()) {
161                    LOG.debug("Attached methodRouter uriPattern: " + endpoint.getUriPattern());
162                }
163            }
164    
165            if (LOG.isDebugEnabled()) {
166                LOG.debug("Attached restlet uriPattern: " + endpoint.getUriPattern() + " method: " + endpoint.getRestletMethod());
167            }
168        }
169    
170        public void disconnect(RestletConsumer consumer) throws Exception {
171            RestletEndpoint endpoint = (RestletEndpoint)consumer.getEndpoint();
172            MethodBasedRouter router = getMethodRouter(endpoint.getUriPattern());
173            router.removeRoute(endpoint.getRestletMethod());
174    
175            if (LOG.isDebugEnabled()) {
176                LOG.debug("Detached restlet uriPattern: " + endpoint.getUriPattern() + " method: " + endpoint.getRestletMethod());
177            }
178        }    
179        
180        private MethodBasedRouter getMethodRouter(String uriPattern) {
181            synchronized (routers) {
182                MethodBasedRouter result = routers.get(uriPattern);
183                if (result == null) {
184                    result = new MethodBasedRouter(uriPattern);
185                    if (LOG.isDebugEnabled()) {
186                        LOG.debug("Added method based router: " + result);
187                    }
188                    routers.put(uriPattern, result);
189                }
190                return result;
191            }    
192        }
193        
194        private void addServerIfNeccessary(RestletEndpoint endpoint) throws Exception {
195            String key = buildKey(endpoint);
196            Server server;
197            synchronized (servers) {
198                server = servers.get(key);
199                if (server == null) {
200                    server = component.getServers().add(Protocol.valueOf(endpoint.getProtocol()), endpoint.getPort());
201                    servers.put(key, server);
202                    if (LOG.isDebugEnabled()) {
203                        LOG.debug("Added server: " + key);
204                    }
205                    server.start();
206                }
207            }
208        }
209        
210        private static String buildKey(RestletEndpoint endpoint) {
211            return endpoint.getHost() + ":" + endpoint.getPort();
212        }
213        
214    }
215