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);
                 }
             }
 

Reply via email to