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.builder;
018
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.camel.ErrorHandlerFactory;
024import org.apache.camel.Processor;
025import org.apache.camel.model.ModelCamelContext;
026import org.apache.camel.model.OnExceptionDefinition;
027import org.apache.camel.spi.RouteContext;
028import org.apache.camel.util.ObjectHelper;
029
030/**
031 * Represents a proxy to an error handler builder which is resolved by named reference
032 *
033 * @version 
034 */
035public class ErrorHandlerBuilderRef extends ErrorHandlerBuilderSupport {
036    public static final String DEFAULT_ERROR_HANDLER_BUILDER = "CamelDefaultErrorHandlerBuilder";
037    private final String ref;
038    private final Map<RouteContext, ErrorHandlerBuilder> handlers = new HashMap<RouteContext, ErrorHandlerBuilder>();
039    private boolean supportTransacted;
040
041    public ErrorHandlerBuilderRef(String ref) {
042        this.ref = ref;
043    }
044
045    @Override
046    public void addErrorHandlers(RouteContext routeContext, OnExceptionDefinition exception) {
047        ErrorHandlerBuilder handler = handlers.get(routeContext);
048        if (handler != null) {
049            handler.addErrorHandlers(routeContext, exception);
050        }
051        super.addErrorHandlers(routeContext, exception);
052    }
053    
054    @Override
055    public boolean removeOnExceptionList(String id) {
056        for (RouteContext routeContext : handlers.keySet()) {
057            if (getRouteId(routeContext).equals(id)) {
058                handlers.remove(routeContext);
059                break;
060            }
061        }
062        return super.removeOnExceptionList(id);
063    }
064    
065
066    public Processor createErrorHandler(RouteContext routeContext, Processor processor) throws Exception {
067        ErrorHandlerBuilder handler = handlers.get(routeContext);
068        if (handler == null) {
069            handler = createErrorHandler(routeContext);
070            handlers.put(routeContext, handler);
071        }
072        return handler.createErrorHandler(routeContext, processor);
073    }
074
075    public boolean supportTransacted() {
076        return supportTransacted;
077    }
078
079    @Override
080    public ErrorHandlerBuilder cloneBuilder() {
081        ErrorHandlerBuilderRef answer = new ErrorHandlerBuilderRef(ref);
082        cloneBuilder(answer);
083        return answer;
084    }
085
086    protected void cloneBuilder(ErrorHandlerBuilderRef other) {
087        super.cloneBuilder(other);
088
089        // no need to copy the handlers
090
091        other.supportTransacted = supportTransacted;
092    }
093
094    /**
095     * Lookup the error handler by the given ref
096     *
097     * @param routeContext the route context
098     * @param ref          reference id for the error handler
099     * @return the error handler
100     */
101    public static ErrorHandlerFactory lookupErrorHandlerBuilder(RouteContext routeContext, String ref) {
102        ErrorHandlerFactory answer;
103
104        // if the ref is the default then we do not have any explicit error handler configured
105        // if that is the case then use error handlers configured on the route, as for instance
106        // the transacted error handler could have been configured on the route so we should use that one
107        if (!isErrorHandlerBuilderConfigured(ref)) {
108            // see if there has been configured a route builder on the route
109            answer = routeContext.getRoute().getErrorHandlerBuilder();
110            if (answer == null && routeContext.getRoute().getErrorHandlerRef() != null) {
111                answer = routeContext.lookup(routeContext.getRoute().getErrorHandlerRef(), ErrorHandlerBuilder.class);
112            }
113            if (answer == null) {
114                // fallback to the default error handler if none configured on the route
115                answer = new DefaultErrorHandlerBuilder();
116            }
117            // check if its also a ref with no error handler configuration like me
118            if (answer instanceof ErrorHandlerBuilderRef) {
119                ErrorHandlerBuilderRef other = (ErrorHandlerBuilderRef) answer;
120                String otherRef = other.getRef();
121                if (!isErrorHandlerBuilderConfigured(otherRef)) {
122                    // the other has also no explicit error handler configured then fallback to the handler
123                    // configured on the parent camel context
124                    answer = lookupErrorHandlerBuilder((ModelCamelContext)routeContext.getCamelContext());
125                }
126                if (answer == null) {
127                    // the other has also no explicit error handler configured then fallback to the default error handler
128                    // otherwise we could recursive loop forever (triggered by createErrorHandler method)
129                    answer = new DefaultErrorHandlerBuilder();
130                }
131                // inherit the error handlers from the other as they are to be shared
132                // this is needed by camel-spring when none error handler has been explicit configured
133                ((ErrorHandlerBuilder)answer).setErrorHandlers(routeContext, other.getErrorHandlers(routeContext));
134            }
135        } else {
136            // use specific configured error handler
137            answer = routeContext.mandatoryLookup(ref, ErrorHandlerBuilder.class);
138        }
139
140        return answer;
141    }
142
143    protected static ErrorHandlerFactory lookupErrorHandlerBuilder(ModelCamelContext camelContext) {
144        @SuppressWarnings("deprecation")
145        ErrorHandlerFactory answer = camelContext.getErrorHandlerBuilder();
146        if (answer instanceof ErrorHandlerBuilderRef) {
147            ErrorHandlerBuilderRef other = (ErrorHandlerBuilderRef) answer;
148            String otherRef = other.getRef();
149            if (isErrorHandlerBuilderConfigured(otherRef)) {
150                answer = camelContext.getRegistry().lookupByNameAndType(otherRef, ErrorHandlerBuilder.class);
151                if (answer == null) {
152                    throw new IllegalArgumentException("ErrorHandlerBuilder with id " + otherRef + " not found in registry.");
153                }
154            }
155        }
156
157        return answer;
158    }
159
160    /**
161     * Returns whether a specific error handler builder has been configured or not.
162     * <p/>
163     * Can be used to test if none has been configured and then install a custom error handler builder
164     * replacing the default error handler (that would have been used as fallback otherwise).
165     * <br/>
166     * This is for instance used by the transacted policy to setup a TransactedErrorHandlerBuilder
167     * in camel-spring.
168     */
169    public static boolean isErrorHandlerBuilderConfigured(String ref) {
170        return !DEFAULT_ERROR_HANDLER_BUILDER.equals(ref);
171    }
172
173    public String getRef() {
174        return ref;
175    }
176
177    private ErrorHandlerBuilder createErrorHandler(RouteContext routeContext) {
178        ErrorHandlerBuilder handler = (ErrorHandlerBuilder)lookupErrorHandlerBuilder(routeContext, getRef());
179        ObjectHelper.notNull(handler, "error handler '" + ref + "'");
180
181        // configure if the handler support transacted
182        supportTransacted = handler.supportTransacted();
183
184        List<OnExceptionDefinition> list = getErrorHandlers(routeContext);
185        if (list != null) {
186            for (OnExceptionDefinition exceptionType : list) {
187                handler.addErrorHandlers(routeContext, exceptionType);
188            }
189        }
190        return handler;
191    }
192
193    @Override
194    public String toString() {
195        return "ErrorHandlerBuilderRef[" + ref + "]";
196    }
197}