Skip to content

Commit e1ff609

Browse files
committed
CAMEL-22931: A camel specific yaml dsl parser that can parse and load yaml sources to check if they are loadable by Camel which is a faster and lighter than the yaml schema validator which is heavy and memory hungry due to our very big and complex schema file.
1 parent c079b4a commit e1ff609

8 files changed

Lines changed: 349 additions & 1 deletion

File tree

core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ public List<BeanRepository> getRepositories() {
174174
* Adds a custom {@link BeanRepository}.
175175
*/
176176
public void addBeanRepository(BeanRepository repository) {
177-
if (repository == null) {
177+
if (repositories == null) {
178178
repositories = new ArrayList<>();
179179
}
180180
repositories.add(repository);

dsl/camel-yaml-dsl/camel-yaml-dsl-validator/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@
4242
<groupId>org.apache.camel</groupId>
4343
<artifactId>camel-yaml-dsl</artifactId>
4444
</dependency>
45+
<dependency>
46+
<groupId>org.apache.camel</groupId>
47+
<artifactId>camel-core-engine</artifactId>
48+
</dependency>
49+
<dependency>
50+
<groupId>org.apache.camel</groupId>
51+
<artifactId>camel-core-languages</artifactId>
52+
</dependency>
53+
<dependency>
54+
<groupId>org.apache.camel</groupId>
55+
<artifactId>camel-stub</artifactId>
56+
</dependency>
4557
<dependency>
4658
<groupId>com.networknt</groupId>
4759
<artifactId>json-schema-validator</artifactId>
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.camel.dsl.yaml.validator;
18+
19+
import java.io.File;
20+
import java.nio.file.Files;
21+
import java.util.Collections;
22+
import java.util.List;
23+
24+
import com.networknt.schema.ValidationMessage;
25+
import org.apache.camel.CamelContext;
26+
import org.apache.camel.component.stub.StubComponent;
27+
import org.apache.camel.dsl.yaml.YamlRoutesBuilderLoader;
28+
import org.apache.camel.dsl.yaml.validator.stub.StubBeanRepository;
29+
import org.apache.camel.dsl.yaml.validator.stub.StubDataFormat;
30+
import org.apache.camel.dsl.yaml.validator.stub.StubLanguage;
31+
import org.apache.camel.dsl.yaml.validator.stub.StubTransformer;
32+
import org.apache.camel.impl.DefaultCamelContext;
33+
import org.apache.camel.spi.ComponentResolver;
34+
import org.apache.camel.spi.DataFormatResolver;
35+
import org.apache.camel.spi.LanguageResolver;
36+
import org.apache.camel.spi.TransformerResolver;
37+
import org.apache.camel.support.DefaultRegistry;
38+
import org.apache.camel.support.ResourceHelper;
39+
40+
/**
41+
* Camel YAML parser that parses YAML DSL routes and also checks the routes can be loaded by Camel. This parser does not
42+
* start any routes, and will stub every component, dataformat, language which would require to have all dependencies on
43+
* classpath and 3rd party JARs may trigger some initialization that can distort this parser.
44+
*
45+
* This is a faster and lighter parser than the {@link YamlValidator} which uses a similar concept as in camel-jbang.
46+
*/
47+
public class CamelYamlParser {
48+
49+
public List<ValidationMessage> parse(File file) throws Exception {
50+
CamelContext camelContext = null;
51+
try {
52+
DefaultRegistry registry = new DefaultRegistry();
53+
registry.addBeanRepository(new StubBeanRepository());
54+
55+
camelContext = new DefaultCamelContext(registry);
56+
camelContext.setAutoStartup(false);
57+
camelContext.getCamelContextExtension().addContextPlugin(ComponentResolver.class,
58+
(name, context) -> new StubComponent());
59+
camelContext.getCamelContextExtension().addContextPlugin(DataFormatResolver.class,
60+
(name, context) -> new StubDataFormat());
61+
camelContext.getCamelContextExtension().addContextPlugin(LanguageResolver.class,
62+
(name, context) -> new StubLanguage());
63+
camelContext.getCamelContextExtension().addContextPlugin(TransformerResolver.class,
64+
(name, context) -> new StubTransformer());
65+
camelContext.start();
66+
67+
YamlRoutesBuilderLoader loader = new YamlRoutesBuilderLoader();
68+
loader.setCamelContext(camelContext);
69+
loader.start();
70+
71+
var rb = loader.doLoadRouteBuilder(ResourceHelper.fromString(file.getName(), Files.readString(file.toPath())));
72+
camelContext.addRoutes(rb);
73+
return Collections.emptyList();
74+
75+
} catch (Exception e) {
76+
ValidationMessage vm = ValidationMessage.builder().type("parser")
77+
.messageSupplier(() -> e.getClass().getName() + ": " + e.getMessage()).build();
78+
return List.of(vm);
79+
} finally {
80+
if (camelContext != null) {
81+
camelContext.stop();
82+
}
83+
}
84+
}
85+
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.camel.dsl.yaml.validator.stub;
18+
19+
import java.util.Collections;
20+
import java.util.Map;
21+
import java.util.Set;
22+
import java.util.UUID;
23+
24+
import org.apache.camel.processor.DefaultClaimCheckRepository;
25+
import org.apache.camel.processor.aggregate.MemoryAggregationRepository;
26+
import org.apache.camel.spi.AggregationRepository;
27+
import org.apache.camel.spi.BeanRepository;
28+
import org.apache.camel.spi.ClaimCheckRepository;
29+
import org.apache.camel.spi.IdempotentRepository;
30+
import org.apache.camel.spi.StateRepository;
31+
import org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository;
32+
import org.apache.camel.support.processor.state.MemoryStateRepository;
33+
34+
public class StubBeanRepository implements BeanRepository {
35+
36+
@Override
37+
public Object lookupByName(String name) {
38+
return null;
39+
}
40+
41+
@Override
42+
public <T> T lookupByNameAndType(String name, Class<T> type) {
43+
return stubType(type);
44+
}
45+
46+
@Override
47+
public <T> Map<String, T> findByTypeWithName(Class<T> type) {
48+
T answer = stubType(type);
49+
if (answer != null) {
50+
// generate dummy name
51+
String name = UUID.randomUUID().toString();
52+
return Map.of(name, answer);
53+
}
54+
return Collections.EMPTY_MAP;
55+
}
56+
57+
@Override
58+
public <T> Set<T> findByType(Class<T> type) {
59+
T answer = stubType(type);
60+
if (answer != null) {
61+
return Set.of(answer);
62+
}
63+
return Collections.EMPTY_SET;
64+
}
65+
66+
private <T> T stubType(Class<T> type) {
67+
// add repositories and other stuff we need to stub out, so they run noop/in-memory only
68+
// and do not start live connections to databases or other services
69+
if (IdempotentRepository.class.isAssignableFrom(type)) {
70+
return (T) new MemoryIdempotentRepository();
71+
}
72+
if (AggregationRepository.class.isAssignableFrom(type)) {
73+
return (T) new MemoryAggregationRepository();
74+
}
75+
if (ClaimCheckRepository.class.isAssignableFrom(type)) {
76+
return (T) new DefaultClaimCheckRepository();
77+
}
78+
if (StateRepository.class.isAssignableFrom(type)) {
79+
return (T) new MemoryStateRepository();
80+
}
81+
return null;
82+
}
83+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.camel.dsl.yaml.validator.stub;
18+
19+
import java.io.InputStream;
20+
import java.io.OutputStream;
21+
22+
import org.apache.camel.Exchange;
23+
import org.apache.camel.spi.DataFormat;
24+
import org.apache.camel.spi.PropertyConfigurer;
25+
import org.apache.camel.spi.PropertyConfigurerAware;
26+
import org.apache.camel.support.service.ServiceSupport;
27+
28+
/**
29+
* A data format that does nothing
30+
*/
31+
public class StubDataFormat extends ServiceSupport implements DataFormat, PropertyConfigurerAware {
32+
33+
@Override
34+
public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception {
35+
// noop
36+
}
37+
38+
@Override
39+
public Object unmarshal(Exchange exchange, InputStream stream) throws Exception {
40+
return null;
41+
}
42+
43+
@Override
44+
public PropertyConfigurer getPropertyConfigurer(Object instance) {
45+
// dummy configurer tha does nothing
46+
return (camelContext, target, name, value, ignoreCase) -> true;
47+
}
48+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.camel.dsl.yaml.validator.stub;
18+
19+
import org.apache.camel.Expression;
20+
import org.apache.camel.Predicate;
21+
import org.apache.camel.builder.ExpressionBuilder;
22+
import org.apache.camel.builder.PredicateBuilder;
23+
import org.apache.camel.spi.Language;
24+
import org.apache.camel.support.service.ServiceSupport;
25+
26+
/**
27+
* A language that does nothing
28+
*/
29+
public class StubLanguage extends ServiceSupport implements Language {
30+
31+
@Override
32+
public Predicate createPredicate(String expression) {
33+
return PredicateBuilder.constant(true);
34+
}
35+
36+
@Override
37+
public Expression createExpression(String expression) {
38+
return ExpressionBuilder.constantExpression(expression);
39+
}
40+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.camel.dsl.yaml.validator.stub;
18+
19+
import org.apache.camel.Message;
20+
import org.apache.camel.spi.DataType;
21+
import org.apache.camel.spi.Transformer;
22+
23+
/**
24+
* A transformer that does nothing
25+
*/
26+
public class StubTransformer extends Transformer {
27+
28+
@Override
29+
public void transform(Message message, DataType from, DataType to) throws Exception {
30+
// noop
31+
}
32+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.camel.dsl.yaml.validator;
18+
19+
import java.io.File;
20+
21+
import org.junit.jupiter.api.Assertions;
22+
import org.junit.jupiter.api.BeforeAll;
23+
import org.junit.jupiter.api.Test;
24+
25+
public class CamelYamlParserTest {
26+
27+
private static CamelYamlParser parser;
28+
29+
@BeforeAll
30+
public static void setup() throws Exception {
31+
parser = new CamelYamlParser();
32+
}
33+
34+
@Test
35+
public void testParseOk() throws Exception {
36+
Assertions.assertTrue(parser.parse(new File("src/test/resources/foo.yaml")).isEmpty());
37+
}
38+
39+
@Test
40+
public void testParseBad() throws Exception {
41+
var report = parser.parse(new File("src/test/resources/bad.yaml"));
42+
Assertions.assertFalse(report.isEmpty());
43+
Assertions.assertEquals(1, report.size());
44+
Assertions.assertTrue(report.get(0).getMessage().contains("Unknown node id: setCheese"));
45+
Assertions.assertTrue(report.get(0).getMessage().contains("- setCheese:"));
46+
}
47+
}

0 commit comments

Comments
 (0)