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.impl;
018    
019    import java.lang.reflect.ParameterizedType;
020    import java.lang.reflect.Type;
021    import java.util.Map;
022    import java.util.regex.Pattern;
023    
024    import org.apache.camel.CamelContext;
025    import org.apache.camel.CamelContextAware;
026    import org.apache.camel.Component;
027    import org.apache.camel.Endpoint;
028    import org.apache.camel.Exchange;
029    import org.apache.camel.ExchangePattern;
030    import org.apache.camel.PollingConsumer;
031    import org.apache.camel.spi.HasId;
032    import org.apache.camel.util.EndpointHelper;
033    import org.apache.camel.util.ObjectHelper;
034    
035    /**
036     * A default endpoint useful for implementation inheritance.
037     * <p/>
038     * Components which leverages <a href="http://camel.apache.org/asynchronous-routing-engine.html">asynchronous processing model</a>
039     * should check the {@link #isSynchronous()} to determine if asynchronous processing is allowed.
040     * The <tt>synchronous</tt> option on the endpoint allows Camel end users to dictate whether they want the asynchronous model or not.
041     * The option is default <tt>false</tt> which means asynchronous processing is allowed.
042     *
043     * @version $Revision: 21168 $
044     */
045    public abstract class DefaultEndpoint implements Endpoint, HasId, CamelContextAware {
046    
047        //Match any key-value pair in the URI query string whose key contains "passphrase" or "password" (case-insensitive).
048        //First capture group is the key, second is the value.
049        private static final Pattern SECRETS = Pattern.compile("([?&][^=]*(?:passphrase|password)[^=]*)=([^&]*)", Pattern.CASE_INSENSITIVE);
050    
051        private String endpointUri;
052        private CamelContext camelContext;
053        private Component component;
054        private ExchangePattern exchangePattern = ExchangePattern.InOnly;
055        // option to allow end user to dictate whether async processing should be used or not (if possible)
056        private boolean synchronous;
057        private final String id = EndpointHelper.createEndpointId();
058    
059        protected DefaultEndpoint(String endpointUri, Component component) {
060            this(endpointUri, component.getCamelContext());
061            this.component = component;
062        }
063    
064        protected DefaultEndpoint(String endpointUri, CamelContext camelContext) {
065            this(endpointUri);
066            this.camelContext = camelContext;
067        }
068    
069        protected DefaultEndpoint(String endpointUri) {
070            this.setEndpointUri(endpointUri);
071        }
072    
073        protected DefaultEndpoint() {
074            super();
075        }
076    
077        public int hashCode() {
078            return getEndpointUri().hashCode() * 37 + 1;
079        }
080    
081        @Override
082        public boolean equals(Object object) {
083            if (object instanceof DefaultEndpoint) {
084                DefaultEndpoint that = (DefaultEndpoint) object;
085                return ObjectHelper.equal(this.getEndpointUri(), that.getEndpointUri());
086            }
087            return false;
088        }
089    
090        @Override
091        public String toString() {
092            return String.format("Endpoint[%s]", sanitizeUri(getEndpointUri()));
093        }
094    
095        /**
096         * Returns a unique String ID which can be used for aliasing without having to use the whole URI which
097         * is not unique
098         */
099        public String getId() {
100            return id;
101        }
102    
103        public String getEndpointUri() {
104            if (endpointUri == null) {
105                endpointUri = createEndpointUri();
106                if (endpointUri == null) {
107                    throw new IllegalArgumentException("endpointUri is not specified and " + getClass().getName()
108                            + " does not implement createEndpointUri() to create a default value");
109                }
110            }
111            return endpointUri;
112        }
113    
114        public String getEndpointKey() {
115            if (isLenientProperties()) {
116                // only use the endpoint uri without parameters as the properties is lenient
117                String uri = getEndpointUri();
118                if (uri.indexOf('?') != -1) {
119                    return ObjectHelper.before(uri, "?");
120                } else {
121                    return uri;
122                }
123            } else {
124                // use the full endpoint uri
125                return getEndpointUri();
126            }
127        }
128    
129        public CamelContext getCamelContext() {
130            return camelContext;
131        }
132    
133        public Component getComponent() {
134            return component;
135        }
136    
137        public void setCamelContext(CamelContext camelContext) {
138            this.camelContext = camelContext;
139        }
140    
141        public PollingConsumer createPollingConsumer() throws Exception {
142            return new EventDrivenPollingConsumer(this);
143        }
144    
145        public Exchange createExchange(Exchange exchange) {
146            Class<Exchange> exchangeType = getExchangeType();
147            if (exchangeType != null) {
148                if (exchangeType.isInstance(exchange)) {
149                    return exchangeType.cast(exchange);
150                }
151            }
152            return exchange.copy();
153        }
154    
155        /**
156         * Returns the type of the exchange which is generated by this component
157         */
158        @SuppressWarnings("unchecked")
159        public Class<Exchange> getExchangeType() {
160            Type type = getClass().getGenericSuperclass();
161            if (type instanceof ParameterizedType) {
162                ParameterizedType parameterizedType = (ParameterizedType) type;
163                Type[] arguments = parameterizedType.getActualTypeArguments();
164                if (arguments.length > 0) {
165                    Type argumentType = arguments[0];
166                    if (argumentType instanceof Class) {
167                        return (Class<Exchange>) argumentType;
168                    }
169                }
170            }
171            return null;
172        }
173    
174        public Exchange createExchange() {
175            return createExchange(getExchangePattern());
176        }
177    
178        public Exchange createExchange(ExchangePattern pattern) {
179            return new DefaultExchange(this, pattern);
180        }
181    
182        public ExchangePattern getExchangePattern() {
183            return exchangePattern;
184        }
185    
186        public void setExchangePattern(ExchangePattern exchangePattern) {
187            this.exchangePattern = exchangePattern;
188        }
189    
190        public boolean isSynchronous() {
191            return synchronous;
192        }
193    
194        /**
195         * Sets whether synchronous processing should be strictly used, or Camel is allowed to use
196         * asynchronous processing (if supported).
197         *
198         * @param synchronous <tt>true</tt> to enforce synchronous processing
199         */
200        public void setSynchronous(boolean synchronous) {
201            this.synchronous = synchronous;
202        }
203    
204        public void configureProperties(Map<String, Object> options) {
205            // do nothing by default
206        }
207    
208        /**
209         * A factory method to lazily create the endpointUri if none is specified
210         */
211        protected String createEndpointUri() {
212            return null;
213        }
214    
215        /**
216         * Sets the endpointUri if it has not been specified yet via some kind of dependency injection mechanism.
217         * This allows dependency injection frameworks such as Spring or Guice to set the default endpoint URI in cases
218         * where it has not been explicitly configured using the name/context in which an Endpoint is created.
219         */
220        public void setEndpointUriIfNotSpecified(String value) {
221            if (endpointUri == null) {
222                setEndpointUri(value);
223            }
224        }
225    
226        protected void setEndpointUri(String endpointUri) {
227            this.endpointUri = endpointUri;
228        }
229    
230        public boolean isLenientProperties() {
231            // default should be false for most components
232            return false;
233        }
234    
235        public void start() throws Exception {
236            // noop
237        }
238    
239        public void stop() throws Exception {
240            // noop
241        }
242    
243        public static String sanitizeUri(String uri) {
244            return uri == null ? null : SECRETS.matcher(uri).replaceAll("$1=******");
245        }
246    
247    }