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<>();
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        return lookupErrorHandlerBuilder(routeContext, ref, true);
103    }
104
105    /**
106     * Lookup the error handler by the given ref
107     *
108     * @param routeContext the route context
109     * @param ref          reference id for the error handler
110     * @param mandatory    whether the error handler must exists, if not a {@link org.apache.camel.NoSuchBeanException} is thrown
111     * @return the error handler
112     */
113    public static ErrorHandlerFactory lookupErrorHandlerBuilder(RouteContext routeContext, String ref, boolean mandatory) {
114        ErrorHandlerFactory answer;
115
116        // if the ref is the default then we do not have any explicit error handler configured
117        // if that is the case then use error handlers configured on the route, as for instance
118        // the transacted error handler could have been configured on the route so we should use that one
119        if (!isErrorHandlerBuilderConfigured(ref)) {
120            // see if there has been configured a route builder on the route
121            answer = routeContext.getRoute().getErrorHandlerBuilder();
122            if (answer == null && routeContext.getRoute().getErrorHandlerRef() != null) {
123                answer = routeContext.lookup(routeContext.getRoute().getErrorHandlerRef(), ErrorHandlerBuilder.class);
124            }
125            if (answer == null) {
126                // fallback to the default error handler if none configured on the route
127                answer = new DefaultErrorHandlerBuilder();
128            }
129            // check if its also a ref with no error handler configuration like me
130            if (answer instanceof ErrorHandlerBuilderRef) {
131                ErrorHandlerBuilderRef other = (ErrorHandlerBuilderRef) answer;
132                String otherRef = other.getRef();
133                if (!isErrorHandlerBuilderConfigured(otherRef)) {
134                    // the other has also no explicit error handler configured then fallback to the handler
135                    // configured on the parent camel context
136                    answer = lookupErrorHandlerBuilder((ModelCamelContext)routeContext.getCamelContext());
137                }
138                if (answer == null) {
139                    // the other has also no explicit error handler configured then fallback to the default error handler
140                    // otherwise we could recursive loop forever (triggered by createErrorHandler method)
141                    answer = new DefaultErrorHandlerBuilder();
142                }
143                // inherit the error handlers from the other as they are to be shared
144                // this is needed by camel-spring when none error handler has been explicit configured
145                ((ErrorHandlerBuilder)answer).setErrorHandlers(routeContext, other.getErrorHandlers(routeContext));
146            }
147        } else {
148            // use specific configured error handler
149            if (mandatory) {
150                answer = routeContext.mandatoryLookup(ref, ErrorHandlerBuilder.class);
151            } else {
152                answer = routeContext.lookup(ref, ErrorHandlerBuilder.class);
153            }
154        }
155
156        return answer;
157    }
158
159    protected static ErrorHandlerFactory lookupErrorHandlerBuilder(ModelCamelContext camelContext) {
160        @SuppressWarnings("deprecation")
161        ErrorHandlerFactory answer = camelContext.getErrorHandlerBuilder();
162        if (answer instanceof ErrorHandlerBuilderRef) {
163            ErrorHandlerBuilderRef other = (ErrorHandlerBuilderRef) answer;
164            String otherRef = other.getRef();
165            if (isErrorHandlerBuilderConfigured(otherRef)) {
166                answer = camelContext.getRegistry().lookupByNameAndType(otherRef, ErrorHandlerBuilder.class);
167                if (answer == null) {
168                    throw new IllegalArgumentException("ErrorHandlerBuilder with id " + otherRef + " not found in registry.");
169                }
170            }
171        }
172
173        return answer;
174    }
175
176    /**
177     * Returns whether a specific error handler builder has been configured or not.
178     * <p/>
179     * Can be used to test if none has been configured and then install a custom error handler builder
180     * replacing the default error handler (that would have been used as fallback otherwise).
181     * <br/>
182     * This is for instance used by the transacted policy to setup a TransactedErrorHandlerBuilder
183     * in camel-spring.
184     */
185    public static boolean isErrorHandlerBuilderConfigured(String ref) {
186        return !DEFAULT_ERROR_HANDLER_BUILDER.equals(ref);
187    }
188
189    public String getRef() {
190        return ref;
191    }
192
193    private ErrorHandlerBuilder createErrorHandler(RouteContext routeContext) {
194        ErrorHandlerBuilder handler = (ErrorHandlerBuilder)lookupErrorHandlerBuilder(routeContext, getRef());
195        ObjectHelper.notNull(handler, "error handler '" + ref + "'");
196
197        // configure if the handler support transacted
198        supportTransacted = handler.supportTransacted();
199
200        List<OnExceptionDefinition> list = getErrorHandlers(routeContext);
201        if (list != null) {
202            for (OnExceptionDefinition exceptionType : list) {
203                handler.addErrorHandlers(routeContext, exceptionType);
204            }
205        }
206        return handler;
207    }
208
209    @Override
210    public String toString() {
211        return "ErrorHandlerBuilderRef[" + ref + "]";
212    }
213}