Skip to content

Commit

Permalink
yaml-loader: add support for OnException
Browse files Browse the repository at this point in the history
  • Loading branch information
lburgazzoli committed May 26, 2020
1 parent af1fdce commit 83c24fe
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class FromStepParser implements StartStepParser {
public ProcessorDefinition<?> toStartProcessor(Context context) {
final FromStepDefinition definition = context.node(FromStepDefinition.class);
final String uri = definition.getEndpointUri();
final RouteDefinition route = new RouteDefinition().from(uri);
final RouteDefinition route = context.builder().from(uri);

// as this is a start converter, steps are mandatory
StepParserSupport.notNull(definition.steps, "steps");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.k.loader.yaml.parser;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonAlias;
import org.apache.camel.k.annotation.yaml.YAMLNodeDefinition;
import org.apache.camel.k.annotation.yaml.YAMLStepParser;
import org.apache.camel.k.loader.yaml.model.Step;
import org.apache.camel.k.loader.yaml.spi.ProcessorStepParser;
import org.apache.camel.k.loader.yaml.spi.StartStepParser;
import org.apache.camel.k.loader.yaml.spi.StepParserSupport;
import org.apache.camel.model.ExpressionSubElementDefinition;
import org.apache.camel.model.OnExceptionDefinition;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.model.RedeliveryPolicyDefinition;
import org.apache.camel.model.WhenDefinition;
import org.apache.camel.model.language.ExpressionDefinition;
import org.apache.camel.reifier.OnExceptionReifier;
import org.apache.camel.spi.ClassResolver;
import org.apache.camel.util.ObjectHelper;

import static org.apache.camel.util.ObjectHelper.ifNotEmpty;

@YAMLStepParser("on-exception")
public class OnExceptionStepParser implements StartStepParser, ProcessorStepParser {
@Override
public ProcessorDefinition<?> toStartProcessor(Context context) {
final ClassResolver resolver = context.getCamelContext().getClassResolver();
final Definition definition = context.node(Definition.class);

if (ObjectHelper.isEmpty(definition.exceptions)) {
definition.exceptions = List.of(Exception.class.getName());
}

OnExceptionDefinition onException = null;

//
// don0t know if it is correct but builder.onException(A, B, C) seems to
// do the same, so it loops over the given exception classes and creates
// a OnExceptionDefinition for each exception.
//
for (String className: definition.exceptions) {
onException = context.builder().onException(resolver.resolveClass(className, Throwable.class));
onException.setRouteScoped(false);

mapToOnException(context, definition, onException);
}

return StepParserSupport.convertSteps(
context,
onException,
definition.steps);
}

@Override
public ProcessorDefinition<?> toProcessor(Context context) {
final Definition definition = context.node(Definition.class);
final OnExceptionDefinition onException = new OnExceptionDefinition();

if (ObjectHelper.isEmpty(definition.exceptions)) {
definition.exceptions = List.of(Exception.class.getName());
}

onException.setExceptions(definition.exceptions);
onException.setRouteScoped(true);

mapToOnException(context, definition, onException);

return StepParserSupport.convertSteps(
context,
onException,
definition.steps);
}

private static void mapToOnException(Context context, Definition definition, OnExceptionDefinition onException) {
ifNotEmpty(definition.retryWhile, onException::setRetryWhile);
ifNotEmpty(definition.handled, onException::setHandled);
ifNotEmpty(definition.continued, onException::setContinued);
ifNotEmpty(definition.continued, onException::setContinued);
ifNotEmpty(definition.redeliveryPolicyType, onException::setRedeliveryPolicyType);
ifNotEmpty(definition.redeliveryPolicyRef, onException::setRedeliveryPolicyRef);
ifNotEmpty(definition.onRedeliveryRef, onException::setOnRedeliveryRef);
ifNotEmpty(definition.onExceptionOccurredRef, onException::setOnExceptionOccurredRef);
ifNotEmpty(definition.useOriginalMessage, val -> onException.setUseOriginalMessage(Boolean.toString(val)));
ifNotEmpty(definition.useOriginalBody, val -> onException.setUseOriginalBody(Boolean.toString(val)));

if (definition.onWhen != null) {
StepParserSupport.notNull(definition.onWhen.steps, "onWhen.steps");

//
// This is needed as when processing the start step, the same definition
// is applied to multiple OnExceptionDefinition
//
WhenDefinition whenDefinition = new WhenDefinition();
whenDefinition.setExpression(definition.onWhen.getExpression());

StepParserSupport.convertSteps(
context,
whenDefinition,
definition.onWhen.steps
);

onException.setOnWhen(whenDefinition);
}
}

@YAMLNodeDefinition(reifiers = OnExceptionReifier.class)
public static final class Definition {
public List<Step> steps;

@JsonAlias("exceptions")
public List<String> exceptions;

@JsonAlias({"when", "on-when"})
public When onWhen;
@JsonAlias("retry-while")
public ExpressionElement retryWhile;
@JsonAlias("handled")
public ExpressionElement handled;
@JsonAlias("continued")
public ExpressionElement continued;

@JsonAlias("redelivery-policy")
public RedeliveryPolicyDefinition redeliveryPolicyType;
@JsonAlias("redelivery-policy-ref")
public String redeliveryPolicyRef;

@JsonAlias("on-redelivery-ref")
public String onRedeliveryRef;
@JsonAlias("on-exception-occurred-ref")
public String onExceptionOccurredRef;
@JsonAlias("use-original-message")
public boolean useOriginalMessage;
@JsonAlias("use-original-body")
public boolean useOriginalBody;

public static final class When extends WhenDefinition implements HasExpression {
public List<Step> steps;
}

public static final class ExpressionElement extends ExpressionSubElementDefinition implements HasExpression {
@Override
public void setExpression(ExpressionDefinition expressionDefinition) {
super.setExpressionType(expressionDefinition);
}

@Override
public ExpressionDefinition getExpression() {
return super.getExpressionType();
}
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public ProcessorDefinition<?> toStartProcessor(Context context) {
StepParserSupport.notNull(definition.verb, "verb");
StepParserSupport.notNull(definition.steps, "steps");

RestDefinition rest = new RestDefinition().verb(definition.verb, definition.uri);
RestDefinition rest = context.builder().rest().verb(definition.verb, definition.uri);

ObjectHelper.ifNotEmpty(definition.apiDocs, rest::apiDocs);
ObjectHelper.ifNotEmpty(definition.enableCORS, rest::enableCORS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
public interface StartStepParser extends StepParser {
/**
* @param context
* @return
*/
ProcessorDefinition<?> toStartProcessor(Context context);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.camel.CamelContext;
import org.apache.camel.ExtendedCamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.spi.HasCamelContext;
import org.apache.camel.util.ObjectHelper;

Expand All @@ -35,20 +36,24 @@ public interface StepParser {
*/
class Context implements HasCamelContext {
private final ObjectMapper mapper;
private final CamelContext camelContext;
private final RouteBuilder builder;
private final JsonNode node;
private final Resolver resolver;

public Context(CamelContext camelContext, ObjectMapper mapper, JsonNode node, Resolver resolver) {
this.camelContext = camelContext;
public Context(RouteBuilder builder, ObjectMapper mapper, JsonNode node, Resolver resolver) {
this.builder = builder;
this.mapper = mapper;
this.node = node;
this.resolver = ObjectHelper.notNull(resolver, "resolver");
}

@Override
public CamelContext getCamelContext() {
return camelContext;
return builder.getContext();
}

public RouteBuilder builder() {
return builder;
}

public JsonNode node() {
Expand All @@ -75,7 +80,7 @@ public <T> T node(Class<T> type) {
}

public <T extends StepParser> T lookup(Class<T> type, String stepId) {
StepParser parser = resolver.resolve(camelContext, stepId);
StepParser parser = resolver.resolve(builder.getContext(), stepId);
if (type.isInstance(parser)) {
return type.cast(parser);
}
Expand All @@ -85,7 +90,7 @@ public <T extends StepParser> T lookup(Class<T> type, String stepId) {

public static Context of(Context context, JsonNode step) {
return new Context(
context.camelContext,
context.builder,
context.mapper,
step,
context.resolver
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

Expand All @@ -31,7 +30,6 @@
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.k.Runtime;
import org.apache.camel.k.Source;
Expand All @@ -40,11 +38,6 @@
import org.apache.camel.k.loader.yaml.model.Step;
import org.apache.camel.k.loader.yaml.spi.StartStepParser;
import org.apache.camel.k.loader.yaml.spi.StepParser;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.model.RouteDefinition;
import org.apache.camel.model.RoutesDefinition;
import org.apache.camel.model.rest.RestDefinition;
import org.apache.camel.model.rest.RestsDefinition;

@Loader("yaml")
public class YamlSourceLoader implements SourceLoader {
Expand Down Expand Up @@ -94,41 +87,12 @@ final RouteBuilder builder(InputStream is) {
@Override
public void configure() throws Exception {
final StepParser.Resolver resolver = StepParser.Resolver.caching(new YamlStepResolver());
final CamelContext camelContext = getContext();
final List<RouteDefinition> routes = new ArrayList<>();
final List<RestDefinition> rests = new ArrayList<>();

try (is) {
for (Step step : mapper.readValue(is, Step[].class)) {
final StepParser.Context context = new StepParser.Context(camelContext, mapper, step.node, resolver);
final ProcessorDefinition<?> root = StartStepParser.invoke(context, step.id);

if (root == null) {
throw new IllegalStateException("No route definition");
}
if (!(root instanceof RouteDefinition)) {
throw new IllegalStateException("Root definition should be of type RouteDefinition");
}

RouteDefinition r = (RouteDefinition) root;
if (r.getRestDefinition() == null) {
routes.add(r);
} else {
rests.add(r.getRestDefinition());
}
}

if (!routes.isEmpty()) {
RoutesDefinition definition = new RoutesDefinition();
definition.setRoutes(routes);

setRouteCollection(definition);
}
if (!rests.isEmpty()) {
RestsDefinition definition = new RestsDefinition();
definition.setRests(rests);

setRestCollection(definition);
StartStepParser.invoke(
new StepParser.Context(this, mapper, step.node, resolver),
step.id);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package org.apache.camel.k.loader.yaml
import com.fasterxml.jackson.databind.JsonNode
import groovy.util.logging.Slf4j
import org.apache.camel.CamelContext
import org.apache.camel.builder.RouteBuilder
import org.apache.camel.component.mock.MockEndpoint
import org.apache.camel.impl.DefaultCamelContext
import org.apache.camel.k.loader.yaml.spi.ProcessorStepParser
Expand All @@ -36,13 +37,23 @@ class TestSupport extends Specification {

static StepParser.Context stepContext(String content) {
def node = MAPPER.readTree(content.stripMargin())
def cctx = new DefaultCamelContext()
def builder = new RouteBuilder(new DefaultCamelContext()) {
@Override
void configure() throws Exception {
}
}

return new StepParser.Context(cctx, MAPPER, node, RESOLVER)
return new StepParser.Context(builder, MAPPER, node, RESOLVER)
}

static StepParser.Context stepContext(JsonNode content) {
return new StepParser.Context(new DefaultCamelContext(), MAPPER, content, RESOLVER)
def builder = new RouteBuilder(new DefaultCamelContext()) {
@Override
void configure() throws Exception {
}
}

return new StepParser.Context(builder, MAPPER, content, RESOLVER)
}

static CamelContext startContext(String content) {
Expand Down
Loading

0 comments on commit 83c24fe

Please sign in to comment.