This is an automated email from the ASF dual-hosted git repository. ggrzybek pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
commit c26ddd3f02d709cefbf0ddc65e235f61ebcc1de4 Author: Grzegorz Grzybek <gr.grzy...@gmail.com> AuthorDate: Thu May 4 11:47:47 2023 +0200 [CAMEL-18189] Use XmlStreamDetector in dsl/camel-xml-io-dsl to speed up parsing --- .../services/org/apache/camel/model.properties | 2 + .../org/apache/camel/model/app/beans.json | 22 +++++ .../org/apache/camel/model/app/camel-app.json | 21 +++++ .../org/apache/camel/model/app/jaxb.index | 3 + .../camel/model/app/ApplicationDefinition.java | 29 +++++++ .../apache/camel/model/app/BeansDefinition.java | 98 ++++++++++++++++++++++ .../camel/model/app/ComponentScanDefinition.java | 63 ++++++++++++++ .../org/apache/camel/model/app/package-info.java | 25 ++++++ .../camel/dsl/xml/io/XmlRoutesBuilderLoader.java | 35 ++++---- 9 files changed, 281 insertions(+), 17 deletions(-) diff --git a/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties b/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties index eaa4845cf60..129263b7cd9 100644 --- a/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties +++ b/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties @@ -8,10 +8,12 @@ base64 basicAuth batch-config bean +beans bearerToken bindy blacklistServiceFilter cachingServiceDiscovery +camel-app cbor choice circuitBreaker diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/app/beans.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/app/beans.json new file mode 100644 index 00000000000..e55cd059c1f --- /dev/null +++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/app/beans.json @@ -0,0 +1,22 @@ +{ + "model": { + "kind": "model", + "name": "beans", + "title": "Beans", + "description": "A groupping POJO (and related XML root element) that's historically associated with entire application (or its distinguished fragment). beans root element to define the application comes from Spring Framework and it can be treated as de-facto standard.", + "deprecated": false, + "label": "configuration", + "javaType": "org.apache.camel.model.app.BeansDefinition", + "abstract": false, + "input": false, + "output": false + }, + "properties": { + "component-scan": { "kind": "element", "displayName": "Component-scan", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.app.ComponentScanDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Component scanning definition(s). But unlike package\/packageScan\/contextScan, we're not scanning only for org.apache.camel.builder.RouteBuilder." }, + "rest": { "kind": "element", "displayName": "Rest", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.rest.RestDefinition>", "deprecated": false, "autowired": false, "secret": false }, + "routeConfiguration": { "kind": "element", "displayName": "Route Configuration", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteConfigurationDefinition>", "deprecated": false, "autowired": false, "secret": false }, + "routeTemplate": { "kind": "element", "displayName": "Route Template", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteTemplateDefinition>", "deprecated": false, "autowired": false, "secret": false }, + "templatedRoute": { "kind": "element", "displayName": "Templated Route", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.TemplatedRouteDefinition>", "deprecated": false, "autowired": false, "secret": false }, + "route": { "kind": "element", "displayName": "Route", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteDefinition>", "deprecated": false, "autowired": false, "secret": false } + } +} diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/app/camel-app.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/app/camel-app.json new file mode 100644 index 00000000000..e941602f7e0 --- /dev/null +++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/app/camel-app.json @@ -0,0 +1,21 @@ +{ + "model": { + "kind": "model", + "name": "camel-app", + "title": "Camel-app", + "description": "If beans reminds Spring application too much, we can use camel-app (similar to web-app from Servlet API specification).", + "deprecated": false, + "javaType": "org.apache.camel.model.app.ApplicationDefinition", + "abstract": false, + "input": false, + "output": false + }, + "properties": { + "component-scan": { "kind": "element", "displayName": "Component-scan", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.app.ComponentScanDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Component scanning definition(s). But unlike package\/packageScan\/contextScan, we're not scanning only for org.apache.camel.builder.RouteBuilder." }, + "rest": { "kind": "element", "displayName": "Rest", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.rest.RestDefinition>", "deprecated": false, "autowired": false, "secret": false }, + "routeConfiguration": { "kind": "element", "displayName": "Route Configuration", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteConfigurationDefinition>", "deprecated": false, "autowired": false, "secret": false }, + "routeTemplate": { "kind": "element", "displayName": "Route Template", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteTemplateDefinition>", "deprecated": false, "autowired": false, "secret": false }, + "templatedRoute": { "kind": "element", "displayName": "Templated Route", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.TemplatedRouteDefinition>", "deprecated": false, "autowired": false, "secret": false }, + "route": { "kind": "element", "displayName": "Route", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteDefinition>", "deprecated": false, "autowired": false, "secret": false } + } +} diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/app/jaxb.index b/core/camel-core-model/src/generated/resources/org/apache/camel/model/app/jaxb.index new file mode 100644 index 00000000000..63678282437 --- /dev/null +++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/app/jaxb.index @@ -0,0 +1,3 @@ +# Generated by camel build tools - do NOT edit this file! +ApplicationDefinition +BeansDefinition diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/app/ApplicationDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/app/ApplicationDefinition.java new file mode 100644 index 00000000000..96733157ab9 --- /dev/null +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/app/ApplicationDefinition.java @@ -0,0 +1,29 @@ +/* + * 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.model.app; + +import jakarta.xml.bind.annotation.XmlRootElement; +import org.apache.camel.spi.Metadata; + +/** + * If "beans" reminds Spring application too much, we can use "camel-app" (similar to + * "web-app" from Servlet API specification). + */ +@Metadata(label = "configuration") +@XmlRootElement(name = "camel-app") +public class ApplicationDefinition extends BeansDefinition { +} diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/app/BeansDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/app/BeansDefinition.java new file mode 100644 index 00000000000..b89600bb7ee --- /dev/null +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/app/BeansDefinition.java @@ -0,0 +1,98 @@ +/* + * 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.model.app; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; +import org.apache.camel.model.RouteConfigurationDefinition; +import org.apache.camel.model.RouteDefinition; +import org.apache.camel.model.RouteTemplateDefinition; +import org.apache.camel.model.TemplatedRouteDefinition; +import org.apache.camel.model.rest.RestDefinition; +import org.apache.camel.spi.Metadata; + +/** + * A groupping POJO (and related XML root element) that's historically associated with "entire application" + * (or its distinguished fragment). "beans" root element to define "the application" comes from Spring + * Framework and it can be treated as de-facto standard. + */ +@Metadata(label = "configuration") +@XmlRootElement(name = "beans") +@XmlAccessorType(XmlAccessType.FIELD) +public class BeansDefinition { + + /** + * Component scanning definition(s). But unlike package/packageScan/contextScan, + * we're not scanning only for org.apache.camel.builder.RouteBuilder. + */ + @XmlElement(name = "component-scan") + private final List<ComponentScanDefinition> componentScanning = new ArrayList<>(); + + // this is a place for <bean> element definition, but there's already org.apache.camel.model.BeanDefinition + // model. However it is for "bean processor", not "bean definition". + // also, it'd be nice to support all that Spring's <bean> can support (constructor args, maps/lists/constants) + // for now let's stick to package scanning for JSR330 annotations and SupplierRegistry + + // the order comes from <camelContext> (org.apache.camel.spring.xml.CamelContextFactoryBean) + // to make things less confusing, as it's not easy to simply tell JAXB to use <xsd:choice maxOccurs="unbounded"> + // over a set of unrelated elements + + // initially we'll be supporting only these elements which are parsed by + // org.apache.camel.dsl.xml.io.XmlRoutesBuilderLoader in camel-xml-io-dsl + + @XmlElement(name = "rest") + private final List<RestDefinition> rests = new ArrayList<>(); + @XmlElement(name = "routeConfiguration") + private final List<RouteConfigurationDefinition> routeConfigurations = new ArrayList<>(); + @XmlElement(name = "routeTemplate") + private final List<RouteTemplateDefinition> routeTemplates = new ArrayList<>(); + @XmlElement(name = "templatedRoute") + private final List<TemplatedRouteDefinition> templatedRoutes = new ArrayList<>(); + @XmlElement(name = "route") + private final List<RouteDefinition> routes = new ArrayList<>(); + + public List<ComponentScanDefinition> getComponentScanning() { + return componentScanning; + } + + public List<RestDefinition> getRests() { + return rests; + } + + public List<RouteConfigurationDefinition> getRouteConfigurations() { + return routeConfigurations; + } + + public List<RouteTemplateDefinition> getRouteTemplates() { + return routeTemplates; + } + + public List<TemplatedRouteDefinition> getTemplatedRoutes() { + return templatedRoutes; + } + + public List<RouteDefinition> getRoutes() { + return routes; + } + +} diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/app/ComponentScanDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/app/ComponentScanDefinition.java new file mode 100644 index 00000000000..a9e04d06e92 --- /dev/null +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/app/ComponentScanDefinition.java @@ -0,0 +1,63 @@ +/* + * 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.model.app; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAttribute; +import org.apache.camel.spi.Metadata; +import org.apache.camel.support.DefaultRegistry; + +/** + * <p> + * An equivalent of Spring's {@code <context:component-scan>} element that can be used to populate underlying + * bean registry. + * </p> + * <p> + * With Spring application, the bean registry is provided by Spring itself, but if we want to use Camel without + * Spring, we have an option to use {@link DefaultRegistry} with underlying, supporting bean + * {@link org.apache.camel.spi.Registry registries} and {@link org.apache.camel.spi.BeanRepository repositories}. + * </p> + */ +@Metadata(label = "configuration") +@XmlAccessorType(XmlAccessType.FIELD) +public class ComponentScanDefinition { + + @XmlAttribute(name = "base-package") + private String basePackage; + + /** Whether to use {@code jakarta.inject} annotations like {@code @Inject} or {@code @Named} */ + @XmlAttribute(name = "use-jsr-330") + private boolean useJsr330 = true; + + public String getBasePackage() { + return basePackage; + } + + public void setBasePackage(String basePackage) { + this.basePackage = basePackage; + } + + public void setUseJsr330(boolean useJsr330) { + this.useJsr330 = useJsr330; + } + + public boolean isJsr330Enabled() { + return useJsr330; + } + +} diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/app/package-info.java b/core/camel-core-model/src/main/java/org/apache/camel/model/app/package-info.java new file mode 100644 index 00000000000..bf2e2bcd2e6 --- /dev/null +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/app/package-info.java @@ -0,0 +1,25 @@ +/* + * 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. + */ + +/** + * The JAXB POJOs representing the part of the model, which can be used to create a view of Camel application. + * This model can be used to group some Camel definitions (routes, templates) without using full Spring + * application context, but when Spring's {@code <beans>} concept feels appropriate. + */ +@jakarta.xml.bind.annotation.XmlSchema(namespace = "http://camel.apache.org/schema/spring", + elementFormDefault = jakarta.xml.bind.annotation.XmlNsForm.QUALIFIED) +package org.apache.camel.model.app; diff --git a/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java b/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java index c7ed134cddf..eefb5744794 100644 --- a/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java +++ b/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java @@ -55,22 +55,22 @@ public class XmlRoutesBuilderLoader extends RouteBuilderLoaderSupport { @Override public RouteBuilder doLoadRouteBuilder(Resource input) throws Exception { - return new RouteConfigurationBuilder() { - final Resource resource = new CachedResource(input); + final Resource resource = new CachedResource(input); + // instead of parsing the document NxM times (for each namespace x root element combination), + // we preparse it using XmlStreamDetector and then parse it fully knowing what's inside. + // we could even do better, by passing already preparsed information through config file, but + // it's getting complicated when using multiple files. + XmlStreamDetector detector = new XmlStreamDetector(resource.getInputStream()); + XmlStreamInfo xmlInfo = detector.information(); + if (!xmlInfo.isValid()) { + // should be valid, because we checked it before + LOG.warn("Invalid XML document: {}", xmlInfo.getProblem().getMessage()); + return null; + } + return new RouteConfigurationBuilder() { @Override public void configure() throws Exception { - // instead of parsing the document NxM times (for each namespace x root element combination), - // we preparse it using XmlStreamDetector and then parse it fully knowing what's inside. - // we could even do better, by passing already preparsed information through config file, but - // it's getting complicated when using multiple files. - XmlStreamDetector detector = new XmlStreamDetector(resource.getInputStream()); - XmlStreamInfo xmlInfo = detector.information(); - if (!xmlInfo.isValid()) { - // should be valid, because we checked it before - LOG.warn("Invalid XML document: {}", xmlInfo.getProblem().getMessage()); - return; - } switch (xmlInfo.getRootElementName()) { case "routeTemplate", "routeTemplates" -> new ModelParser(resource, xmlInfo.getRootElementNamespace()) @@ -93,10 +93,11 @@ public class XmlRoutesBuilderLoader extends RouteBuilderLoaderSupport { @Override public void configuration() throws Exception { - for (String ns : NAMESPACES) { - new ModelParser(resource, ns) - .parseRouteConfigurationsDefinition() - .ifPresent(this::addConfigurations); + switch (xmlInfo.getRootElementName()) { + case "routeConfigurations", "routeConfiguration" -> + new ModelParser(resource, xmlInfo.getRootElementNamespace()) + .parseRouteConfigurationsDefinition() + .ifPresent(this::addConfigurations); } }