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