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