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 */ 017package org.apache.camel.model; 018 019import java.util.List; 020 021import javax.xml.bind.annotation.XmlAccessType; 022import javax.xml.bind.annotation.XmlAccessorType; 023import javax.xml.bind.annotation.XmlAttribute; 024import javax.xml.bind.annotation.XmlRootElement; 025 026import org.apache.camel.CamelContext; 027import org.apache.camel.Endpoint; 028import org.apache.camel.Predicate; 029import org.apache.camel.Processor; 030import org.apache.camel.impl.InterceptSendToEndpoint; 031import org.apache.camel.processor.InterceptEndpointProcessor; 032import org.apache.camel.spi.AsPredicate; 033import org.apache.camel.spi.EndpointStrategy; 034import org.apache.camel.spi.Metadata; 035import org.apache.camel.spi.RouteContext; 036import org.apache.camel.util.EndpointHelper; 037import org.apache.camel.util.URISupport; 038 039/** 040 * Intercepts messages being sent to an endpoint 041 * 042 * @version 043 */ 044@Metadata(label = "configuration") 045@XmlRootElement(name = "interceptSendToEndpoint") 046@XmlAccessorType(XmlAccessType.FIELD) 047public class InterceptSendToEndpointDefinition extends OutputDefinition<InterceptSendToEndpointDefinition> { 048 049 // TODO: Support lookup endpoint by ref (requires a bit more work) 050 051 // TODO: interceptSendToEndpoint needs to proxy the endpoints at very first 052 // so when other processors uses an endpoint its already proxied, see workaround in SendProcessor 053 // needed when we haven't proxied beforehand. This requires some work in the route builder in Camel 054 // to implement so that should be a part of a bigger rework/improvement in the future 055 056 @XmlAttribute(required = true) 057 private String uri; 058 @XmlAttribute 059 private Boolean skipSendToOriginalEndpoint; 060 061 public InterceptSendToEndpointDefinition() { 062 } 063 064 public InterceptSendToEndpointDefinition(String uri) { 065 this.uri = uri; 066 } 067 068 @Override 069 public String toString() { 070 return "InterceptSendToEndpoint[" + uri + " -> " + getOutputs() + "]"; 071 } 072 073 @Override 074 public String getShortName() { 075 return "interceptSendToEndpoint"; 076 } 077 078 @Override 079 public String getLabel() { 080 return "interceptSendToEndpoint[" + uri + "]"; 081 } 082 083 @Override 084 public boolean isAbstract() { 085 return true; 086 } 087 088 @Override 089 public boolean isTopLevelOnly() { 090 return true; 091 } 092 093 @Override 094 public Processor createProcessor(final RouteContext routeContext) throws Exception { 095 // create the detour 096 final Processor detour = this.createChildProcessor(routeContext, true); 097 final String matchURI = getUri(); 098 099 // register endpoint callback so we can proxy the endpoint 100 routeContext.getCamelContext().addRegisterEndpointCallback(new EndpointStrategy() { 101 public Endpoint registerEndpoint(String uri, Endpoint endpoint) { 102 if (endpoint instanceof InterceptSendToEndpoint) { 103 // endpoint already decorated 104 return endpoint; 105 } else if (matchURI == null || matchPattern(routeContext.getCamelContext(), uri, matchURI)) { 106 // only proxy if the uri is matched decorate endpoint with our proxy 107 // should be false by default 108 boolean skip = getSkipSendToOriginalEndpoint() != null && getSkipSendToOriginalEndpoint(); 109 InterceptSendToEndpoint proxy = new InterceptSendToEndpoint(endpoint, skip); 110 proxy.setDetour(detour); 111 return proxy; 112 } else { 113 // no proxy so return regular endpoint 114 return endpoint; 115 } 116 } 117 }); 118 119 120 // remove the original intercepted route from the outputs as we do not intercept as the regular interceptor 121 // instead we use the proxy endpoints producer do the triggering. That is we trigger when someone sends 122 // an exchange to the endpoint, see InterceptSendToEndpoint for details. 123 RouteDefinition route = routeContext.getRoute(); 124 List<ProcessorDefinition<?>> outputs = route.getOutputs(); 125 outputs.remove(this); 126 127 return new InterceptEndpointProcessor(matchURI, detour); 128 } 129 130 /** 131 * Does the uri match the pattern. 132 * 133 * @param camelContext the CamelContext 134 * @param uri the uri 135 * @param pattern the pattern, which can be an endpoint uri as well 136 * @return <tt>true</tt> if matched and we should intercept, <tt>false</tt> if not matched, and not intercept. 137 */ 138 protected boolean matchPattern(CamelContext camelContext, String uri, String pattern) { 139 // match using the pattern as-is 140 boolean match = EndpointHelper.matchEndpoint(camelContext, uri, pattern); 141 if (!match) { 142 try { 143 // the pattern could be an uri, so we need to normalize it before matching again 144 pattern = URISupport.normalizeUri(pattern); 145 match = EndpointHelper.matchEndpoint(camelContext, uri, pattern); 146 } catch (Exception e) { 147 // ignore 148 } 149 } 150 return match; 151 } 152 153 /** 154 * Applies this interceptor only if the given predicate is true 155 * 156 * @param predicate the predicate 157 * @return the builder 158 */ 159 public InterceptSendToEndpointDefinition when(@AsPredicate Predicate predicate) { 160 WhenDefinition when = new WhenDefinition(predicate); 161 addOutput(when); 162 return this; 163 } 164 165 /** 166 * Skip sending the {@link org.apache.camel.Exchange} to the original intended endpoint 167 * 168 * @return the builder 169 */ 170 public InterceptSendToEndpointDefinition skipSendToOriginalEndpoint() { 171 setSkipSendToOriginalEndpoint(Boolean.TRUE); 172 return this; 173 } 174 175 /** 176 * This method is <b>only</b> for handling some post configuration 177 * that is needed since this is an interceptor, and we have to do 178 * a bit of magic logic to fixup to handle predicates 179 * with or without proceed/stop set as well. 180 */ 181 public void afterPropertiesSet() { 182 // okay the intercept endpoint works a bit differently than the regular interceptors 183 // so we must fix the route definition yet again 184 185 if (getOutputs().size() == 0) { 186 // no outputs 187 return; 188 } 189 190 // if there is a when definition at first, then its a predicate for this interceptor 191 ProcessorDefinition<?> first = getOutputs().get(0); 192 if (first instanceof WhenDefinition && !(first instanceof WhenSkipSendToEndpointDefinition)) { 193 WhenDefinition when = (WhenDefinition) first; 194 195 // create a copy of when to use as replacement 196 WhenSkipSendToEndpointDefinition newWhen = new WhenSkipSendToEndpointDefinition(); 197 newWhen.setExpression(when.getExpression()); 198 newWhen.setId(when.getId()); 199 newWhen.setInheritErrorHandler(when.isInheritErrorHandler()); 200 newWhen.setParent(when.getParent()); 201 newWhen.setOtherAttributes(when.getOtherAttributes()); 202 newWhen.setDescription(when.getDescription()); 203 204 // move this outputs to the when, expect the first one 205 // as the first one is the interceptor itself 206 for (int i = 1; i < outputs.size(); i++) { 207 ProcessorDefinition<?> out = outputs.get(i); 208 newWhen.addOutput(out); 209 } 210 // remove the moved from the original output, by just keeping the first one 211 clearOutput(); 212 outputs.add(newWhen); 213 } 214 } 215 216 public Boolean getSkipSendToOriginalEndpoint() { 217 return skipSendToOriginalEndpoint; 218 } 219 220 /** 221 * If set to true then the message is not sent to the original endpoint. 222 * By default (false) the message is both intercepted and then sent to the original endpoint. 223 */ 224 public void setSkipSendToOriginalEndpoint(Boolean skipSendToOriginalEndpoint) { 225 this.skipSendToOriginalEndpoint = skipSendToOriginalEndpoint; 226 } 227 228 public String getUri() { 229 return uri; 230 } 231 232 /** 233 * Intercept sending to the uri or uri pattern. 234 */ 235 public void setUri(String uri) { 236 this.uri = uri; 237 } 238}