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