ENTAXY-248 release 1.8.1
This commit is contained in:
201
underlying/LICENSE.txt
Normal file
201
underlying/LICENSE.txt
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed 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.
|
75
underlying/camel.entaxy/core/camel-base/pom.xml
Normal file
75
underlying/camel.entaxy/core/camel-base/pom.xml
Normal file
@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache.camel</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>3.4.5-ENTAXY</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>camel-base</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Camel :: Base :: Entaxy</name>
|
||||
<description>The Base Camel Framework</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- required dependencies by camel-base -->
|
||||
<dependency>
|
||||
<groupId>org.apache.camel</groupId>
|
||||
<artifactId>camel-api</artifactId>
|
||||
<version>${camel.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.camel</groupId>
|
||||
<artifactId>camel-management-api</artifactId>
|
||||
<version>${camel.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.camel</groupId>
|
||||
<artifactId>camel-support</artifactId>
|
||||
<version>${camel.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.camel</groupId>
|
||||
<artifactId>camel-util</artifactId>
|
||||
<version>${camel.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.camel</groupId>
|
||||
<artifactId>camel-jms</artifactId>
|
||||
<version>${camel.version}</version>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.apache.camel</groupId>-->
|
||||
<!-- <artifactId>camel-base</artifactId>-->
|
||||
<!-- <version>${camel.version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- required logging api dependency by camel-base -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j-api-version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,38 @@
|
||||
/* Generated by camel build tools - do NOT edit this file! */
|
||||
package org.apache.camel.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.TypeConversionException;
|
||||
import org.apache.camel.TypeConverterLoaderException;
|
||||
import org.apache.camel.spi.TypeConverterLoader;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.SimpleTypeConverter;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
import org.apache.camel.util.DoubleMap;
|
||||
|
||||
/**
|
||||
* Generated by camel build tools - do NOT edit this file!
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class CamelConverterLoader implements TypeConverterLoader {
|
||||
|
||||
public CamelConverterLoader() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
|
||||
registerConverters(registry);
|
||||
}
|
||||
|
||||
private void registerConverters(TypeConverterRegistry registry) {
|
||||
addTypeConverter(registry, org.apache.camel.Processor.class, org.apache.camel.Expression.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CamelConverter.toProcessor((org.apache.camel.Expression) value));
|
||||
addTypeConverter(registry, org.apache.camel.Processor.class, org.apache.camel.Predicate.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CamelConverter.toProcessor((org.apache.camel.Predicate) value));
|
||||
}
|
||||
|
||||
private static void addTypeConverter(TypeConverterRegistry registry, Class<?> toType, Class<?> fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) {
|
||||
registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/* Generated by camel build tools - do NOT edit this file! */
|
||||
package org.apache.camel.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.TypeConversionException;
|
||||
import org.apache.camel.TypeConverterLoaderException;
|
||||
import org.apache.camel.spi.TypeConverterLoader;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.SimpleTypeConverter;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
import org.apache.camel.util.DoubleMap;
|
||||
|
||||
/**
|
||||
* Generated by camel build tools - do NOT edit this file!
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class CollectionConverterLoader implements TypeConverterLoader {
|
||||
|
||||
public CollectionConverterLoader() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
|
||||
registerConverters(registry);
|
||||
}
|
||||
|
||||
private void registerConverters(TypeConverterRegistry registry) {
|
||||
addTypeConverter(registry, java.lang.Object[].class, java.util.Collection.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CollectionConverter.toArray((java.util.Collection) value));
|
||||
addTypeConverter(registry, java.util.ArrayList.class, java.util.Iterator.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CollectionConverter.toArrayList((java.util.Iterator) value));
|
||||
addTypeConverter(registry, java.util.HashMap.class, java.util.Map.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CollectionConverter.toHashMap((java.util.Map) value));
|
||||
addTypeConverter(registry, java.util.Hashtable.class, java.util.Map.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CollectionConverter.toHashtable((java.util.Map) value));
|
||||
addTypeConverter(registry, java.util.List.class, java.lang.Iterable.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CollectionConverter.toList((java.lang.Iterable) value));
|
||||
addTypeConverter(registry, java.util.List.class, java.lang.Object[].class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CollectionConverter.toList((java.lang.Object[]) value));
|
||||
addTypeConverter(registry, java.util.List.class, java.util.Collection.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CollectionConverter.toList((java.util.Collection) value));
|
||||
addTypeConverter(registry, java.util.Properties.class, java.util.Map.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CollectionConverter.toProperties((java.util.Map) value));
|
||||
addTypeConverter(registry, java.util.Set.class, java.lang.Object[].class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CollectionConverter.toSet((java.lang.Object[]) value));
|
||||
addTypeConverter(registry, java.util.Set.class, java.util.Collection.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CollectionConverter.toSet((java.util.Collection) value));
|
||||
addTypeConverter(registry, java.util.Set.class, java.util.Map.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.CollectionConverter.toSet((java.util.Map) value));
|
||||
}
|
||||
|
||||
private static void addTypeConverter(TypeConverterRegistry registry, Class<?> toType, Class<?> fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) {
|
||||
registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/* Generated by camel build tools - do NOT edit this file! */
|
||||
package org.apache.camel.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.TypeConversionException;
|
||||
import org.apache.camel.TypeConverterLoaderException;
|
||||
import org.apache.camel.spi.TypeConverterLoader;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.SimpleTypeConverter;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
import org.apache.camel.util.DoubleMap;
|
||||
|
||||
/**
|
||||
* Generated by camel build tools - do NOT edit this file!
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class DateTimeConverterLoader implements TypeConverterLoader {
|
||||
|
||||
public DateTimeConverterLoader() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
|
||||
registerConverters(registry);
|
||||
}
|
||||
|
||||
private void registerConverters(TypeConverterRegistry registry) {
|
||||
addTypeConverter(registry, java.lang.Long.class, java.util.Date.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.DateTimeConverter.toLong((java.util.Date) value));
|
||||
addTypeConverter(registry, java.util.Date.class, java.lang.Long.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.DateTimeConverter.toDate((java.lang.Long) value));
|
||||
addTypeConverter(registry, java.util.TimeZone.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.DateTimeConverter.toTimeZone((java.lang.String) value));
|
||||
}
|
||||
|
||||
private static void addTypeConverter(TypeConverterRegistry registry, Class<?> toType, Class<?> fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) {
|
||||
registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/* Generated by camel build tools - do NOT edit this file! */
|
||||
package org.apache.camel.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.TypeConversionException;
|
||||
import org.apache.camel.TypeConverterLoaderException;
|
||||
import org.apache.camel.spi.TypeConverterLoader;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.SimpleTypeConverter;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
import org.apache.camel.util.DoubleMap;
|
||||
|
||||
/**
|
||||
* Generated by camel build tools - do NOT edit this file!
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class DurationConverterLoader implements TypeConverterLoader {
|
||||
|
||||
public DurationConverterLoader() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
|
||||
registerConverters(registry);
|
||||
}
|
||||
|
||||
private void registerConverters(TypeConverterRegistry registry) {
|
||||
addTypeConverter(registry, java.lang.Long.class, java.time.Duration.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.DurationConverter.toMilliSeconds((java.time.Duration) value));
|
||||
addTypeConverter(registry, java.lang.String.class, java.time.Duration.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.DurationConverter.toString((java.time.Duration) value));
|
||||
addTypeConverter(registry, java.time.Duration.class, java.lang.Long.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.DurationConverter.toDuration((java.lang.Long) value));
|
||||
addTypeConverter(registry, java.time.Duration.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.DurationConverter.toDuration((java.lang.String) value));
|
||||
}
|
||||
|
||||
private static void addTypeConverter(TypeConverterRegistry registry, Class<?> toType, Class<?> fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) {
|
||||
registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/* Generated by camel build tools - do NOT edit this file! */
|
||||
package org.apache.camel.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.TypeConversionException;
|
||||
import org.apache.camel.TypeConverterLoaderException;
|
||||
import org.apache.camel.spi.TypeConverterLoader;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.SimpleTypeConverter;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
import org.apache.camel.util.DoubleMap;
|
||||
|
||||
/**
|
||||
* Generated by camel build tools - do NOT edit this file!
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class IOConverterLoader implements TypeConverterLoader {
|
||||
|
||||
public IOConverterLoader() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
|
||||
registerConverters(registry);
|
||||
}
|
||||
|
||||
private void registerConverters(TypeConverterRegistry registry) {
|
||||
addTypeConverter(registry, byte[].class, java.io.BufferedReader.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toByteArray((java.io.BufferedReader) value, exchange));
|
||||
addTypeConverter(registry, byte[].class, java.io.ByteArrayOutputStream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toByteArray((java.io.ByteArrayOutputStream) value));
|
||||
addTypeConverter(registry, byte[].class, java.io.File.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toByteArray((java.io.File) value));
|
||||
addTypeConverter(registry, byte[].class, java.io.InputStream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toBytes((java.io.InputStream) value));
|
||||
addTypeConverter(registry, byte[].class, java.io.Reader.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toByteArray((java.io.Reader) value, exchange));
|
||||
addTypeConverter(registry, byte[].class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toByteArray((java.lang.String) value, exchange));
|
||||
addTypeConverter(registry, java.io.BufferedReader.class, java.io.File.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toReader((java.io.File) value, exchange));
|
||||
addTypeConverter(registry, java.io.BufferedWriter.class, java.io.File.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toWriter((java.io.File) value, exchange));
|
||||
addTypeConverter(registry, java.io.File.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toFile((java.lang.String) value));
|
||||
addTypeConverter(registry, java.io.InputStream.class, byte[].class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toInputStream((byte[]) value));
|
||||
addTypeConverter(registry, java.io.InputStream.class, java.io.BufferedReader.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toInputStream((java.io.BufferedReader) value, exchange));
|
||||
addTypeConverter(registry, java.io.InputStream.class, java.io.ByteArrayOutputStream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toInputStream((java.io.ByteArrayOutputStream) value));
|
||||
addTypeConverter(registry, java.io.InputStream.class, java.io.File.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toInputStream((java.io.File) value));
|
||||
addTypeConverter(registry, java.io.InputStream.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toInputStream((java.lang.String) value, exchange));
|
||||
addTypeConverter(registry, java.io.InputStream.class, java.lang.StringBuffer.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toInputStream((java.lang.StringBuffer) value, exchange));
|
||||
addTypeConverter(registry, java.io.InputStream.class, java.lang.StringBuilder.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toInputStream((java.lang.StringBuilder) value, exchange));
|
||||
addTypeConverter(registry, java.io.InputStream.class, java.net.URL.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toInputStream((java.net.URL) value));
|
||||
addTypeConverter(registry, java.io.InputStream.class, java.util.stream.Stream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toInputStream((java.util.stream.Stream) value, exchange));
|
||||
addTypeConverter(registry, java.io.ObjectInput.class, java.io.InputStream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toObjectInput((java.io.InputStream) value, exchange));
|
||||
addTypeConverter(registry, java.io.ObjectOutput.class, java.io.OutputStream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toObjectOutput((java.io.OutputStream) value));
|
||||
addTypeConverter(registry, java.io.OutputStream.class, java.io.File.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toOutputStream((java.io.File) value));
|
||||
addTypeConverter(registry, java.io.Reader.class, byte[].class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toReader((byte[]) value, exchange));
|
||||
addTypeConverter(registry, java.io.Reader.class, java.io.InputStream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toReader((java.io.InputStream) value, exchange));
|
||||
addTypeConverter(registry, java.io.StringReader.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toReader((java.lang.String) value));
|
||||
addTypeConverter(registry, java.io.Writer.class, java.io.OutputStream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toWriter((java.io.OutputStream) value, exchange));
|
||||
addTypeConverter(registry, java.lang.String.class, byte[].class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toString((byte[]) value, exchange));
|
||||
addTypeConverter(registry, java.lang.String.class, java.io.BufferedReader.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toString((java.io.BufferedReader) value));
|
||||
addTypeConverter(registry, java.lang.String.class, java.io.ByteArrayOutputStream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toString((java.io.ByteArrayOutputStream) value, exchange));
|
||||
addTypeConverter(registry, java.lang.String.class, java.io.File.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toString((java.io.File) value, exchange));
|
||||
addTypeConverter(registry, java.lang.String.class, java.io.InputStream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toString((java.io.InputStream) value, exchange));
|
||||
addTypeConverter(registry, java.lang.String.class, java.io.Reader.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toString((java.io.Reader) value));
|
||||
addTypeConverter(registry, java.lang.String.class, java.net.URL.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toString((java.net.URL) value, exchange));
|
||||
addTypeConverter(registry, java.nio.ByteBuffer.class, java.io.InputStream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.covertToByteBuffer((java.io.InputStream) value));
|
||||
addTypeConverter(registry, java.util.Properties.class, java.io.File.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toProperties((java.io.File) value));
|
||||
addTypeConverter(registry, java.util.Properties.class, java.io.InputStream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toProperties((java.io.InputStream) value));
|
||||
addTypeConverter(registry, java.util.Properties.class, java.io.Reader.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.IOConverter.toProperties((java.io.Reader) value));
|
||||
}
|
||||
|
||||
private static void addTypeConverter(TypeConverterRegistry registry, Class<?> toType, Class<?> fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) {
|
||||
registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/* Generated by camel build tools - do NOT edit this file! */
|
||||
package org.apache.camel.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.TypeConversionException;
|
||||
import org.apache.camel.TypeConverterLoaderException;
|
||||
import org.apache.camel.spi.TypeConverterLoader;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.SimpleTypeConverter;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
import org.apache.camel.util.DoubleMap;
|
||||
|
||||
/**
|
||||
* Generated by camel build tools - do NOT edit this file!
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class NIOConverterLoader implements TypeConverterLoader {
|
||||
|
||||
public NIOConverterLoader() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
|
||||
registerConverters(registry);
|
||||
}
|
||||
|
||||
private void registerConverters(TypeConverterRegistry registry) {
|
||||
addTypeConverter(registry, byte[].class, java.nio.ByteBuffer.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.NIOConverter.toByteArray((java.nio.ByteBuffer) value));
|
||||
addTypeConverter(registry, java.io.InputStream.class, java.nio.ByteBuffer.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.NIOConverter.toInputStream((java.nio.ByteBuffer) value));
|
||||
addTypeConverter(registry, java.lang.String.class, java.nio.ByteBuffer.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.NIOConverter.toString((java.nio.ByteBuffer) value, exchange));
|
||||
addTypeConverter(registry, java.nio.ByteBuffer.class, byte[].class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.NIOConverter.toByteBuffer((byte[]) value));
|
||||
addTypeConverter(registry, java.nio.ByteBuffer.class, java.io.ByteArrayOutputStream.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.NIOConverter.toByteBuffer((java.io.ByteArrayOutputStream) value));
|
||||
addTypeConverter(registry, java.nio.ByteBuffer.class, java.io.File.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.NIOConverter.toByteBuffer((java.io.File) value));
|
||||
addTypeConverter(registry, java.nio.ByteBuffer.class, java.lang.Double.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.NIOConverter.toByteBuffer((java.lang.Double) value));
|
||||
addTypeConverter(registry, java.nio.ByteBuffer.class, java.lang.Float.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.NIOConverter.toByteBuffer((java.lang.Float) value));
|
||||
addTypeConverter(registry, java.nio.ByteBuffer.class, java.lang.Integer.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.NIOConverter.toByteBuffer((java.lang.Integer) value));
|
||||
addTypeConverter(registry, java.nio.ByteBuffer.class, java.lang.Long.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.NIOConverter.toByteBuffer((java.lang.Long) value));
|
||||
addTypeConverter(registry, java.nio.ByteBuffer.class, java.lang.Short.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.NIOConverter.toByteBuffer((java.lang.Short) value));
|
||||
addTypeConverter(registry, java.nio.ByteBuffer.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.NIOConverter.toByteBuffer((java.lang.String) value, exchange));
|
||||
}
|
||||
|
||||
private static void addTypeConverter(TypeConverterRegistry registry, Class<?> toType, Class<?> fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) {
|
||||
registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/* Generated by camel build tools - do NOT edit this file! */
|
||||
package org.apache.camel.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.TypeConversionException;
|
||||
import org.apache.camel.TypeConverterLoaderException;
|
||||
import org.apache.camel.spi.TypeConverterLoader;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.SimpleTypeConverter;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
import org.apache.camel.util.DoubleMap;
|
||||
|
||||
/**
|
||||
* Generated by camel build tools - do NOT edit this file!
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class ObjectConverterLoader implements TypeConverterLoader {
|
||||
|
||||
public ObjectConverterLoader() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
|
||||
registerConverters(registry);
|
||||
}
|
||||
|
||||
private void registerConverters(TypeConverterRegistry registry) {
|
||||
addTypeConverter(registry, boolean.class, java.lang.Object.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toBool(value));
|
||||
addTypeConverter(registry, char.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toChar((java.lang.String) value));
|
||||
addTypeConverter(registry, char[].class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toCharArray((java.lang.String) value));
|
||||
addTypeConverter(registry, java.lang.Boolean.class, java.lang.Object.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toBoolean(value));
|
||||
addTypeConverter(registry, java.lang.Boolean.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toBoolean((java.lang.String) value));
|
||||
addTypeConverter(registry, java.lang.Byte.class, java.lang.Number.class, true,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toByte((java.lang.Number) value));
|
||||
addTypeConverter(registry, java.lang.Byte.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toByte((java.lang.String) value));
|
||||
addTypeConverter(registry, java.lang.Character.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toCharacter((java.lang.String) value));
|
||||
addTypeConverter(registry, java.lang.Class.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toClass((java.lang.String) value, exchange));
|
||||
addTypeConverter(registry, java.lang.Double.class, java.lang.Number.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toDouble((java.lang.Number) value));
|
||||
addTypeConverter(registry, java.lang.Double.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toDouble((java.lang.String) value));
|
||||
addTypeConverter(registry, java.lang.Float.class, java.lang.Number.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toFloat((java.lang.Number) value));
|
||||
addTypeConverter(registry, java.lang.Float.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toFloat((java.lang.String) value));
|
||||
addTypeConverter(registry, java.lang.Integer.class, java.lang.Number.class, true,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toInteger((java.lang.Number) value));
|
||||
addTypeConverter(registry, java.lang.Integer.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toInteger((java.lang.String) value));
|
||||
addTypeConverter(registry, java.lang.Iterable.class, java.lang.Object.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.iterable(value));
|
||||
addTypeConverter(registry, java.lang.Long.class, java.lang.Number.class, true,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toLong((java.lang.Number) value));
|
||||
addTypeConverter(registry, java.lang.Long.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toLong((java.lang.String) value));
|
||||
addTypeConverter(registry, java.lang.Short.class, java.lang.Number.class, true,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toShort((java.lang.Number) value));
|
||||
addTypeConverter(registry, java.lang.Short.class, java.lang.String.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toShort((java.lang.String) value));
|
||||
addTypeConverter(registry, java.lang.String.class, char[].class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.fromCharArray((char[]) value));
|
||||
addTypeConverter(registry, java.lang.String.class, java.lang.Boolean.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toString((java.lang.Boolean) value));
|
||||
addTypeConverter(registry, java.lang.String.class, java.lang.Integer.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toString((java.lang.Integer) value));
|
||||
addTypeConverter(registry, java.lang.String.class, java.lang.Long.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toString((java.lang.Long) value));
|
||||
addTypeConverter(registry, java.lang.String.class, java.lang.StringBuffer.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toString((java.lang.StringBuffer) value));
|
||||
addTypeConverter(registry, java.lang.String.class, java.lang.StringBuilder.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toString((java.lang.StringBuilder) value));
|
||||
addTypeConverter(registry, java.math.BigInteger.class, java.lang.Object.class, true,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.toBigInteger(value));
|
||||
addTypeConverter(registry, java.util.Iterator.class, java.lang.Object.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.ObjectConverter.iterator(value));
|
||||
}
|
||||
|
||||
private static void addTypeConverter(TypeConverterRegistry registry, Class<?> toType, Class<?> fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) {
|
||||
registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/* Generated by camel build tools - do NOT edit this file! */
|
||||
package org.apache.camel.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.TypeConversionException;
|
||||
import org.apache.camel.TypeConverterLoaderException;
|
||||
import org.apache.camel.spi.TypeConverterLoader;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.SimpleTypeConverter;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
import org.apache.camel.util.DoubleMap;
|
||||
|
||||
/**
|
||||
* Generated by camel build tools - do NOT edit this file!
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class SQLConverterLoader implements TypeConverterLoader {
|
||||
|
||||
public SQLConverterLoader() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
|
||||
registerConverters(registry);
|
||||
}
|
||||
|
||||
private void registerConverters(TypeConverterRegistry registry) {
|
||||
addTypeConverter(registry, java.lang.Long.class, java.sql.Timestamp.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.SQLConverter.toLong((java.sql.Timestamp) value));
|
||||
addTypeConverter(registry, java.sql.Timestamp.class, java.lang.Long.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.converter.SQLConverter.toTimestamp((java.lang.Long) value));
|
||||
}
|
||||
|
||||
private static void addTypeConverter(TypeConverterRegistry registry, Class<?> toType, Class<?> fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) {
|
||||
registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/* Generated by camel build tools - do NOT edit this file! */
|
||||
package org.apache.camel.impl.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.TypeConversionException;
|
||||
import org.apache.camel.TypeConverterLoaderException;
|
||||
import org.apache.camel.spi.TypeConverterLoader;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.SimpleTypeConverter;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
import org.apache.camel.util.DoubleMap;
|
||||
|
||||
/**
|
||||
* Generated by camel build tools - do NOT edit this file!
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class UriTypeConverterLoader implements TypeConverterLoader {
|
||||
|
||||
public UriTypeConverterLoader() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
|
||||
registerConverters(registry);
|
||||
}
|
||||
|
||||
private void registerConverters(TypeConverterRegistry registry) {
|
||||
addTypeConverter(registry, java.lang.CharSequence.class, java.net.URI.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.impl.converter.UriTypeConverter.toCharSequence((java.net.URI) value));
|
||||
addTypeConverter(registry, java.net.URI.class, java.lang.CharSequence.class, false,
|
||||
(type, exchange, value) -> org.apache.camel.impl.converter.UriTypeConverter.toUri((java.lang.CharSequence) value));
|
||||
}
|
||||
|
||||
private static void addTypeConverter(TypeConverterRegistry registry, Class<?> toType, Class<?> fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) {
|
||||
registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# Generated by camel build tools - do NOT edit this file!
|
||||
org.apache.camel.converter.CamelConverterLoader
|
||||
org.apache.camel.converter.CollectionConverterLoader
|
||||
org.apache.camel.converter.DateTimeConverterLoader
|
||||
org.apache.camel.converter.DurationConverterLoader
|
||||
org.apache.camel.converter.IOConverterLoader
|
||||
org.apache.camel.converter.NIOConverterLoader
|
||||
org.apache.camel.converter.ObjectConverterLoader
|
||||
org.apache.camel.converter.SQLConverterLoader
|
||||
org.apache.camel.impl.converter.UriTypeConverterLoader
|
@ -0,0 +1,2 @@
|
||||
# Generated by camel build tools - do NOT edit this file!
|
||||
class=org.apache.camel.component.properties.PropertiesComponent
|
@ -0,0 +1,843 @@
|
||||
[[properties-component]]
|
||||
= Properties Component
|
||||
//Written by hand, not generated.
|
||||
:docTitle: Properties
|
||||
:artifactId: camel-base
|
||||
:description: The properties component is used for property placeholders in your Camel application, such as endpoint URIs.
|
||||
:since: 2.3
|
||||
:supportLevel: Stable
|
||||
|
||||
*Since Camel {since}*
|
||||
|
||||
The properties component is used for property placeholders in your Camel application, such as endpoint URIs.
|
||||
It is *not* a regular Camel component with producer and consumer for routing messages. However for historical
|
||||
reasons it was named `PropertiesComponent` and this name is commonly known and therfore we keep using it.
|
||||
|
||||
== Spring Boot Auto-Configuration
|
||||
|
||||
The component supports 10 options, which are listed below.
|
||||
|
||||
|
||||
|
||||
[width="100%",cols="2,5,^1,2",options="header"]
|
||||
|===
|
||||
| Name | Description | Default | Type
|
||||
| *camel.component.properties.auto-discover-properties-sources* | Whether to automatically discovery instances of PropertiesSource from registry and service factory. | true | Boolean
|
||||
| *camel.component.properties.default-fallback-enabled* | If false, the component does not attempt to find a default for the key by looking after the colon separator. | true | Boolean
|
||||
| *camel.component.properties.encoding* | Encoding to use when loading properties file from the file system or classpath. If no encoding has been set, then the properties files is loaded using ISO-8859-1 encoding (latin-1) as documented by java.util.Properties#load(java.io.InputStream) | | String
|
||||
| *camel.component.properties.environment-variable-mode* | Sets the OS environment variables mode (0 = never, 1 = fallback, 2 = override). The default mode (override) is to use OS environment variables if present, and override any existing properties. OS environment variable mode is checked before JVM system property mode | 2 | Integer
|
||||
| *camel.component.properties.ignore-missing-location* | Whether to silently ignore if a location cannot be located, such as a properties file not found. | false | Boolean
|
||||
| *camel.component.properties.initial-properties* | Sets initial properties which will be used before any locations are resolved. The option is a java.util.Properties type. | | String
|
||||
| *camel.component.properties.location* | A list of locations to load properties. You can use comma to separate multiple locations. This option will override any default locations and only use the locations from this option. | | String
|
||||
| *camel.component.properties.override-properties* | Sets a special list of override properties that take precedence and will use first, if a property exist. The option is a java.util.Properties type. | | String
|
||||
| *camel.component.properties.properties-parser* | To use a custom PropertiesParser. The option is a org.apache.camel.component.properties.PropertiesParser type. | | String
|
||||
| *camel.component.properties.system-properties-mode* | Sets the JVM system property mode (0 = never, 1 = fallback, 2 = override). The default mode (override) is to use system properties if present, and override any existing properties. OS environment variable mode is checked before JVM system property mode | 2 | Integer
|
||||
|===
|
||||
|
||||
[TIP]
|
||||
**Resolving property from Java code** +
|
||||
You can use the method `resolvePropertyPlaceholders` on the
|
||||
`CamelContext` to resolve a property from any Java code.
|
||||
|
||||
== Using PropertyPlaceholder
|
||||
|
||||
Camel now provides a new `PropertiesComponent` in *camel-core* which
|
||||
allows you to use property placeholders when defining Camel
|
||||
Endpoint URIs.
|
||||
|
||||
This works much like you would do if using Spring's
|
||||
`<property-placeholder>` tag. However Spring have a limitation which
|
||||
prevents 3rd party frameworks to leverage Spring property placeholders
|
||||
to the fullest. See more at
|
||||
xref:manual:faq:how-do-i-use-spring-property-placeholder-with-camel-xml.adoc[How do
|
||||
I use Spring Property Placeholder with Camel XML].
|
||||
|
||||
[TIP]
|
||||
**Bridging Spring and Camel property placeholders** +
|
||||
You can bridge the Spring property placeholder
|
||||
with Camel, see further below for more details.
|
||||
|
||||
The property placeholder is generally in use when doing:
|
||||
|
||||
* lookup or creating endpoints
|
||||
* lookup of beans in the Registry
|
||||
* additional supported in Spring XML (see below in examples)
|
||||
* using Blueprint PropertyPlaceholder with Camel
|
||||
xref:properties-component.adoc[Properties] component
|
||||
* using `@PropertyInject` to inject a property in a POJO
|
||||
* Using default value if a property does not exists
|
||||
* Include out of the box functions, to lookup property
|
||||
values from OS environment variables, JVM system properties, or the
|
||||
service idiom.
|
||||
* Using custom functions, which can be plugged into the
|
||||
property component.
|
||||
|
||||
== Syntax
|
||||
|
||||
The syntax to use Camel's property placeholder is to use `{\{key\}}` for
|
||||
example `{{file.uri}}` where `file.uri` is the property key.
|
||||
|
||||
You can use property placeholders in parts of the endpoint URI's which
|
||||
for example you can use placeholders for parameters in the URIs.
|
||||
|
||||
You can specify a default value to use if
|
||||
a property with the key does not exists, eg `file.url:/some/path` where
|
||||
the default value is the text after the colon (eg /some/path).
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Do not use colon in the property key. The colon is used as a separator
|
||||
token when you are providing a default value.
|
||||
====
|
||||
|
||||
== Defining location
|
||||
|
||||
The properties component need to know a location(s) where to resolve the
|
||||
properties. You can define 1 to many locations. If you define the
|
||||
location in a single String property you can separate multiple locations
|
||||
with comma such as:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
pc.setLocation("com/mycompany/myprop.properties,com/mycompany/other.properties");
|
||||
----
|
||||
|
||||
You can set which location can be discarded if missing by by setting the ``optional`` attribute, which is false by default, i.e:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
pc.setLocations(
|
||||
"com/mycompany/override.properties;optional=true"
|
||||
"com/mycompany/defaults.properties");
|
||||
----
|
||||
|
||||
== Using system and environment variables in locations
|
||||
|
||||
The location now supports using placeholders for JVM system properties
|
||||
and OS environments variables.
|
||||
|
||||
For example:
|
||||
|
||||
[source]
|
||||
----
|
||||
location=file:${karaf.home}/etc/foo.properties
|
||||
----
|
||||
|
||||
In the location above we defined a location using the file scheme using
|
||||
the JVM system property with key `karaf.home`.
|
||||
|
||||
To use an OS environment variable instead you would have to prefix with
|
||||
env:
|
||||
|
||||
[source]
|
||||
----
|
||||
location=file:${env:APP_HOME}/etc/foo.properties
|
||||
----
|
||||
|
||||
Where `APP_HOME` is an OS environment.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Some OS'es (such as Linux) do not support dashes in environment variable names,
|
||||
so here we are using `APP_HOME`. But if you specify `APP-HOME` then Camel 3 will automatic lookup
|
||||
the value as `APP_HOME` (with underscore) as fallback.
|
||||
====
|
||||
|
||||
You can have multiple placeholders in the same location, such as:
|
||||
|
||||
[source]
|
||||
----
|
||||
location=file:${env:APP_HOME}/etc/${prop.name}.properties
|
||||
----
|
||||
|
||||
== Configuring in Java DSL
|
||||
|
||||
You have to create and register the `PropertiesComponent` under the name
|
||||
`properties` such as:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
PropertiesComponent pc = camelContext.getPropertiesComponent();
|
||||
pc.setLocation("classpath:com/mycompany/myprop.properties");
|
||||
----
|
||||
|
||||
== Configuring in Spring XML
|
||||
|
||||
Spring XML offers two variations to configure. You can define a spring
|
||||
bean as a `PropertiesComponent` which resembles the way done in Java
|
||||
DSL. Or you can use the `<propertyPlaceholder>` tag.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="properties" class="org.apache.camel.component.properties.PropertiesComponent">
|
||||
<property name="location" value="classpath:com/mycompany/myprop.properties"/>
|
||||
</bean>
|
||||
----
|
||||
|
||||
Using the `<propertyPlaceholder>` tag makes the configuration a bit more
|
||||
fresh such as:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<camelContext ...>
|
||||
<propertyPlaceholder id="properties" location="com/mycompany/myprop.properties"/>
|
||||
</camelContext>
|
||||
----
|
||||
|
||||
Setting the properties location through the location tag works just fine but sometime you have a number of resources to take into account and starting from *Camel 2.19.0* you can set the properties location with a dedicated propertiesLocation:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<camelContext ...>
|
||||
<propertyPlaceholder id="myPropertyPlaceholder">
|
||||
<propertiesLocation
|
||||
resolver = "classpath"
|
||||
path = "com/my/company/something/my-properties-1.properties"
|
||||
optional = "false"/>
|
||||
<propertiesLocation
|
||||
resolver = "classpath"
|
||||
path = "com/my/company/something/my-properties-2.properties"
|
||||
optional = "false"/>
|
||||
<propertiesLocation
|
||||
resolver = "file"
|
||||
path = "${karaf.home}/etc/my-override.properties"
|
||||
optional = "true"/>
|
||||
</propertyPlaceholder>
|
||||
</camelContext>
|
||||
----
|
||||
|
||||
[TIP]
|
||||
**Specifying the cache option inside XML** +
|
||||
Camel supports specifying a value for the cache option both
|
||||
inside the Spring as well as the Blueprint XML.
|
||||
|
||||
== Using a Properties from the Registry
|
||||
|
||||
For example in OSGi you may want to expose a service which returns the
|
||||
properties as a `java.util.Properties` object.
|
||||
|
||||
Then you could setup the xref:properties-component.adoc[Properties] component as
|
||||
follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<propertyPlaceholder id="properties" location="ref:myProperties"/>
|
||||
----
|
||||
|
||||
Where `myProperties` is the id to use for lookup in the OSGi registry.
|
||||
Notice we use the `ref:` prefix to tell Camel that it should lookup the
|
||||
properties for the Registry.
|
||||
|
||||
== Examples using properties component
|
||||
|
||||
When using property placeholders in the endpoint URIs you can either use
|
||||
the `properties:` component or define the placeholders directly in the
|
||||
URI. We will show example of both cases, starting with the former.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
// properties
|
||||
cool.end=mock:result
|
||||
|
||||
// route
|
||||
from("direct:start").to("{{cool.end}}");
|
||||
----
|
||||
|
||||
You can also use placeholders as a part of the endpoint uri:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
// properties
|
||||
cool.foo=result
|
||||
|
||||
// route
|
||||
from("direct:start").to("mock:{{cool.foo}}");
|
||||
----
|
||||
|
||||
In the example above the to endpoint will be resolved to `mock:result`.
|
||||
|
||||
You can also have properties with refer to each other such as:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
// properties
|
||||
cool.foo=result
|
||||
cool.concat=mock:{{cool.foo}}
|
||||
|
||||
// route
|
||||
from("direct:start").to("mock:{{cool.concat}}");
|
||||
----
|
||||
|
||||
Notice how `cool.concat` refer to another property.
|
||||
|
||||
And you can use placeholders several times:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
// properties
|
||||
cool.start=direct:start
|
||||
cool.showid=true
|
||||
cool.result=result
|
||||
|
||||
// route
|
||||
from("{{cool.start}}")
|
||||
.to("log:{{cool.start}}?showBodyType=false&showExchangeId={{cool.showid}}")
|
||||
.to("mock:{{cool.result}}");
|
||||
----
|
||||
|
||||
You can also your property placeholders when using
|
||||
ProducerTemplate for example:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
template.sendBody("{{cool.start}}", "Hello World");
|
||||
----
|
||||
|
||||
== Example with xref:languages:simple-language.adoc[Simple] language
|
||||
|
||||
The xref:languages:simple-language.adoc[Simple] language now also support using property
|
||||
placeholders, for example in the route below:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
// properties
|
||||
cheese.quote=Camel rocks
|
||||
|
||||
// route
|
||||
from("direct:start")
|
||||
.transform().simple("Hi ${body} do you think ${properties:cheese.quote}?");
|
||||
----
|
||||
|
||||
== Additional property placeholder supported in Spring XML
|
||||
|
||||
The property placeholders is also supported in many of the Camel Spring
|
||||
XML tags such as
|
||||
`<package>, <packageScan>, <contextScan>, <jmxAgent>, <endpoint>, <routeBuilder>, <proxy>`
|
||||
and the others.
|
||||
|
||||
The example below has property placeholder in the `<jmxAgent>` tag:
|
||||
|
||||
You can also define property placeholders in the various attributes on
|
||||
the `<camelContext>` tag such as `trace` as shown here:
|
||||
|
||||
== Using JVM system properties or Environment variables as override or fallback values
|
||||
|
||||
The properties components supports using JVM system properties and also OS environment variables
|
||||
as values which can either be used as override or fallback values.
|
||||
|
||||
The default mode is that both of them are in override mode, and they are check in the following order:
|
||||
|
||||
1. OS environment variable (override mode)
|
||||
2. JVM system property (override mode)
|
||||
3. Property files and other locations
|
||||
4. OS environment variable (fallback mode)
|
||||
5. JVM system property (fallback mode)
|
||||
|
||||
The check stops at first found property value for the key.
|
||||
|
||||
You can control these modes using the `systemPropertiesMode` and `environmentVariableMode`
|
||||
options on the properties component.
|
||||
|
||||
== Using property placeholders for any kind of attribute in the XML DSL
|
||||
|
||||
In the example below we use the `prop` prefix for the namespace
|
||||
camel.apache.org/schema/placeholder by which we can use the
|
||||
`prop` prefix in the attributes in the XML DSLs. Notice how we use that
|
||||
in the Multicast to indicate that the option
|
||||
`stopOnException` should be the value of the placeholder with the key
|
||||
"stop".
|
||||
|
||||
In our properties file we have the value defined as
|
||||
|
||||
[source]
|
||||
----
|
||||
stop=true
|
||||
----
|
||||
|
||||
== Using Blueprint property placeholder with Camel routes
|
||||
|
||||
Camel supports Blueprint
|
||||
which also offers a property placeholder service. Camel supports
|
||||
convention over configuration, so all you have to do is to define the
|
||||
OSGi Blueprint property placeholder in the XML file as shown below:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
|
||||
xsi:schemaLocation="
|
||||
http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
|
||||
|
||||
<!-- OSGI blueprint property placeholder -->
|
||||
<cm:property-placeholder id="myblueprint.placeholder" persistent-id="camel.blueprint">
|
||||
<!-- list some properties as needed -->
|
||||
<cm:default-properties>
|
||||
<cm:property name="result" value="mock:result"/>
|
||||
</cm:default-properties>
|
||||
</cm:property-placeholder>
|
||||
|
||||
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
|
||||
<!-- in the route we can use {{ }} placeholders which will lookup in blueprint
|
||||
as Camel will auto detect the OSGi blueprint property placeholder and use it -->
|
||||
<route>
|
||||
<from uri="direct:start"/>
|
||||
<to uri="mock:foo"/>
|
||||
<to uri="{{result}}"/>
|
||||
</route>
|
||||
</camelContext>
|
||||
</blueprint>
|
||||
----
|
||||
|
||||
=== Using OSGi blueprint property placeholders in Camel routes
|
||||
|
||||
By default Camel detects and uses OSGi blueprint property placeholder
|
||||
service. You can disable this by setting the attribute
|
||||
`useBlueprintPropertyResolver` to false on the `<camelContext>`
|
||||
definition.
|
||||
|
||||
=== About placeholder syntax
|
||||
|
||||
Notice how we can use the Camel syntax for placeholders `{{` and `}}` in the
|
||||
Camel route, which will lookup the value from OSGi blueprint.
|
||||
|
||||
The blueprint syntax for placeholders is `${ }`. So outside the
|
||||
`<camelContext>` you must use the `${ }` syntax. Where as inside
|
||||
`<camelContext>` you must use `{{` and `}}` syntax.
|
||||
|
||||
OSGi blueprint allows you to configure the syntax, so you can actually
|
||||
align those if you want.
|
||||
|
||||
You can also explicit refer to a specific OSGi blueprint property
|
||||
placeholder by its id. For that you need to use the Camel's
|
||||
`<propertyPlaceholder>` as shown in the example below:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
|
||||
xsi:schemaLocation="
|
||||
http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
|
||||
|
||||
<!-- OSGI blueprint property placeholder -->
|
||||
<cm:property-placeholder id="myblueprint.placeholder" persistent-id="camel.blueprint">
|
||||
<!-- list some properties as needed -->
|
||||
<cm:default-properties>
|
||||
<cm:property name="prefix.result" value="mock:result"/>
|
||||
</cm:default-properties>
|
||||
</cm:property-placeholder>
|
||||
|
||||
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
|
||||
<!-- using Camel properties component and refer to the blueprint property placeholder by its id -->
|
||||
<propertyPlaceholder id="properties" location="blueprint:myblueprint.placeholder"/>
|
||||
|
||||
<!-- in the route we can use {{ }} placeholders which will lookup in blueprint -->
|
||||
<route>
|
||||
<from uri="direct:start"/>
|
||||
<to uri="mock:foo"/>
|
||||
<to uri="{{prefix.result}}"/>
|
||||
</route>
|
||||
</camelContext>
|
||||
</blueprint>
|
||||
----
|
||||
|
||||
|
||||
== Explicit referring to a OSGi blueprint placeholder in Camel
|
||||
|
||||
Notice how we use the `blueprint` scheme to refer to the OSGi blueprint
|
||||
placeholder by its id. This allows you to mix and match, for example you
|
||||
can also have additional schemes in the location. For example to load a
|
||||
file from the classpath you can do:
|
||||
|
||||
[source]
|
||||
----
|
||||
location="blueprint:myblueprint.placeholder,classpath:myproperties.properties"
|
||||
----
|
||||
|
||||
Each location is separated by comma.
|
||||
|
||||
== Overriding Blueprint property placeholders outside CamelContext
|
||||
|
||||
When using Blueprint property placeholder in the Blueprint XML file, you
|
||||
can declare the properties directly in the XML file as shown below:
|
||||
|
||||
Notice that we have a `<bean>` which refers to one of the properties. And
|
||||
in the Camel route we refer to the other using the `{{` and `}}` notation.
|
||||
|
||||
Now if you want to override these Blueprint properties from an unit
|
||||
test, you can do this as shown below:
|
||||
|
||||
To do this we override and implement the
|
||||
`useOverridePropertiesWithConfigAdmin` method. We can then put the
|
||||
properties we want to override on the given props parameter. And the
|
||||
return value *must* be the `persistence-id` of the
|
||||
`<cm:property-placeholder>` tag, which you define in the blueprint XML
|
||||
file.
|
||||
|
||||
== Using .cfg or .properties file for Blueprint property placeholders
|
||||
|
||||
When using Blueprint property placeholder in the Blueprint XML file, you
|
||||
can declare the properties in a `.properties` or `.cfg` file. If you use
|
||||
Apache ServieMix / Karaf then this container has a convention that it
|
||||
loads the properties from a file in the etc directory with the naming
|
||||
`etc/pid.cfg`, where `pid` is the `persistence-id`.
|
||||
|
||||
For example in the blueprint XML file we have the
|
||||
`persistence-id="stuff"`, which mean it will load the configuration file
|
||||
as `etc/stuff.cfg`.
|
||||
|
||||
Now if you want to unit test this blueprint XML file, then you can
|
||||
override the `loadConfigAdminConfigurationFile` and tell Camel which
|
||||
file to load as shown below:
|
||||
|
||||
Notice that this method requires to return a `String[]` with 2 values. The
|
||||
1st value is the path for the configuration file to load.
|
||||
The 2nd value is the `persistence-id` of the `<cm:property-placeholder>`
|
||||
tag.
|
||||
|
||||
The `stuff.cfg` file is just a plain properties file with the property
|
||||
placeholders such as:
|
||||
|
||||
[source]
|
||||
----
|
||||
== this is a comment
|
||||
greeting=Bye
|
||||
----
|
||||
|
||||
== Using .cfg file and overriding properties for Blueprint property placeholders
|
||||
|
||||
You can do both as well. Here is a complete example. First we have the
|
||||
Blueprint XML file:
|
||||
|
||||
And in the unit test class we do as follows:
|
||||
|
||||
And the `etc/stuff.cfg` configuration file contains
|
||||
|
||||
[source]
|
||||
----
|
||||
greeting=Bye
|
||||
echo=Yay
|
||||
destination=mock:result
|
||||
----
|
||||
|
||||
== Bridging Spring and Camel property placeholders
|
||||
|
||||
The Spring Framework does not allow 3rd party frameworks such as Apache
|
||||
Camel to seamless hook into the Spring property placeholder mechanism.
|
||||
However you can easily bridge Spring and Camel by declaring a Spring
|
||||
bean with the type
|
||||
`org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer`, which
|
||||
is a Spring
|
||||
`org.springframework.beans.factory.config.PropertyPlaceholderConfigurer`
|
||||
type.
|
||||
|
||||
To bridge Spring and Camel you must define a single bean as shown below:
|
||||
|
||||
*Bridging Spring and Camel property placeholders*
|
||||
|
||||
You *must not* use the spring <context:property-placeholder> namespace
|
||||
at the same time; this is not possible.
|
||||
|
||||
After declaring this bean, you can define property placeholders using
|
||||
both the Spring style, and the Camel style within the <camelContext> tag
|
||||
as shown below:
|
||||
|
||||
*Using bridge property placeholders*
|
||||
|
||||
Notice how the hello bean is using pure Spring property placeholders
|
||||
using the `${ }` notation. And in the Camel routes we use the Camel
|
||||
placeholder notation with `{{` and `}}`.
|
||||
|
||||
== Clashing Spring property placeholders with Camels Simple language
|
||||
|
||||
Take notice when using Spring bridging placeholder then the spring `${ }`
|
||||
syntax clashes with the xref:languages:simple-language.adoc[Simple] in Camel, and therefore
|
||||
take care. For example:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<setHeader name="Exchange.FILE_NAME">
|
||||
<simple>{{file.rootdir}}/${in.header.CamelFileName}</simple>
|
||||
</setHeader>
|
||||
----
|
||||
|
||||
clashes with Spring property placeholders, and you should use `$simple{ }`
|
||||
to indicate using the xref:languages:simple-language.adoc[Simple] language in Camel.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<setHeader name="Exchange.FILE_NAME">
|
||||
<simple>{{file.rootdir}}/$simple{in.header.CamelFileName}</simple>
|
||||
</setHeader>
|
||||
----
|
||||
|
||||
An alternative is to configure the `PropertyPlaceholderConfigurer` with
|
||||
`ignoreUnresolvablePlaceholders` option to `true`.
|
||||
|
||||
== Overriding properties from Camel test kit
|
||||
|
||||
When Testing with Camel and using the
|
||||
xref:properties-component.adoc[Properties] component, you may want to be able to
|
||||
provide the properties to be used from directly within the unit test
|
||||
source code. +
|
||||
Camel test kits, eg `CamelTestSupport` class offers the following methods
|
||||
|
||||
* `useOverridePropertiesWithPropertiesComponent`
|
||||
* `ignoreMissingLocationWithPropertiesComponent`
|
||||
|
||||
So for example in your unit test classes, you can override the
|
||||
`useOverridePropertiesWithPropertiesComponent` method and return a
|
||||
`java.util.Properties` that contains the properties which should be
|
||||
preferred to be used.
|
||||
|
||||
=== Providing properties from within unit test source
|
||||
|
||||
This can be done from any of the Camel Test kits, such as camel-test,
|
||||
camel-test-spring, and camel-test-blueprint.
|
||||
|
||||
The `ignoreMissingLocationWithPropertiesComponent` can be used to
|
||||
instruct Camel to ignore any locations which was not discoverable, for
|
||||
example if you run the unit test, in an environment that does not have
|
||||
access to the location of the properties.
|
||||
|
||||
== Using @PropertyInject
|
||||
|
||||
Camel allows to inject property placeholders in POJOs using the
|
||||
`@PropertyInject` annotation which can be set on fields and setter
|
||||
methods.
|
||||
|
||||
For example you can use that with `RouteBuilder` classes, such as shown
|
||||
below:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public class MyRouteBuilder extends RouteBuilder {
|
||||
|
||||
@PropertyInject("hello")
|
||||
private String greeting;
|
||||
|
||||
@Override
|
||||
public void configure() throws Exception {
|
||||
from("direct:start")
|
||||
.transform().constant(greeting)
|
||||
.to("{{result}}");
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Notice we have annotated the greeting field with `@PropertyInject` and
|
||||
define it to use the key `"hello"`. Camel will then lookup the property
|
||||
with this key and inject its value, converted to a String type.
|
||||
|
||||
You can also use multiple placeholders and text in the key, for example
|
||||
we can do:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@PropertyInject("Hello {{name}} how are you?")
|
||||
private String greeting;
|
||||
----
|
||||
|
||||
This will lookup the placeholder with they key `"name"`.
|
||||
|
||||
You can also add a default value if the key does not exists, such as:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@PropertyInject(value = "myTimeout", defaultValue = "5000")
|
||||
private int timeout;
|
||||
----
|
||||
|
||||
== Using out of the box functions
|
||||
|
||||
The xref:properties-component.adoc[Properties] component includes the following
|
||||
functions out of the box
|
||||
|
||||
* `env` - A function to lookup the property from OS environment variables
|
||||
* `sys` - A function to lookup the property from Java JVM system
|
||||
properties
|
||||
* `service` - A function to lookup the property from OS environment
|
||||
variables using the service naming idiom
|
||||
* `service.name` - A function to lookup the
|
||||
property from OS environment variables using the service naming idiom
|
||||
returning the hostname part only
|
||||
* `service.port` - A function to lookup the
|
||||
property from OS environment variables using the service naming idiom
|
||||
returning the port part only
|
||||
|
||||
As you can see these functions is intended to make it easy to lookup
|
||||
values from the environment. As they are provided out of the box, they
|
||||
can easily be used as shown below:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
|
||||
<route>
|
||||
<from uri="direct:start"/>
|
||||
<to uri="{`{env:SOMENAME}`}"/>
|
||||
<to uri="{`{sys:MyJvmPropertyName}`}"/>
|
||||
</route>
|
||||
</camelContext>
|
||||
----
|
||||
|
||||
You can use default values as well, so if the property does not exists,
|
||||
you can define a default value as shown below, where the default value
|
||||
is a `log:foo` and `log:bar` value.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
|
||||
<route>
|
||||
<from uri="direct:start"/>
|
||||
<to uri="{`{env:SOMENAME:log:foo}`}"/>
|
||||
<to uri="{`{sys:MyJvmPropertyName:log:bar}`}"/>
|
||||
</route>
|
||||
</camelContext>
|
||||
----
|
||||
|
||||
The service function is for looking up a service which is defined using
|
||||
OS environment variables using the service naming idiom, to refer to a
|
||||
service location using `hostname : port`
|
||||
|
||||
* __NAME__**_SERVICE_HOST**
|
||||
* __NAME__**_SERVICE_PORT**
|
||||
|
||||
in other words the service uses `_SERVICE_HOST` and `_SERVICE_PORT` as
|
||||
prefix. So if the service is named FOO, then the OS environment
|
||||
variables should be set as
|
||||
|
||||
[source]
|
||||
----
|
||||
export $FOO_SERVICE_HOST=myserver
|
||||
export $FOO_SERVICE_PORT=8888
|
||||
----
|
||||
|
||||
For example if the FOO service a remote HTTP service, then we can refer
|
||||
to the service in the Camel endpoint uri, and use
|
||||
the HTTP component to make the HTTP call:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
|
||||
<route>
|
||||
<from uri="direct:start"/>
|
||||
<to uri="http://{`{service:FOO}`}/myapp"/>
|
||||
</route>
|
||||
</camelContext>
|
||||
----
|
||||
|
||||
And we can use default values if the service has not been defined, for
|
||||
example to call a service on localhost, maybe for unit testing etc
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
|
||||
<route>
|
||||
<from uri="direct:start"/>
|
||||
<to uri="http://{`{service:FOO:localhost:8080}`}/myapp"/>
|
||||
</route>
|
||||
</camelContext>
|
||||
----
|
||||
|
||||
== Using custom functions (advanced)
|
||||
|
||||
The xref:properties-component.adoc[Properties] component allow to plugin 3rd party
|
||||
functions which can be used during parsing of the property placeholders.
|
||||
These functions are then able to do custom logic to resolve the
|
||||
placeholders, such as looking up in databases, do custom computations,
|
||||
or whatnot. The name of the function becomes the prefix used in the
|
||||
placeholder. This is best illustrated in the example code below
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="beerFunction" class="MyBeerFunction"/>
|
||||
|
||||
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
|
||||
<propertyPlaceholder id="properties">
|
||||
<propertiesFunction ref="beerFunction"/>
|
||||
</propertyPlaceholder>
|
||||
|
||||
<route>
|
||||
<from uri="direct:start"/>
|
||||
<to uri="{`{beer:FOO}`}"/>
|
||||
<to uri="{`{beer:BAR}`}"/>
|
||||
</route>
|
||||
</camelContext>
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The location attribute (on propertyPlaceholder tag) is not mandatory
|
||||
====
|
||||
|
||||
Here we have a Camel XML route where we have defined the
|
||||
`<propertyPlaceholder>` to use a custom function, which we refer to be the
|
||||
bean id - eg the `beerFunction`. As the beer function uses `"beer"` as its
|
||||
name, then the placeholder syntax can trigger the beer function by
|
||||
starting with `beer:value`.
|
||||
|
||||
The implementation of the function is only two methods as shown below:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public static final class MyBeerFunction implements PropertiesFunction {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "beer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String remainder) {
|
||||
return "mock:" + remainder.toLowerCase();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The function must implement
|
||||
the `org.apache.camel.component.properties.PropertiesFunction`
|
||||
interface. The method `getName` is the name of the function, eg beer.
|
||||
And the `apply` method is where we implement the custom logic to do. As
|
||||
the sample code is from an unit test, it just returns a value to refer
|
||||
to a mock endpoint.
|
||||
|
||||
To register a custom function from Java code is as shown below:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
PropertiesComponent pc = (org.apache.camel.componennt.properties.PropertiesComponent) context.getPropertiesComponent();
|
||||
pc.addFunction(new MyBeerFunction());
|
||||
----
|
||||
|
||||
|
||||
== Using 3rd-party properties sources
|
||||
|
||||
The properties component allows to plugin 3rd party sources to load and lookup properties via the `PropertySource`
|
||||
API from camel-api. For example the `camel-microprofile-config` component is implemented using this.
|
||||
The 3rd-party `PropertySource` can automatic be discoverd from classpath when Camel is starting up.
|
||||
This is done by include the file `META-INF/services/org/apache/camel/property-source-factory` file
|
||||
which refers to the fully qualified class name of the `PropertySource` implementation.
|
||||
See the `camel-microprofile-config` for an example.
|
||||
|
||||
You can also register 3rd-part property sources via Java API
|
||||
|
||||
[source,java]
|
||||
----
|
||||
PropertiesComponent pc = ...
|
||||
pc.addPropertySource(myPropertySource);
|
||||
----
|
||||
|
||||
=== LoadablePropertySource
|
||||
|
||||
A `PropertySource` can define that it supports loading all its properties from the source at once,
|
||||
for example from file system. This allows Camel properties component to load these properties at once
|
||||
during startup.
|
||||
|
||||
=== PropertySource
|
||||
|
||||
The regular `PropertySource` will lookup the property on-demand, for example to lookup
|
||||
values from a backend source such as a database or HashiCorp Vault etc.
|
||||
|
||||
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.spi.LoadablePropertiesSource;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.apache.camel.util.OrderedProperties;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Base class for {@link LoadablePropertiesSource} which can load properties from a source such as classpath or file system.
|
||||
*/
|
||||
public abstract class AbstractLocationPropertiesSource extends ServiceSupport implements LoadablePropertiesSource, LocationPropertiesSource {
|
||||
|
||||
private final Properties properties = new OrderedProperties();
|
||||
private final org.apache.camel.component.properties.PropertiesComponent propertiesComponent;
|
||||
private final org.apache.camel.component.properties.PropertiesLocation location;
|
||||
|
||||
protected AbstractLocationPropertiesSource(org.apache.camel.component.properties.PropertiesComponent propertiesComponent, org.apache.camel.component.properties.PropertiesLocation location) {
|
||||
this.propertiesComponent = propertiesComponent;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
abstract Properties loadPropertiesFromLocation(PropertiesComponent propertiesComponent, org.apache.camel.component.properties.PropertiesLocation location);
|
||||
|
||||
@Override
|
||||
public PropertiesLocation getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties loadProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties loadProperties(Predicate<String> filter) {
|
||||
Properties answer = new OrderedProperties();
|
||||
|
||||
for (String name: properties.stringPropertyNames()) {
|
||||
if (filter.test(name)) {
|
||||
answer.put(name, properties.get(name));
|
||||
}
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String name) {
|
||||
return properties.getProperty(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() throws Exception {
|
||||
super.doInit();
|
||||
|
||||
Properties prop = loadPropertiesFromLocation(propertiesComponent, location);
|
||||
if (prop != null) {
|
||||
prop = prepareLoadedProperties(prop);
|
||||
properties.putAll(prop);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy to prepare loaded properties before being used by Camel.
|
||||
* <p/>
|
||||
* This implementation will ensure values are trimmed, as loading properties from
|
||||
* a file with values having trailing spaces is not automatic trimmed by the Properties API
|
||||
* from the JDK.
|
||||
*
|
||||
* @param properties the properties
|
||||
* @return the prepared properties
|
||||
*/
|
||||
protected static Properties prepareLoadedProperties(Properties properties) {
|
||||
Properties answer = new OrderedProperties();
|
||||
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
|
||||
Object key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof String) {
|
||||
String s = (String) value;
|
||||
|
||||
// trim any trailing spaces which can be a problem when loading from
|
||||
// a properties file, note that java.util.Properties does already this
|
||||
// for any potential leading spaces so there's nothing to do there
|
||||
value = trimTrailingWhitespaces(s);
|
||||
}
|
||||
answer.put(key, value);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
private static String trimTrailingWhitespaces(String s) {
|
||||
int endIndex = s.length();
|
||||
for (int index = s.length() - 1; index >= 0; index--) {
|
||||
if (s.charAt(index) == ' ') {
|
||||
endIndex = index;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
String answer = s.substring(0, endIndex);
|
||||
return answer;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.RuntimeCamelException;
|
||||
import org.apache.camel.util.IOHelper;
|
||||
import org.apache.camel.util.OrderedProperties;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Properties;
|
||||
|
||||
public class ClasspathPropertiesSource extends AbstractLocationPropertiesSource {
|
||||
|
||||
public ClasspathPropertiesSource(org.apache.camel.component.properties.PropertiesComponent propertiesComponent, org.apache.camel.component.properties.PropertiesLocation location) {
|
||||
super(propertiesComponent, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ClasspathPropertiesSource[" + getLocation().getPath() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Properties loadPropertiesFromLocation(PropertiesComponent propertiesComponent, PropertiesLocation location) {
|
||||
Properties answer = new OrderedProperties();
|
||||
String path = location.getPath();
|
||||
|
||||
InputStream is = propertiesComponent.getCamelContext().getClassResolver().loadResourceAsStream(path);
|
||||
Reader reader = null;
|
||||
if (is == null) {
|
||||
if (!propertiesComponent.isIgnoreMissingLocation() && !location.isOptional()) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(new FileNotFoundException("Properties file " + path + " not found in classpath"));
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (propertiesComponent.getEncoding() != null) {
|
||||
reader = new BufferedReader(new InputStreamReader(is, propertiesComponent.getEncoding()));
|
||||
answer.load(reader);
|
||||
} else {
|
||||
answer.load(is);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
} finally {
|
||||
IOHelper.close(reader, is);
|
||||
}
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.NoTypeConversionAvailableException;
|
||||
import org.apache.camel.RuntimeCamelException;
|
||||
import org.apache.camel.spi.PropertiesSource;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Default {@link PropertiesLookup} which lookup properties from a {@link java.util.Properties} with all existing properties.
|
||||
*/
|
||||
public class DefaultPropertiesLookup implements PropertiesLookup {
|
||||
|
||||
private final org.apache.camel.component.properties.PropertiesComponent component;
|
||||
|
||||
public DefaultPropertiesLookup(PropertiesComponent component) {
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String lookup(String name) {
|
||||
try {
|
||||
return doLookup(name);
|
||||
} catch (NoTypeConversionAvailableException e) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String doLookup(String name) throws NoTypeConversionAvailableException {
|
||||
String answer = null;
|
||||
|
||||
// override takes precedence
|
||||
if (component.getOverrideProperties() != null) {
|
||||
// use get as the value can potentially be stored as a non string value
|
||||
Object value = component.getOverrideProperties().get(name);
|
||||
if (value != null) {
|
||||
answer = component.getCamelContext().getTypeConverter().mandatoryConvertTo(String.class, value);
|
||||
}
|
||||
}
|
||||
if (answer == null) {
|
||||
// try till first found source
|
||||
Iterator<PropertiesSource> it2 = component.getSources().iterator();
|
||||
while (answer == null && it2.hasNext()) {
|
||||
answer = it2.next().getProperty(name);
|
||||
}
|
||||
}
|
||||
// initial properties are last
|
||||
if (answer == null && component.getInitialProperties() != null) {
|
||||
// use get as the value can potentially be stored as a non string value
|
||||
Object value = component.getInitialProperties().get(name);
|
||||
if (value != null) {
|
||||
answer = component.getCamelContext().getTypeConverter().mandatoryConvertTo(String.class, value);
|
||||
}
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
}
|
@ -0,0 +1,348 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.spi.PropertiesFunction;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
import org.apache.camel.util.StringHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.camel.spi.PropertiesComponent.PREFIX_TOKEN;
|
||||
import static org.apache.camel.spi.PropertiesComponent.SUFFIX_TOKEN;
|
||||
import static org.apache.camel.util.IOHelper.lookupEnvironmentVariable;
|
||||
|
||||
/**
|
||||
* A parser to parse a string which contains property placeholders.
|
||||
*/
|
||||
public class DefaultPropertiesParser implements PropertiesParser {
|
||||
private static final String GET_OR_ELSE_TOKEN = ":";
|
||||
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private org.apache.camel.component.properties.PropertiesComponent propertiesComponent;
|
||||
|
||||
public DefaultPropertiesParser() {
|
||||
}
|
||||
|
||||
public DefaultPropertiesParser(org.apache.camel.component.properties.PropertiesComponent propertiesComponent) {
|
||||
this.propertiesComponent = propertiesComponent;
|
||||
}
|
||||
|
||||
public org.apache.camel.component.properties.PropertiesComponent getPropertiesComponent() {
|
||||
return propertiesComponent;
|
||||
}
|
||||
|
||||
public void setPropertiesComponent(org.apache.camel.component.properties.PropertiesComponent propertiesComponent) {
|
||||
this.propertiesComponent = propertiesComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parseUri(String text, org.apache.camel.component.properties.PropertiesLookup properties, boolean defaultFallbackEnabled) throws IllegalArgumentException {
|
||||
ParsingContext context = new ParsingContext(properties, defaultFallbackEnabled);
|
||||
return context.parse(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parseProperty(String key, String value, org.apache.camel.component.properties.PropertiesLookup properties) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This inner class helps replacing properties.
|
||||
*/
|
||||
private final class ParsingContext {
|
||||
private final org.apache.camel.component.properties.PropertiesLookup properties;
|
||||
private final boolean defaultFallbackEnabled;
|
||||
|
||||
ParsingContext(PropertiesLookup properties, boolean defaultFallbackEnabled) {
|
||||
this.properties = properties;
|
||||
this.defaultFallbackEnabled = defaultFallbackEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given input string and replaces all properties
|
||||
*
|
||||
* @param input Input string
|
||||
* @return Evaluated string
|
||||
*/
|
||||
public String parse(String input) {
|
||||
return doParse(input, new HashSet<String>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively parses the given input string and replaces all properties
|
||||
*
|
||||
* @param input Input string
|
||||
* @param replacedPropertyKeys Already replaced property keys used for tracking circular references
|
||||
* @return Evaluated string
|
||||
*/
|
||||
private String doParse(String input, Set<String> replacedPropertyKeys) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
String answer = input;
|
||||
Property property;
|
||||
while ((property = readProperty(answer)) != null) {
|
||||
// Check for circular references
|
||||
if (replacedPropertyKeys.contains(property.getKey())) {
|
||||
throw new IllegalArgumentException("Circular reference detected with key [" + property.getKey() + "] from text: " + input);
|
||||
}
|
||||
|
||||
Set<String> newReplaced = new HashSet<>(replacedPropertyKeys);
|
||||
newReplaced.add(property.getKey());
|
||||
|
||||
String before = answer.substring(0, property.getBeginIndex());
|
||||
String after = answer.substring(property.getEndIndex());
|
||||
answer = before + doParse(property.getValue(), newReplaced) + after;
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a property in the given string. It returns {@code null} if there's no property defined.
|
||||
*
|
||||
* @param input Input string
|
||||
* @return A property in the given string or {@code null} if not found
|
||||
*/
|
||||
private Property readProperty(String input) {
|
||||
// Find the index of the first valid suffix token
|
||||
int suffix = getSuffixIndex(input);
|
||||
|
||||
// If not found, ensure that there is no valid prefix token in the string
|
||||
if (suffix == -1) {
|
||||
if (getMatchingPrefixIndex(input, input.length()) != -1) {
|
||||
throw new IllegalArgumentException(String.format("Missing %s from the text: %s", SUFFIX_TOKEN, input));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the index of the prefix token that matches the suffix token
|
||||
int prefix = getMatchingPrefixIndex(input, suffix);
|
||||
if (prefix == -1) {
|
||||
throw new IllegalArgumentException(String.format("Missing %s from the text: %s", PREFIX_TOKEN, input));
|
||||
}
|
||||
|
||||
String key = input.substring(prefix + PREFIX_TOKEN.length(), suffix);
|
||||
String value = getPropertyValue(key, input);
|
||||
return new Property(prefix, suffix + SUFFIX_TOKEN.length(), key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first index of the suffix token that is not surrounded by quotes
|
||||
*
|
||||
* @param input Input string
|
||||
* @return First index of the suffix token that is not surrounded by quotes
|
||||
*/
|
||||
private int getSuffixIndex(String input) {
|
||||
int index = -1;
|
||||
do {
|
||||
index = input.indexOf(SUFFIX_TOKEN, index + 1);
|
||||
} while (index != -1 && isQuoted(input, index, SUFFIX_TOKEN));
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the prefix token that matches the suffix at the given index and that is not surrounded by quotes
|
||||
*
|
||||
* @param input Input string
|
||||
* @param suffixIndex Index of the suffix token
|
||||
* @return Index of the prefix token that matches the suffix at the given index and that is not surrounded by quotes
|
||||
*/
|
||||
private int getMatchingPrefixIndex(String input, int suffixIndex) {
|
||||
int index = suffixIndex;
|
||||
do {
|
||||
index = input.lastIndexOf(PREFIX_TOKEN, index - 1);
|
||||
} while (index != -1 && isQuoted(input, index, PREFIX_TOKEN));
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not the token at the given index is surrounded by single or double quotes
|
||||
*
|
||||
* @param input Input string
|
||||
* @param index Index of the token
|
||||
* @param token Token
|
||||
* @return {@code true}
|
||||
*/
|
||||
private boolean isQuoted(String input, int index, String token) {
|
||||
int beforeIndex = index - 1;
|
||||
int afterIndex = index + token.length();
|
||||
if (beforeIndex >= 0 && afterIndex < input.length()) {
|
||||
char before = input.charAt(beforeIndex);
|
||||
char after = input.charAt(afterIndex);
|
||||
return (before == after) && (before == '\'' || before == '"');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property with given key
|
||||
*
|
||||
* @param key Key of the property
|
||||
* @param input Input string (used for exception message if value not found)
|
||||
* @return Value of the property with the given key
|
||||
*/
|
||||
private String getPropertyValue(String key, String input) {
|
||||
|
||||
// the key may be a function, so lets check this first
|
||||
if (propertiesComponent != null) {
|
||||
for (PropertiesFunction function : propertiesComponent.getFunctions().values()) {
|
||||
String token = function.getName() + ":";
|
||||
if (key.startsWith(token)) {
|
||||
String remainder = key.substring(token.length());
|
||||
log.debug("Property with key [{}] is applied by function [{}]", key, function.getName());
|
||||
String value = function.apply(remainder);
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("Property with key [" + key + "] using function [" + function.getName() + "]"
|
||||
+ " returned null value which is not allowed, from input: " + input);
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Property with key [{}] applied by function [{}] -> {}", key, function.getName(), value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// they key may have a get or else expression
|
||||
String defaultValue = null;
|
||||
if (defaultFallbackEnabled && key.contains(GET_OR_ELSE_TOKEN)) {
|
||||
defaultValue = StringHelper.after(key, GET_OR_ELSE_TOKEN);
|
||||
key = StringHelper.before(key, GET_OR_ELSE_TOKEN);
|
||||
}
|
||||
|
||||
String value = doGetPropertyValue(key);
|
||||
if (value == null && defaultValue != null) {
|
||||
log.debug("Property with key [{}] not found, using default value: {}", key, defaultValue);
|
||||
value = defaultValue;
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
StringBuilder esb = new StringBuilder();
|
||||
esb.append("Property with key [").append(key).append("] ");
|
||||
esb.append("not found in properties from text: ").append(input);
|
||||
throw new IllegalArgumentException(esb.toString());
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property with the given key, it returns {@code null} if the property is not found
|
||||
*
|
||||
* @param key Key of the property
|
||||
* @return Value of the property or {@code null} if not found
|
||||
*/
|
||||
private String doGetPropertyValue(String key) {
|
||||
if (ObjectHelper.isEmpty(key)) {
|
||||
return parseProperty(key, null, properties);
|
||||
}
|
||||
|
||||
String value = null;
|
||||
|
||||
// override is the default mode for ENV
|
||||
int envMode = propertiesComponent != null ? propertiesComponent.getEnvironmentVariableMode() : org.apache.camel.component.properties.PropertiesComponent.ENVIRONMENT_VARIABLES_MODE_FALLBACK;
|
||||
// override is the default mode for SYS
|
||||
int sysMode = propertiesComponent != null ? propertiesComponent.getSystemPropertiesMode() : org.apache.camel.component.properties.PropertiesComponent.SYSTEM_PROPERTIES_MODE_OVERRIDE;
|
||||
|
||||
if (envMode == org.apache.camel.component.properties.PropertiesComponent.ENVIRONMENT_VARIABLES_MODE_OVERRIDE) {
|
||||
value = lookupEnvironmentVariable(key);
|
||||
if (value != null) {
|
||||
log.debug("Found an OS environment property: {} with value: {} to be used.", key, value);
|
||||
}
|
||||
}
|
||||
if (value == null && sysMode == org.apache.camel.component.properties.PropertiesComponent.SYSTEM_PROPERTIES_MODE_OVERRIDE) {
|
||||
value = System.getProperty(key);
|
||||
if (value != null) {
|
||||
log.debug("Found a JVM system property: {} with value: {} to be used.", key, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (value == null && properties != null) {
|
||||
value = properties.lookup(key);
|
||||
if (value != null) {
|
||||
log.debug("Found property: {} with value: {} to be used.", key, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (value == null && envMode == org.apache.camel.component.properties.PropertiesComponent.ENVIRONMENT_VARIABLES_MODE_FALLBACK) {
|
||||
value = lookupEnvironmentVariable(key);
|
||||
if (value != null) {
|
||||
log.debug("Found an OS environment property: {} with value: {} to be used.", key, value);
|
||||
}
|
||||
}
|
||||
if (value == null && sysMode == PropertiesComponent.SYSTEM_PROPERTIES_MODE_FALLBACK) {
|
||||
value = System.getProperty(key);
|
||||
if (value != null) {
|
||||
log.debug("Found a JVM system property: {} with value: {} to be used.", key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return parseProperty(key, value, properties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This inner class is the definition of a property used in a string
|
||||
*/
|
||||
private static final class Property {
|
||||
private final int beginIndex;
|
||||
private final int endIndex;
|
||||
private final String key;
|
||||
private final String value;
|
||||
|
||||
private Property(int beginIndex, int endIndex, String key, String value) {
|
||||
this.beginIndex = beginIndex;
|
||||
this.endIndex = endIndex;
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the begin index of the property (including the prefix token).
|
||||
*/
|
||||
public int getBeginIndex() {
|
||||
return beginIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the end index of the property (including the suffix token).
|
||||
*/
|
||||
public int getEndIndex() {
|
||||
return endIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key of the property.
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property.
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.spi.PropertiesFunction;
|
||||
import org.apache.camel.util.StringHelper;
|
||||
|
||||
/**
|
||||
* A {@link PropertiesFunction} that lookup the property value from
|
||||
* OS environment variables.
|
||||
*/
|
||||
public class EnvPropertiesFunction implements PropertiesFunction {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "env";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String remainder) {
|
||||
String key = remainder;
|
||||
String defaultValue = null;
|
||||
|
||||
if (remainder.contains(":")) {
|
||||
key = StringHelper.before(remainder, ":");
|
||||
defaultValue = StringHelper.after(remainder, ":");
|
||||
}
|
||||
|
||||
// lookup OS environment variable using upper case key
|
||||
key = key.toUpperCase();
|
||||
|
||||
String value = System.getenv(key);
|
||||
// some OS do not support dashes in keys, so replace with underscore
|
||||
if (value == null) {
|
||||
String noDashKey = key.replace('-', '_');
|
||||
value = System.getenv(noDashKey);
|
||||
}
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.RuntimeCamelException;
|
||||
import org.apache.camel.util.IOHelper;
|
||||
import org.apache.camel.util.OrderedProperties;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Properties;
|
||||
|
||||
public class FilePropertiesSource extends AbstractLocationPropertiesSource {
|
||||
|
||||
protected FilePropertiesSource(org.apache.camel.component.properties.PropertiesComponent propertiesComponent, PropertiesLocation location) {
|
||||
super(propertiesComponent, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "FilePropertiesSource[" + getLocation().getPath() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Properties loadPropertiesFromLocation(PropertiesComponent propertiesComponent, PropertiesLocation location) {
|
||||
Properties answer = new OrderedProperties();
|
||||
String path = location.getPath();
|
||||
|
||||
InputStream is = null;
|
||||
Reader reader = null;
|
||||
try {
|
||||
is = new FileInputStream(path);
|
||||
if (propertiesComponent.getEncoding() != null) {
|
||||
reader = new BufferedReader(new InputStreamReader(is, propertiesComponent.getEncoding()));
|
||||
answer.load(reader);
|
||||
} else {
|
||||
answer.load(is);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
if (!propertiesComponent.isIgnoreMissingLocation() && !location.isOptional()) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
} finally {
|
||||
IOHelper.close(reader, is);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.spi.PropertiesSource;
|
||||
|
||||
/**
|
||||
* A {@link PropertiesSource} which was created from a {@link PropertiesLocation}.
|
||||
*/
|
||||
public interface LocationPropertiesSource extends PropertiesSource {
|
||||
|
||||
/**
|
||||
* Gets the location of the properties
|
||||
*/
|
||||
PropertiesLocation getLocation();
|
||||
}
|
@ -0,0 +1,634 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.CamelContextAware;
|
||||
import org.apache.camel.ExtendedCamelContext;
|
||||
import org.apache.camel.StaticService;
|
||||
import org.apache.camel.api.management.ManagedAttribute;
|
||||
import org.apache.camel.api.management.ManagedResource;
|
||||
import org.apache.camel.spi.FactoryFinder;
|
||||
import org.apache.camel.spi.LoadablePropertiesSource;
|
||||
import org.apache.camel.spi.PropertiesFunction;
|
||||
import org.apache.camel.spi.PropertiesSource;
|
||||
import org.apache.camel.spi.annotations.JdkService;
|
||||
import org.apache.camel.support.OrderedComparator;
|
||||
import org.apache.camel.support.service.ServiceHelper;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.apache.camel.util.FilePathResolver;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
import org.apache.camel.util.OrderedProperties;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The properties component allows you to use property placeholders in Camel.
|
||||
*/
|
||||
@ManagedResource(description = "Managed PropertiesComponent")
|
||||
@JdkService(org.apache.camel.spi.PropertiesComponent.FACTORY)
|
||||
public class PropertiesComponent extends ServiceSupport implements org.apache.camel.spi.PropertiesComponent, StaticService, CamelContextAware {
|
||||
|
||||
/**
|
||||
* Never check system properties.
|
||||
*/
|
||||
public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0;
|
||||
|
||||
/**
|
||||
* Check system properties if not resolvable in the specified properties.
|
||||
*/
|
||||
public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1;
|
||||
|
||||
/**
|
||||
* Check system properties variables) first, before trying the specified properties.
|
||||
* This allows system properties to override any other property source
|
||||
* (environment variable and then system properties takes precedence).
|
||||
* <p/>
|
||||
* This is the default.
|
||||
*/
|
||||
public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2;
|
||||
|
||||
/**
|
||||
* Never check OS environment variables.
|
||||
*/
|
||||
public static final int ENVIRONMENT_VARIABLES_MODE_NEVER = 0;
|
||||
|
||||
/**
|
||||
* Check OS environment variables if not resolvable in the specified properties.
|
||||
* <p/>
|
||||
* This is the default.
|
||||
*/
|
||||
public static final int ENVIRONMENT_VARIABLES_MODE_FALLBACK = 1;
|
||||
|
||||
/**
|
||||
* Check OS environment variables first, before trying the specified properties.
|
||||
* This allows environment variables to override any other property source
|
||||
* (environment variable and then system properties takes precedence).
|
||||
*/
|
||||
public static final int ENVIRONMENT_VARIABLES_MODE_OVERRIDE = 2;
|
||||
|
||||
/**
|
||||
* Key for stores special override properties that containers such as OSGi can store
|
||||
* in the OSGi service registry
|
||||
*/
|
||||
public static final String OVERRIDE_PROPERTIES = PropertiesComponent.class.getName() + ".OverrideProperties";
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PropertiesComponent.class);
|
||||
|
||||
private CamelContext camelContext;
|
||||
private final Map<String, PropertiesFunction> functions = new LinkedHashMap<>();
|
||||
private PropertiesParser propertiesParser = new DefaultPropertiesParser(this);
|
||||
private final PropertiesLookup propertiesLookup = new DefaultPropertiesLookup(this);
|
||||
private final List<PropertiesSource> sources = new ArrayList<>();
|
||||
private List<PropertiesLocation> locations = new ArrayList<>();
|
||||
private String location;
|
||||
private boolean ignoreMissingLocation;
|
||||
private String encoding;
|
||||
private boolean defaultFallbackEnabled = true;
|
||||
private Properties initialProperties;
|
||||
private Properties overrideProperties;
|
||||
private int systemPropertiesMode = SYSTEM_PROPERTIES_MODE_OVERRIDE;
|
||||
private int environmentVariableMode = ENVIRONMENT_VARIABLES_MODE_OVERRIDE;
|
||||
private boolean autoDiscoverPropertiesSources = true;
|
||||
|
||||
public PropertiesComponent() {
|
||||
// include out of the box functions
|
||||
addPropertiesFunction(new EnvPropertiesFunction());
|
||||
addPropertiesFunction(new SysPropertiesFunction());
|
||||
addPropertiesFunction(new ServicePropertiesFunction());
|
||||
addPropertiesFunction(new ServiceHostPropertiesFunction());
|
||||
addPropertiesFunction(new ServicePortPropertiesFunction());
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of locations to load properties. You can use comma to separate multiple locations.
|
||||
*/
|
||||
public PropertiesComponent(String location) {
|
||||
this();
|
||||
setLocation(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of locations to load properties.
|
||||
*/
|
||||
public PropertiesComponent(String... locations) {
|
||||
this();
|
||||
setLocations(locations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CamelContext getCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCamelContext(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parseUri(String uri) {
|
||||
return parseUri(uri, propertiesLookup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> resolveProperty(String key) {
|
||||
try {
|
||||
String value = parseUri(key, propertiesLookup);
|
||||
return Optional.of(value);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// property not found
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties loadProperties() {
|
||||
// this method may be replaced by loadProperties(k -> true) but the underlying sources
|
||||
// may have some optimization for bulk load so let's keep it
|
||||
|
||||
Properties prop = new OrderedProperties();
|
||||
|
||||
// use initial properties
|
||||
if (initialProperties != null) {
|
||||
prop.putAll(initialProperties);
|
||||
}
|
||||
|
||||
if (!sources.isEmpty()) {
|
||||
// sources are ordered according to {@link org.apache.camel.support.OrderComparator} so
|
||||
// it is needed to iterate them in reverse order otherwise lower priority sources may
|
||||
// override properties from higher priority ones
|
||||
for (int i = sources.size(); i-- > 0;) {
|
||||
PropertiesSource ps = sources.get(i);
|
||||
if (ps instanceof LoadablePropertiesSource) {
|
||||
LoadablePropertiesSource lps = (LoadablePropertiesSource) ps;
|
||||
Properties p = lps.loadProperties();
|
||||
prop.putAll(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use override properties
|
||||
if (overrideProperties != null) {
|
||||
// make a copy to avoid affecting the original properties
|
||||
Properties override = new OrderedProperties();
|
||||
override.putAll(prop);
|
||||
override.putAll(overrideProperties);
|
||||
prop = override;
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties loadProperties(Predicate<String> filter) {
|
||||
Properties prop = new OrderedProperties();
|
||||
|
||||
// use initial properties
|
||||
if (initialProperties != null) {
|
||||
for (String name: initialProperties.stringPropertyNames()) {
|
||||
if (filter.test(name)) {
|
||||
prop.put(name, initialProperties.get(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sources.isEmpty()) {
|
||||
// sources are ordered according to {@link org.apache.camel.support.OrderComparator} so
|
||||
// it is needed to iterate them in reverse order otherwise lower priority sources may
|
||||
// override properties from higher priority ones
|
||||
for (int i = sources.size(); i-- > 0;) {
|
||||
PropertiesSource ps = sources.get(i);
|
||||
if (ps instanceof LoadablePropertiesSource) {
|
||||
LoadablePropertiesSource lps = (LoadablePropertiesSource) ps;
|
||||
Properties p = lps.loadProperties(filter);
|
||||
prop.putAll(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use override properties
|
||||
if (overrideProperties != null) {
|
||||
for (String name: overrideProperties.stringPropertyNames()) {
|
||||
if (filter.test(name)) {
|
||||
prop.put(name, overrideProperties.get(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
protected String parseUri(String uri, PropertiesLookup properties) {
|
||||
// enclose tokens if missing
|
||||
if (!uri.contains(PREFIX_TOKEN) && !uri.startsWith(PREFIX_TOKEN)) {
|
||||
uri = PREFIX_TOKEN + uri;
|
||||
}
|
||||
if (!uri.contains(SUFFIX_TOKEN) && !uri.endsWith(SUFFIX_TOKEN)) {
|
||||
uri = uri + SUFFIX_TOKEN;
|
||||
}
|
||||
|
||||
LOG.trace("Parsing uri {}", uri);
|
||||
return propertiesParser.parseUri(uri, properties, defaultFallbackEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<String> getLocations() {
|
||||
if (locations.isEmpty()) {
|
||||
return Collections.EMPTY_LIST;
|
||||
} else {
|
||||
return locations.stream().map(PropertiesLocation::toString).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of locations to load properties.
|
||||
* This option will override any default locations and only use the locations from this option.
|
||||
*/
|
||||
public void setLocations(List<PropertiesLocation> locations) {
|
||||
// reset locations
|
||||
locations = parseLocations(locations);
|
||||
this.locations = Collections.unmodifiableList(locations);
|
||||
|
||||
// we need to re-create the property sources which may have already been created from locations
|
||||
this.sources.removeIf(s -> s instanceof LocationPropertiesSource);
|
||||
for (PropertiesLocation loc : locations) {
|
||||
addPropertiesLocationsAsPropertiesSource(loc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of locations to load properties.
|
||||
* This option will override any default locations and only use the locations from this option.
|
||||
*/
|
||||
public void setLocations(String[] locationStrings) {
|
||||
List<PropertiesLocation> locations = new ArrayList<>();
|
||||
if (locationStrings != null) {
|
||||
for (String locationString : locationStrings) {
|
||||
locations.add(new PropertiesLocation(locationString));
|
||||
}
|
||||
}
|
||||
|
||||
setLocations(locations);
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of locations to load properties.
|
||||
* This option will override any default locations and only use the locations from this option.
|
||||
*/
|
||||
public void setLocations(Collection<String> locationStrings) {
|
||||
List<PropertiesLocation> locations = new ArrayList<>();
|
||||
if (locationStrings != null) {
|
||||
for (String locationString : locationStrings) {
|
||||
locations.add(new PropertiesLocation(locationString));
|
||||
}
|
||||
}
|
||||
|
||||
setLocations(locations);
|
||||
}
|
||||
|
||||
public void addLocation(PropertiesLocation location) {
|
||||
this.locations.add(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocation(String location) {
|
||||
if (location != null) {
|
||||
List<PropertiesLocation> newLocations = new ArrayList<>();
|
||||
for (String loc : location.split(",")) {
|
||||
newLocations.add(new PropertiesLocation(loc));
|
||||
}
|
||||
List<PropertiesLocation> current = locations;
|
||||
if (!current.isEmpty()) {
|
||||
newLocations.addAll(0, current);
|
||||
}
|
||||
setLocations(newLocations);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of locations to load properties. You can use comma to separate multiple locations.
|
||||
* This option will override any default locations and only use the locations from this option.
|
||||
*/
|
||||
@Override
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
if (location != null) {
|
||||
setLocations(location.split(","));
|
||||
}
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
@ManagedAttribute(description = "Encoding to use when loading properties file from the file system or classpath")
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoding to use when loading properties file from the file system or classpath.
|
||||
* <p/>
|
||||
* If no encoding has been set, then the properties files is loaded using ISO-8859-1 encoding (latin-1)
|
||||
* as documented by {@link Properties#load(java.io.InputStream)}
|
||||
*/
|
||||
@Override
|
||||
public void setEncoding(String encoding) {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
public PropertiesParser getPropertiesParser() {
|
||||
return propertiesParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* To use a custom PropertiesParser
|
||||
*/
|
||||
public void setPropertiesParser(PropertiesParser propertiesParser) {
|
||||
this.propertiesParser = propertiesParser;
|
||||
}
|
||||
|
||||
@ManagedAttribute(description = "Whether to support using fallback values if a property cannot be found")
|
||||
public boolean isDefaultFallbackEnabled() {
|
||||
return defaultFallbackEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* If false, the component does not attempt to find a default for the key by looking after the colon separator.
|
||||
*/
|
||||
public void setDefaultFallbackEnabled(boolean defaultFallbackEnabled) {
|
||||
this.defaultFallbackEnabled = defaultFallbackEnabled;
|
||||
}
|
||||
|
||||
@ManagedAttribute(description = "Ignore missing location")
|
||||
public boolean isIgnoreMissingLocation() {
|
||||
return ignoreMissingLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to silently ignore if a location cannot be located, such as a properties file not found.
|
||||
*/
|
||||
@Override
|
||||
public void setIgnoreMissingLocation(boolean ignoreMissingLocation) {
|
||||
this.ignoreMissingLocation = ignoreMissingLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list of properties which will be used before any locations are resolved (can't be null).
|
||||
*/
|
||||
public Properties getInitialProperties() {
|
||||
if (initialProperties == null) {
|
||||
initialProperties = new Properties();
|
||||
}
|
||||
|
||||
return initialProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets initial properties which will be used before any locations are resolved.
|
||||
*/
|
||||
@Override
|
||||
public void setInitialProperties(Properties initialProperties) {
|
||||
this.initialProperties = initialProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list of properties that take precedence and will use first, if a property exist (can't be null).
|
||||
*/
|
||||
public Properties getOverrideProperties() {
|
||||
if (overrideProperties == null) {
|
||||
overrideProperties = new Properties();
|
||||
}
|
||||
|
||||
return overrideProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a special list of override properties that take precedence
|
||||
* and will use first, if a property exist.
|
||||
*/
|
||||
@Override
|
||||
public void setOverrideProperties(Properties overrideProperties) {
|
||||
this.overrideProperties = overrideProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the functions registered in this properties component.
|
||||
*/
|
||||
public Map<String, PropertiesFunction> getFunctions() {
|
||||
return functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the {@link PropertiesFunction} as a function to this component.
|
||||
*/
|
||||
public void addPropertiesFunction(PropertiesFunction function) {
|
||||
this.functions.put(function.getName(), function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there a {@link PropertiesFunction} with the given name?
|
||||
*/
|
||||
public boolean hasFunction(String name) {
|
||||
return functions.containsKey(name);
|
||||
}
|
||||
|
||||
@ManagedAttribute(description = "System properties mode")
|
||||
public int getSystemPropertiesMode() {
|
||||
return systemPropertiesMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the JVM system property mode (0 = never, 1 = fallback, 2 = override).
|
||||
*
|
||||
* The default mode (override) is to use system properties if present,
|
||||
* and override any existing properties.
|
||||
*
|
||||
* OS environment variable mode is checked before JVM system property mode
|
||||
*
|
||||
* @see #SYSTEM_PROPERTIES_MODE_NEVER
|
||||
* @see #SYSTEM_PROPERTIES_MODE_FALLBACK
|
||||
* @see #SYSTEM_PROPERTIES_MODE_OVERRIDE
|
||||
*/
|
||||
public void setSystemPropertiesMode(int systemPropertiesMode) {
|
||||
this.systemPropertiesMode = systemPropertiesMode;
|
||||
}
|
||||
|
||||
@ManagedAttribute(description = "Environment variable mode")
|
||||
public int getEnvironmentVariableMode() {
|
||||
return environmentVariableMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the OS environment variables mode (0 = never, 1 = fallback, 2 = override).
|
||||
*
|
||||
* The default mode (override) is to use OS environment variables if present,
|
||||
* and override any existing properties.
|
||||
*
|
||||
* OS environment variable mode is checked before JVM system property mode
|
||||
*
|
||||
* @see #ENVIRONMENT_VARIABLES_MODE_NEVER
|
||||
* @see #ENVIRONMENT_VARIABLES_MODE_FALLBACK
|
||||
* @see #ENVIRONMENT_VARIABLES_MODE_OVERRIDE
|
||||
*/
|
||||
public void setEnvironmentVariableMode(int environmentVariableMode) {
|
||||
this.environmentVariableMode = environmentVariableMode;
|
||||
}
|
||||
|
||||
public boolean isAutoDiscoverPropertiesSources() {
|
||||
return autoDiscoverPropertiesSources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to automatically discovery instances of {@link PropertiesSource} from registry and service factory.
|
||||
*/
|
||||
public void setAutoDiscoverPropertiesSources(boolean autoDiscoverPropertiesSources) {
|
||||
this.autoDiscoverPropertiesSources = autoDiscoverPropertiesSources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertiesSource(PropertiesSource propertiesSource) {
|
||||
if (propertiesSource instanceof CamelContextAware) {
|
||||
((CamelContextAware) propertiesSource).setCamelContext(getCamelContext());
|
||||
}
|
||||
synchronized (lock) {
|
||||
sources.add(propertiesSource);
|
||||
if (!isNew()) {
|
||||
// if we have already initialized or started then we should also init the source
|
||||
ServiceHelper.initService(propertiesSource);
|
||||
}
|
||||
if (isStarted()) {
|
||||
ServiceHelper.startService(propertiesSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<PropertiesSource> getSources() {
|
||||
return sources;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() throws Exception {
|
||||
super.doInit();
|
||||
|
||||
ObjectHelper.notNull(camelContext, "CamelContext", this);
|
||||
|
||||
if (systemPropertiesMode != SYSTEM_PROPERTIES_MODE_NEVER
|
||||
&& systemPropertiesMode != SYSTEM_PROPERTIES_MODE_FALLBACK
|
||||
&& systemPropertiesMode != SYSTEM_PROPERTIES_MODE_OVERRIDE) {
|
||||
throw new IllegalArgumentException("Option systemPropertiesMode has invalid value: " + systemPropertiesMode);
|
||||
}
|
||||
if (environmentVariableMode != ENVIRONMENT_VARIABLES_MODE_NEVER
|
||||
&& environmentVariableMode != ENVIRONMENT_VARIABLES_MODE_FALLBACK
|
||||
&& environmentVariableMode != ENVIRONMENT_VARIABLES_MODE_OVERRIDE) {
|
||||
throw new IllegalArgumentException("Option environmentVariableMode has invalid value: " + environmentVariableMode);
|
||||
}
|
||||
|
||||
// inject the component to the parser
|
||||
if (propertiesParser instanceof DefaultPropertiesParser) {
|
||||
((DefaultPropertiesParser) propertiesParser).setPropertiesComponent(this);
|
||||
}
|
||||
|
||||
if (isAutoDiscoverPropertiesSources()) {
|
||||
// discover any 3rd party properties sources
|
||||
try {
|
||||
for (PropertiesSource source : getCamelContext().getRegistry().findByType(PropertiesSource.class)) {
|
||||
addPropertiesSource(source);
|
||||
LOG.info("PropertiesComponent added custom PropertiesSource (registry): {}", source);
|
||||
}
|
||||
|
||||
FactoryFinder factoryFinder = getCamelContext().adapt(ExtendedCamelContext.class).getFactoryFinder("META-INF/services/org/apache/camel/");
|
||||
Class<?> type = factoryFinder.findClass("properties-source-factory").orElse(null);
|
||||
if (type != null) {
|
||||
Object obj = getCamelContext().getInjector().newInstance(type, false);
|
||||
if (obj instanceof PropertiesSource) {
|
||||
PropertiesSource ps = (PropertiesSource) obj;
|
||||
addPropertiesSource(ps);
|
||||
LOG.info("PropertiesComponent added custom PropertiesSource (factory): {}", ps);
|
||||
} else if (obj != null) {
|
||||
LOG.warn("PropertiesComponent cannot add custom PropertiesSource as the type is not a org.apache.camel.component.properties.PropertiesSource but: " + type.getName());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.debug("Error discovering and using custom PropertiesSource due to " + e.getMessage() + ". This exception is ignored", e);
|
||||
}
|
||||
}
|
||||
|
||||
sources.sort(OrderedComparator.get());
|
||||
ServiceHelper.initService(sources);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception {
|
||||
ServiceHelper.startService(sources);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception {
|
||||
ServiceHelper.stopService(sources);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doShutdown() throws Exception {
|
||||
ServiceHelper.stopAndShutdownServices(sources);
|
||||
}
|
||||
|
||||
private void addPropertiesLocationsAsPropertiesSource(PropertiesLocation location) {
|
||||
if ("ref".equals(location.getResolver())) {
|
||||
addPropertiesSource(new RefPropertiesSource(this, location));
|
||||
} else if ("file".equals(location.getResolver())) {
|
||||
addPropertiesSource(new FilePropertiesSource(this, location));
|
||||
} else if ("classpath".equals(location.getResolver())) {
|
||||
addPropertiesSource(new ClasspathPropertiesSource(this, location));
|
||||
}
|
||||
}
|
||||
private List<PropertiesLocation> parseLocations(List<PropertiesLocation> locations) {
|
||||
List<PropertiesLocation> answer = new ArrayList<>();
|
||||
|
||||
for (PropertiesLocation location : locations) {
|
||||
LOG.trace("Parsing location: {}", location);
|
||||
|
||||
try {
|
||||
String path = FilePathResolver.resolvePath(location.getPath());
|
||||
LOG.debug("Parsed location: {}", path);
|
||||
if (ObjectHelper.isNotEmpty(path)) {
|
||||
answer.add(new PropertiesLocation(
|
||||
location.getResolver(),
|
||||
path,
|
||||
location.isOptional())
|
||||
);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (!ignoreMissingLocation && !location.isOptional()) {
|
||||
throw e;
|
||||
} else {
|
||||
LOG.debug("Ignored missing location: {}", location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// must return a not-null answer
|
||||
return answer;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.util.StringHelper;
|
||||
|
||||
public final class PropertiesLocation {
|
||||
private final String resolver;
|
||||
private final String path;
|
||||
private final boolean optional;
|
||||
|
||||
public PropertiesLocation(String location) {
|
||||
// make sure to trim as people may use new lines when configuring using XML
|
||||
// and do this in the setter as Spring/Blueprint resolves placeholders before
|
||||
// Camel is being started
|
||||
location = location.trim();
|
||||
|
||||
int idx = location.indexOf(':');
|
||||
if (idx != -1) {
|
||||
this.resolver = location.substring(0, idx);
|
||||
location = location.substring(idx + 1);
|
||||
} else {
|
||||
this.resolver = "classpath";
|
||||
}
|
||||
|
||||
idx = location.lastIndexOf(';');
|
||||
if (idx != -1) {
|
||||
this.optional = StringHelper.after(location.substring(idx + 1), "optional=", Boolean::valueOf).orElse(false);
|
||||
location = location.substring(0, idx);
|
||||
} else {
|
||||
this.optional = false;
|
||||
}
|
||||
|
||||
this.path = location;
|
||||
}
|
||||
|
||||
public PropertiesLocation(String resolver, String path) {
|
||||
this(resolver, path, false);
|
||||
}
|
||||
|
||||
public PropertiesLocation(String resolver, String path, Boolean optional) {
|
||||
this.resolver = resolver;
|
||||
this.path = path;
|
||||
this.optional = optional;
|
||||
}
|
||||
|
||||
// *****************************
|
||||
// Getters
|
||||
// *****************************
|
||||
|
||||
public String getResolver() {
|
||||
return resolver;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public boolean isOptional() {
|
||||
return optional;
|
||||
}
|
||||
|
||||
// *****************************
|
||||
// Equals/HashCode/ToString
|
||||
// *****************************
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertiesLocation location = (PropertiesLocation) o;
|
||||
|
||||
if (optional != location.optional) {
|
||||
return false;
|
||||
}
|
||||
if (resolver != null ? !resolver.equals(location.resolver) : location.resolver != null) {
|
||||
return false;
|
||||
}
|
||||
return path != null ? path.equals(location.path) : location.path == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = resolver != null ? resolver.hashCode() : 0;
|
||||
result = 31 * result + (path != null ? path.hashCode() : 0);
|
||||
result = 31 * result + (optional ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return resolver + ":" + path + (optional ? ";optional=true" : "");
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
/**
|
||||
* Used by {@link PropertiesParser} to lookup properties by their name
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PropertiesLookup {
|
||||
|
||||
/**
|
||||
* Lookup the property with the given name
|
||||
*
|
||||
* @param name property name
|
||||
* @return the property value, or <tt>null</tt> if the properties does not exist.
|
||||
*/
|
||||
String lookup(String name);
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
/**
|
||||
* A parser to parse properties for a given input
|
||||
*/
|
||||
public interface PropertiesParser {
|
||||
|
||||
/**
|
||||
* Parses the string and replaces the property placeholders with values from the given properties.
|
||||
*
|
||||
* @param text the text to be parsed
|
||||
* @param properties the properties resolved which values should be looked up
|
||||
* @param fallback whether to support using fallback values if a property cannot be found
|
||||
* @return the parsed text with replaced placeholders
|
||||
* @throws IllegalArgumentException if uri syntax is not valid or a property is not found
|
||||
*/
|
||||
String parseUri(String text, PropertiesLookup properties, boolean fallback) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* While parsing the uri using parseUri method each parsed property found invokes this callback.
|
||||
* <p/>
|
||||
* This strategy method allows you to hook into the parsing and do custom lookup and return the actual value to use.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @param properties the properties resolved which values should be looked up
|
||||
* @return the value to use
|
||||
*/
|
||||
String parseProperty(String key, String value, PropertiesLookup properties);
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.RuntimeCamelException;
|
||||
import org.apache.camel.util.OrderedProperties;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
public class RefPropertiesSource implements LocationPropertiesSource {
|
||||
|
||||
private final org.apache.camel.component.properties.PropertiesComponent propertiesComponent;
|
||||
private final PropertiesLocation location;
|
||||
|
||||
public RefPropertiesSource(org.apache.camel.component.properties.PropertiesComponent propertiesComponent, PropertiesLocation location) {
|
||||
this.propertiesComponent = propertiesComponent;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RefPropertiesSource[" + getLocation().getPath() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertiesLocation getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String name) {
|
||||
// this will lookup the property on-demand
|
||||
Properties properties = lookupPropertiesInRegistry(propertiesComponent, location);
|
||||
if (properties != null) {
|
||||
return properties.getProperty(name);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected Properties lookupPropertiesInRegistry(PropertiesComponent propertiesComponent, PropertiesLocation location) {
|
||||
String path = location.getPath();
|
||||
Properties answer = null;
|
||||
|
||||
Object obj = propertiesComponent.getCamelContext().getRegistry().lookupByName(path);
|
||||
if (obj instanceof Properties) {
|
||||
answer = (Properties) obj;
|
||||
} else if (obj instanceof Map) {
|
||||
answer = new OrderedProperties();
|
||||
answer.putAll((Map<?, ?>) obj);
|
||||
} else if (!propertiesComponent.isIgnoreMissingLocation() && !location.isOptional()) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(new FileNotFoundException("Properties " + path + " not found in registry"));
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.spi.PropertiesFunction;
|
||||
import org.apache.camel.util.StringHelper;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* A {@link PropertiesFunction} that lookup the property value from
|
||||
* OS environment variables using the service idiom.
|
||||
* <p/>
|
||||
* A service is defined using two environment variables where name is name of the service:
|
||||
* <ul>
|
||||
* <li><tt>NAME_SERVICE_HOST</tt></li>
|
||||
* <li><tt>NAME_SERVICE_PORT</tt></li>
|
||||
* </ul>
|
||||
* in other words the service uses <tt>_SERVICE_HOST</tt> and <tt>_SERVICE_PORT</tt> as prefix.
|
||||
* <p/>
|
||||
* This implementation is to return the host part only.
|
||||
*
|
||||
* @see ServicePropertiesFunction
|
||||
* @see ServicePortPropertiesFunction
|
||||
*/
|
||||
public class ServiceHostPropertiesFunction implements PropertiesFunction {
|
||||
|
||||
private static final String HOST_PREFIX = "_SERVICE_HOST";
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "service.host";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String remainder) {
|
||||
String key = remainder;
|
||||
String defaultValue = null;
|
||||
|
||||
if (remainder.contains(":")) {
|
||||
key = StringHelper.before(remainder, ":");
|
||||
defaultValue = StringHelper.after(remainder, ":");
|
||||
}
|
||||
|
||||
// make sure to use upper case
|
||||
if (key != null) {
|
||||
// make sure to use underscore as dash is not supported as ENV variables
|
||||
key = key.toUpperCase(Locale.ENGLISH).replace('-', '_');
|
||||
|
||||
// a service should have both the host and port defined
|
||||
String host = System.getenv(key + HOST_PREFIX);
|
||||
|
||||
if (host != null) {
|
||||
return host;
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.spi.PropertiesFunction;
|
||||
import org.apache.camel.util.StringHelper;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* A {@link PropertiesFunction} that lookup the property value from
|
||||
* OS environment variables using the service idiom.
|
||||
* <p/>
|
||||
* A service is defined using two environment variables where name is name of the service:
|
||||
* <ul>
|
||||
* <li><tt>NAME_SERVICE_HOST</tt></li>
|
||||
* <li><tt>NAME_SERVICE_PORT</tt></li>
|
||||
* </ul>
|
||||
* in other words the service uses <tt>_SERVICE_HOST</tt> and <tt>_SERVICE_PORT</tt> as prefix.
|
||||
* <p/>
|
||||
* This implementation is to return the port part only.
|
||||
*
|
||||
* @see ServicePropertiesFunction
|
||||
* @see ServiceHostPropertiesFunction
|
||||
*/
|
||||
public class ServicePortPropertiesFunction implements PropertiesFunction {
|
||||
|
||||
private static final String PORT_PREFIX = "_SERVICE_PORT";
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "service.port";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String remainder) {
|
||||
String key = remainder;
|
||||
String defaultValue = null;
|
||||
|
||||
if (remainder.contains(":")) {
|
||||
key = StringHelper.before(remainder, ":");
|
||||
defaultValue = StringHelper.after(remainder, ":");
|
||||
}
|
||||
|
||||
// make sure to use upper case
|
||||
if (key != null) {
|
||||
// make sure to use underscore as dash is not supported as ENV variables
|
||||
key = key.toUpperCase(Locale.ENGLISH).replace('-', '_');
|
||||
|
||||
// a service should have both the host and port defined
|
||||
String port = System.getenv(key + PORT_PREFIX);
|
||||
|
||||
if (port != null) {
|
||||
return port;
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.spi.PropertiesFunction;
|
||||
import org.apache.camel.util.StringHelper;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* A {@link PropertiesFunction} that lookup the property value from
|
||||
* OS environment variables using the service idiom.
|
||||
* <p/>
|
||||
* A service is defined using two environment variables where name is name of the service:
|
||||
* <ul>
|
||||
* <li><tt>NAME_SERVICE_HOST</tt></li>
|
||||
* <li><tt>NAME_SERVICE_PORT</tt></li>
|
||||
* </ul>
|
||||
* in other words the service uses <tt>_SERVICE_HOST</tt> and <tt>_SERVICE_PORT</tt> as prefix.
|
||||
*/
|
||||
public class ServicePropertiesFunction implements PropertiesFunction {
|
||||
|
||||
private static final String HOST_PREFIX = "_SERVICE_HOST";
|
||||
private static final String PORT_PREFIX = "_SERVICE_PORT";
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "service";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String remainder) {
|
||||
String key = remainder;
|
||||
String defaultValue = null;
|
||||
|
||||
if (remainder.contains(":")) {
|
||||
key = StringHelper.before(remainder, ":");
|
||||
defaultValue = StringHelper.after(remainder, ":");
|
||||
}
|
||||
|
||||
// make sure to use upper case
|
||||
if (key != null) {
|
||||
// make sure to use underscore as dash is not supported as ENV variables
|
||||
key = key.toUpperCase(Locale.ENGLISH).replace('-', '_');
|
||||
|
||||
// a service should have both the host and port defined
|
||||
String host = System.getenv(key + HOST_PREFIX);
|
||||
String port = System.getenv(key + PORT_PREFIX);
|
||||
|
||||
if (host != null && port != null) {
|
||||
return host + ":" + port;
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.component.properties;
|
||||
|
||||
import org.apache.camel.spi.PropertiesFunction;
|
||||
import org.apache.camel.util.StringHelper;
|
||||
|
||||
/**
|
||||
* A {@link PropertiesFunction} that lookup the property value from
|
||||
* JVM system property.
|
||||
*/
|
||||
public class SysPropertiesFunction implements PropertiesFunction {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "sys";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String remainder) {
|
||||
String key = remainder;
|
||||
String defaultValue = null;
|
||||
|
||||
if (remainder.contains(":")) {
|
||||
key = StringHelper.before(remainder, ":");
|
||||
defaultValue = StringHelper.after(remainder, ":");
|
||||
}
|
||||
|
||||
String value = System.getProperty(key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
The <a href="http://activemq.apache.org/properties.html">Properties Component</a> for lookup of property
|
||||
placeholders for endpoint URI.
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.converter;
|
||||
|
||||
import org.apache.camel.*;
|
||||
|
||||
/**
|
||||
* Some useful converters for Camel APIs such as to convert a {@link Predicate} or {@link Expression}
|
||||
* to a {@link Processor}
|
||||
*/
|
||||
@Converter(generateLoader = true)
|
||||
public final class CamelConverter {
|
||||
|
||||
/**
|
||||
* Utility classes should not have a public constructor.
|
||||
*/
|
||||
private CamelConverter() {
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Processor toProcessor(final Predicate predicate) {
|
||||
return exchange -> {
|
||||
// the response from a predicate should be set on OUT
|
||||
boolean answer = predicate.matches(exchange);
|
||||
Message out = exchange.getOut();
|
||||
out.copyFrom(exchange.getIn());
|
||||
out.setBody(answer);
|
||||
};
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Processor toProcessor(final Expression expression) {
|
||||
return exchange -> {
|
||||
// the response from a expression should be set on OUT
|
||||
Object answer = expression.evaluate(exchange, Object.class);
|
||||
Message out = exchange.getOut();
|
||||
out.copyFrom(exchange.getIn());
|
||||
out.setBody(answer);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.converter;
|
||||
|
||||
import org.apache.camel.Converter;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Some core java.util Collection based
|
||||
* <a href="http://camel.apache.org/type-converter.html">Type Converters</a>
|
||||
*/
|
||||
@Converter(generateLoader = true)
|
||||
public final class CollectionConverter {
|
||||
|
||||
/**
|
||||
* Utility classes should not have a public constructor.
|
||||
*/
|
||||
private CollectionConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a collection to an array
|
||||
*/
|
||||
@Converter
|
||||
public static Object[] toArray(Collection<?> value) {
|
||||
return value.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array to a collection
|
||||
*/
|
||||
@Converter
|
||||
public static List<Object> toList(Object[] array) {
|
||||
return Arrays.asList(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a collection to a List if it is not already
|
||||
*/
|
||||
@Converter
|
||||
public static <T> List<T> toList(Collection<T> collection) {
|
||||
return new ArrayList<>(collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an {@link Iterator} to a {@link ArrayList}
|
||||
*/
|
||||
@Converter
|
||||
public static <T> ArrayList<T> toArrayList(Iterator<T> it) {
|
||||
ArrayList<T> list = new ArrayList<>();
|
||||
while (it.hasNext()) {
|
||||
list.add(it.next());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Set<Object> toSet(Object[] array) {
|
||||
Set<Object> answer = new HashSet<>();
|
||||
answer.addAll(Arrays.asList(array));
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static <T> Set<T> toSet(Collection<T> collection) {
|
||||
return new HashSet<>(collection);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static <K, V> Set<Map.Entry<K, V>> toSet(Map<K, V> map) {
|
||||
return map.entrySet();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Properties toProperties(Map<Object, Object> map) {
|
||||
Properties answer = new Properties();
|
||||
answer.putAll(map);
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static <K, V> Hashtable<K, V> toHashtable(Map<? extends K, ? extends V> map) {
|
||||
return new Hashtable<>(map);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static <K, V> HashMap<K, V> toHashMap(Map<? extends K, ? extends V> map) {
|
||||
return new HashMap<>(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an {@link Iterable} into a {@link List}
|
||||
*/
|
||||
@Converter
|
||||
public static <T> List<T> toList(Iterable<T> iterable) {
|
||||
if (iterable instanceof List) {
|
||||
return (List<T>) iterable;
|
||||
}
|
||||
List<T> result = new LinkedList<>();
|
||||
for (T value : iterable) {
|
||||
result.add(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.converter;
|
||||
|
||||
import org.apache.camel.Converter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Date and time related converters.
|
||||
*/
|
||||
@Converter(generateLoader = true)
|
||||
public final class DateTimeConverter {
|
||||
|
||||
/**
|
||||
* Utility classes should not have a public constructor.
|
||||
*/
|
||||
private DateTimeConverter() {
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static TimeZone toTimeZone(String s) {
|
||||
return TimeZone.getTimeZone(s);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Date toDate(Long l) {
|
||||
return new Date(l);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Long toLong(Date date) {
|
||||
return date.getTime();
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.converter;
|
||||
|
||||
import org.apache.camel.Converter;
|
||||
import org.apache.camel.util.TimeUtils;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* Converters for java.time.Duration.
|
||||
*/
|
||||
@Converter(generateLoader = true)
|
||||
public final class DurationConverter {
|
||||
|
||||
/**
|
||||
* Utility classes should not have a public constructor.
|
||||
*/
|
||||
private DurationConverter() {
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Long toMilliSeconds(Duration source) {
|
||||
return source.toMillis();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Duration toDuration(Long source) {
|
||||
return Duration.ofMillis(source);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Duration toDuration(String source) {
|
||||
if (source.startsWith("P") || source.startsWith("-P") || source.startsWith("p") || source.startsWith("-p")) {
|
||||
return Duration.parse(source);
|
||||
} else {
|
||||
return Duration.ofMillis(TimeUtils.toMilliSeconds(source));
|
||||
}
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(Duration source) {
|
||||
return source.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* 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.converter;
|
||||
|
||||
import org.apache.camel.Converter;
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.support.ExchangeHelper;
|
||||
import org.apache.camel.support.InputStreamIterator;
|
||||
import org.apache.camel.util.IOHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Some core java.io based <a
|
||||
* href="http://camel.apache.org/type-converter.html">Type Converters</a>
|
||||
*/
|
||||
@Converter(generateLoader = true)
|
||||
public final class IOConverter {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(IOConverter.class);
|
||||
|
||||
/**
|
||||
* Utility classes should not have a public constructor.
|
||||
*/
|
||||
private IOConverter() {
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static InputStream toInputStream(Stream stream, Exchange exchange) {
|
||||
Iterator it = stream.iterator();
|
||||
return new InputStreamIterator(exchange.getContext().getTypeConverter(), it);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static InputStream toInputStream(URL url) throws IOException {
|
||||
return IOHelper.buffered(url.openStream());
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static InputStream toInputStream(File file) throws IOException {
|
||||
return IOHelper.buffered(new FileInputStream(file));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static BufferedReader toReader(File file, Exchange exchange) throws IOException {
|
||||
return IOHelper.toReader(file, ExchangeHelper.getCharsetName(exchange));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static File toFile(String name) {
|
||||
return new File(name);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static OutputStream toOutputStream(File file) throws FileNotFoundException {
|
||||
return IOHelper.buffered(new FileOutputStream(file));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static BufferedWriter toWriter(File file, Exchange exchange) throws IOException {
|
||||
FileOutputStream os = new FileOutputStream(file, false);
|
||||
return IOHelper.toWriter(os, ExchangeHelper.getCharsetName(exchange));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Reader toReader(InputStream in, Exchange exchange) throws IOException {
|
||||
return IOHelper.buffered(new InputStreamReader(in, ExchangeHelper.getCharsetName(exchange)));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Reader toReader(byte[] data, Exchange exchange) throws IOException {
|
||||
return toReader(new ByteArrayInputStream(data), exchange);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Writer toWriter(OutputStream out, Exchange exchange) throws IOException {
|
||||
return IOHelper.buffered(new OutputStreamWriter(out, ExchangeHelper.getCharsetName(exchange)));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static StringReader toReader(String text) {
|
||||
// no buffering required as the complete string input is already passed
|
||||
// over as a whole
|
||||
return new StringReader(text);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static InputStream toInputStream(String text, Exchange exchange) throws IOException {
|
||||
return toInputStream(text.getBytes(ExchangeHelper.getCharsetName(exchange)));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static InputStream toInputStream(StringBuffer buffer, Exchange exchange) throws IOException {
|
||||
return toInputStream(buffer.toString(), exchange);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static InputStream toInputStream(StringBuilder builder, Exchange exchange) throws IOException {
|
||||
return toInputStream(builder.toString(), exchange);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static InputStream toInputStream(BufferedReader buffer, Exchange exchange) throws IOException {
|
||||
return toInputStream(toString(buffer), exchange);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(byte[] data, Exchange exchange) throws IOException {
|
||||
return new String(data, ExchangeHelper.getCharsetName(exchange));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(File file, Exchange exchange) throws IOException {
|
||||
return toString(toReader(file, exchange));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static byte[] toByteArray(File file) throws IOException {
|
||||
InputStream is = toInputStream(file);
|
||||
try {
|
||||
return toBytes(is);
|
||||
} finally {
|
||||
IOHelper.close(is, "file", LOG);
|
||||
}
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static byte[] toByteArray(Reader reader, Exchange exchange) throws IOException {
|
||||
return toByteArray(IOHelper.buffered(reader), exchange);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(URL url, Exchange exchange) throws IOException {
|
||||
InputStream is = toInputStream(url);
|
||||
try {
|
||||
return toString(is, exchange);
|
||||
} finally {
|
||||
IOHelper.close(is, "url", LOG);
|
||||
}
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(Reader reader) throws IOException {
|
||||
return IOHelper.toString(reader);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(BufferedReader reader) throws IOException {
|
||||
return IOHelper.toString(reader);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static byte[] toByteArray(BufferedReader reader, Exchange exchange) throws IOException {
|
||||
String s = toString(reader);
|
||||
return toByteArray(s, exchange);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static byte[] toByteArray(String value, Exchange exchange) throws IOException {
|
||||
return value.getBytes(ExchangeHelper.getCharsetName(exchange));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(InputStream in, Exchange exchange) throws IOException {
|
||||
return toString(toReader(in, exchange));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static InputStream toInputStream(byte[] data) {
|
||||
// no buffering required as the complete byte input is already passed
|
||||
// over as a whole
|
||||
return new ByteArrayInputStream(data);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static ObjectOutput toObjectOutput(OutputStream stream) throws IOException {
|
||||
if (stream instanceof ObjectOutput) {
|
||||
return (ObjectOutput) stream;
|
||||
} else {
|
||||
return new ObjectOutputStream(IOHelper.buffered(stream));
|
||||
}
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static ObjectInput toObjectInput(final InputStream stream, final Exchange exchange) throws IOException {
|
||||
if (stream instanceof ObjectInput) {
|
||||
return (ObjectInput) stream;
|
||||
} else {
|
||||
return new ObjectInputStream(IOHelper.buffered(stream)) {
|
||||
@Override
|
||||
protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
|
||||
// need to let Camel be able to resolve class using ClassResolver SPI, to let class loading
|
||||
// work in OSGi and other containers
|
||||
Class<?> answer = null;
|
||||
String name = objectStreamClass.getName();
|
||||
if (exchange != null) {
|
||||
LOG.trace("Loading class {} using Camel ClassResolver", name);
|
||||
answer = exchange.getContext().getClassResolver().resolveClass(name);
|
||||
}
|
||||
if (answer == null) {
|
||||
LOG.trace("Loading class {} using JDK default implementation", name);
|
||||
answer = super.resolveClass(objectStreamClass);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static byte[] toBytes(InputStream stream) throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
IOHelper.copy(IOHelper.buffered(stream), bos);
|
||||
|
||||
// no need to close the ByteArrayOutputStream as it's close()
|
||||
// implementation is noop
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static byte[] toByteArray(ByteArrayOutputStream os) {
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static ByteBuffer covertToByteBuffer(InputStream is) throws IOException {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
IOHelper.copyAndCloseInput(is, os);
|
||||
return ByteBuffer.wrap(os.toByteArray());
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(ByteArrayOutputStream os, Exchange exchange) throws IOException {
|
||||
return os.toString(ExchangeHelper.getCharsetName(exchange));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static InputStream toInputStream(ByteArrayOutputStream os) {
|
||||
// no buffering required as the complete byte array input is already
|
||||
// passed over as a whole
|
||||
return new ByteArrayInputStream(os.toByteArray());
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Properties toProperties(File file) throws IOException {
|
||||
return toProperties(new FileInputStream(file));
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Properties toProperties(InputStream is) throws IOException {
|
||||
Properties prop = new Properties();
|
||||
try {
|
||||
prop.load(is);
|
||||
} finally {
|
||||
IOHelper.close(is);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Properties toProperties(Reader reader) throws IOException {
|
||||
Properties prop = new Properties();
|
||||
try {
|
||||
prop.load(reader);
|
||||
} finally {
|
||||
IOHelper.close(reader);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.converter;
|
||||
|
||||
import org.apache.camel.Converter;
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.util.IOHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static org.apache.camel.util.BufferCaster.cast;
|
||||
|
||||
/**
|
||||
* Some core java.nio based
|
||||
* <a href="http://camel.apache.org/type-converter.html">Type Converters</a>
|
||||
*/
|
||||
@Converter(generateLoader = true)
|
||||
public final class NIOConverter {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(NIOConverter.class);
|
||||
|
||||
/**
|
||||
* Utility classes should not have a public constructor.
|
||||
*/
|
||||
private NIOConverter() {
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static byte[] toByteArray(ByteBuffer buffer) {
|
||||
byte[] bArray = new byte[buffer.limit()];
|
||||
buffer.get(bArray);
|
||||
return bArray;
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(ByteBuffer buffer, Exchange exchange) throws IOException {
|
||||
return org.apache.camel.converter.IOConverter.toString(toByteArray(buffer), exchange);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static ByteBuffer toByteBuffer(byte[] data) {
|
||||
return ByteBuffer.wrap(data);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static ByteBuffer toByteBuffer(ByteArrayOutputStream baos) {
|
||||
return ByteBuffer.wrap(baos.toByteArray());
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static ByteBuffer toByteBuffer(File file) throws IOException {
|
||||
InputStream in = null;
|
||||
try {
|
||||
byte[] buf = new byte[(int)file.length()];
|
||||
in = IOHelper.buffered(new FileInputStream(file));
|
||||
int sizeLeft = (int)file.length();
|
||||
int offset = 0;
|
||||
while (sizeLeft > 0) {
|
||||
int readSize = in.read(buf, offset, sizeLeft);
|
||||
sizeLeft -= readSize;
|
||||
offset += readSize;
|
||||
}
|
||||
return ByteBuffer.wrap(buf);
|
||||
} finally {
|
||||
IOHelper.close(in, "Failed to close file stream: " + file.getPath(), LOG);
|
||||
}
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static ByteBuffer toByteBuffer(String value, Exchange exchange) {
|
||||
byte[] bytes = null;
|
||||
if (exchange != null) {
|
||||
String charsetName = exchange.getProperty(Exchange.CHARSET_NAME, String.class);
|
||||
if (charsetName != null) {
|
||||
try {
|
||||
bytes = value.getBytes(charsetName);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
LOG.warn("Cannot convert the byte to String with the charset {}", charsetName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bytes == null) {
|
||||
bytes = value.getBytes();
|
||||
}
|
||||
return ByteBuffer.wrap(bytes);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static ByteBuffer toByteBuffer(Short value) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(2);
|
||||
buf.putShort(value);
|
||||
cast(buf).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static ByteBuffer toByteBuffer(Integer value) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(4);
|
||||
buf.putInt(value);
|
||||
cast(buf).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static ByteBuffer toByteBuffer(Long value) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(8);
|
||||
buf.putLong(value);
|
||||
cast(buf).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static ByteBuffer toByteBuffer(Float value) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(4);
|
||||
buf.putFloat(value);
|
||||
cast(buf).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static ByteBuffer toByteBuffer(Double value) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(8);
|
||||
buf.putDouble(value);
|
||||
cast(buf).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static InputStream toInputStream(ByteBuffer bufferbuffer) {
|
||||
return IOConverter.toInputStream(toByteArray(bufferbuffer));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* 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.converter;
|
||||
|
||||
import org.apache.camel.Converter;
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.support.ObjectHelper;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Some core java.lang based <a
|
||||
* href="http://camel.apache.org/type-converter.html">Type Converters</a>
|
||||
*/
|
||||
@Converter(generateLoader = true)
|
||||
public final class ObjectConverter {
|
||||
|
||||
/**
|
||||
* Utility classes should not have a public constructor.
|
||||
*/
|
||||
private ObjectConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given value to a boolean, handling strings or Boolean
|
||||
* objects; otherwise returning false if the value could not be converted to
|
||||
* a boolean
|
||||
*/
|
||||
@Converter
|
||||
public static boolean toBool(Object value) {
|
||||
Boolean answer = toBoolean(value);
|
||||
if (answer == null) {
|
||||
throw new IllegalArgumentException("Cannot convert type: " + value.getClass().getName() + " to boolean");
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given value to a Boolean, handling strings or Boolean
|
||||
* objects; otherwise returning null if the value cannot be converted to a
|
||||
* boolean
|
||||
*/
|
||||
@Converter
|
||||
public static Boolean toBoolean(Object value) {
|
||||
return org.apache.camel.util.ObjectHelper.toBoolean(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an iterator over the value
|
||||
*/
|
||||
@Converter
|
||||
public static Iterator<?> iterator(Object value) {
|
||||
return ObjectHelper.createIterator(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an iterable over the value
|
||||
*/
|
||||
@Converter
|
||||
public static Iterable<?> iterable(Object value) {
|
||||
return ObjectHelper.createIterable(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the converted value, or null if the value is null
|
||||
*/
|
||||
@Converter(allowNull = true)
|
||||
public static Byte toByte(Number value) {
|
||||
if (org.apache.camel.util.ObjectHelper.isNaN(value)) {
|
||||
return null;
|
||||
}
|
||||
return value.byteValue();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Byte toByte(String value) {
|
||||
return Byte.valueOf(value);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static char[] toCharArray(String value) {
|
||||
return value.toCharArray();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Character toCharacter(String value) {
|
||||
return toChar(value);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static char toChar(String value) {
|
||||
// must be string with the length of 1
|
||||
if (value.length() != 1) {
|
||||
throw new IllegalArgumentException("String must have exactly a length of 1: " + value);
|
||||
}
|
||||
return value.charAt(0);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String fromCharArray(char[] value) {
|
||||
return new String(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the converted value, or null if the value is null
|
||||
*/
|
||||
@Converter
|
||||
public static Class<?> toClass(String value, Exchange exchange) {
|
||||
// prefer to use class resolver API
|
||||
if (exchange != null) {
|
||||
return exchange.getContext().getClassResolver().resolveClass(value);
|
||||
} else {
|
||||
return org.apache.camel.util.ObjectHelper.loadClass(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the converted value, or null if the value is null
|
||||
*/
|
||||
@Converter(allowNull = true)
|
||||
public static Short toShort(Number value) {
|
||||
if (org.apache.camel.util.ObjectHelper.isNaN(value)) {
|
||||
return null;
|
||||
}
|
||||
return value.shortValue();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Short toShort(String value) {
|
||||
return Short.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the converted value, or null if the value is null
|
||||
*/
|
||||
@Converter(allowNull = true)
|
||||
public static Integer toInteger(Number value) {
|
||||
if (org.apache.camel.util.ObjectHelper.isNaN(value)) {
|
||||
return null;
|
||||
}
|
||||
return value.intValue();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Integer toInteger(String value) {
|
||||
return Integer.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the converted value, or null if the value is null
|
||||
*/
|
||||
@Converter(allowNull = true)
|
||||
public static Long toLong(Number value) {
|
||||
if (org.apache.camel.util.ObjectHelper.isNaN(value)) {
|
||||
return null;
|
||||
}
|
||||
return value.longValue();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Long toLong(String value) {
|
||||
return Long.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the converted value, or null if the value is null
|
||||
*/
|
||||
@Converter(allowNull = true)
|
||||
public static BigInteger toBigInteger(Object value) {
|
||||
if (org.apache.camel.util.ObjectHelper.isNaN(value)) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof String) {
|
||||
return new BigInteger((String) value);
|
||||
}
|
||||
|
||||
Long num = null;
|
||||
if (value instanceof Number) {
|
||||
Number number = (Number) value;
|
||||
num = number.longValue();
|
||||
}
|
||||
if (num != null) {
|
||||
return BigInteger.valueOf(num);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the converted value, or null if the value is null
|
||||
*/
|
||||
@Converter
|
||||
public static Float toFloat(Number value) {
|
||||
if (org.apache.camel.util.ObjectHelper.isNaN(value)) {
|
||||
return Float.NaN;
|
||||
}
|
||||
return value.floatValue();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Float toFloat(String value) {
|
||||
return Float.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the converted value, or null if the value is null
|
||||
*/
|
||||
@Converter
|
||||
public static Double toDouble(Number value) {
|
||||
if (org.apache.camel.util.ObjectHelper.isNaN(value)) {
|
||||
return Double.NaN;
|
||||
}
|
||||
return value.doubleValue();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Double toDouble(String value) {
|
||||
return Double.valueOf(value);
|
||||
}
|
||||
|
||||
// add fast type converters from most common used
|
||||
|
||||
@Converter
|
||||
public static String toString(Integer value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(Long value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(Boolean value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(StringBuffer value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static String toString(StringBuilder value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Boolean toBoolean(String value) {
|
||||
return org.apache.camel.util.ObjectHelper.toBoolean(value);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.converter;
|
||||
|
||||
import org.apache.camel.Converter;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* SQL Date and time related converters.
|
||||
*/
|
||||
@Converter(generateLoader = true)
|
||||
public final class SQLConverter {
|
||||
|
||||
/**
|
||||
* Utility classes should not have a public constructor.
|
||||
*/
|
||||
private SQLConverter() {
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Timestamp toTimestamp(Long l) {
|
||||
return new Timestamp(l);
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static Long toLong(Timestamp ts) {
|
||||
return ts.getTime();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
A set of helper classes for converting from different types of Java object to be used by the
|
||||
<a href="http://camel.apache.org/type-converter.html">Type Conversion Support</a>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,414 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.Converter;
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.TypeConverter;
|
||||
import org.apache.camel.TypeConverterLoaderException;
|
||||
import org.apache.camel.spi.PackageScanClassResolver;
|
||||
import org.apache.camel.spi.TypeConverterLoader;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.util.CastUtils;
|
||||
import org.apache.camel.util.IOHelper;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
import org.apache.camel.util.StringHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
import static java.lang.reflect.Modifier.*;
|
||||
|
||||
/**
|
||||
* A class which will auto-discover {@link Converter} objects and methods to pre-load
|
||||
* the {@link TypeConverterRegistry} of converters on startup.
|
||||
* <p/>
|
||||
* This implementation supports scanning for type converters in JAR files. The {@link #META_INF_SERVICES}
|
||||
* contains a list of packages or FQN class names for {@link Converter} classes. The FQN class names
|
||||
* is loaded first and directly by the class loader.
|
||||
* <p/>
|
||||
* The {@link PackageScanClassResolver} is being used to scan packages for {@link Converter} classes and
|
||||
* this procedure is slower than loading the {@link Converter} classes directly by its FQN class name.
|
||||
* Therefore its recommended to specify FQN class names in the {@link #META_INF_SERVICES} file.
|
||||
* Likewise the procedure for scanning using {@link PackageScanClassResolver} may require custom implementations
|
||||
* to work in various containers such as JBoss, OSGi, etc.
|
||||
*/
|
||||
public class AnnotationTypeConverterLoader implements TypeConverterLoader {
|
||||
public static final String META_INF_SERVICES = "META-INF/services/org/apache/camel/TypeConverter";
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AnnotationTypeConverterLoader.class);
|
||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
protected PackageScanClassResolver resolver;
|
||||
protected Set<Class<?>> visitedClasses = new HashSet<>();
|
||||
protected Set<String> visitedURIs = new HashSet<>();
|
||||
|
||||
public AnnotationTypeConverterLoader(PackageScanClassResolver resolver) {
|
||||
this.resolver = resolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
|
||||
String[] packageNames;
|
||||
|
||||
LOG.trace("Searching for {} services", META_INF_SERVICES);
|
||||
try {
|
||||
packageNames = findPackageNames();
|
||||
if (packageNames == null || packageNames.length == 0) {
|
||||
LOG.debug("No package names found to be used for classpath scanning for annotated type converters.");
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new TypeConverterLoaderException("Cannot find package names to be used for classpath scanning for annotated type converters.", e);
|
||||
}
|
||||
|
||||
// if we only have camel-core on the classpath then we have already pre-loaded all its type converters
|
||||
// but we exposed the "org.apache.camel.core" package in camel-core. This ensures there is at least one
|
||||
// packageName to scan, which triggers the scanning process. That allows us to ensure that we look for
|
||||
// META-INF/services in all the JARs.
|
||||
if (packageNames.length == 1 && "org.apache.camel.core".equals(packageNames[0])) {
|
||||
LOG.debug("No additional package names found in classpath for annotated type converters.");
|
||||
// no additional package names found to load type converters so break out
|
||||
return;
|
||||
}
|
||||
|
||||
// now filter out org.apache.camel.core as its not needed anymore (it was just a dummy)
|
||||
packageNames = filterUnwantedPackage("org.apache.camel.core", packageNames);
|
||||
|
||||
// filter out package names which can be loaded as a class directly so we avoid package scanning which
|
||||
// is much slower and does not work 100% in all runtime containers
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
packageNames = filterPackageNamesOnly(resolver, packageNames, classes);
|
||||
if (!classes.isEmpty()) {
|
||||
LOG.debug("Loaded {} @Converter classes", classes.size());
|
||||
}
|
||||
|
||||
// if there is any packages to scan and load @Converter classes, then do it
|
||||
if (packageNames != null && packageNames.length > 0) {
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Found converter packages to scan: {}", String.join(", ", packageNames));
|
||||
}
|
||||
Set<Class<?>> scannedClasses = resolver.findAnnotated(Converter.class, packageNames);
|
||||
if (scannedClasses.isEmpty()) {
|
||||
throw new TypeConverterLoaderException("Cannot find any type converter classes from the following packages: " + Arrays.asList(packageNames));
|
||||
}
|
||||
LOG.debug("Found {} packages with {} @Converter classes to load", packageNames.length, scannedClasses.size());
|
||||
classes.addAll(scannedClasses);
|
||||
}
|
||||
|
||||
// load all the found classes into the type converter registry
|
||||
for (Class<?> type : classes) {
|
||||
if (acceptClass(type)) {
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Loading converter class: {}", ObjectHelper.name(type));
|
||||
}
|
||||
loadConverterMethods(registry, type);
|
||||
}
|
||||
}
|
||||
|
||||
// now clear the maps so we do not hold references
|
||||
visitedClasses.clear();
|
||||
visitedURIs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the given list of packages and returns an array of <b>only</b> package names.
|
||||
* <p/>
|
||||
* This implementation will check the given list of packages, and if it contains a class name,
|
||||
* that class will be loaded directly and added to the list of classes. This optimizes the
|
||||
* type converter to avoid excessive file scanning for .class files.
|
||||
*
|
||||
* @param resolver the class resolver
|
||||
* @param packageNames the package names
|
||||
* @param classes to add loaded @Converter classes
|
||||
* @return the filtered package names
|
||||
*/
|
||||
protected String[] filterPackageNamesOnly(PackageScanClassResolver resolver, String[] packageNames, Set<Class<?>> classes) {
|
||||
if (packageNames == null || packageNames.length == 0) {
|
||||
return packageNames;
|
||||
}
|
||||
|
||||
// optimize for CorePackageScanClassResolver
|
||||
if (resolver.getClassLoaders().isEmpty()) {
|
||||
return packageNames;
|
||||
}
|
||||
|
||||
// the filtered packages to return
|
||||
List<String> packages = new ArrayList<>();
|
||||
|
||||
// try to load it as a class first
|
||||
for (String name : packageNames) {
|
||||
// must be a FQN class name by having an upper case letter
|
||||
if (StringHelper.isClassName(name)) {
|
||||
Class<?> clazz = null;
|
||||
for (ClassLoader loader : resolver.getClassLoaders()) {
|
||||
try {
|
||||
clazz = ObjectHelper.loadClass(name, loader);
|
||||
if (clazz != null) {
|
||||
LOG.trace("Loaded {} as class {}", name, clazz);
|
||||
classes.add(clazz);
|
||||
// class found, so no need to load it with another class loader
|
||||
}
|
||||
break;
|
||||
} catch (Throwable e) {
|
||||
// do nothing here
|
||||
}
|
||||
}
|
||||
if (clazz == null) {
|
||||
// ignore as its not a class (will be package scan afterwards)
|
||||
packages.add(name);
|
||||
}
|
||||
} else {
|
||||
// ignore as its not a class (will be package scan afterwards)
|
||||
packages.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
// return the packages which is not FQN classes
|
||||
return packages.toArray(new String[packages.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the names of the packages to search for on the classpath looking
|
||||
* for text files on the classpath at the {@link #META_INF_SERVICES} location.
|
||||
*
|
||||
* @return a collection of packages to search for
|
||||
* @throws IOException is thrown for IO related errors
|
||||
*/
|
||||
protected String[] findPackageNames() throws IOException {
|
||||
Set<String> packages = new HashSet<>();
|
||||
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
|
||||
if (ccl != null) {
|
||||
findPackages(packages, ccl);
|
||||
}
|
||||
findPackages(packages, getClass().getClassLoader());
|
||||
return packages.toArray(new String[packages.size()]);
|
||||
}
|
||||
|
||||
protected void findPackages(Set<String> packages, ClassLoader classLoader) throws IOException {
|
||||
Enumeration<URL> resources = classLoader.getResources(META_INF_SERVICES);
|
||||
while (resources.hasMoreElements()) {
|
||||
URL url = resources.nextElement();
|
||||
String path = url.getPath();
|
||||
if (!visitedURIs.contains(path)) {
|
||||
// remember we have visited this uri so we wont read it twice
|
||||
visitedURIs.add(path);
|
||||
LOG.debug("Loading file {} to retrieve list of packages, from url: {}", META_INF_SERVICES, url);
|
||||
BufferedReader reader = IOHelper.buffered(new InputStreamReader(url.openStream(), UTF8));
|
||||
try {
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
line = line.trim();
|
||||
if (line.startsWith("#") || line.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
tokenize(packages, line);
|
||||
}
|
||||
} finally {
|
||||
IOHelper.close(reader, null, LOG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenizes the line from the META-IN/services file using commas and
|
||||
* ignoring whitespace between packages
|
||||
*/
|
||||
private void tokenize(Set<String> packages, String line) {
|
||||
StringTokenizer iter = new StringTokenizer(line, ",");
|
||||
while (iter.hasMoreTokens()) {
|
||||
String name = iter.nextToken().trim();
|
||||
if (name.length() > 0) {
|
||||
packages.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all of the converter methods for the given type
|
||||
*/
|
||||
protected void loadConverterMethods(TypeConverterRegistry registry, Class<?> type) {
|
||||
if (visitedClasses.contains(type)) {
|
||||
return;
|
||||
}
|
||||
visitedClasses.add(type);
|
||||
try {
|
||||
Method[] methods = type.getDeclaredMethods();
|
||||
org.apache.camel.impl.converter.CachingInjector<?> injector = null;
|
||||
|
||||
for (Method method : methods) {
|
||||
// this may be prone to ClassLoader or packaging problems when the same class is defined
|
||||
// in two different jars (as is the case sometimes with specs).
|
||||
if (ObjectHelper.hasAnnotation(method, Converter.class, true)) {
|
||||
boolean allowNull = false;
|
||||
if (method.getAnnotation(Converter.class) != null) {
|
||||
allowNull = method.getAnnotation(Converter.class).allowNull();
|
||||
}
|
||||
boolean fallback = method.getAnnotation(Converter.class).fallback();
|
||||
if (fallback) {
|
||||
injector = handleHasFallbackConverterAnnotation(registry, type, injector, method, allowNull);
|
||||
} else {
|
||||
injector = handleHasConverterAnnotation(registry, type, injector, method, allowNull);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> superclass = type.getSuperclass();
|
||||
if (superclass != null && !superclass.equals(Object.class)) {
|
||||
loadConverterMethods(registry, superclass);
|
||||
}
|
||||
} catch (NoClassDefFoundError e) {
|
||||
boolean ignore = false;
|
||||
// does the class allow to ignore the type converter when having load errors
|
||||
if (ObjectHelper.hasAnnotation(type, Converter.class, true)) {
|
||||
if (type.getAnnotation(Converter.class) != null) {
|
||||
ignore = type.getAnnotation(Converter.class).ignoreOnLoadError();
|
||||
}
|
||||
}
|
||||
// if we should ignore then only log at debug level
|
||||
if (ignore) {
|
||||
LOG.debug("Ignoring converter type: " + type.getCanonicalName() + " as a dependent class could not be found: " + e, e);
|
||||
} else {
|
||||
LOG.warn("Ignoring converter type: " + type.getCanonicalName() + " as a dependent class could not be found: " + e, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean acceptClass(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private org.apache.camel.impl.converter.CachingInjector<?> handleHasConverterAnnotation(TypeConverterRegistry registry, Class<?> type,
|
||||
org.apache.camel.impl.converter.CachingInjector<?> injector, Method method, boolean allowNull) {
|
||||
if (isValidConverterMethod(method)) {
|
||||
int modifiers = method.getModifiers();
|
||||
if (isAbstract(modifiers) || !isPublic(modifiers)) {
|
||||
LOG.warn("Ignoring bad converter on type: " + type.getCanonicalName() + " method: " + method
|
||||
+ " as a converter method is not a public and concrete method");
|
||||
} else {
|
||||
Class<?> toType = method.getReturnType();
|
||||
if (toType.equals(Void.class)) {
|
||||
LOG.warn("Ignoring bad converter on type: " + type.getCanonicalName() + " method: "
|
||||
+ method + " as a converter method returns a void method");
|
||||
} else {
|
||||
Class<?> fromType = method.getParameterTypes()[0];
|
||||
if (isStatic(modifiers)) {
|
||||
registerTypeConverter(registry, method, toType, fromType,
|
||||
new StaticMethodTypeConverter(method, allowNull));
|
||||
} else {
|
||||
if (injector == null) {
|
||||
injector = new org.apache.camel.impl.converter.CachingInjector<>(registry, CastUtils.cast(type, Object.class));
|
||||
}
|
||||
registerTypeConverter(registry, method, toType, fromType,
|
||||
new InstanceMethodTypeConverter(injector, method, registry, allowNull));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG.warn("Ignoring bad converter on type: " + type.getCanonicalName() + " method: " + method
|
||||
+ " as a converter method should have one parameter");
|
||||
}
|
||||
return injector;
|
||||
}
|
||||
|
||||
private org.apache.camel.impl.converter.CachingInjector<?> handleHasFallbackConverterAnnotation(TypeConverterRegistry registry, Class<?> type,
|
||||
org.apache.camel.impl.converter.CachingInjector<?> injector, Method method, boolean allowNull) {
|
||||
if (isValidFallbackConverterMethod(method)) {
|
||||
int modifiers = method.getModifiers();
|
||||
if (isAbstract(modifiers) || !isPublic(modifiers)) {
|
||||
LOG.warn("Ignoring bad fallback converter on type: " + type.getCanonicalName() + " method: " + method
|
||||
+ " as a fallback converter method is not a public and concrete method");
|
||||
} else {
|
||||
Class<?> toType = method.getReturnType();
|
||||
if (toType.equals(Void.class)) {
|
||||
LOG.warn("Ignoring bad fallback converter on type: " + type.getCanonicalName() + " method: "
|
||||
+ method + " as a fallback converter method returns a void method");
|
||||
} else {
|
||||
if (isStatic(modifiers)) {
|
||||
registerFallbackTypeConverter(registry, new StaticMethodFallbackTypeConverter(method, registry, allowNull), method);
|
||||
} else {
|
||||
if (injector == null) {
|
||||
injector = new CachingInjector<>(registry, CastUtils.cast(type, Object.class));
|
||||
}
|
||||
registerFallbackTypeConverter(registry, new InstanceMethodFallbackTypeConverter(injector, method, registry, allowNull), method);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG.warn("Ignoring bad fallback converter on type: " + type.getCanonicalName() + " method: " + method
|
||||
+ " as a fallback converter method should have one parameter");
|
||||
}
|
||||
return injector;
|
||||
}
|
||||
|
||||
protected void registerTypeConverter(TypeConverterRegistry registry,
|
||||
Method method, Class<?> toType, Class<?> fromType, TypeConverter typeConverter) {
|
||||
registry.addTypeConverter(toType, fromType, typeConverter);
|
||||
}
|
||||
|
||||
protected boolean isValidConverterMethod(Method method) {
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
return (parameterTypes != null) && (parameterTypes.length == 1
|
||||
|| (parameterTypes.length == 2 && Exchange.class.isAssignableFrom(parameterTypes[1])));
|
||||
}
|
||||
|
||||
protected void registerFallbackTypeConverter(TypeConverterRegistry registry, TypeConverter typeConverter, Method method) {
|
||||
boolean canPromote = false;
|
||||
// check whether the annotation may indicate it can promote
|
||||
if (method.getAnnotation(Converter.class) != null) {
|
||||
canPromote = method.getAnnotation(Converter.class).fallbackCanPromote();
|
||||
}
|
||||
registry.addFallbackTypeConverter(typeConverter, canPromote);
|
||||
}
|
||||
|
||||
protected boolean isValidFallbackConverterMethod(Method method) {
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
return (parameterTypes != null) && (parameterTypes.length == 3
|
||||
|| (parameterTypes.length == 4 && Exchange.class.isAssignableFrom(parameterTypes[1]))
|
||||
&& (TypeConverterRegistry.class.isAssignableFrom(parameterTypes[parameterTypes.length - 1])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the given list of packages
|
||||
*
|
||||
* @param name the name to filter out
|
||||
* @param packageNames the packages
|
||||
* @return he packages without the given name
|
||||
*/
|
||||
protected static String[] filterUnwantedPackage(String name, String[] packageNames) {
|
||||
// the filtered packages to return
|
||||
List<String> packages = new ArrayList<>();
|
||||
|
||||
for (String s : packageNames) {
|
||||
if (!name.equals(s)) {
|
||||
packages.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
return packages.toArray(new String[packages.size()]);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A type converter which is used to convert to and from array types
|
||||
* particularly for derived types of array component types and dealing with
|
||||
* primitive array types.
|
||||
*/
|
||||
public class ArrayTypeConverter extends TypeConverterSupport {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
|
||||
if (type.isArray()) {
|
||||
if (value instanceof Collection) {
|
||||
Collection<?> collection = (Collection<?>)value;
|
||||
Object array = Array.newInstance(type.getComponentType(), collection.size());
|
||||
if (array instanceof Object[]) {
|
||||
collection.toArray((Object[])array);
|
||||
} else {
|
||||
int index = 0;
|
||||
for (Object element : collection) {
|
||||
Array.set(array, index++, element);
|
||||
}
|
||||
}
|
||||
return (T)array;
|
||||
} else if (value != null && value.getClass().isArray()) {
|
||||
int size = Array.getLength(value);
|
||||
Object answer = Array.newInstance(type.getComponentType(), size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Array.set(answer, i, Array.get(value, i));
|
||||
}
|
||||
return (T)answer;
|
||||
}
|
||||
} else if (Collection.class.isAssignableFrom(type)) {
|
||||
if (value != null) {
|
||||
if (value instanceof Object[]) {
|
||||
return (T)Arrays.asList((Object[])value);
|
||||
} else if (value.getClass().isArray()) {
|
||||
int size = Array.getLength(value);
|
||||
List<Object> answer = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
answer.add(Array.get(value, i));
|
||||
}
|
||||
return (T)answer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.AsyncProcessor;
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.Processor;
|
||||
import org.apache.camel.support.AsyncProcessorConverterHelper;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
|
||||
/**
|
||||
* A simple converter that can convert any {@link Processor} to an {@link AsyncProcessor}.
|
||||
* Processing will still occur synchronously but it will provide the required
|
||||
* notifications that the caller expects.
|
||||
*/
|
||||
public class AsyncProcessorTypeConverter extends TypeConverterSupport {
|
||||
|
||||
@Override
|
||||
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
|
||||
if (type.equals(AsyncProcessor.class)) {
|
||||
if (value instanceof Processor) {
|
||||
return type.cast(AsyncProcessorConverterHelper.convert((Processor) value));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,287 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.spi.FactoryFinder;
|
||||
import org.apache.camel.spi.Injector;
|
||||
import org.apache.camel.spi.PackageScanClassResolver;
|
||||
import org.apache.camel.spi.TypeConverterLoader;
|
||||
import org.apache.camel.util.IOHelper;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Base implementation of a type converter registry used for
|
||||
* <a href="http://camel.apache.org/type-converter.html">type converters</a> in Camel.
|
||||
*/
|
||||
public abstract class BaseTypeConverterRegistry extends CoreTypeConverterRegistry {
|
||||
|
||||
public static final String META_INF_SERVICES_TYPE_CONVERTER_LOADER = "META-INF/services/org/apache/camel/TypeConverterLoader";
|
||||
public static final String META_INF_SERVICES_FALLBACK_TYPE_CONVERTER = "META-INF/services/org/apache/camel/FallbackTypeConverter";
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BaseTypeConverterRegistry.class);
|
||||
|
||||
protected final List<TypeConverterLoader> typeConverterLoaders = new ArrayList<>();
|
||||
protected CamelContext camelContext;
|
||||
protected PackageScanClassResolver resolver;
|
||||
protected Injector injector;
|
||||
protected final FactoryFinder factoryFinder;
|
||||
|
||||
public BaseTypeConverterRegistry(CamelContext camelContext, PackageScanClassResolver resolver, Injector injector, FactoryFinder factoryFinder) {
|
||||
this.camelContext = camelContext;
|
||||
this.injector = injector;
|
||||
this.factoryFinder = factoryFinder;
|
||||
this.resolver = resolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CamelContext getCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCamelContext(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
}
|
||||
|
||||
public List<TypeConverterLoader> getTypeConverterLoaders() {
|
||||
return typeConverterLoaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTypeConverters(TypeConverters typeConverters) {
|
||||
LOG.trace("Adding type converters: {}", typeConverters);
|
||||
try {
|
||||
// scan the class for @Converter and load them into this registry
|
||||
TypeConvertersLoader loader = new TypeConvertersLoader(typeConverters);
|
||||
loader.load(this);
|
||||
} catch (TypeConverterLoaderException e) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFallbackTypeConverter(TypeConverter typeConverter, boolean canPromote) {
|
||||
super.addFallbackTypeConverter(typeConverter, canPromote);
|
||||
if (typeConverter instanceof CamelContextAware) {
|
||||
CamelContextAware camelContextAware = (CamelContextAware) typeConverter;
|
||||
if (camelContext != null) {
|
||||
camelContextAware.setCamelContext(camelContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addCoreFallbackTypeConverterToList(TypeConverter typeConverter, boolean canPromote,
|
||||
List<FallbackTypeConverter> converters) {
|
||||
LOG.trace("Adding core fallback type converter: {} which can promote: {}", typeConverter, canPromote);
|
||||
|
||||
// add in top of fallback as the toString() fallback will nearly always be able to convert
|
||||
// the last one which is add to the FallbackTypeConverter will be called at the first place
|
||||
converters.add(0, new FallbackTypeConverter(typeConverter, canPromote));
|
||||
|
||||
if (typeConverter instanceof CamelContextAware) {
|
||||
CamelContextAware camelContextAware = (CamelContextAware) typeConverter;
|
||||
if (camelContext != null) {
|
||||
camelContextAware.setCamelContext(camelContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injector getInjector() {
|
||||
return injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInjector(Injector injector) {
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
public PackageScanClassResolver getResolver() {
|
||||
return resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the core type converters which is mandatory to use Camel,
|
||||
* and also loads the fast type converters (generated via @Converter(loader = true).
|
||||
*/
|
||||
public void loadCoreAndFastTypeConverters() throws Exception {
|
||||
Collection<String> names = findTypeConverterLoaderClasses();
|
||||
for (String name : names) {
|
||||
LOG.debug("Resolving TypeConverterLoader: {}", name);
|
||||
Class<?> clazz = null;
|
||||
for (ClassLoader loader : getResolver().getClassLoaders()) {
|
||||
try {
|
||||
clazz = loader.loadClass(name);
|
||||
} catch (Throwable e) {
|
||||
// ignore
|
||||
}
|
||||
if (clazz != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (clazz == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
Object obj = getInjector().newInstance(clazz, false);
|
||||
if (obj instanceof TypeConverterLoader) {
|
||||
TypeConverterLoader loader = (TypeConverterLoader) obj;
|
||||
LOG.debug("TypeConverterLoader: {} loading converters", name);
|
||||
loader.load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the type converter loader classes from the classpath looking
|
||||
* for text files on the classpath at the {@link #META_INF_SERVICES_TYPE_CONVERTER_LOADER} location.
|
||||
*/
|
||||
protected Collection<String> findTypeConverterLoaderClasses() throws IOException {
|
||||
Set<String> loaders = new LinkedHashSet<>();
|
||||
Collection<URL> loaderResources = getLoaderUrls();
|
||||
for (URL url : loaderResources) {
|
||||
LOG.debug("Loading file {} to retrieve list of type converters, from url: {}", META_INF_SERVICES_TYPE_CONVERTER_LOADER, url);
|
||||
BufferedReader reader = IOHelper.buffered(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8));
|
||||
String line;
|
||||
do {
|
||||
line = reader.readLine();
|
||||
if (line != null && !line.startsWith("#") && !line.isEmpty()) {
|
||||
loaders.add(line);
|
||||
}
|
||||
} while (line != null);
|
||||
IOHelper.close(reader);
|
||||
}
|
||||
return loaders;
|
||||
}
|
||||
|
||||
protected Collection<URL> getLoaderUrls() throws IOException {
|
||||
List<URL> loaderResources = new ArrayList<>();
|
||||
for (ClassLoader classLoader : resolver.getClassLoaders()) {
|
||||
Enumeration<URL> resources = classLoader.getResources(META_INF_SERVICES_TYPE_CONVERTER_LOADER);
|
||||
while (resources.hasMoreElements()) {
|
||||
URL url = resources.nextElement();
|
||||
loaderResources.add(url);
|
||||
}
|
||||
}
|
||||
return loaderResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the registry is loaded and if not lazily load it
|
||||
*/
|
||||
protected void loadTypeConverters() throws Exception {
|
||||
for (TypeConverterLoader typeConverterLoader : getTypeConverterLoaders()) {
|
||||
typeConverterLoader.load(this);
|
||||
}
|
||||
|
||||
// lets try load any other fallback converters
|
||||
try {
|
||||
loadFallbackTypeConverters();
|
||||
} catch (NoFactoryAvailableException e) {
|
||||
// ignore its fine to have none
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the fallback type converter classes from the classpath looking
|
||||
* for text files on the classpath at the {@link #META_INF_SERVICES_FALLBACK_TYPE_CONVERTER} location.
|
||||
*/
|
||||
protected Collection<String> findFallbackTypeConverterClasses() throws IOException {
|
||||
Set<String> loaders = new LinkedHashSet<>();
|
||||
Collection<URL> loaderResources = getFallbackUrls();
|
||||
for (URL url : loaderResources) {
|
||||
LOG.debug("Loading file {} to retrieve list of fallback type converters, from url: {}", META_INF_SERVICES_FALLBACK_TYPE_CONVERTER, url);
|
||||
BufferedReader reader = IOHelper.buffered(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8));
|
||||
try {
|
||||
reader.lines()
|
||||
.map(String::trim)
|
||||
.filter(l -> !l.isEmpty())
|
||||
.filter(l -> !l.startsWith("#"))
|
||||
.forEach(loaders::add);
|
||||
} finally {
|
||||
IOHelper.close(reader, url.toString(), LOG);
|
||||
}
|
||||
}
|
||||
return loaders;
|
||||
}
|
||||
|
||||
protected Collection<URL> getFallbackUrls() throws IOException {
|
||||
List<URL> loaderResources = new ArrayList<>();
|
||||
for (ClassLoader classLoader : resolver.getClassLoaders()) {
|
||||
Enumeration<URL> resources = classLoader.getResources(META_INF_SERVICES_FALLBACK_TYPE_CONVERTER);
|
||||
while (resources.hasMoreElements()) {
|
||||
URL url = resources.nextElement();
|
||||
loaderResources.add(url);
|
||||
}
|
||||
}
|
||||
return loaderResources;
|
||||
}
|
||||
|
||||
protected void loadFallbackTypeConverters() throws IOException, ClassNotFoundException {
|
||||
Collection<String> names = findFallbackTypeConverterClasses();
|
||||
for (String name : names) {
|
||||
LOG.debug("Resolving FallbackTypeConverter: {}", name);
|
||||
Class<?> clazz = getResolver().getClassLoaders().stream()
|
||||
.map(cl -> ObjectHelper.loadClass(name, cl))
|
||||
.filter(Objects::nonNull)
|
||||
.findAny().orElseThrow(() -> new ClassNotFoundException(name));
|
||||
Object obj = getInjector().newInstance(clazz, false);
|
||||
if (obj instanceof TypeConverter) {
|
||||
TypeConverter fb = (TypeConverter) obj;
|
||||
LOG.debug("Adding loaded FallbackTypeConverter: {}", name);
|
||||
addFallbackTypeConverter(fb, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() throws Exception {
|
||||
if (injector == null && camelContext != null) {
|
||||
injector = camelContext.getInjector();
|
||||
}
|
||||
if (resolver == null && camelContext != null) {
|
||||
resolver = camelContext.adapt(ExtendedCamelContext.class).getPackageScanClassResolver();
|
||||
}
|
||||
|
||||
List<FallbackTypeConverter> fallbacks = new ArrayList<>();
|
||||
// add to string first as it will then be last in the last as to string can nearly
|
||||
// always convert something to a string so we want it only as the last resort
|
||||
// ToStringTypeConverter should NOT allow to be promoted
|
||||
addCoreFallbackTypeConverterToList(new ToStringTypeConverter(), false, fallbacks);
|
||||
// enum is okay to be promoted
|
||||
addCoreFallbackTypeConverterToList(new EnumTypeConverter(), true, fallbacks);
|
||||
// arrays is okay to be promoted
|
||||
addCoreFallbackTypeConverterToList(new ArrayTypeConverter(), true, fallbacks);
|
||||
// and future should also not allowed to be promoted
|
||||
addCoreFallbackTypeConverterToList(new FutureTypeConverter(this), false, fallbacks);
|
||||
// add sync processor to async processor converter is to be promoted
|
||||
addCoreFallbackTypeConverterToList(new AsyncProcessorTypeConverter(), true, fallbacks);
|
||||
|
||||
// add all core fallback converters at once which is faster (profiler)
|
||||
fallbackConverters.addAll(fallbacks);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
|
||||
/**
|
||||
* A caching proxy
|
||||
*/
|
||||
public class CachingInjector<T> {
|
||||
private final TypeConverterRegistry repository;
|
||||
private final Class<T> type;
|
||||
private T instance;
|
||||
|
||||
public CachingInjector(TypeConverterRegistry repository, Class<T> type) {
|
||||
this.repository = repository;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public synchronized T newInstance() {
|
||||
if (instance == null) {
|
||||
instance = createInstance(type);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected T createInstance(Class<T> t) {
|
||||
return repository.getInjector().newInstance(t);
|
||||
}
|
||||
}
|
@ -0,0 +1,678 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.converter.ObjectConverter;
|
||||
import org.apache.camel.spi.CamelLogger;
|
||||
import org.apache.camel.spi.Injector;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.MessageHelper;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.apache.camel.util.DoubleMap;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
public class CoreTypeConverterRegistry extends ServiceSupport implements TypeConverter, TypeConverterRegistry {
|
||||
|
||||
protected static final TypeConverter MISS_CONVERTER = new TypeConverterSupport() {
|
||||
@Override
|
||||
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException {
|
||||
return (T) MISS_VALUE;
|
||||
}
|
||||
};
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CoreTypeConverterRegistry.class);
|
||||
|
||||
protected final DoubleMap<Class<?>, Class<?>, TypeConverter> typeMappings = new DoubleMap<>(200);
|
||||
protected final List<FallbackTypeConverter> fallbackConverters = new CopyOnWriteArrayList<>();
|
||||
protected TypeConverterExists typeConverterExists = TypeConverterExists.Override;
|
||||
protected LoggingLevel typeConverterExistsLoggingLevel = LoggingLevel.WARN;
|
||||
protected final Statistics statistics = new UtilizationStatistics();
|
||||
protected final LongAdder noopCounter = new LongAdder();
|
||||
protected final LongAdder attemptCounter = new LongAdder();
|
||||
protected final LongAdder missCounter = new LongAdder();
|
||||
protected final LongAdder hitCounter = new LongAdder();
|
||||
protected final LongAdder failedCounter = new LongAdder();
|
||||
|
||||
public CoreTypeConverterRegistry() {
|
||||
}
|
||||
|
||||
public CoreTypeConverterRegistry(TypeConverterRegistry registry) {
|
||||
if (registry instanceof CoreTypeConverterRegistry) {
|
||||
CoreTypeConverterRegistry reg = (CoreTypeConverterRegistry) registry;
|
||||
reg.getTypeMappings().forEach(typeMappings::put);
|
||||
this.fallbackConverters.addAll(reg.getFallbackConverters());
|
||||
} else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
this.typeConverterExistsLoggingLevel = registry.getTypeConverterExistsLoggingLevel();
|
||||
this.typeConverterExists = registry.getTypeConverterExists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInjector(Injector injector) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injector getInjector() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCamelContext(CamelContext camelContext) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CamelContext getCamelContext() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public DoubleMap<Class<?>, Class<?>, TypeConverter> getTypeMappings() {
|
||||
return typeMappings;
|
||||
}
|
||||
|
||||
public List<FallbackTypeConverter> getFallbackConverters() {
|
||||
return fallbackConverters;
|
||||
}
|
||||
|
||||
public <T> T convertTo(Class<T> type, Object value) {
|
||||
return convertTo(type, null, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
|
||||
// optimize for a few common conversions
|
||||
if (value != null) {
|
||||
if (type.isInstance(value)) {
|
||||
// same instance
|
||||
return (T) value;
|
||||
}
|
||||
if (type == boolean.class) {
|
||||
// primitive boolean which must return a value so throw exception if not possible
|
||||
Object answer = ObjectConverter.toBoolean(value);
|
||||
if (answer == null) {
|
||||
throw new TypeConversionException(value, type, new IllegalArgumentException("Cannot convert type: " + value.getClass().getName() + " to boolean"));
|
||||
}
|
||||
return (T) answer;
|
||||
} else if (type == Boolean.class && (value instanceof String)) {
|
||||
// String -> Boolean
|
||||
String str = (String) value;
|
||||
if ("true".equalsIgnoreCase(str)) {
|
||||
return (T) Boolean.TRUE;
|
||||
} else if ("false".equalsIgnoreCase(str)) {
|
||||
return (T) Boolean.FALSE;
|
||||
}
|
||||
} else if (type.isPrimitive()) {
|
||||
// okay its a wrapper -> primitive then return as-is for some common types
|
||||
Class<?> cls = value.getClass();
|
||||
if (cls == Integer.class || cls == Long.class) {
|
||||
return (T) value;
|
||||
}
|
||||
} else if (type == String.class) {
|
||||
// okay its a primitive -> string then return as-is for some common types
|
||||
Class<?> cls = value.getClass();
|
||||
if (cls.isPrimitive()
|
||||
|| cls == Boolean.class || cls == boolean.class
|
||||
|| cls == Integer.class || cls == int.class
|
||||
|| cls == Long.class || cls == long.class) {
|
||||
return (T) value.toString();
|
||||
}
|
||||
}
|
||||
// NOTE: we cannot optimize any more if value is String as it may be time pattern and other patterns
|
||||
}
|
||||
|
||||
return (T) doConvertTo(type, exchange, value, false, false);
|
||||
}
|
||||
|
||||
public <T> T mandatoryConvertTo(Class<T> type, Object value) throws NoTypeConversionAvailableException {
|
||||
return mandatoryConvertTo(type, null, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T mandatoryConvertTo(Class<T> type, Exchange exchange, Object value) throws NoTypeConversionAvailableException {
|
||||
// optimize for a few common conversions
|
||||
if (value != null) {
|
||||
if (type.isInstance(value)) {
|
||||
// same instance
|
||||
return (T) value;
|
||||
}
|
||||
if (type == boolean.class) {
|
||||
// primitive boolean which must return a value so throw exception if not possible
|
||||
Object answer = ObjectConverter.toBoolean(value);
|
||||
if (answer == null) {
|
||||
throw new TypeConversionException(value, type, new IllegalArgumentException("Cannot convert type: " + value.getClass().getName() + " to boolean"));
|
||||
}
|
||||
return (T) answer;
|
||||
} else if (type == Boolean.class && (value instanceof String)) {
|
||||
// String -> Boolean
|
||||
String str = (String) value;
|
||||
if ("true".equalsIgnoreCase(str)) {
|
||||
return (T) Boolean.TRUE;
|
||||
} else if ("false".equalsIgnoreCase(str)) {
|
||||
return (T) Boolean.FALSE;
|
||||
}
|
||||
} else if (type.isPrimitive()) {
|
||||
// okay its a wrapper -> primitive then return as-is for some common types
|
||||
Class<?> cls = value.getClass();
|
||||
if (cls == Integer.class || cls == Long.class) {
|
||||
return (T) value;
|
||||
}
|
||||
} else if (type == String.class) {
|
||||
// okay its a primitive -> string then return as-is for some common types
|
||||
Class<?> cls = value.getClass();
|
||||
if (cls.isPrimitive()
|
||||
|| cls == Boolean.class || cls == boolean.class
|
||||
|| cls == Integer.class || cls == int.class
|
||||
|| cls == Long.class || cls == long.class) {
|
||||
return (T) value.toString();
|
||||
}
|
||||
}
|
||||
// NOTE: we cannot optimize any more if value is String as it may be time pattern and other patterns
|
||||
}
|
||||
|
||||
Object answer = doConvertTo(type, exchange, value, true, false);
|
||||
if (answer == null) {
|
||||
// Could not find suitable conversion
|
||||
throw new NoTypeConversionAvailableException(value, type);
|
||||
}
|
||||
return (T) answer;
|
||||
}
|
||||
|
||||
public <T> T tryConvertTo(Class<T> type, Object value) {
|
||||
return tryConvertTo(type, null, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T tryConvertTo(Class<T> type, Exchange exchange, Object value) {
|
||||
return (T) doConvertTo(type, exchange, value, false, true);
|
||||
}
|
||||
|
||||
protected Object doConvertTo(final Class<?> type, final Exchange exchange, final Object value,
|
||||
final boolean mandatory, final boolean tryConvert) {
|
||||
Object answer;
|
||||
try {
|
||||
answer = doConvertTo(type, exchange, value, tryConvert);
|
||||
} catch (Exception e) {
|
||||
if (statistics.isStatisticsEnabled()) {
|
||||
failedCounter.increment();
|
||||
}
|
||||
if (tryConvert) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if its a ExecutionException then we have rethrow it as its not due to failed conversion
|
||||
// this is special for FutureTypeConverter
|
||||
boolean execution = ObjectHelper.getException(ExecutionException.class, e) != null
|
||||
|| ObjectHelper.getException(CamelExecutionException.class, e) != null;
|
||||
if (execution) {
|
||||
throw CamelExecutionException.wrapCamelExecutionException(exchange, e);
|
||||
}
|
||||
// error occurred during type conversion
|
||||
throw createTypeConversionException(exchange, type, value, e);
|
||||
}
|
||||
if (answer == TypeConverter.MISS_VALUE) {
|
||||
// Could not find suitable conversion
|
||||
if (statistics.isStatisticsEnabled()) {
|
||||
missCounter.increment();
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
if (statistics.isStatisticsEnabled()) {
|
||||
hitCounter.increment();
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
|
||||
protected Object doConvertTo(final Class<?> type, final Exchange exchange, final Object value,
|
||||
final boolean tryConvert) throws Exception {
|
||||
boolean trace = LOG.isTraceEnabled();
|
||||
boolean statisticsEnabled = statistics.isStatisticsEnabled();
|
||||
|
||||
if (trace) {
|
||||
LOG.trace("Finding type converter to convert {} -> {} with value: {}",
|
||||
value == null ? "null" : value.getClass().getCanonicalName(),
|
||||
type.getCanonicalName(), value);
|
||||
}
|
||||
|
||||
|
||||
if (value == null) {
|
||||
// no type conversion was needed
|
||||
if (statisticsEnabled) {
|
||||
noopCounter.increment();
|
||||
}
|
||||
// lets avoid NullPointerException when converting to primitives for null values
|
||||
if (type.isPrimitive()) {
|
||||
if (boolean.class == type) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
if (int.class == type) {
|
||||
return 0;
|
||||
}
|
||||
if (long.class == type) {
|
||||
return 0L;
|
||||
}
|
||||
if (byte.class == type) {
|
||||
return (byte) 0;
|
||||
}
|
||||
if (short.class == type) {
|
||||
return (short) 0;
|
||||
}
|
||||
if (double.class == type) {
|
||||
return 0.0;
|
||||
}
|
||||
if (float.class == type) {
|
||||
return 0.0f;
|
||||
}
|
||||
if (char.class == type) {
|
||||
return '\0';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// same instance type
|
||||
if (type.isInstance(value)) {
|
||||
// no type conversion was needed
|
||||
if (statisticsEnabled) {
|
||||
noopCounter.increment();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// okay we need to attempt to convert
|
||||
if (statisticsEnabled) {
|
||||
attemptCounter.increment();
|
||||
}
|
||||
|
||||
// try to find a suitable type converter
|
||||
TypeConverter converter = getOrFindTypeConverter(type, value.getClass());
|
||||
if (converter != null) {
|
||||
if (trace) {
|
||||
LOG.trace("Using converter: {} to convert [{}=>{}]", converter, value.getClass(), type);
|
||||
}
|
||||
Object rc;
|
||||
if (tryConvert) {
|
||||
rc = converter.tryConvertTo(type, exchange, value);
|
||||
} else {
|
||||
rc = converter.convertTo(type, exchange, value);
|
||||
}
|
||||
if (rc != null) {
|
||||
return rc;
|
||||
} else if (converter.allowNull()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// not found with that type then if it was a primitive type then try again with the wrapper type
|
||||
if (type.isPrimitive()) {
|
||||
Class<?> primitiveType = ObjectHelper.convertPrimitiveTypeToWrapperType(type);
|
||||
if (primitiveType != type) {
|
||||
Class<?> fromType = value.getClass();
|
||||
TypeConverter tc = getOrFindTypeConverter(primitiveType, fromType);
|
||||
if (tc != null) {
|
||||
// add the type as a known type converter as we can convert from primitive to object converter
|
||||
addTypeConverter(type, fromType, tc);
|
||||
Object rc;
|
||||
if (tryConvert) {
|
||||
rc = tc.tryConvertTo(primitiveType, exchange, value);
|
||||
} else {
|
||||
rc = tc.convertTo(primitiveType, exchange, value);
|
||||
}
|
||||
if (rc == null && tc.allowNull()) {
|
||||
return null;
|
||||
} else if (rc != null) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fallback converters
|
||||
for (FallbackTypeConverter fallback : fallbackConverters) {
|
||||
TypeConverter tc = fallback.getFallbackTypeConverter();
|
||||
Object rc;
|
||||
if (tryConvert) {
|
||||
rc = tc.tryConvertTo(type, exchange, value);
|
||||
} else {
|
||||
rc = tc.convertTo(type, exchange, value);
|
||||
}
|
||||
if (rc == null && tc.allowNull()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (rc == TypeConverter.MISS_VALUE) {
|
||||
// it cannot be converted so give up
|
||||
return TypeConverter.MISS_VALUE;
|
||||
}
|
||||
|
||||
if (rc != null) {
|
||||
// if fallback can promote then let it be promoted to a first class type converter
|
||||
if (fallback.isCanPromote()) {
|
||||
// add it as a known type converter since we found a fallback that could do it
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Promoting fallback type converter as a known type converter to convert from: {} to: {} for the fallback converter: {}",
|
||||
type.getCanonicalName(), value.getClass().getCanonicalName(), fallback.getFallbackTypeConverter());
|
||||
}
|
||||
addTypeConverter(type, value.getClass(), fallback.getFallbackTypeConverter());
|
||||
}
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Fallback type converter {} converted type from: {} to: {}",
|
||||
fallback.getFallbackTypeConverter(), type.getCanonicalName(), value.getClass().getCanonicalName());
|
||||
}
|
||||
|
||||
// return converted value
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tryConvert) {
|
||||
// Could not find suitable conversion, so remember it
|
||||
// do not register misses for try conversions
|
||||
typeMappings.put(type, value.getClass(), MISS_CONVERTER);
|
||||
}
|
||||
|
||||
// Could not find suitable conversion, so return Void to indicate not found
|
||||
return TypeConverter.MISS_VALUE;
|
||||
}
|
||||
|
||||
public TypeConverter getTypeConverter(Class<?> toType, Class<?> fromType) {
|
||||
return typeMappings.get(toType, fromType);
|
||||
}
|
||||
|
||||
public void addTypeConverter(Class<?> toType, Class<?> fromType, TypeConverter typeConverter) {
|
||||
LOG.trace("Adding type converter: {}", typeConverter);
|
||||
TypeConverter converter = typeMappings.get(toType, fromType);
|
||||
// only override it if its different
|
||||
// as race conditions can lead to many threads trying to promote the same fallback converter
|
||||
|
||||
if (typeConverter != converter) {
|
||||
|
||||
// add the converter unless we should ignore
|
||||
boolean add = true;
|
||||
|
||||
// if converter is not null then a duplicate exists
|
||||
if (converter != null) {
|
||||
if (typeConverterExists == TypeConverterExists.Override) {
|
||||
CamelLogger logger = new CamelLogger(LOG, typeConverterExistsLoggingLevel);
|
||||
logger.log("Overriding type converter from: " + converter + " to: " + typeConverter);
|
||||
} else if (typeConverterExists == TypeConverterExists.Ignore) {
|
||||
CamelLogger logger = new CamelLogger(LOG, typeConverterExistsLoggingLevel);
|
||||
logger.log("Ignoring duplicate type converter from: " + converter + " to: " + typeConverter);
|
||||
add = false;
|
||||
} else {
|
||||
// we should fail
|
||||
throw new TypeConverterExistsException(toType, fromType);
|
||||
}
|
||||
}
|
||||
|
||||
if (add) {
|
||||
typeMappings.put(toType, fromType, typeConverter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeTypeConverter(Class<?> toType, Class<?> fromType) {
|
||||
LOG.trace("Removing type converter from: {} to: {}", fromType, toType);
|
||||
return typeMappings.remove(toType, fromType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTypeConverters(TypeConverters typeConverters) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void addFallbackTypeConverter(TypeConverter typeConverter, boolean canPromote) {
|
||||
LOG.trace("Adding fallback type converter: {} which can promote: {}", typeConverter, canPromote);
|
||||
|
||||
// add in top of fallback as the toString() fallback will nearly always be able to convert
|
||||
// the last one which is add to the FallbackTypeConverter will be called at the first place
|
||||
fallbackConverters.add(0, new FallbackTypeConverter(typeConverter, canPromote));
|
||||
}
|
||||
|
||||
public TypeConverter lookup(Class<?> toType, Class<?> fromType) {
|
||||
return doLookup(toType, fromType, false);
|
||||
}
|
||||
|
||||
protected TypeConverter getOrFindTypeConverter(Class<?> toType, Class<?> fromType) {
|
||||
TypeConverter converter = typeMappings.get(toType, fromType);
|
||||
if (converter == null) {
|
||||
// converter not found, try to lookup then
|
||||
converter = lookup(toType, fromType);
|
||||
if (converter != null) {
|
||||
typeMappings.put(toType, fromType, converter);
|
||||
}
|
||||
}
|
||||
return converter;
|
||||
}
|
||||
|
||||
protected TypeConverter doLookup(Class<?> toType, Class<?> fromType, boolean isSuper) {
|
||||
|
||||
if (fromType != null) {
|
||||
// lets try if there is a direct match
|
||||
TypeConverter converter = getTypeConverter(toType, fromType);
|
||||
if (converter != null) {
|
||||
return converter;
|
||||
}
|
||||
|
||||
// try the interfaces
|
||||
for (Class<?> type : fromType.getInterfaces()) {
|
||||
converter = getTypeConverter(toType, type);
|
||||
if (converter != null) {
|
||||
return converter;
|
||||
}
|
||||
}
|
||||
|
||||
// try super then
|
||||
Class<?> fromSuperClass = fromType.getSuperclass();
|
||||
if (fromSuperClass != null && !fromSuperClass.equals(Object.class)) {
|
||||
converter = doLookup(toType, fromSuperClass, true);
|
||||
if (converter != null) {
|
||||
return converter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only do these tests as fallback and only on the target type (eg not on its super)
|
||||
if (!isSuper) {
|
||||
if (fromType != null && !fromType.equals(Object.class)) {
|
||||
|
||||
// lets try classes derived from this toType
|
||||
TypeConverter converter = typeMappings.getFirst(
|
||||
toType::isAssignableFrom,
|
||||
// skip Object based we do them last
|
||||
from -> !from.equals(Object.class) && from.isAssignableFrom(fromType));
|
||||
if (converter != null) {
|
||||
return converter;
|
||||
}
|
||||
|
||||
// lets test for Object based converters as last resort
|
||||
converter = getTypeConverter(toType, Object.class);
|
||||
if (converter != null) {
|
||||
return converter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// none found
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Class<?>[]> listAllTypeConvertersFromTo() {
|
||||
List<Class<?>[]> answer = new ArrayList<>();
|
||||
typeMappings.forEach((k1, k2, v) -> answer.add(new Class<?>[]{k2, k1}));
|
||||
return answer;
|
||||
}
|
||||
|
||||
protected TypeConversionException createTypeConversionException(Exchange exchange, Class<?> type, Object
|
||||
value, Throwable cause) {
|
||||
if (cause instanceof TypeConversionException) {
|
||||
if (((TypeConversionException) cause).getToType() == type) {
|
||||
return (TypeConversionException) cause;
|
||||
}
|
||||
}
|
||||
Object body;
|
||||
// extract the body for logging which allows to limit the message body in the exception/stacktrace
|
||||
// and also can be used to turn off logging sensitive message data
|
||||
if (exchange != null) {
|
||||
body = MessageHelper.extractValueForLogging(value, exchange.getIn());
|
||||
} else {
|
||||
body = value;
|
||||
}
|
||||
return new TypeConversionException(body, type, cause);
|
||||
}
|
||||
|
||||
public Statistics getStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return typeMappings.size();
|
||||
}
|
||||
|
||||
public LoggingLevel getTypeConverterExistsLoggingLevel() {
|
||||
return typeConverterExistsLoggingLevel;
|
||||
}
|
||||
|
||||
public void setTypeConverterExistsLoggingLevel(LoggingLevel typeConverterExistsLoggingLevel) {
|
||||
this.typeConverterExistsLoggingLevel = typeConverterExistsLoggingLevel;
|
||||
}
|
||||
|
||||
public TypeConverterExists getTypeConverterExists() {
|
||||
return typeConverterExists;
|
||||
}
|
||||
|
||||
public void setTypeConverterExists(TypeConverterExists typeConverterExists) {
|
||||
this.typeConverterExists = typeConverterExists;
|
||||
}
|
||||
|
||||
protected void doStop() throws Exception {
|
||||
// log utilization statistics when stopping, including mappings
|
||||
if (statistics.isStatisticsEnabled()) {
|
||||
String info = statistics.toString();
|
||||
AtomicInteger misses = new AtomicInteger();
|
||||
typeMappings.forEach((k1, k2, v) -> {
|
||||
if (v == MISS_CONVERTER) {
|
||||
misses.incrementAndGet();
|
||||
}
|
||||
});
|
||||
info += String.format(" mappings[total=%s, misses=%s]", typeMappings.size(), misses);
|
||||
LOG.info(info);
|
||||
}
|
||||
|
||||
typeMappings.clear();
|
||||
statistics.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents utilization statistics
|
||||
*/
|
||||
private final class UtilizationStatistics implements Statistics {
|
||||
|
||||
private boolean statisticsEnabled;
|
||||
|
||||
@Override
|
||||
public long getNoopCounter() {
|
||||
return noopCounter.longValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAttemptCounter() {
|
||||
return attemptCounter.longValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getHitCounter() {
|
||||
return hitCounter.longValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMissCounter() {
|
||||
return missCounter.longValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFailedCounter() {
|
||||
return failedCounter.longValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
noopCounter.reset();
|
||||
attemptCounter.reset();
|
||||
hitCounter.reset();
|
||||
missCounter.reset();
|
||||
failedCounter.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStatisticsEnabled() {
|
||||
return statisticsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatisticsEnabled(boolean statisticsEnabled) {
|
||||
this.statisticsEnabled = statisticsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("TypeConverterRegistry utilization[noop=%s, attempts=%s, hits=%s, misses=%s, failures=%s]",
|
||||
getNoopCounter(), getAttemptCounter(), getHitCounter(), getMissCounter(), getFailedCounter());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a fallback type converter
|
||||
*/
|
||||
public static class FallbackTypeConverter {
|
||||
private final boolean canPromote;
|
||||
private final TypeConverter fallbackTypeConverter;
|
||||
|
||||
FallbackTypeConverter(TypeConverter fallbackTypeConverter, boolean canPromote) {
|
||||
this.canPromote = canPromote;
|
||||
this.fallbackTypeConverter = fallbackTypeConverter;
|
||||
}
|
||||
|
||||
public boolean isCanPromote() {
|
||||
return canPromote;
|
||||
}
|
||||
|
||||
public TypeConverter getFallbackTypeConverter() {
|
||||
return fallbackTypeConverter;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.spi.AnnotationScanTypeConverters;
|
||||
import org.apache.camel.spi.FactoryFinder;
|
||||
import org.apache.camel.spi.Injector;
|
||||
import org.apache.camel.spi.PackageScanClassResolver;
|
||||
import org.apache.camel.util.StopWatch;
|
||||
import org.apache.camel.util.TimeUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Default implementation of a type converter registry used for
|
||||
* <a href="http://camel.apache.org/type-converter.html">type converters</a> in Camel.
|
||||
* <p/>
|
||||
* This implementation will load type converters up-front on startup.
|
||||
*/
|
||||
public class DefaultTypeConverter extends BaseTypeConverterRegistry implements AnnotationScanTypeConverters {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultTypeConverter.class);
|
||||
|
||||
private volatile boolean loadTypeConvertersDone;
|
||||
private final boolean loadTypeConverters;
|
||||
|
||||
public DefaultTypeConverter(PackageScanClassResolver resolver, Injector injector,
|
||||
FactoryFinder factoryFinder, boolean loadTypeConverters) {
|
||||
this(null, resolver, injector, factoryFinder, loadTypeConverters);
|
||||
}
|
||||
|
||||
public DefaultTypeConverter(CamelContext camelContext, PackageScanClassResolver resolver, Injector injector,
|
||||
FactoryFinder factoryFinder, boolean loadTypeConverters) {
|
||||
super(camelContext, resolver, injector, factoryFinder);
|
||||
this.loadTypeConverters = loadTypeConverters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunAllowed() {
|
||||
// as type converter is used during initialization then allow it to always run
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() throws Exception {
|
||||
StopWatch watch = new StopWatch();
|
||||
|
||||
super.doInit();
|
||||
|
||||
// core type converters is always loaded which does not use any classpath scanning and therefore is fast
|
||||
loadCoreAndFastTypeConverters();
|
||||
|
||||
String time = TimeUtils.printDuration(watch.taken());
|
||||
LOG.debug("Loaded {} type converters in {}", typeMappings.size(), time);
|
||||
|
||||
if (!loadTypeConvertersDone && isLoadTypeConverters()) {
|
||||
scanTypeConverters();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLoadTypeConverters() {
|
||||
boolean load = loadTypeConverters;
|
||||
if (camelContext != null) {
|
||||
// camel context can override
|
||||
load = camelContext.isLoadTypeConverters();
|
||||
}
|
||||
return load;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanTypeConverters() throws Exception {
|
||||
StopWatch watch = new StopWatch();
|
||||
|
||||
// we are using backwards compatible legacy mode to detect additional converters
|
||||
if (!loadTypeConvertersDone) {
|
||||
loadTypeConvertersDone = true;
|
||||
|
||||
if (resolver != null) {
|
||||
typeConverterLoaders.add(new AnnotationTypeConverterLoader(resolver));
|
||||
}
|
||||
|
||||
int fast = typeMappings.size();
|
||||
// load type converters up front
|
||||
loadTypeConverters();
|
||||
int additional = typeMappings.size() - fast;
|
||||
|
||||
// report how many type converters we have loaded
|
||||
if (additional > 0) {
|
||||
LOG.info("Type converters loaded (fast: {}, scanned: {})", fast, additional);
|
||||
LOG.warn("Annotation scanning mode loaded {} type converters. Its recommended to migrate to @Converter(loader = true) for fast type converter mode.", additional);
|
||||
}
|
||||
|
||||
// lets clear the cache from the resolver as its often only used during startup
|
||||
if (resolver != null) {
|
||||
resolver.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
String time = TimeUtils.printDuration(watch.taken());
|
||||
LOG.debug("Scanned {} type converters in {}", typeMappings.size(), time);
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.RuntimeCamelException;
|
||||
import org.apache.camel.support.ObjectHelper;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* A type converter which is used to convert from String to enum type
|
||||
*/
|
||||
public class EnumTypeConverter extends TypeConverterSupport {
|
||||
|
||||
@Override
|
||||
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
|
||||
return EnumTypeConverter.doConvertTo(type, exchange, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T doConvertTo(Class<T> type, Exchange exchange, Object value) {
|
||||
if (type.isEnum()) {
|
||||
String text = value.toString();
|
||||
Class<Enum> enumClass = (Class<Enum>) type;
|
||||
|
||||
// we want to match case insensitive for enums
|
||||
for (Enum enumValue : enumClass.getEnumConstants()) {
|
||||
if (enumValue.name().equalsIgnoreCase(text)) {
|
||||
return type.cast(enumValue);
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to the JDK valueOf which is case-sensitive and throws exception if not found
|
||||
Method method;
|
||||
try {
|
||||
method = type.getMethod("valueOf", String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeCamelException("Could not find valueOf method on enum type: " + type.getName());
|
||||
}
|
||||
return (T) ObjectHelper.invokeMethod(method, null, text);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.Converter;
|
||||
import org.apache.camel.spi.PackageScanClassResolver;
|
||||
|
||||
public final class FastAnnotationTypeConverterLoader extends AnnotationTypeConverterLoader {
|
||||
|
||||
public FastAnnotationTypeConverterLoader(PackageScanClassResolver resolver) {
|
||||
super(resolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean acceptClass(Class<?> clazz) {
|
||||
Converter conv = clazz.getAnnotation(Converter.class);
|
||||
if (conv != null) {
|
||||
// skip all loader classes as they have already been loaded
|
||||
return !conv.generateLoader();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.StreamCache;
|
||||
import org.apache.camel.TypeConversionException;
|
||||
import org.apache.camel.TypeConverter;
|
||||
import org.apache.camel.support.ExchangeHelper;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Future type converter.
|
||||
*/
|
||||
public final class FutureTypeConverter extends TypeConverterSupport {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FutureTypeConverter.class);
|
||||
|
||||
private final TypeConverter converter;
|
||||
|
||||
public FutureTypeConverter(TypeConverter converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
|
||||
try {
|
||||
return doConvertTo(type, exchange, value);
|
||||
} catch (Exception e) {
|
||||
throw new TypeConversionException(value, type, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T doConvertTo(Class<T> type, Exchange exchange, Object value) throws Exception {
|
||||
// do not convert to stream cache
|
||||
if (StreamCache.class.isAssignableFrom(value.getClass())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Future.class.isAssignableFrom(value.getClass())) {
|
||||
|
||||
Future<?> future = (Future<?>) value;
|
||||
|
||||
if (future.isCancelled()) {
|
||||
// return void to indicate its not possible to convert at this time
|
||||
return (T) MISS_VALUE;
|
||||
}
|
||||
|
||||
// do some trace logging as the get is blocking until the response is ready
|
||||
LOG.trace("Getting future response");
|
||||
|
||||
Object body = future.get();
|
||||
LOG.trace("Got future response");
|
||||
|
||||
if (body == null) {
|
||||
// return void to indicate its not possible to convert at this time
|
||||
return (T) MISS_VALUE;
|
||||
}
|
||||
|
||||
// maybe from is already the type we want
|
||||
if (type.isAssignableFrom(body.getClass())) {
|
||||
return type.cast(body);
|
||||
} else if (body instanceof Exchange) {
|
||||
Exchange result = (Exchange) body;
|
||||
body = ExchangeHelper.extractResultBody(result, result.getPattern());
|
||||
}
|
||||
|
||||
// no then convert to the type
|
||||
return converter.convertTo(type, exchange, body);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.RuntimeCamelException;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.ObjectHelper;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* A {@link org.apache.camel.TypeConverter} implementation which instantiates an object
|
||||
* so that an instance method can be used as a fallback type converter
|
||||
*/
|
||||
public class InstanceMethodFallbackTypeConverter extends TypeConverterSupport {
|
||||
private final org.apache.camel.impl.converter.CachingInjector<?> injector;
|
||||
private final Method method;
|
||||
private final boolean useExchange;
|
||||
private final TypeConverterRegistry registry;
|
||||
private final boolean allowNull;
|
||||
|
||||
public InstanceMethodFallbackTypeConverter(CachingInjector<?> injector, Method method, TypeConverterRegistry registry, boolean allowNull) {
|
||||
this.injector = injector;
|
||||
this.method = method;
|
||||
this.useExchange = method.getParameterCount() == 4;
|
||||
this.registry = registry;
|
||||
this.allowNull = allowNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InstanceMethodFallbackTypeConverter: " + method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowNull() {
|
||||
return allowNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
|
||||
Object instance = injector.newInstance();
|
||||
if (instance == null) {
|
||||
throw new RuntimeCamelException("Could not instantiate an instance of: " + type.getCanonicalName());
|
||||
}
|
||||
return useExchange
|
||||
? (T)ObjectHelper.invokeMethod(method, instance, type, exchange, value, registry) : (T) ObjectHelper
|
||||
.invokeMethod(method, instance, type, value, registry);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.RuntimeCamelException;
|
||||
import org.apache.camel.TypeConverter;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.ObjectHelper;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* A {@link TypeConverter} implementation which instantiates an object
|
||||
* so that an instance method can be used as a type converter
|
||||
*/
|
||||
public class InstanceMethodTypeConverter extends TypeConverterSupport {
|
||||
private final org.apache.camel.impl.converter.CachingInjector<?> injector;
|
||||
private final Method method;
|
||||
private final boolean useExchange;
|
||||
private final boolean allowNull;
|
||||
|
||||
public InstanceMethodTypeConverter(CachingInjector<?> injector, Method method, TypeConverterRegistry registry, boolean allowNull) {
|
||||
this.injector = injector;
|
||||
this.method = method;
|
||||
this.useExchange = method.getParameterCount() == 2;
|
||||
this.allowNull = allowNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InstanceMethodTypeConverter: " + method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowNull() {
|
||||
return allowNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
|
||||
Object instance = injector.newInstance();
|
||||
if (instance == null) {
|
||||
throw new RuntimeCamelException("Could not instantiate an instance of: " + type.getCanonicalName());
|
||||
}
|
||||
return useExchange
|
||||
? (T)ObjectHelper.invokeMethod(method, instance, value, exchange) : (T) ObjectHelper
|
||||
.invokeMethod(method, instance, value);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.spi.TypeConverterRegistry;
|
||||
import org.apache.camel.support.ObjectHelper;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* A {@link org.apache.camel.TypeConverter} implementation which invokes a static method
|
||||
* as a fallback type converter from a type to another type
|
||||
*/
|
||||
public class StaticMethodFallbackTypeConverter extends TypeConverterSupport {
|
||||
private final Method method;
|
||||
private final boolean useExchange;
|
||||
private final TypeConverterRegistry registry;
|
||||
private final boolean allowNull;
|
||||
|
||||
public StaticMethodFallbackTypeConverter(Method method, TypeConverterRegistry registry, boolean allowNull) {
|
||||
this.method = method;
|
||||
this.useExchange = method.getParameterCount() == 4;
|
||||
this.registry = registry;
|
||||
this.allowNull = allowNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowNull() {
|
||||
return allowNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StaticMethodFallbackTypeConverter: " + method;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
|
||||
return useExchange ? (T)ObjectHelper.invokeMethod(method, null, type, exchange, value, registry)
|
||||
: (T) ObjectHelper.invokeMethod(method, null, type, value, registry);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.TypeConverter;
|
||||
import org.apache.camel.support.ObjectHelper;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* A {@link TypeConverter} implementation which invokes a static method to convert from a type to another type
|
||||
*/
|
||||
public class StaticMethodTypeConverter extends TypeConverterSupport {
|
||||
private final Method method;
|
||||
private final boolean useExchange;
|
||||
private final boolean allowNull;
|
||||
|
||||
public StaticMethodTypeConverter(Method method, boolean allowNull) {
|
||||
this.method = method;
|
||||
this.useExchange = method.getParameterCount() == 2;
|
||||
this.allowNull = allowNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StaticMethodTypeConverter: " + method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowNull() {
|
||||
return allowNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
|
||||
return useExchange ? (T)ObjectHelper.invokeMethod(method, null, value, exchange)
|
||||
: (T) ObjectHelper.invokeMethod(method, null, value);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.Message;
|
||||
import org.apache.camel.WrappedFile;
|
||||
import org.apache.camel.support.TypeConverterSupport;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* A simple converter that can convert any object to a String type by using the
|
||||
* toString() method of the object.
|
||||
*/
|
||||
public class ToStringTypeConverter extends TypeConverterSupport {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T convertTo(Class<T> toType, Exchange exchange, Object value) {
|
||||
|
||||
// should not try to convert these specific types
|
||||
if (value instanceof Message || value instanceof WrappedFile || value instanceof Future) {
|
||||
return (T) MISS_VALUE;
|
||||
}
|
||||
|
||||
if (toType.equals(String.class)) {
|
||||
return (T) value.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.TypeConverters;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A type converter loader, that <b>only</b> supports scanning a {@link TypeConverters} class
|
||||
* for methods that has been annotated with {@link org.apache.camel.Converter}.
|
||||
*/
|
||||
public class TypeConvertersLoader extends AnnotationTypeConverterLoader {
|
||||
|
||||
private final TypeConverters typeConverters;
|
||||
|
||||
/**
|
||||
* Creates the loader
|
||||
*
|
||||
* @param typeConverters The implementation that has the type converters
|
||||
*/
|
||||
public TypeConvertersLoader(TypeConverters typeConverters) {
|
||||
super(new TypeConvertersPackageScanClassResolver(typeConverters.getClass()));
|
||||
this.typeConverters = typeConverters;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] findPackageNames() throws IOException {
|
||||
// this method doesn't change the behavior of the CorePackageScanClassResolver
|
||||
String name = typeConverters.getClass().getPackage().getName();
|
||||
return new String[]{name};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.spi.PackageScanClassResolver;
|
||||
import org.apache.camel.spi.PackageScanFilter;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A {@link org.apache.camel.spi.ClassResolver} which loads type converters
|
||||
* from an instance that implements {@link org.apache.camel.TypeConverters}.
|
||||
* <p/>
|
||||
* This is used when adding converters manually using the
|
||||
* {@link org.apache.camel.impl.converter.BaseTypeConverterRegistry#addTypeConverters(org.apache.camel.TypeConverters)} method.
|
||||
*/
|
||||
public class TypeConvertersPackageScanClassResolver extends ServiceSupport implements PackageScanClassResolver {
|
||||
|
||||
private final Set<ClassLoader> classLoaders = new LinkedHashSet<>();
|
||||
private final Set<Class<?>> converters = new LinkedHashSet<>();
|
||||
|
||||
public TypeConvertersPackageScanClassResolver(Class<?> clazz) {
|
||||
converters.add(clazz);
|
||||
// use the classloader that loaded the class
|
||||
classLoaders.add(clazz.getClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ClassLoader> getClassLoaders() {
|
||||
// return a new set to avoid any concurrency issues in other runtimes such as OSGi
|
||||
return Collections.unmodifiableSet(new LinkedHashSet<>(classLoaders));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClassLoader(ClassLoader classLoader) {
|
||||
classLoaders.add(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> findAnnotated(Class<? extends Annotation> annotation, String... packageNames) {
|
||||
return converters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> findAnnotated(Set<Class<? extends Annotation>> annotations, String... packageNames) {
|
||||
return converters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> findImplementations(Class<?> parent, String... packageNames) {
|
||||
// noop
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> findByFilter(PackageScanFilter filter, String... packageNames) {
|
||||
// noop
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFilter(PackageScanFilter filter) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFilter(PackageScanFilter filter) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAcceptableSchemes(String schemes) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache() {
|
||||
// noop
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.impl.converter;
|
||||
|
||||
import org.apache.camel.Converter;
|
||||
import org.apache.camel.TypeConversionException;
|
||||
import org.apache.camel.TypeConverter;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
/**
|
||||
* A {@link TypeConverter} that converts to and from {@link URI}s.
|
||||
*/
|
||||
@Converter(generateLoader = true)
|
||||
public final class UriTypeConverter {
|
||||
|
||||
private UriTypeConverter() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static CharSequence toCharSequence(final URI value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static URI toUri(final CharSequence value) {
|
||||
final String stringValue = String.valueOf(value);
|
||||
|
||||
try {
|
||||
return new URI(stringValue);
|
||||
} catch (final URISyntaxException e) {
|
||||
throw new TypeConversionException(value, URI.class, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Default implementation classes the <a href="http://camel.apache.org/type-converter.html">Type Conversion Strategies</a>
|
||||
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.ExtendedCamelContext;
|
||||
import org.apache.camel.StaticService;
|
||||
import org.apache.camel.spi.RouteController;
|
||||
import org.apache.camel.support.LRUCache;
|
||||
import org.apache.camel.support.LRUCacheFactory;
|
||||
import org.apache.camel.support.service.ServiceHelper;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Base implementation for {@link org.apache.camel.spi.TransformerRegistry}, {@link org.apache.camel.spi.ValidatorRegistry}
|
||||
* and {@link org.apache.camel.spi.EndpointRegistry}.
|
||||
*/
|
||||
public class AbstractDynamicRegistry<K, V> extends AbstractMap<K, V> implements StaticService {
|
||||
|
||||
protected final ExtendedCamelContext context;
|
||||
protected final RouteController routeController;
|
||||
protected final int maxCacheSize;
|
||||
protected final Map<K, V> dynamicMap;
|
||||
protected final Map<K, V> staticMap;
|
||||
|
||||
public AbstractDynamicRegistry(CamelContext context, int maxCacheSize) {
|
||||
this.context = (ExtendedCamelContext) context;
|
||||
this.routeController = context.getRouteController();
|
||||
this.maxCacheSize = maxCacheSize;
|
||||
// do not stop on eviction, as the transformer may still be in use
|
||||
this.dynamicMap = LRUCacheFactory.newLRUCache(this.maxCacheSize, this.maxCacheSize, false);
|
||||
// static map to hold transformers we do not want to be evicted
|
||||
this.staticMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (dynamicMap instanceof LRUCache) {
|
||||
((LRUCache) dynamicMap).resetStatistics();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object o) {
|
||||
// try static map first
|
||||
V answer = staticMap.get(o);
|
||||
if (answer == null) {
|
||||
answer = dynamicMap.get(o);
|
||||
if (answer != null && (context.isSetupRoutes() || routeController.isStartingRoutes())) {
|
||||
dynamicMap.remove(o);
|
||||
staticMap.put((K) o, answer);
|
||||
}
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V transformer) {
|
||||
// at first we must see if the key already exists and then replace it back, so it stays the same spot
|
||||
V answer = staticMap.remove(key);
|
||||
if (answer != null) {
|
||||
// replace existing
|
||||
staticMap.put(key, transformer);
|
||||
return answer;
|
||||
}
|
||||
|
||||
answer = dynamicMap.remove(key);
|
||||
if (answer != null) {
|
||||
// replace existing
|
||||
dynamicMap.put(key, transformer);
|
||||
return answer;
|
||||
}
|
||||
|
||||
// we want transformers to be static if they are part of setting up or starting routes
|
||||
if (context.isSetupRoutes() || routeController.isStartingRoutes()) {
|
||||
answer = staticMap.put(key, transformer);
|
||||
} else {
|
||||
answer = dynamicMap.put(key, transformer);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return staticMap.containsKey(o) || dynamicMap.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return staticMap.containsValue(o) || dynamicMap.containsValue(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return staticMap.size() + dynamicMap.size();
|
||||
}
|
||||
|
||||
public int staticSize() {
|
||||
return staticMap.size();
|
||||
}
|
||||
|
||||
public int dynamicSize() {
|
||||
return dynamicMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return staticMap.isEmpty() && dynamicMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object o) {
|
||||
V answer = staticMap.remove(o);
|
||||
if (answer == null) {
|
||||
answer = dynamicMap.remove(o);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
staticMap.clear();
|
||||
dynamicMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
return new AbstractSet<Entry<K, V>>() {
|
||||
@Override
|
||||
public Iterator<Entry<K, V>> iterator() {
|
||||
return new org.apache.camel.impl.engine.CompoundIterator<>(Arrays.asList(
|
||||
staticMap.entrySet().iterator(), dynamicMap.entrySet().iterator()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return staticMap.size() + dynamicMap.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public int getMaximumCacheSize() {
|
||||
return maxCacheSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges the cache
|
||||
*/
|
||||
public void purge() {
|
||||
// only purge the dynamic part
|
||||
dynamicMap.clear();
|
||||
}
|
||||
|
||||
public void cleanUp() {
|
||||
if (dynamicMap instanceof LRUCache) {
|
||||
((LRUCache) dynamicMap).cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isStatic(K key) {
|
||||
return staticMap.containsKey(key);
|
||||
}
|
||||
|
||||
public boolean isDynamic(K key) {
|
||||
return dynamicMap.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
ServiceHelper.stopService(staticMap.values(), dynamicMap.values());
|
||||
purge();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Registry for " + context.getName() + ", capacity: " + maxCacheSize;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,536 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.NamedNode;
|
||||
import org.apache.camel.StaticService;
|
||||
import org.apache.camel.spi.ExecutorServiceManager;
|
||||
import org.apache.camel.spi.LifecycleStrategy;
|
||||
import org.apache.camel.spi.ThreadPoolFactory;
|
||||
import org.apache.camel.spi.ThreadPoolProfile;
|
||||
import org.apache.camel.support.CamelContextHelper;
|
||||
import org.apache.camel.support.DefaultThreadPoolFactory;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.apache.camel.util.*;
|
||||
import org.apache.camel.util.concurrent.CamelThreadFactory;
|
||||
import org.apache.camel.util.concurrent.SizedScheduledExecutorService;
|
||||
import org.apache.camel.util.concurrent.ThreadHelper;
|
||||
import org.apache.camel.util.concurrent.ThreadPoolRejectedPolicy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Base {@link ExecutorServiceManager} which can be used for implementations
|
||||
*/
|
||||
public class BaseExecutorServiceManager extends ServiceSupport implements ExecutorServiceManager {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BaseExecutorServiceManager.class);
|
||||
|
||||
private final CamelContext camelContext;
|
||||
private ThreadPoolFactory threadPoolFactory = new DefaultThreadPoolFactory();
|
||||
private final List<ExecutorService> executorServices = new CopyOnWriteArrayList<>();
|
||||
private String threadNamePattern;
|
||||
private long shutdownAwaitTermination = 10000;
|
||||
private String defaultThreadPoolProfileId = "defaultThreadPoolProfile";
|
||||
private final Map<String, ThreadPoolProfile> threadPoolProfiles = new ConcurrentHashMap<>();
|
||||
private ThreadPoolProfile defaultProfile;
|
||||
|
||||
public BaseExecutorServiceManager(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
|
||||
defaultProfile = new ThreadPoolProfile(defaultThreadPoolProfileId);
|
||||
defaultProfile.setDefaultProfile(true);
|
||||
defaultProfile.setPoolSize(10);
|
||||
defaultProfile.setMaxPoolSize(20);
|
||||
defaultProfile.setKeepAliveTime(60L);
|
||||
defaultProfile.setTimeUnit(TimeUnit.SECONDS);
|
||||
defaultProfile.setMaxQueueSize(1000);
|
||||
defaultProfile.setAllowCoreThreadTimeOut(true);
|
||||
defaultProfile.setRejectedPolicy(ThreadPoolRejectedPolicy.CallerRuns);
|
||||
|
||||
registerThreadPoolProfile(defaultProfile);
|
||||
}
|
||||
|
||||
public CamelContext getCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadPoolFactory getThreadPoolFactory() {
|
||||
return threadPoolFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThreadPoolFactory(ThreadPoolFactory threadPoolFactory) {
|
||||
this.threadPoolFactory = threadPoolFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerThreadPoolProfile(ThreadPoolProfile profile) {
|
||||
ObjectHelper.notNull(profile, "profile");
|
||||
StringHelper.notEmpty(profile.getId(), "id", profile);
|
||||
threadPoolProfiles.put(profile.getId(), profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadPoolProfile getThreadPoolProfile(String id) {
|
||||
return threadPoolProfiles.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadPoolProfile getDefaultThreadPoolProfile() {
|
||||
return getThreadPoolProfile(defaultThreadPoolProfileId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultThreadPoolProfile(ThreadPoolProfile defaultThreadPoolProfile) {
|
||||
threadPoolProfiles.remove(defaultThreadPoolProfileId);
|
||||
defaultThreadPoolProfile.addDefaults(defaultProfile);
|
||||
|
||||
LOG.info("Using custom DefaultThreadPoolProfile: {}", defaultThreadPoolProfile);
|
||||
|
||||
this.defaultThreadPoolProfileId = defaultThreadPoolProfile.getId();
|
||||
defaultThreadPoolProfile.setDefaultProfile(true);
|
||||
registerThreadPoolProfile(defaultThreadPoolProfile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThreadNamePattern() {
|
||||
return threadNamePattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThreadNamePattern(String threadNamePattern) {
|
||||
// must set camel id here in the pattern and let the other placeholders be resolved on demand
|
||||
this.threadNamePattern = StringHelper.replaceAll(threadNamePattern, "#camelId#", this.camelContext.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getShutdownAwaitTermination() {
|
||||
return shutdownAwaitTermination;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShutdownAwaitTermination(long shutdownAwaitTermination) {
|
||||
this.shutdownAwaitTermination = shutdownAwaitTermination;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String resolveThreadName(String name) {
|
||||
return ThreadHelper.resolveThreadName(threadNamePattern, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(String name, Runnable runnable) {
|
||||
ThreadFactory factory = createThreadFactory(name, true);
|
||||
return factory.newThread(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutorService newDefaultThreadPool(Object source, String name) {
|
||||
return newThreadPool(source, name, getDefaultThreadPoolProfile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledExecutorService newDefaultScheduledThreadPool(Object source, String name) {
|
||||
return newScheduledThreadPool(source, name, getDefaultThreadPoolProfile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutorService newThreadPool(Object source, String name, String profileId) {
|
||||
ThreadPoolProfile profile = getThreadPoolProfile(profileId);
|
||||
if (profile != null) {
|
||||
return newThreadPool(source, name, profile);
|
||||
} else {
|
||||
// no profile with that id
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutorService newThreadPool(Object source, String name, ThreadPoolProfile profile) {
|
||||
String sanitizedName = URISupport.sanitizeUri(name);
|
||||
ObjectHelper.notNull(profile, "ThreadPoolProfile");
|
||||
|
||||
ThreadPoolProfile defaultProfile = getDefaultThreadPoolProfile();
|
||||
profile.addDefaults(defaultProfile);
|
||||
|
||||
ThreadFactory threadFactory = createThreadFactory(sanitizedName, true);
|
||||
ExecutorService executorService = threadPoolFactory.newThreadPool(profile, threadFactory);
|
||||
onThreadPoolCreated(executorService, source, profile.getId());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Created new ThreadPool for source: {} with name: {}. -> {}", source, sanitizedName, executorService);
|
||||
}
|
||||
|
||||
return executorService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutorService newThreadPool(Object source, String name, int poolSize, int maxPoolSize) {
|
||||
ThreadPoolProfile profile = new ThreadPoolProfile(name);
|
||||
profile.setPoolSize(poolSize);
|
||||
profile.setMaxPoolSize(maxPoolSize);
|
||||
return newThreadPool(source, name, profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutorService newSingleThreadExecutor(Object source, String name) {
|
||||
return newFixedThreadPool(source, name, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutorService newCachedThreadPool(Object source, String name) {
|
||||
String sanitizedName = URISupport.sanitizeUri(name);
|
||||
ExecutorService answer = threadPoolFactory.newCachedThreadPool(createThreadFactory(sanitizedName, true));
|
||||
onThreadPoolCreated(answer, source, null);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Created new CachedThreadPool for source: {} with name: {}. -> {}", source, sanitizedName, answer);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutorService newFixedThreadPool(Object source, String name, int poolSize) {
|
||||
ThreadPoolProfile profile = new ThreadPoolProfile(name);
|
||||
profile.setPoolSize(poolSize);
|
||||
profile.setMaxPoolSize(poolSize);
|
||||
profile.setKeepAliveTime(0L);
|
||||
profile.setAllowCoreThreadTimeOut(false);
|
||||
return newThreadPool(source, name, profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledExecutorService newSingleThreadScheduledExecutor(Object source, String name) {
|
||||
ThreadPoolProfile profile = new ThreadPoolProfile(name);
|
||||
profile.setPoolSize(1);
|
||||
profile.setAllowCoreThreadTimeOut(false);
|
||||
return newScheduledThreadPool(source, name, profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledExecutorService newScheduledThreadPool(Object source, String name, ThreadPoolProfile profile) {
|
||||
String sanitizedName = URISupport.sanitizeUri(name);
|
||||
profile.addDefaults(getDefaultThreadPoolProfile());
|
||||
ScheduledExecutorService answer = threadPoolFactory.newScheduledThreadPool(profile, createThreadFactory(sanitizedName, true));
|
||||
onThreadPoolCreated(answer, source, null);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Created new ScheduledThreadPool for source: {} with name: {} -> {}", source, sanitizedName, answer);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledExecutorService newScheduledThreadPool(Object source, String name, String profileId) {
|
||||
ThreadPoolProfile profile = getThreadPoolProfile(profileId);
|
||||
if (profile != null) {
|
||||
return newScheduledThreadPool(source, name, profile);
|
||||
} else {
|
||||
// no profile with that id
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledExecutorService newScheduledThreadPool(Object source, String name, int poolSize) {
|
||||
ThreadPoolProfile profile = new ThreadPoolProfile(name);
|
||||
profile.setPoolSize(poolSize);
|
||||
return newScheduledThreadPool(source, name, profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown(ExecutorService executorService) {
|
||||
doShutdown(executorService, 0, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownGraceful(ExecutorService executorService) {
|
||||
doShutdown(executorService, getShutdownAwaitTermination(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownGraceful(ExecutorService executorService, long shutdownAwaitTermination) {
|
||||
doShutdown(executorService, shutdownAwaitTermination, false);
|
||||
}
|
||||
|
||||
private boolean doShutdown(ExecutorService executorService, long shutdownAwaitTermination, boolean failSafe) {
|
||||
if (executorService == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean warned = false;
|
||||
|
||||
// shutting down a thread pool is a 2 step process. First we try graceful, and if that fails, then we go more aggressively
|
||||
// and try shutting down again. In both cases we wait at most the given shutdown timeout value given
|
||||
// (total wait could then be 2 x shutdownAwaitTermination, but when we shutdown the 2nd time we are aggressive and thus
|
||||
// we ought to shutdown much faster)
|
||||
if (!executorService.isShutdown()) {
|
||||
StopWatch watch = new StopWatch();
|
||||
|
||||
LOG.trace("Shutdown of ExecutorService: {} with await termination: {} millis", executorService, shutdownAwaitTermination);
|
||||
executorService.shutdown();
|
||||
|
||||
if (shutdownAwaitTermination > 0) {
|
||||
try {
|
||||
if (!awaitTermination(executorService, shutdownAwaitTermination)) {
|
||||
warned = true;
|
||||
LOG.warn("Forcing shutdown of ExecutorService: {} due first await termination elapsed.", executorService);
|
||||
executorService.shutdownNow();
|
||||
// we are now shutting down aggressively, so wait to see if we can completely shutdown or not
|
||||
if (!awaitTermination(executorService, shutdownAwaitTermination)) {
|
||||
LOG.warn("Cannot completely force shutdown of ExecutorService: {} due second await termination elapsed.", executorService);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
warned = true;
|
||||
LOG.warn("Forcing shutdown of ExecutorService: {} due interrupted.", executorService);
|
||||
// we were interrupted during shutdown, so force shutdown
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
// if we logged at WARN level, then report at INFO level when we are complete so the end user can see this in the log
|
||||
if (warned) {
|
||||
LOG.info("Shutdown of ExecutorService: {} is shutdown: {} and terminated: {} took: {}.",
|
||||
executorService, executorService.isShutdown(), executorService.isTerminated(), TimeUtils.printDuration(watch.taken()));
|
||||
} else if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Shutdown of ExecutorService: {} is shutdown: {} and terminated: {} took: {}.",
|
||||
executorService, executorService.isShutdown(), executorService.isTerminated(), TimeUtils.printDuration(watch.taken()));
|
||||
}
|
||||
}
|
||||
|
||||
// let lifecycle strategy be notified as well which can let it be managed in JMX as well
|
||||
ThreadPoolExecutor threadPool = null;
|
||||
if (executorService instanceof ThreadPoolExecutor) {
|
||||
threadPool = (ThreadPoolExecutor) executorService;
|
||||
} else if (executorService instanceof SizedScheduledExecutorService) {
|
||||
threadPool = ((SizedScheduledExecutorService) executorService).getScheduledThreadPoolExecutor();
|
||||
}
|
||||
if (threadPool != null) {
|
||||
for (LifecycleStrategy lifecycle : camelContext.getLifecycleStrategies()) {
|
||||
lifecycle.onThreadPoolRemove(camelContext, threadPool);
|
||||
}
|
||||
}
|
||||
|
||||
// remove reference as its shutdown (do not remove if fail-safe)
|
||||
if (!failSafe) {
|
||||
executorServices.remove(executorService);
|
||||
}
|
||||
|
||||
return warned;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Runnable> shutdownNow(ExecutorService executorService) {
|
||||
return doShutdownNow(executorService, false);
|
||||
}
|
||||
|
||||
private List<Runnable> doShutdownNow(ExecutorService executorService, boolean failSafe) {
|
||||
ObjectHelper.notNull(executorService, "executorService");
|
||||
|
||||
List<Runnable> answer = null;
|
||||
if (!executorService.isShutdown()) {
|
||||
if (failSafe) {
|
||||
// log as warn, as we shutdown as fail-safe, so end user should see more details in the log.
|
||||
LOG.warn("Forcing shutdown of ExecutorService: {}", executorService);
|
||||
} else {
|
||||
LOG.debug("Forcing shutdown of ExecutorService: {}", executorService);
|
||||
}
|
||||
answer = executorService.shutdownNow();
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Shutdown of ExecutorService: {} is shutdown: {} and terminated: {}.",
|
||||
executorService, executorService.isShutdown(), executorService.isTerminated());
|
||||
}
|
||||
}
|
||||
|
||||
// let lifecycle strategy be notified as well which can let it be managed in JMX as well
|
||||
ThreadPoolExecutor threadPool = null;
|
||||
if (executorService instanceof ThreadPoolExecutor) {
|
||||
threadPool = (ThreadPoolExecutor) executorService;
|
||||
} else if (executorService instanceof SizedScheduledExecutorService) {
|
||||
threadPool = ((SizedScheduledExecutorService) executorService).getScheduledThreadPoolExecutor();
|
||||
}
|
||||
if (threadPool != null) {
|
||||
for (LifecycleStrategy lifecycle : camelContext.getLifecycleStrategies()) {
|
||||
lifecycle.onThreadPoolRemove(camelContext, threadPool);
|
||||
}
|
||||
}
|
||||
|
||||
// remove reference as its shutdown (do not remove if fail-safe)
|
||||
if (!failSafe) {
|
||||
executorServices.remove(executorService);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(ExecutorService executorService, long shutdownAwaitTermination) throws InterruptedException {
|
||||
// log progress every 2nd second so end user is aware of we are shutting down
|
||||
StopWatch watch = new StopWatch();
|
||||
long interval = Math.min(2000, shutdownAwaitTermination);
|
||||
boolean done = false;
|
||||
while (!done && interval > 0) {
|
||||
if (executorService.awaitTermination(interval, TimeUnit.MILLISECONDS)) {
|
||||
done = true;
|
||||
} else {
|
||||
LOG.info("Waited {} for ExecutorService: {} to terminate...", TimeUtils.printDuration(watch.taken()), executorService);
|
||||
// recalculate interval
|
||||
interval = Math.min(2000, shutdownAwaitTermination - watch.taken());
|
||||
}
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy callback when a new {@link ExecutorService} have been created.
|
||||
*
|
||||
* @param executorService the created {@link ExecutorService}
|
||||
*/
|
||||
protected void onNewExecutorService(ExecutorService executorService) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() throws Exception {
|
||||
super.doInit();
|
||||
if (threadNamePattern == null) {
|
||||
// set default name pattern which includes the camel context name
|
||||
threadNamePattern = "Camel (" + camelContext.getName() + ") thread ##counter# - #name#";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doShutdown() throws Exception {
|
||||
// shutdown all remainder executor services by looping and doing this aggressively
|
||||
// as by normal all threads pool should have been shutdown using proper lifecycle
|
||||
// by their EIPs, components etc. This is acting as a fail-safe during shutdown
|
||||
// of CamelContext itself.
|
||||
Set<ExecutorService> forced = new LinkedHashSet<>();
|
||||
if (!executorServices.isEmpty()) {
|
||||
// at first give a bit of time to shutdown nicely as the thread pool is most likely in the process of being shutdown also
|
||||
LOG.debug("Giving time for {} ExecutorService's to shutdown properly (acting as fail-safe)", executorServices.size());
|
||||
for (ExecutorService executorService : executorServices) {
|
||||
try {
|
||||
boolean warned = doShutdown(executorService, getShutdownAwaitTermination(), true);
|
||||
// remember the thread pools that was forced to shutdown (eg warned)
|
||||
if (warned) {
|
||||
forced.add(executorService);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// only log if something goes wrong as we want to shutdown them all
|
||||
LOG.warn("Error occurred during shutdown of ExecutorService: "
|
||||
+ executorService + ". This exception will be ignored.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// log the thread pools which was forced to shutdown so it may help the user to identify a problem of his
|
||||
if (!forced.isEmpty()) {
|
||||
LOG.warn("Forced shutdown of {} ExecutorService's which has not been shutdown properly (acting as fail-safe)", forced.size());
|
||||
for (ExecutorService executorService : forced) {
|
||||
LOG.warn(" forced -> {}", executorService);
|
||||
}
|
||||
}
|
||||
forced.clear();
|
||||
|
||||
// clear list
|
||||
executorServices.clear();
|
||||
|
||||
// do not clear the default profile as we could potential be restarted
|
||||
Iterator<ThreadPoolProfile> it = threadPoolProfiles.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
ThreadPoolProfile profile = it.next();
|
||||
if (!profile.isDefaultProfile()) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a new thread pool is created.
|
||||
* This implementation will invoke the {@link LifecycleStrategy#onThreadPoolAdd(CamelContext,
|
||||
* ThreadPoolExecutor, String, String, String, String) LifecycleStrategy.onThreadPoolAdd} method,
|
||||
* which for example will enlist the thread pool in JMX management.
|
||||
*
|
||||
* @param executorService the thread pool
|
||||
* @param source the source to use the thread pool
|
||||
* @param threadPoolProfileId profile id, if the thread pool was created from a thread pool profile
|
||||
*/
|
||||
private void onThreadPoolCreated(ExecutorService executorService, Object source, String threadPoolProfileId) {
|
||||
// add to internal list of thread pools
|
||||
executorServices.add(executorService);
|
||||
|
||||
String id;
|
||||
String sourceId = null;
|
||||
String routeId = null;
|
||||
|
||||
// extract id from source
|
||||
if (source instanceof NamedNode) {
|
||||
id = ((NamedNode) source).getId();
|
||||
// and let source be the short name of the pattern
|
||||
sourceId = ((NamedNode) source).getShortName();
|
||||
} else if (source instanceof String) {
|
||||
id = (String) source;
|
||||
} else if (source != null) {
|
||||
if (source instanceof StaticService) {
|
||||
// the source is static service so its name would be unique
|
||||
id = source.getClass().getSimpleName();
|
||||
} else {
|
||||
// fallback and use the simple class name with hashcode for the id so its unique for this given source
|
||||
id = source.getClass().getSimpleName() + "(" + ObjectHelper.getIdentityHashCode(source) + ")";
|
||||
}
|
||||
} else {
|
||||
// no source, so fallback and use the simple class name from thread pool and its hashcode identity so its unique
|
||||
id = executorService.getClass().getSimpleName() + "(" + ObjectHelper.getIdentityHashCode(executorService) + ")";
|
||||
}
|
||||
|
||||
// id is mandatory
|
||||
StringHelper.notEmpty(id, "id for thread pool " + executorService);
|
||||
|
||||
// extract route id if possible
|
||||
if (source instanceof NamedNode) {
|
||||
routeId = CamelContextHelper.getRouteId((NamedNode) source);
|
||||
}
|
||||
|
||||
// let lifecycle strategy be notified as well which can let it be managed in JMX as well
|
||||
ThreadPoolExecutor threadPool = null;
|
||||
if (executorService instanceof ThreadPoolExecutor) {
|
||||
threadPool = (ThreadPoolExecutor) executorService;
|
||||
} else if (executorService instanceof SizedScheduledExecutorService) {
|
||||
threadPool = ((SizedScheduledExecutorService) executorService).getScheduledThreadPoolExecutor();
|
||||
}
|
||||
if (threadPool != null) {
|
||||
for (LifecycleStrategy lifecycle : camelContext.getLifecycleStrategies()) {
|
||||
lifecycle.onThreadPoolAdd(camelContext, threadPool, id, sourceId, routeId, threadPoolProfileId);
|
||||
}
|
||||
}
|
||||
|
||||
// now call strategy to allow custom logic
|
||||
onNewExecutorService(executorService);
|
||||
}
|
||||
|
||||
protected ThreadFactory createThreadFactory(String name, boolean isDaemon) {
|
||||
return new CamelThreadFactory(threadNamePattern, name, isDaemon);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.CamelContextAware;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Base class for package scan resolvers.
|
||||
*/
|
||||
public abstract class BasePackageScanResolver extends ServiceSupport implements CamelContextAware {
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
protected String[] acceptableSchemes = {};
|
||||
private final Set<ClassLoader> classLoaders = new LinkedHashSet<>();
|
||||
private CamelContext camelContext;
|
||||
|
||||
public BasePackageScanResolver() {
|
||||
try {
|
||||
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
|
||||
if (ccl != null) {
|
||||
log.trace("Adding ContextClassLoader from current thread: {}", ccl);
|
||||
classLoaders.add(ccl);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore this exception
|
||||
log.warn("Cannot add ContextClassLoader from current thread due {}. This exception will be ignored.", e.getMessage());
|
||||
}
|
||||
|
||||
classLoaders.add(BasePackageScanResolver.class.getClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CamelContext getCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCamelContext(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
}
|
||||
|
||||
public void addClassLoader(ClassLoader classLoader) {
|
||||
classLoaders.add(classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* To specify a set of accepted schemas to use for loading resources as URL connections
|
||||
* (besides http and https schemas)
|
||||
*/
|
||||
public void setAcceptableSchemes(String schemes) {
|
||||
if (schemes != null) {
|
||||
acceptableSchemes = schemes.split(";");
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isAcceptableScheme(String urlPath) {
|
||||
if (urlPath != null) {
|
||||
for (String scheme : acceptableSchemes) {
|
||||
if (urlPath.startsWith(scheme)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Set<ClassLoader> getClassLoaders() {
|
||||
// return a new set to avoid any concurrency issues in other runtimes such as OSGi
|
||||
return Collections.unmodifiableSet(new LinkedHashSet<>(classLoaders));
|
||||
}
|
||||
|
||||
// We can override this method to support the custom ResourceLocator
|
||||
protected URL customResourceLocator(URL url) throws IOException {
|
||||
// Do nothing here
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy to get the resources by the given classloader.
|
||||
* <p/>
|
||||
* Notice that in WebSphere platforms there is a {@link WebSpherePackageScanClassResolver}
|
||||
* to take care of WebSphere's oddity of resource loading.
|
||||
*
|
||||
* @param loader the classloader
|
||||
* @param packageName the packagename for the package to load
|
||||
* @return URL's for the given package
|
||||
* @throws IOException is thrown by the classloader
|
||||
*/
|
||||
protected Enumeration<URL> getResources(ClassLoader loader, String packageName) throws IOException {
|
||||
log.trace("Getting resource URL for package: {} with classloader: {}", packageName, loader);
|
||||
|
||||
// If the URL is a jar, the URLClassloader.getResources() seems to require a trailing slash. The
|
||||
// trailing slash is harmless for other URLs
|
||||
if (!packageName.endsWith("/")) {
|
||||
packageName = packageName + "/";
|
||||
}
|
||||
return loader.getResources(packageName);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.ExtendedCamelContext;
|
||||
import org.apache.camel.spi.FactoryFinder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class BaseServiceResolver<T> {
|
||||
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
protected final String factoryKey;
|
||||
protected final Class<T> factoryClass;
|
||||
|
||||
public BaseServiceResolver(String factoryKey, Class<T> factoryClass) {
|
||||
this.factoryKey = factoryKey;
|
||||
this.factoryClass = factoryClass;
|
||||
}
|
||||
|
||||
public Optional<T> resolve(CamelContext context) {
|
||||
// use factory finder to find a custom implementations
|
||||
Class<?> type = null;
|
||||
try {
|
||||
FactoryFinder finder = context.adapt(ExtendedCamelContext.class).getFactoryFinder(FactoryFinder.DEFAULT_PATH);
|
||||
type = finder.findClass(factoryKey).orElse(null);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (type != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Found {}: {} via: {}{}", factoryClass.getSimpleName(), type.getName(), FactoryFinder.DEFAULT_PATH, factoryKey);
|
||||
}
|
||||
if (factoryClass.isAssignableFrom(type)) {
|
||||
T answer = factoryClass.cast(context.getInjector().newInstance(type, false));
|
||||
log.debug("Detected and using {}: {}", factoryClass.getSimpleName(), answer);
|
||||
return Optional.of(answer);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Type is not a " + factoryClass.getSimpleName() + " implementation. Found: " + type.getName());
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,477 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.spi.BeanProxyFactory;
|
||||
import org.apache.camel.spi.GeneratedPropertyConfigurer;
|
||||
import org.apache.camel.spi.PropertiesComponent;
|
||||
import org.apache.camel.support.CamelContextHelper;
|
||||
import org.apache.camel.support.PropertyBindingSupport;
|
||||
import org.apache.camel.support.service.ServiceHelper;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A helper class for Camel based injector or post processing hooks which can be
|
||||
* reused by both the <a href="http://camel.apache.org/spring.html">Spring</a>
|
||||
* and <a href="http://camel.apache.org/blueprint.html">Blueprint</a> support.
|
||||
*/
|
||||
public class CamelPostProcessorHelper implements CamelContextAware {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CamelPostProcessorHelper.class);
|
||||
|
||||
private CamelContext camelContext;
|
||||
|
||||
public CamelPostProcessorHelper() {
|
||||
}
|
||||
|
||||
public CamelPostProcessorHelper(CamelContext camelContext) {
|
||||
this.setCamelContext(camelContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CamelContext getCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCamelContext(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
}
|
||||
|
||||
public void consumerInjection(Method method, Object bean, String beanName) {
|
||||
Consume consume = method.getAnnotation(Consume.class);
|
||||
if (consume != null) {
|
||||
LOG.debug("Creating a consumer for: {}", consume);
|
||||
String uri = consume.value().isEmpty() ? consume.uri() : consume.value();
|
||||
subscribeMethod(method, bean, beanName, uri, consume.property(), consume.predicate());
|
||||
}
|
||||
}
|
||||
|
||||
public void subscribeMethod(Method method, Object bean, String beanName, String endpointUri, String endpointProperty, String predicate) {
|
||||
// lets bind this method to a listener
|
||||
String injectionPointName = method.getName();
|
||||
Endpoint endpoint = getEndpointInjection(bean, endpointUri, endpointProperty, injectionPointName, true);
|
||||
if (endpoint != null) {
|
||||
boolean multipleConsumer = false;
|
||||
if (endpoint instanceof MultipleConsumersSupport) {
|
||||
multipleConsumer = ((MultipleConsumersSupport) endpoint).isMultipleConsumersSupported();
|
||||
}
|
||||
try {
|
||||
SubscribeMethodProcessor processor = getConsumerProcessor(endpoint);
|
||||
// if multiple consumer then create a new consumer per subscribed method
|
||||
if (multipleConsumer || processor == null) {
|
||||
// create new processor and new consumer which happens the first time
|
||||
processor = new SubscribeMethodProcessor(endpoint);
|
||||
// make sure processor is registered in registry so we can reuse it (eg we can look it up)
|
||||
endpoint.getCamelContext().addService(processor, true);
|
||||
processor.addMethod(bean, method, endpoint, predicate);
|
||||
Consumer consumer = endpoint.createConsumer(processor);
|
||||
startService(consumer, endpoint.getCamelContext(), bean, beanName);
|
||||
} else {
|
||||
// add method to existing processor
|
||||
processor.addMethod(bean, method, endpoint, predicate);
|
||||
}
|
||||
if (predicate != null) {
|
||||
LOG.debug("Subscribed method: {} to consume from endpoint: {} with predicate: {}", method, endpoint, predicate);
|
||||
} else {
|
||||
LOG.debug("Subscribed method: {} to consume from endpoint: {}", method, endpoint);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stats the given service
|
||||
*/
|
||||
protected void startService(Service service, CamelContext camelContext, Object bean, String beanName) throws Exception {
|
||||
// defer starting the service until CamelContext has started all its initial services
|
||||
if (camelContext != null) {
|
||||
camelContext.deferStartService(service, true);
|
||||
} else {
|
||||
// mo CamelContext then start service manually
|
||||
ServiceHelper.startService(service);
|
||||
}
|
||||
|
||||
boolean singleton = isSingleton(bean, beanName);
|
||||
if (!singleton) {
|
||||
LOG.debug("Service is not singleton so you must remember to stop it manually {}", service);
|
||||
}
|
||||
}
|
||||
|
||||
protected SubscribeMethodProcessor getConsumerProcessor(Endpoint endpoint) {
|
||||
Set<SubscribeMethodProcessor> processors = endpoint.getCamelContext().hasServices(SubscribeMethodProcessor.class);
|
||||
return processors.stream().filter(s -> s.getEndpoint() == endpoint).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public Endpoint getEndpointInjection(Object bean, String uri, String propertyName,
|
||||
String injectionPointName, boolean mandatory) {
|
||||
Endpoint answer;
|
||||
if (ObjectHelper.isEmpty(uri)) {
|
||||
// if no uri then fallback and try the endpoint property
|
||||
answer = doGetEndpointInjection(bean, propertyName, injectionPointName);
|
||||
} else {
|
||||
answer = doGetEndpointInjection(uri, injectionPointName, mandatory);
|
||||
}
|
||||
// it may be a delegate endpoint via ref component
|
||||
if (answer instanceof DelegateEndpoint) {
|
||||
answer = ((DelegateEndpoint) answer).getEndpoint();
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
private Endpoint doGetEndpointInjection(String uri, String injectionPointName, boolean mandatory) {
|
||||
return CamelContextHelper.getEndpointInjection(getCamelContext(), uri, injectionPointName, mandatory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the injection endpoint from a bean property.
|
||||
*
|
||||
* @param bean the bean
|
||||
* @param propertyName the property name on the bean
|
||||
*/
|
||||
private Endpoint doGetEndpointInjection(Object bean, String propertyName, String injectionPointName) {
|
||||
// fall back and use the method name if no explicit property name was given
|
||||
if (ObjectHelper.isEmpty(propertyName)) {
|
||||
propertyName = injectionPointName;
|
||||
}
|
||||
|
||||
// we have a property name, try to lookup a getter method on the bean with that name using this strategy
|
||||
// 1. first the getter with the name as given
|
||||
// 2. then the getter with Endpoint as postfix
|
||||
// 3. then if start with on then try step 1 and 2 again, but omit the on prefix
|
||||
try {
|
||||
Object value = getCamelContext().adapt(ExtendedCamelContext.class).getBeanIntrospection().getOrElseProperty(bean, propertyName, null, false);
|
||||
if (value == null) {
|
||||
// try endpoint as postfix
|
||||
value = getCamelContext().adapt(ExtendedCamelContext.class).getBeanIntrospection().getOrElseProperty(bean, propertyName + "Endpoint", null, false);
|
||||
}
|
||||
if (value == null && propertyName.startsWith("on")) {
|
||||
// retry but without the on as prefix
|
||||
propertyName = propertyName.substring(2);
|
||||
return doGetEndpointInjection(bean, propertyName, injectionPointName);
|
||||
}
|
||||
if (value == null) {
|
||||
return null;
|
||||
} else if (value instanceof Endpoint) {
|
||||
return (Endpoint) value;
|
||||
} else {
|
||||
String uriOrRef = getCamelContext().getTypeConverter().mandatoryConvertTo(String.class, value);
|
||||
return getCamelContext().getEndpoint(uriOrRef);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Error getting property " + propertyName + " from bean " + bean + " due " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the object to be injected for an
|
||||
* {@link org.apache.camel.EndpointInject} or
|
||||
* {@link org.apache.camel.Produce} injection point
|
||||
*/
|
||||
public Object getInjectionValue(Class<?> type, String endpointUri, String endpointProperty,
|
||||
String injectionPointName, Object bean, String beanName) {
|
||||
return getInjectionValue(type, endpointUri, endpointProperty, injectionPointName, bean, beanName, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the object to be injected for an
|
||||
* {@link org.apache.camel.EndpointInject} or
|
||||
* {@link org.apache.camel.Produce} injection point
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getInjectionValue(Class<?> type, String endpointUri, String endpointProperty,
|
||||
String injectionPointName, Object bean, String beanName, boolean binding) {
|
||||
if (type.isAssignableFrom(ProducerTemplate.class)) {
|
||||
return createInjectionProducerTemplate(endpointUri, endpointProperty, injectionPointName, bean);
|
||||
} else if (type.isAssignableFrom(FluentProducerTemplate.class)) {
|
||||
return createInjectionFluentProducerTemplate(endpointUri, endpointProperty, injectionPointName, bean);
|
||||
} else if (type.isAssignableFrom(ConsumerTemplate.class)) {
|
||||
return createInjectionConsumerTemplate(endpointUri, endpointProperty, injectionPointName);
|
||||
} else {
|
||||
Endpoint endpoint = getEndpointInjection(bean, endpointUri, endpointProperty, injectionPointName, true);
|
||||
if (endpoint != null) {
|
||||
if (type.isInstance(endpoint)) {
|
||||
return endpoint;
|
||||
} else if (type.isAssignableFrom(Producer.class)) {
|
||||
return createInjectionProducer(endpoint, bean, beanName);
|
||||
} else if (type.isAssignableFrom(PollingConsumer.class)) {
|
||||
return createInjectionPollingConsumer(endpoint, bean, beanName);
|
||||
} else if (type.isInterface()) {
|
||||
// lets create a proxy
|
||||
try {
|
||||
// use proxy service
|
||||
BeanProxyFactory factory = endpoint.getCamelContext().adapt(ExtendedCamelContext.class).getBeanProxyFactory();
|
||||
return factory.createProxy(endpoint, binding, type);
|
||||
} catch (Exception e) {
|
||||
throw createProxyInstantiationRuntimeException(type, endpoint, e);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid type: " + type.getName()
|
||||
+ " which cannot be injected via @EndpointInject/@Produce for: " + endpoint);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Object getInjectionPropertyValue(Class<?> type, String propertyName, String propertyDefaultValue,
|
||||
String injectionPointName, Object bean, String beanName) {
|
||||
try {
|
||||
String key;
|
||||
String prefix = PropertiesComponent.PREFIX_TOKEN;
|
||||
String suffix = PropertiesComponent.SUFFIX_TOKEN;
|
||||
if (!propertyName.contains(prefix)) {
|
||||
// must enclose the property name with prefix/suffix to have it resolved
|
||||
key = prefix + propertyName + suffix;
|
||||
} else {
|
||||
// key has already prefix/suffix so use it as-is as it may be a compound key
|
||||
key = propertyName;
|
||||
}
|
||||
String value = getCamelContext().resolvePropertyPlaceholders(key);
|
||||
if (value != null) {
|
||||
return getCamelContext().getTypeConverter().mandatoryConvertTo(type, value);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (ObjectHelper.isNotEmpty(propertyDefaultValue)) {
|
||||
try {
|
||||
return getCamelContext().getTypeConverter().mandatoryConvertTo(type, propertyDefaultValue);
|
||||
} catch (Exception e2) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e2);
|
||||
}
|
||||
}
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getInjectionBeanValue(Class<?> type, String name) {
|
||||
if (ObjectHelper.isEmpty(name)) {
|
||||
// is it camel context itself?
|
||||
if (getCamelContext() != null && type.isAssignableFrom(getCamelContext().getClass())) {
|
||||
return getCamelContext();
|
||||
}
|
||||
Set<?> found = getCamelContext().getRegistry().findByType(type);
|
||||
if (found == null || found.isEmpty()) {
|
||||
throw new NoSuchBeanException(name, type.getName());
|
||||
} else if (found.size() > 1) {
|
||||
throw new NoSuchBeanException("Found " + found.size() + " beans of type: " + type + ". Only one bean expected.");
|
||||
} else {
|
||||
// we found only one
|
||||
return found.iterator().next();
|
||||
}
|
||||
} else {
|
||||
return CamelContextHelper.mandatoryLookup(getCamelContext(), name, type);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getInjectionBeanConfigValue(Class<?> type, String name) {
|
||||
ExtendedCamelContext ecc = (ExtendedCamelContext) getCamelContext();
|
||||
|
||||
// is it a map or properties
|
||||
boolean mapType = false;
|
||||
Map map = null;
|
||||
if (type.isAssignableFrom(Map.class)) {
|
||||
map = new LinkedHashMap();
|
||||
mapType = true;
|
||||
} else if (type.isAssignableFrom(Properties.class)) {
|
||||
map = new Properties();
|
||||
mapType = true;
|
||||
}
|
||||
|
||||
// create an instance of type
|
||||
Object bean = null;
|
||||
if (map == null) {
|
||||
Set<?> instances = ecc.getRegistry().findByType(type);
|
||||
if (instances.size() == 1) {
|
||||
bean = instances.iterator().next();
|
||||
} else if (instances.size() > 1) {
|
||||
return null;
|
||||
} else {
|
||||
// attempt to create a new instance
|
||||
try {
|
||||
bean = ecc.getInjector().newInstance(type);
|
||||
} catch (Throwable e) {
|
||||
// ignore
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// root key
|
||||
String rootKey = name;
|
||||
// clip trailing dot
|
||||
if (rootKey.endsWith(".")) {
|
||||
rootKey = rootKey.substring(0, rootKey.length() - 1);
|
||||
}
|
||||
String uRootKey = rootKey.toUpperCase(Locale.US);
|
||||
|
||||
// get all properties and transfer to map
|
||||
Properties props = ecc.getPropertiesComponent().loadProperties();
|
||||
if (map == null) {
|
||||
map = new LinkedHashMap<>();
|
||||
}
|
||||
for (String key : props.stringPropertyNames()) {
|
||||
String uKey = key.toUpperCase(Locale.US);
|
||||
// need to ignore case
|
||||
if (uKey.startsWith(uRootKey)) {
|
||||
// strip prefix
|
||||
String sKey = key.substring(rootKey.length());
|
||||
if (sKey.startsWith(".")) {
|
||||
sKey = sKey.substring(1);
|
||||
}
|
||||
map.put(sKey, props.getProperty(key));
|
||||
}
|
||||
}
|
||||
if (mapType) {
|
||||
return map;
|
||||
}
|
||||
|
||||
// lookup configurer if there is any
|
||||
// use FQN class name first, then simple name, and root key last
|
||||
GeneratedPropertyConfigurer configurer = null;
|
||||
String[] names = new String[]{type.getName() + "-configurer", type.getSimpleName() + "-configurer", rootKey + "-configurer"};
|
||||
for (String n : names) {
|
||||
configurer = ecc.getConfigurerResolver().resolvePropertyConfigurer(n, ecc);
|
||||
if (configurer != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
new PropertyBindingSupport.Builder()
|
||||
.withCamelContext(ecc)
|
||||
.withIgnoreCase(true)
|
||||
.withTarget(bean)
|
||||
.withConfigurer(configurer)
|
||||
.withProperties(map)
|
||||
.bind();
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a {@link ProducerTemplate} to
|
||||
* be injected into a POJO
|
||||
*/
|
||||
protected ProducerTemplate createInjectionProducerTemplate(String endpointUri, String endpointProperty,
|
||||
String injectionPointName, Object bean) {
|
||||
// endpoint is optional for this injection point
|
||||
Endpoint endpoint = getEndpointInjection(bean, endpointUri, endpointProperty, injectionPointName, false);
|
||||
CamelContext context = endpoint != null ? endpoint.getCamelContext() : getCamelContext();
|
||||
ProducerTemplate answer = new DefaultProducerTemplate(context, endpoint);
|
||||
// start the template so its ready to use
|
||||
try {
|
||||
// no need to defer the template as it can adjust to the endpoint at runtime
|
||||
startService(answer, context, bean, null);
|
||||
} catch (Exception e) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a
|
||||
* {@link FluentProducerTemplate} to be injected into a
|
||||
* POJO
|
||||
*/
|
||||
protected FluentProducerTemplate createInjectionFluentProducerTemplate(String endpointUri, String endpointProperty,
|
||||
String injectionPointName, Object bean) {
|
||||
// endpoint is optional for this injection point
|
||||
Endpoint endpoint = getEndpointInjection(bean, endpointUri, endpointProperty, injectionPointName, false);
|
||||
CamelContext context = endpoint != null ? endpoint.getCamelContext() : getCamelContext();
|
||||
FluentProducerTemplate answer = new DefaultFluentProducerTemplate(context);
|
||||
answer.setDefaultEndpoint(endpoint);
|
||||
// start the template so its ready to use
|
||||
try {
|
||||
// no need to defer the template as it can adjust to the endpoint at runtime
|
||||
startService(answer, context, bean, null);
|
||||
} catch (Exception e) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a {@link ConsumerTemplate} to
|
||||
* be injected into a POJO
|
||||
*/
|
||||
protected ConsumerTemplate createInjectionConsumerTemplate(String endpointUri, String endpointProperty,
|
||||
String injectionPointName) {
|
||||
ConsumerTemplate answer = new DefaultConsumerTemplate(getCamelContext());
|
||||
// start the template so its ready to use
|
||||
try {
|
||||
startService(answer, null, null, null);
|
||||
} catch (Exception e) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a started
|
||||
* {@link PollingConsumer} to be injected into a POJO
|
||||
*/
|
||||
protected PollingConsumer createInjectionPollingConsumer(Endpoint endpoint, Object bean, String beanName) {
|
||||
try {
|
||||
PollingConsumer consumer = endpoint.createPollingConsumer();
|
||||
startService(consumer, endpoint.getCamelContext(), bean, beanName);
|
||||
return consumer;
|
||||
} catch (Exception e) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Factory method to create a started {@link Producer} to
|
||||
* be injected into a POJO
|
||||
*/
|
||||
protected Producer createInjectionProducer(Endpoint endpoint, Object bean, String beanName) {
|
||||
try {
|
||||
return endpoint.getCamelContext().adapt(ExtendedCamelContext.class).getDeferServiceFactory().createProducer(endpoint);
|
||||
} catch (Exception e) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected RuntimeException createProxyInstantiationRuntimeException(Class<?> type, Endpoint endpoint, Exception e) {
|
||||
return new ProxyInstantiationException(type, endpoint, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementations can override this method to determine if the bean is
|
||||
* singleton.
|
||||
*
|
||||
* @param bean the bean
|
||||
* @return <tt>true</tt> if its singleton scoped, for prototype scoped
|
||||
* <tt>false</tt> is returned.
|
||||
*/
|
||||
protected boolean isSingleton(Object bean, String beanName) {
|
||||
if (bean instanceof IsSingleton) {
|
||||
IsSingleton singleton = (IsSingleton) bean;
|
||||
return singleton.isSingleton();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Compound iterator to iterate over multiple iterators sequentially.
|
||||
*/
|
||||
class CompoundIterator<T> implements Iterator<T> {
|
||||
|
||||
final Iterator<Iterator<T>> it;
|
||||
Iterator<T> current;
|
||||
|
||||
public CompoundIterator(Iterable<Iterator<T>> it) {
|
||||
this(it.iterator());
|
||||
}
|
||||
|
||||
public CompoundIterator(Iterator<Iterator<T>> it) {
|
||||
this.it = it;
|
||||
this.current = it.hasNext() ? it.next() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
while (current != null) {
|
||||
if (current.hasNext()) {
|
||||
return true;
|
||||
} else {
|
||||
current = it.hasNext() ? it.next() : null;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (current != null) {
|
||||
return current.next();
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
current.remove();
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.spi.AnnotationBasedProcessorFactory;
|
||||
import org.apache.camel.support.CamelContextHelper;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
public final class DefaultAnnotationBasedProcessorFactory implements AnnotationBasedProcessorFactory {
|
||||
|
||||
@Override
|
||||
public AsyncProcessor createDynamicRouter(CamelContext camelContext, DynamicRouter annotation) {
|
||||
org.apache.camel.processor.DynamicRouter dynamicRouter = new org.apache.camel.processor.DynamicRouter(camelContext);
|
||||
dynamicRouter.setDelimiter(annotation.delimiter());
|
||||
dynamicRouter.setIgnoreInvalidEndpoints(annotation.ignoreInvalidEndpoints());
|
||||
dynamicRouter.setCacheSize(annotation.cacheSize());
|
||||
return dynamicRouter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncProcessor createRecipientList(CamelContext camelContext, RecipientList annotation) {
|
||||
org.apache.camel.processor.RecipientList recipientList = new org.apache.camel.processor.RecipientList(camelContext, annotation.delimiter());
|
||||
recipientList.setStopOnException(annotation.stopOnException());
|
||||
recipientList.setStopOnAggregateException(annotation.stopOnAggregateException());
|
||||
recipientList.setIgnoreInvalidEndpoints(annotation.ignoreInvalidEndpoints());
|
||||
recipientList.setParallelProcessing(annotation.parallelProcessing());
|
||||
recipientList.setParallelAggregate(annotation.parallelAggregate());
|
||||
recipientList.setStreaming(annotation.streaming());
|
||||
recipientList.setTimeout(annotation.timeout());
|
||||
recipientList.setCacheSize(annotation.cacheSize());
|
||||
recipientList.setShareUnitOfWork(annotation.shareUnitOfWork());
|
||||
|
||||
if (ObjectHelper.isNotEmpty(annotation.executorServiceRef())) {
|
||||
ExecutorService executor = camelContext.getExecutorServiceManager().newDefaultThreadPool(this, annotation.executorServiceRef());
|
||||
recipientList.setExecutorService(executor);
|
||||
}
|
||||
|
||||
if (annotation.parallelProcessing() && recipientList.getExecutorService() == null) {
|
||||
// we are running in parallel so we need a thread pool
|
||||
ExecutorService executor = camelContext.getExecutorServiceManager().newDefaultThreadPool(this, "@RecipientList");
|
||||
recipientList.setExecutorService(executor);
|
||||
}
|
||||
|
||||
if (ObjectHelper.isNotEmpty(annotation.strategyRef())) {
|
||||
AggregationStrategy strategy = CamelContextHelper.mandatoryLookup(camelContext, annotation.strategyRef(), AggregationStrategy.class);
|
||||
recipientList.setAggregationStrategy(strategy);
|
||||
}
|
||||
|
||||
if (ObjectHelper.isNotEmpty(annotation.onPrepareRef())) {
|
||||
Processor onPrepare = CamelContextHelper.mandatoryLookup(camelContext, annotation.onPrepareRef(), Processor.class);
|
||||
recipientList.setOnPrepare(onPrepare);
|
||||
}
|
||||
|
||||
return recipientList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncProcessor createRoutingSlip(CamelContext camelContext, RoutingSlip annotation) {
|
||||
org.apache.camel.processor.RoutingSlip routingSlip = new org.apache.camel.processor.RoutingSlip(camelContext);
|
||||
routingSlip.setDelimiter(annotation.delimiter());
|
||||
routingSlip.setIgnoreInvalidEndpoints(annotation.ignoreInvalidEndpoints());
|
||||
routingSlip.setCacheSize(annotation.cacheSize());
|
||||
return routingSlip;
|
||||
}
|
||||
}
|
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.spi.AsyncProcessorAwaitManager;
|
||||
import org.apache.camel.spi.ExchangeFormatter;
|
||||
import org.apache.camel.spi.ReactiveExecutor;
|
||||
import org.apache.camel.support.ExchangeHelper;
|
||||
import org.apache.camel.support.MessageHelper;
|
||||
import org.apache.camel.support.processor.DefaultExchangeFormatter;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class DefaultAsyncProcessorAwaitManager extends ServiceSupport implements AsyncProcessorAwaitManager, StaticService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultAsyncProcessorAwaitManager.class);
|
||||
|
||||
private final Statistics statistics = new UtilizationStatistics();
|
||||
private final AtomicLong blockedCounter = new AtomicLong();
|
||||
private final AtomicLong interruptedCounter = new AtomicLong();
|
||||
private final AtomicLong totalDuration = new AtomicLong();
|
||||
private final AtomicLong minDuration = new AtomicLong();
|
||||
private final AtomicLong maxDuration = new AtomicLong();
|
||||
private final AtomicLong meanDuration = new AtomicLong();
|
||||
|
||||
private final Map<Exchange, AwaitThread> inflight = new ConcurrentHashMap<>();
|
||||
private final ExchangeFormatter exchangeFormatter;
|
||||
private boolean interruptThreadsWhileStopping = true;
|
||||
|
||||
public DefaultAsyncProcessorAwaitManager() {
|
||||
// setup exchange formatter to be used for message history dump
|
||||
DefaultExchangeFormatter formatter = new DefaultExchangeFormatter();
|
||||
formatter.setShowExchangeId(true);
|
||||
formatter.setMultiline(true);
|
||||
formatter.setShowHeaders(true);
|
||||
formatter.setStyle(DefaultExchangeFormatter.OutputStyle.Fixed);
|
||||
this.exchangeFormatter = formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the async version of the processor's process method and waits
|
||||
* for it to complete before returning. This can be used by {@link AsyncProcessor}
|
||||
* objects to implement their sync version of the process method.
|
||||
* <p/>
|
||||
* <b>Important:</b> This method is discouraged to be used, as its better to invoke the asynchronous
|
||||
* {@link AsyncProcessor#process(Exchange, org.apache.camel.AsyncCallback)} method, whenever possible.
|
||||
*
|
||||
* @param processor the processor
|
||||
* @param exchange the exchange
|
||||
*/
|
||||
@Override
|
||||
public void process(final AsyncProcessor processor, final Exchange exchange) {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
processor.process(exchange, doneSync -> countDown(exchange, latch));
|
||||
if (latch.getCount() > 0) {
|
||||
await(exchange, latch);
|
||||
}
|
||||
}
|
||||
|
||||
public void await(Exchange exchange, CountDownLatch latch) {
|
||||
ReactiveExecutor reactiveExecutor = exchange.getContext().adapt(ExtendedCamelContext.class).getReactiveExecutor();
|
||||
// Early exit for pending reactive queued work
|
||||
do {
|
||||
if (latch.getCount() <= 0) {
|
||||
return;
|
||||
}
|
||||
} while (reactiveExecutor.executeFromQueue());
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Waiting for asynchronous callback before continuing for exchangeId: {} -> {}",
|
||||
exchange.getExchangeId(), exchange);
|
||||
}
|
||||
try {
|
||||
if (statistics.isStatisticsEnabled()) {
|
||||
blockedCounter.incrementAndGet();
|
||||
}
|
||||
inflight.put(exchange, new AwaitThreadEntry(Thread.currentThread(), exchange, latch));
|
||||
latch.await();
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Asynchronous callback received, will continue routing exchangeId: {} -> {}",
|
||||
exchange.getExchangeId(), exchange);
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Interrupted while waiting for callback, will continue routing exchangeId: {} -> {}",
|
||||
exchange.getExchangeId(), exchange);
|
||||
}
|
||||
exchange.setException(e);
|
||||
} finally {
|
||||
AwaitThread thread = inflight.remove(exchange);
|
||||
|
||||
if (statistics.isStatisticsEnabled() && thread != null) {
|
||||
long time = thread.getWaitDuration();
|
||||
long total = totalDuration.get() + time;
|
||||
totalDuration.set(total);
|
||||
|
||||
if (time < minDuration.get()) {
|
||||
minDuration.set(time);
|
||||
} else if (time > maxDuration.get()) {
|
||||
maxDuration.set(time);
|
||||
}
|
||||
|
||||
// update mean
|
||||
long count = blockedCounter.get();
|
||||
long mean = count > 0 ? total / count : 0;
|
||||
meanDuration.set(mean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void countDown(Exchange exchange, CountDownLatch latch) {
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Asynchronous callback received for exchangeId: {}", exchange.getExchangeId());
|
||||
}
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return inflight.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<AwaitThread> browse() {
|
||||
return Collections.unmodifiableCollection(inflight.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupt(String exchangeId) {
|
||||
// need to find the exchange with the given exchange id
|
||||
Exchange found = null;
|
||||
for (AwaitThread entry : browse()) {
|
||||
Exchange exchange = entry.getExchange();
|
||||
if (exchangeId.equals(exchange.getExchangeId())) {
|
||||
found = exchange;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found != null) {
|
||||
interrupt(found);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupt(Exchange exchange) {
|
||||
AwaitThreadEntry entry = (AwaitThreadEntry) inflight.get(exchange);
|
||||
if (entry != null) {
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Interrupted while waiting for asynchronous callback, will release the following blocked thread which was waiting for exchange to finish processing with exchangeId: ");
|
||||
sb.append(exchange.getExchangeId());
|
||||
sb.append("\n");
|
||||
|
||||
sb.append(dumpBlockedThread(entry));
|
||||
|
||||
// dump a route stack trace of the exchange
|
||||
String routeStackTrace = MessageHelper.dumpMessageHistoryStacktrace(exchange, exchangeFormatter, false);
|
||||
if (routeStackTrace != null) {
|
||||
sb.append(routeStackTrace);
|
||||
}
|
||||
LOG.warn(sb.toString());
|
||||
|
||||
} catch (Exception e) {
|
||||
throw RuntimeCamelException.wrapRuntimeCamelException(e);
|
||||
} finally {
|
||||
if (statistics.isStatisticsEnabled()) {
|
||||
interruptedCounter.incrementAndGet();
|
||||
}
|
||||
exchange.setException(new RejectedExecutionException("Interrupted while waiting for asynchronous callback for exchangeId: " + exchange.getExchangeId()));
|
||||
exchange.adapt(ExtendedExchange.class).setInterrupted(true);
|
||||
entry.getLatch().countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInterruptThreadsWhileStopping() {
|
||||
return interruptThreadsWhileStopping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInterruptThreadsWhileStopping(boolean interruptThreadsWhileStopping) {
|
||||
this.interruptThreadsWhileStopping = interruptThreadsWhileStopping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statistics getStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception {
|
||||
Collection<AwaitThread> threads = browse();
|
||||
int count = threads.size();
|
||||
if (count > 0) {
|
||||
LOG.warn("Shutting down while there are still {} inflight threads currently blocked.", count);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (AwaitThread entry : threads) {
|
||||
sb.append(dumpBlockedThread(entry));
|
||||
}
|
||||
|
||||
if (isInterruptThreadsWhileStopping()) {
|
||||
LOG.warn("The following threads are blocked and will be interrupted so the threads are released:\n{}", sb);
|
||||
for (AwaitThread entry : threads) {
|
||||
try {
|
||||
interrupt(entry.getExchange());
|
||||
} catch (Throwable e) {
|
||||
LOG.warn("Error while interrupting thread: " + entry.getBlockedThread().getName() + ". This exception is ignored.", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG.warn("The following threads are blocked, and may reside in the JVM:\n{}", sb);
|
||||
}
|
||||
} else {
|
||||
LOG.debug("Shutting down with no inflight threads.");
|
||||
}
|
||||
|
||||
inflight.clear();
|
||||
}
|
||||
|
||||
private static String dumpBlockedThread(AwaitThread entry) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\n");
|
||||
sb.append("Blocked Thread\n");
|
||||
sb.append("---------------------------------------------------------------------------------------------------------------------------------------\n");
|
||||
|
||||
sb.append(style("Id:")).append(entry.getBlockedThread().getId()).append("\n");
|
||||
sb.append(style("Name:")).append(entry.getBlockedThread().getName()).append("\n");
|
||||
sb.append(style("RouteId:")).append(safeNull(entry.getRouteId())).append("\n");
|
||||
sb.append(style("NodeId:")).append(safeNull(entry.getNodeId())).append("\n");
|
||||
sb.append(style("Duration:")).append(entry.getWaitDuration()).append(" msec.\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String style(String label) {
|
||||
return String.format("\t%-20s", label);
|
||||
}
|
||||
|
||||
private static String safeNull(Object value) {
|
||||
return value != null ? value.toString() : "";
|
||||
}
|
||||
|
||||
private static final class AwaitThreadEntry implements AwaitThread {
|
||||
private final Thread thread;
|
||||
private final Exchange exchange;
|
||||
private final CountDownLatch latch;
|
||||
private final long start;
|
||||
|
||||
private AwaitThreadEntry(Thread thread, Exchange exchange, CountDownLatch latch) {
|
||||
this.thread = thread;
|
||||
this.exchange = exchange;
|
||||
this.latch = latch;
|
||||
this.start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread getBlockedThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange getExchange() {
|
||||
return exchange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getWaitDuration() {
|
||||
return System.currentTimeMillis() - start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRouteId() {
|
||||
return ExchangeHelper.getAtRouteId(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNodeId() {
|
||||
return exchange.adapt(ExtendedExchange.class).getHistoryNodeId();
|
||||
}
|
||||
|
||||
public CountDownLatch getLatch() {
|
||||
return latch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AwaitThreadEntry[name=" + thread.getName() + ", exchangeId=" + exchange.getExchangeId() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents utilization statistics
|
||||
*/
|
||||
private final class UtilizationStatistics implements Statistics {
|
||||
|
||||
private boolean statisticsEnabled;
|
||||
|
||||
@Override
|
||||
public long getThreadsBlocked() {
|
||||
return blockedCounter.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getThreadsInterrupted() {
|
||||
return interruptedCounter.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalDuration() {
|
||||
return totalDuration.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinDuration() {
|
||||
return minDuration.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxDuration() {
|
||||
return maxDuration.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMeanDuration() {
|
||||
return meanDuration.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
blockedCounter.set(0);
|
||||
interruptedCounter.set(0);
|
||||
totalDuration.set(0);
|
||||
minDuration.set(0);
|
||||
maxDuration.set(0);
|
||||
meanDuration.set(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStatisticsEnabled() {
|
||||
return statisticsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatisticsEnabled(boolean statisticsEnabled) {
|
||||
this.statisticsEnabled = statisticsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("AsyncProcessAwaitManager utilization[blocked=%s, interrupted=%s, total=%s min=%s, max=%s, mean=%s]",
|
||||
getThreadsBlocked(), getThreadsInterrupted(), getTotalDuration(), getMinDuration(), getMaxDuration(), getMeanDuration());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.spi.BeanIntrospection;
|
||||
import org.apache.camel.spi.CamelLogger;
|
||||
import org.apache.camel.support.IntrospectionSupport;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class DefaultBeanIntrospection extends ServiceSupport implements BeanIntrospection, CamelContextAware, StartupListener {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultBeanIntrospection.class);
|
||||
private static final Pattern SECRETS = Pattern.compile(".*(passphrase|password|secretKey).*", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private CamelContext camelContext;
|
||||
private volatile boolean preStartDone;
|
||||
private final List<String> preStartLogs = new ArrayList<>();
|
||||
private final AtomicLong invoked = new AtomicLong();
|
||||
private volatile boolean extendedStatistics;
|
||||
private LoggingLevel loggingLevel = LoggingLevel.TRACE;
|
||||
private CamelLogger logger = new CamelLogger(LOG, loggingLevel);
|
||||
|
||||
@Override
|
||||
public CamelContext getCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCamelContext(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getInvokedCounter() {
|
||||
return invoked.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetCounters() {
|
||||
invoked.set(0);
|
||||
}
|
||||
|
||||
public boolean isExtendedStatistics() {
|
||||
return extendedStatistics;
|
||||
}
|
||||
|
||||
public void setExtendedStatistics(boolean extendedStatistics) {
|
||||
this.extendedStatistics = extendedStatistics;
|
||||
}
|
||||
|
||||
public LoggingLevel getLoggingLevel() {
|
||||
return loggingLevel;
|
||||
}
|
||||
|
||||
public void setLoggingLevel(LoggingLevel loggingLevel) {
|
||||
this.loggingLevel = loggingLevel;
|
||||
// recreate logger as level is changed
|
||||
this.logger = new CamelLogger(LOG, loggingLevel);
|
||||
}
|
||||
|
||||
private void log(String method, Object target, Object... args) {
|
||||
Object obj = "null";
|
||||
if (args != null && args.length > 0) {
|
||||
obj = Arrays.asList(args);
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
// use Object.toString as target logging
|
||||
target = target.getClass().getName() + "@" + Integer.toHexString(target.hashCode());
|
||||
}
|
||||
|
||||
String line;
|
||||
if (target == null) {
|
||||
line = "Invoked: " + invoked.get() + " times (overall) [Method: " + method + "]";
|
||||
} else if (args == null) {
|
||||
line = "Invoked: " + invoked.get() + " times (overall) [Method: " + method + ", Target: " + target + "]";
|
||||
} else {
|
||||
line = "Invoked: " + invoked.get() + " times (overall) [Method: " + method + ", Target: " + target + ", Arguments: " + obj + "]";
|
||||
}
|
||||
|
||||
if (preStartDone) {
|
||||
logger.log(line);
|
||||
} else {
|
||||
// remember log lines before we are starting
|
||||
preStartLogs.add(line);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassInfo cacheClass(Class<?> clazz) {
|
||||
invoked.incrementAndGet();
|
||||
if (!preStartDone || logger.shouldLog()) {
|
||||
log("cacheClass", clazz);
|
||||
}
|
||||
return IntrospectionSupport.cacheClass(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache() {
|
||||
if (invoked.get() > 0) {
|
||||
invoked.incrementAndGet();
|
||||
if (!preStartDone || logger.shouldLog()) {
|
||||
log("clearCache", null);
|
||||
}
|
||||
IntrospectionSupport.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCachedClassesCounter() {
|
||||
if (invoked.get() > 0) {
|
||||
return IntrospectionSupport.getCacheCounter();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getProperties(Object target, Map<String, Object> properties, String optionPrefix) {
|
||||
invoked.incrementAndGet();
|
||||
if (!preStartDone || logger.shouldLog()) {
|
||||
log("getProperties", target);
|
||||
}
|
||||
return IntrospectionSupport.getProperties(target, properties, optionPrefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getProperties(Object target, Map<String, Object> properties, String optionPrefix, boolean includeNull) {
|
||||
invoked.incrementAndGet();
|
||||
if (!preStartDone || logger.shouldLog()) {
|
||||
log("getProperties", target);
|
||||
}
|
||||
return IntrospectionSupport.getProperties(target, properties, optionPrefix, includeNull);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOrElseProperty(Object target, String propertyName, Object defaultValue, boolean ignoreCase) {
|
||||
invoked.incrementAndGet();
|
||||
if (!preStartDone || logger.shouldLog()) {
|
||||
log("getOrElseProperty", target, propertyName);
|
||||
}
|
||||
return IntrospectionSupport.getOrElseProperty(target, propertyName, defaultValue, ignoreCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getPropertyGetter(Class<?> type, String propertyName, boolean ignoreCase) throws NoSuchMethodException {
|
||||
invoked.incrementAndGet();
|
||||
if (!preStartDone || logger.shouldLog()) {
|
||||
log("getPropertyGetter", type, propertyName);
|
||||
}
|
||||
return IntrospectionSupport.getPropertyGetter(type, propertyName, ignoreCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setProperty(CamelContext context, TypeConverter typeConverter, Object target, String name, Object value, String refName, boolean allowBuilderPattern, boolean allowPrivateSetter, boolean ignoreCase) throws Exception {
|
||||
invoked.incrementAndGet();
|
||||
if (!preStartDone || logger.shouldLog()) {
|
||||
Object text = value;
|
||||
if (SECRETS.matcher(name).find()) {
|
||||
text = "xxxxxx";
|
||||
}
|
||||
log("setProperty", target, name, text);
|
||||
}
|
||||
return IntrospectionSupport.setProperty(context, typeConverter, target, name, value, refName, allowBuilderPattern, allowPrivateSetter, ignoreCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setProperty(CamelContext context, Object target, String name, Object value) throws Exception {
|
||||
invoked.incrementAndGet();
|
||||
if (!preStartDone || logger.shouldLog()) {
|
||||
Object text = value;
|
||||
if (SECRETS.matcher(name).find()) {
|
||||
text = "xxxxxx";
|
||||
}
|
||||
log("setProperty", target, name, text);
|
||||
}
|
||||
return IntrospectionSupport.setProperty(context, target, name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Method> findSetterMethods(Class<?> clazz, String name, boolean allowBuilderPattern, boolean allowPrivateSetter, boolean ignoreCase) {
|
||||
invoked.incrementAndGet();
|
||||
if (!preStartDone || logger.shouldLog()) {
|
||||
log("findSetterMethods", clazz);
|
||||
}
|
||||
return IntrospectionSupport.findSetterMethods(clazz, name, allowBuilderPattern, allowPrivateSetter, ignoreCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesConfigured(CamelContext camelContext) {
|
||||
// log any pre starting logs so we can see them all
|
||||
preStartLogs.forEach(logger::log);
|
||||
preStartLogs.clear();
|
||||
preStartDone = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() throws Exception {
|
||||
if (camelContext != null) {
|
||||
camelContext.addStartupListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception {
|
||||
if (invoked.get() > 0) {
|
||||
IntrospectionSupport.stop();
|
||||
}
|
||||
if (extendedStatistics) {
|
||||
LOG.info("Stopping BeanIntrospection which was invoked: {} times", invoked.get());
|
||||
} else {
|
||||
LOG.debug("Stopping BeanIntrospection which was invoked: {} times", invoked.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCamelContextStarted(CamelContext context, boolean alreadyStarted) throws Exception {
|
||||
// ensure after properties is called
|
||||
afterPropertiesConfigured(camelContext);
|
||||
}
|
||||
}
|
@ -0,0 +1,548 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.spi.CamelBeanPostProcessor;
|
||||
import org.apache.camel.spi.Registry;
|
||||
import org.apache.camel.support.DefaultEndpoint;
|
||||
import org.apache.camel.util.ReflectionHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.apache.camel.support.ObjectHelper.invokeMethod;
|
||||
import static org.apache.camel.util.ObjectHelper.isEmpty;
|
||||
|
||||
/**
|
||||
* A bean post processor which implements the <a href="http://camel.apache.org/bean-integration.html">Bean Integration</a>
|
||||
* features in Camel. Features such as the <a href="http://camel.apache.org/bean-injection.html">Bean Injection</a> of objects like
|
||||
* {@link org.apache.camel.Endpoint} and
|
||||
* {@link org.apache.camel.ProducerTemplate} together with support for
|
||||
* <a href="http://camel.apache.org/pojo-consuming.html">POJO Consuming</a> via the
|
||||
* {@link org.apache.camel.Consume} annotation along with
|
||||
* <a href="http://camel.apache.org/pojo-producing.html">POJO Producing</a> via the
|
||||
* {@link Produce} annotation along with other annotations such as
|
||||
* {@link org.apache.camel.DynamicRouter} for creating <a href="http://camel.apache.org/dynamicrouter-annotation.html">a Dynamic router via annotations</a>.
|
||||
* {@link org.apache.camel.RecipientList} for creating <a href="http://camel.apache.org/recipientlist-annotation.html">a Recipient List router via annotations</a>.
|
||||
* {@link org.apache.camel.RoutingSlip} for creating <a href="http://camel.apache.org/routingslip-annotation.html">a Routing Slip router via annotations</a>.
|
||||
* <p/>
|
||||
* Components such as <tt>camel-spring</tt>, and <tt>camel-blueprint</tt> can leverage this post processor to hook in Camel
|
||||
* bean post processing into their bean processing framework.
|
||||
*/
|
||||
public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor {
|
||||
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(DefaultCamelBeanPostProcessor.class);
|
||||
protected CamelPostProcessorHelper camelPostProcessorHelper;
|
||||
protected CamelContext camelContext;
|
||||
|
||||
public DefaultCamelBeanPostProcessor() {
|
||||
}
|
||||
|
||||
public DefaultCamelBeanPostProcessor(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
|
||||
LOG.trace("Camel bean processing before initialization for bean: {}", beanName);
|
||||
|
||||
// some beans cannot be post processed at this given time, so we gotta check beforehand
|
||||
if (!canPostProcessBean(bean, beanName)) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
// do bean binding on simple types first, and then afterwards on complex types
|
||||
injectFirstPass(bean, beanName, type -> !isComplexUserType(type));
|
||||
injectSecondPass(bean, beanName, type -> isComplexUserType(type));
|
||||
|
||||
if (bean instanceof CamelContextAware && canSetCamelContext(bean, beanName)) {
|
||||
CamelContextAware contextAware = (CamelContextAware)bean;
|
||||
DeferredContextBinding deferredBinding = bean.getClass().getAnnotation(DeferredContextBinding.class);
|
||||
CamelContext context = getOrLookupCamelContext();
|
||||
|
||||
if (context == null && deferredBinding == null) {
|
||||
LOG.warn("No CamelContext defined yet so cannot inject into bean: {}", beanName);
|
||||
} else if (context != null) {
|
||||
contextAware.setCamelContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
|
||||
LOG.trace("Camel bean processing after initialization for bean: {}", beanName);
|
||||
|
||||
// some beans cannot be post processed at this given time, so we gotta check beforehand
|
||||
if (!canPostProcessBean(bean, beanName)) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
if (bean instanceof DefaultEndpoint) {
|
||||
DefaultEndpoint defaultEndpoint = (DefaultEndpoint) bean;
|
||||
defaultEndpoint.setEndpointUriIfNotSpecified(beanName);
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy to get the {@link CamelContext} to use.
|
||||
*/
|
||||
public CamelContext getOrLookupCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy to get the {@link CamelPostProcessorHelper}
|
||||
*/
|
||||
public CamelPostProcessorHelper getPostProcessorHelper() {
|
||||
if (camelPostProcessorHelper == null) {
|
||||
camelPostProcessorHelper = new CamelPostProcessorHelper(getOrLookupCamelContext());
|
||||
}
|
||||
return camelPostProcessorHelper;
|
||||
}
|
||||
|
||||
protected boolean canPostProcessBean(Object bean, String beanName) {
|
||||
if (beanName != null && "properties".equals(beanName)) {
|
||||
// we cannot process the properties component
|
||||
// its instantiated very eager during creation of camel context
|
||||
return false;
|
||||
}
|
||||
return bean != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether support for the annotation {@link BindToRegistry} is supported.
|
||||
* This is only intended for standalone runtimes such as camel-main, camel-quarkus, etc.
|
||||
*/
|
||||
protected boolean bindToRegistrySupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean canSetCamelContext(Object bean, String beanName) {
|
||||
if (bean instanceof CamelContextAware) {
|
||||
CamelContextAware camelContextAware = (CamelContextAware) bean;
|
||||
CamelContext context = camelContextAware.getCamelContext();
|
||||
if (context != null) {
|
||||
LOG.trace("CamelContext already set on bean with id [{}]. Will keep existing CamelContext on bean.", beanName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void injectFirstPass(Object bean, String beanName, Function<Class, Boolean> filter) {
|
||||
// on first pass do field and methods first
|
||||
injectFields(bean, beanName, filter);
|
||||
injectMethods(bean, beanName, filter);
|
||||
|
||||
if (bindToRegistrySupported()) {
|
||||
injectClass(bean, beanName);
|
||||
injectNestedClasses(bean, beanName);
|
||||
injectBindToRegistryFields(bean, beanName, filter);
|
||||
injectBindToRegistryMethods(bean, beanName, filter);
|
||||
}
|
||||
}
|
||||
|
||||
protected void injectSecondPass(Object bean, String beanName, Function<Class, Boolean> filter) {
|
||||
// on second pass do bind to registry beforehand as they may be used by field/method injections below
|
||||
if (bindToRegistrySupported()) {
|
||||
injectClass(bean, beanName);
|
||||
injectNestedClasses(bean, beanName);
|
||||
injectBindToRegistryFields(bean, beanName, filter);
|
||||
injectBindToRegistryMethods(bean, beanName, filter);
|
||||
}
|
||||
|
||||
injectFields(bean, beanName, filter);
|
||||
injectMethods(bean, beanName, filter);
|
||||
}
|
||||
|
||||
protected void injectFields(final Object bean, final String beanName, Function<Class, Boolean> accept) {
|
||||
ReflectionHelper.doWithFields(bean.getClass(), field -> {
|
||||
if (accept != null && !accept.apply(field.getType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
PropertyInject propertyInject = field.getAnnotation(PropertyInject.class);
|
||||
if (propertyInject != null) {
|
||||
injectFieldProperty(field, propertyInject.value(), propertyInject.defaultValue(), bean, beanName);
|
||||
}
|
||||
|
||||
BeanInject beanInject = field.getAnnotation(BeanInject.class);
|
||||
if (beanInject != null) {
|
||||
injectFieldBean(field, beanInject.value(), bean, beanName);
|
||||
}
|
||||
|
||||
BeanConfigInject beanConfigInject = field.getAnnotation(BeanConfigInject.class);
|
||||
if (beanConfigInject != null) {
|
||||
injectFieldBeanConfig(field, beanConfigInject.value(), bean, beanName);
|
||||
}
|
||||
|
||||
EndpointInject endpointInject = field.getAnnotation(EndpointInject.class);
|
||||
if (endpointInject != null) {
|
||||
@SuppressWarnings("deprecation")
|
||||
String uri = endpointInject.value().isEmpty() ? endpointInject.uri() : endpointInject.value();
|
||||
injectField(field, uri, endpointInject.property(), bean, beanName);
|
||||
}
|
||||
|
||||
Produce produce = field.getAnnotation(Produce.class);
|
||||
if (produce != null) {
|
||||
@SuppressWarnings("deprecation")
|
||||
String uri = produce.value().isEmpty() ? produce.uri() : produce.value();
|
||||
injectField(field, uri, produce.property(), bean, beanName, produce.binding());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void injectBindToRegistryFields(final Object bean, final String beanName, Function<Class, Boolean> accept) {
|
||||
ReflectionHelper.doWithFields(bean.getClass(), field -> {
|
||||
if (accept != null && !accept.apply(field.getType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
BindToRegistry bind = field.getAnnotation(BindToRegistry.class);
|
||||
if (bind != null) {
|
||||
bindToRegistry(field, bind.value(), bean, beanName, bind.beanPostProcess());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void injectField(Field field, String endpointUri, String endpointProperty,
|
||||
Object bean, String beanName) {
|
||||
injectField(field, endpointUri, endpointProperty, bean, beanName, true);
|
||||
}
|
||||
|
||||
public void injectField(Field field, String endpointUri, String endpointProperty,
|
||||
Object bean, String beanName, boolean binding) {
|
||||
ReflectionHelper.setField(field, bean,
|
||||
getPostProcessorHelper().getInjectionValue(field.getType(), endpointUri, endpointProperty,
|
||||
field.getName(), bean, beanName, binding));
|
||||
}
|
||||
|
||||
public void injectFieldBean(Field field, String name, Object bean, String beanName) {
|
||||
ReflectionHelper.setField(field, bean,
|
||||
getPostProcessorHelper().getInjectionBeanValue(field.getType(), name));
|
||||
}
|
||||
|
||||
public void injectFieldBeanConfig(Field field, String name, Object bean, String beanName) {
|
||||
ReflectionHelper.setField(field, bean,
|
||||
getPostProcessorHelper().getInjectionBeanConfigValue(field.getType(), name));
|
||||
}
|
||||
|
||||
public void injectFieldProperty(Field field, String propertyName, String propertyDefaultValue, Object bean, String beanName) {
|
||||
ReflectionHelper.setField(field, bean,
|
||||
getPostProcessorHelper().getInjectionPropertyValue(field.getType(), propertyName, propertyDefaultValue,
|
||||
field.getName(), bean, beanName));
|
||||
}
|
||||
|
||||
protected void injectMethods(final Object bean, final String beanName, Function<Class, Boolean> accept) {
|
||||
ReflectionHelper.doWithMethods(bean.getClass(), method -> {
|
||||
if (accept != null && !accept.apply(method.getReturnType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
setterInjection(method, bean, beanName);
|
||||
getPostProcessorHelper().consumerInjection(method, bean, beanName);
|
||||
});
|
||||
}
|
||||
|
||||
protected void injectBindToRegistryMethods(final Object bean, final String beanName, Function<Class, Boolean> accept) {
|
||||
// sort the methods so the simplest are used first
|
||||
|
||||
final List<Method> methods = new ArrayList<>();
|
||||
ReflectionHelper.doWithMethods(bean.getClass(), method -> {
|
||||
if (accept != null && !accept.apply(method.getReturnType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
BindToRegistry bind = method.getAnnotation(BindToRegistry.class);
|
||||
if (bind != null) {
|
||||
methods.add(method);
|
||||
}
|
||||
});
|
||||
|
||||
// sort methods on shortest number of parameters as we want to process the most simplest first
|
||||
methods.sort(Comparator.comparingInt(Method::getParameterCount));
|
||||
|
||||
// then do a more complex sorting where we check inter-dependency among the methods
|
||||
methods.sort((m1, m2) -> {
|
||||
Class[] types1 = m1.getParameterTypes();
|
||||
Class[] types2 = m2.getParameterTypes();
|
||||
|
||||
// favour methods that has no parameters
|
||||
if (types1.length == 0 && types2.length == 0) {
|
||||
return 0;
|
||||
} else if (types1.length == 0) {
|
||||
return -1;
|
||||
} else if (types2.length == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// okay then compare so we favour methods that does not use parameter types that are returned from other methods
|
||||
boolean usedByOthers1 = false;
|
||||
for (Class clazz : types1) {
|
||||
usedByOthers1 |= methods.stream().anyMatch(m -> m.getParameterCount() > 0 && clazz.isAssignableFrom(m.getReturnType()));
|
||||
}
|
||||
boolean usedByOthers2 = false;
|
||||
for (Class clazz : types2) {
|
||||
usedByOthers2 |= methods.stream().anyMatch(m -> m.getParameterCount() > 0 && clazz.isAssignableFrom(m.getReturnType()));
|
||||
}
|
||||
return Boolean.compare(usedByOthers1, usedByOthers2);
|
||||
});
|
||||
|
||||
LOG.trace("Discovered {} @BindToRegistry methods", methods.size());
|
||||
|
||||
// bind each method
|
||||
methods.forEach(method -> {
|
||||
BindToRegistry bind = method.getAnnotation(BindToRegistry.class);
|
||||
bindToRegistry(method, bind.value(), bean, beanName, bind.beanPostProcess());
|
||||
});
|
||||
}
|
||||
|
||||
protected void injectClass(final Object bean, final String beanName) {
|
||||
Class<?> clazz = bean.getClass();
|
||||
BindToRegistry ann = clazz.getAnnotation(BindToRegistry.class);
|
||||
if (ann != null) {
|
||||
bindToRegistry(clazz, ann.value(), bean, beanName, ann.beanPostProcess());
|
||||
}
|
||||
}
|
||||
|
||||
protected void injectNestedClasses(final Object bean, final String beanName) {
|
||||
ReflectionHelper.doWithClasses(bean.getClass(), clazz -> {
|
||||
BindToRegistry ann = (BindToRegistry) clazz.getAnnotation(BindToRegistry.class);
|
||||
if (ann != null) {
|
||||
// its a nested class so we dont have a bean instance for it
|
||||
bindToRegistry(clazz, ann.value(), null, null, ann.beanPostProcess());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void setterInjection(Method method, Object bean, String beanName) {
|
||||
PropertyInject propertyInject = method.getAnnotation(PropertyInject.class);
|
||||
if (propertyInject != null) {
|
||||
setterPropertyInjection(method, propertyInject.value(), propertyInject.defaultValue(), bean, beanName);
|
||||
}
|
||||
|
||||
BeanInject beanInject = method.getAnnotation(BeanInject.class);
|
||||
if (beanInject != null) {
|
||||
setterBeanInjection(method, beanInject.value(), bean, beanName);
|
||||
}
|
||||
|
||||
BeanConfigInject beanConfigInject = method.getAnnotation(BeanConfigInject.class);
|
||||
if (beanConfigInject != null) {
|
||||
setterBeanConfigInjection(method, beanConfigInject.value(), bean, beanName);
|
||||
}
|
||||
|
||||
EndpointInject endpointInject = method.getAnnotation(EndpointInject.class);
|
||||
if (endpointInject != null) {
|
||||
String uri = endpointInject.value().isEmpty() ? endpointInject.uri() : endpointInject.value();
|
||||
setterInjection(method, bean, beanName, uri, endpointInject.property());
|
||||
}
|
||||
|
||||
Produce produce = method.getAnnotation(Produce.class);
|
||||
if (produce != null) {
|
||||
String uri = produce.value().isEmpty() ? produce.uri() : produce.value();
|
||||
setterInjection(method, bean, beanName, uri, produce.property());
|
||||
}
|
||||
}
|
||||
|
||||
public void setterInjection(Method method, Object bean, String beanName, String endpointUri, String endpointProperty) {
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
if (parameterTypes.length != 1) {
|
||||
LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: {}", method);
|
||||
} else {
|
||||
String propertyName = org.apache.camel.util.ObjectHelper.getPropertyName(method);
|
||||
Object value = getPostProcessorHelper().getInjectionValue(parameterTypes[0], endpointUri, endpointProperty,
|
||||
propertyName, bean, beanName);
|
||||
invokeMethod(method, bean, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setterPropertyInjection(Method method, String propertyValue, String propertyDefaultValue,
|
||||
Object bean, String beanName) {
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
if (parameterTypes.length != 1) {
|
||||
LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: {}", method);
|
||||
} else {
|
||||
String propertyName = org.apache.camel.util.ObjectHelper.getPropertyName(method);
|
||||
Object value = getPostProcessorHelper().getInjectionPropertyValue(parameterTypes[0], propertyValue, propertyDefaultValue, propertyName, bean, beanName);
|
||||
invokeMethod(method, bean, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setterBeanInjection(Method method, String name, Object bean, String beanName) {
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
if (parameterTypes.length != 1) {
|
||||
LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: {}", method);
|
||||
} else {
|
||||
Object value = getPostProcessorHelper().getInjectionBeanValue(parameterTypes[0], name);
|
||||
invokeMethod(method, bean, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setterBeanConfigInjection(Method method, String name, Object bean, String beanName) {
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
if (parameterTypes.length != 1) {
|
||||
LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: {}", method);
|
||||
} else {
|
||||
Object value = getPostProcessorHelper().getInjectionBeanConfigValue(parameterTypes[0], name);
|
||||
invokeMethod(method, bean, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void bindToRegistry(Class<?> clazz, String name, Object bean, String beanName, boolean beanPostProcess) {
|
||||
if (isEmpty(name)) {
|
||||
name = clazz.getSimpleName();
|
||||
}
|
||||
if (bean == null) {
|
||||
// no bean so then create an instance from its type
|
||||
bean = getOrLookupCamelContext().getInjector().newInstance(clazz);
|
||||
}
|
||||
if (beanPostProcess) {
|
||||
try {
|
||||
getOrLookupCamelContext().adapt(ExtendedCamelContext.class).getBeanPostProcessor().postProcessBeforeInitialization(bean, beanName);
|
||||
getOrLookupCamelContext().adapt(ExtendedCamelContext.class).getBeanPostProcessor().postProcessAfterInitialization(bean, beanName);
|
||||
} catch (Exception e) {
|
||||
throw RuntimeCamelException.wrapRuntimeException(e);
|
||||
}
|
||||
}
|
||||
getOrLookupCamelContext().getRegistry().bind(name, bean);
|
||||
}
|
||||
|
||||
private void bindToRegistry(Field field, String name, Object bean, String beanName, boolean beanPostProcess) {
|
||||
if (isEmpty(name)) {
|
||||
name = field.getName();
|
||||
}
|
||||
Object value = ReflectionHelper.getField(field, bean);
|
||||
if (value != null) {
|
||||
if (beanPostProcess) {
|
||||
try {
|
||||
getOrLookupCamelContext().adapt(ExtendedCamelContext.class).getBeanPostProcessor().postProcessBeforeInitialization(value, beanName);
|
||||
getOrLookupCamelContext().adapt(ExtendedCamelContext.class).getBeanPostProcessor().postProcessAfterInitialization(value, beanName);
|
||||
} catch (Exception e) {
|
||||
throw RuntimeCamelException.wrapRuntimeException(e);
|
||||
}
|
||||
}
|
||||
getOrLookupCamelContext().getRegistry().bind(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void bindToRegistry(Method method, String name, Object bean, String beanName, boolean beanPostProcess) {
|
||||
if (isEmpty(name)) {
|
||||
name = method.getName();
|
||||
}
|
||||
Class<?> returnType = method.getReturnType();
|
||||
if (returnType == null || returnType == Void.TYPE) {
|
||||
throw new IllegalArgumentException("@BindToRegistry on class: " + method.getDeclaringClass()
|
||||
+ " method: " + method.getName() + " with void return type is not allowed");
|
||||
}
|
||||
|
||||
Object value;
|
||||
Object[] parameters = bindToRegistryParameterMapping(method);
|
||||
if (parameters != null) {
|
||||
value = invokeMethod(method, bean, parameters);
|
||||
} else {
|
||||
value = invokeMethod(method, bean);
|
||||
}
|
||||
if (value != null) {
|
||||
if (beanPostProcess) {
|
||||
try {
|
||||
getOrLookupCamelContext().adapt(ExtendedCamelContext.class).getBeanPostProcessor().postProcessBeforeInitialization(value, beanName);
|
||||
getOrLookupCamelContext().adapt(ExtendedCamelContext.class).getBeanPostProcessor().postProcessAfterInitialization(value, beanName);
|
||||
} catch (Exception e) {
|
||||
throw RuntimeCamelException.wrapRuntimeException(e);
|
||||
}
|
||||
}
|
||||
getOrLookupCamelContext().getRegistry().bind(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private Object[] bindToRegistryParameterMapping(Method method) {
|
||||
if (method.getParameterCount() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// map each parameter if possible
|
||||
Object[] parameters = new Object[method.getParameterCount()];
|
||||
for (int i = 0; i < method.getParameterCount(); i++) {
|
||||
Class<?> type = method.getParameterTypes()[i];
|
||||
if (type.isAssignableFrom(CamelContext.class)) {
|
||||
parameters[i] = getOrLookupCamelContext();
|
||||
} else if (type.isAssignableFrom(Registry.class)) {
|
||||
parameters[i] = getOrLookupCamelContext().getRegistry();
|
||||
} else if (type.isAssignableFrom(TypeConverter.class)) {
|
||||
parameters[i] = getOrLookupCamelContext().getTypeConverter();
|
||||
} else {
|
||||
// we also support @BeanInject and @PropertyInject annotations
|
||||
Annotation[] anns = method.getParameterAnnotations()[i];
|
||||
if (anns.length == 1) {
|
||||
// we dont assume there are multiple annotations on the same parameter so grab first
|
||||
Annotation ann = anns[0];
|
||||
if (ann.annotationType() == PropertyInject.class) {
|
||||
PropertyInject pi = (PropertyInject) ann;
|
||||
Object result = getPostProcessorHelper().getInjectionPropertyValue(type, pi.value(), pi.defaultValue(), null, null, null);
|
||||
parameters[i] = result;
|
||||
} else if (ann.annotationType() == BeanConfigInject.class) {
|
||||
BeanConfigInject pi = (BeanConfigInject) ann;
|
||||
Object result = getPostProcessorHelper().getInjectionBeanConfigValue(type, pi.value());
|
||||
parameters[i] = result;
|
||||
} else if (ann.annotationType() == BeanInject.class) {
|
||||
BeanInject bi = (BeanInject) ann;
|
||||
Object result = getPostProcessorHelper().getInjectionBeanValue(type, bi.value());
|
||||
parameters[i] = result;
|
||||
}
|
||||
} else {
|
||||
// okay attempt to default to singleton instances from the registry
|
||||
Set<?> instances = getOrLookupCamelContext().getRegistry().findByType(type);
|
||||
if (instances.size() == 1) {
|
||||
parameters[i] = instances.iterator().next();
|
||||
} else if (instances.size() > 1) {
|
||||
// there are multiple instances of the same type, so barf
|
||||
throw new IllegalArgumentException("Multiple beans of the same type: " + type
|
||||
+ " exists in the Camel registry. Specify the bean name on @BeanInject to bind to a single bean, at the method: " + method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// each parameter must be mapped
|
||||
if (parameters[i] == null) {
|
||||
int pos = i + 1;
|
||||
throw new IllegalArgumentException("@BindToProperty cannot bind parameter #" + pos + " on method: " + method);
|
||||
}
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private static boolean isComplexUserType(Class type) {
|
||||
// lets consider all non java, as complex types
|
||||
return type != null && !type.isPrimitive() && !type.getName().startsWith("java.");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.spi.CamelContextNameStrategy;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A default name strategy which auto assigns a name using a prefix-counter pattern.
|
||||
*/
|
||||
public class DefaultCamelContextNameStrategy implements CamelContextNameStrategy {
|
||||
|
||||
private static final AtomicInteger CONTEXT_COUNTER = new AtomicInteger(0);
|
||||
private final String prefix;
|
||||
private String name;
|
||||
|
||||
public DefaultCamelContextNameStrategy() {
|
||||
this("camel");
|
||||
}
|
||||
|
||||
public DefaultCamelContextNameStrategy(String prefix) {
|
||||
this.prefix = prefix;
|
||||
this.name = getNextName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
if (name == null) {
|
||||
name = getNextName();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNextName() {
|
||||
return prefix + "-" + getNextCounter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFixedName() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int getNextCounter() {
|
||||
// we want to start counting from 1, so increment first
|
||||
return CONTEXT_COUNTER.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* To reset the counter, should only be used for testing purposes.
|
||||
*
|
||||
* @param value the counter value
|
||||
*/
|
||||
public static void setCounter(int value) {
|
||||
CONTEXT_COUNTER.set(value);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.spi.ClaimCheckRepository;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The default {@link ClaimCheckRepository} implementation that is an in-memory storage.
|
||||
*/
|
||||
public class DefaultClaimCheckRepository implements ClaimCheckRepository {
|
||||
|
||||
private final Map<String, Exchange> map = new HashMap<>();
|
||||
private final Deque<Exchange> stack = new ArrayDeque<>();
|
||||
|
||||
@Override
|
||||
public boolean add(String key, Exchange exchange) {
|
||||
return map.put(key, exchange) == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
return map.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange get(String key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange getAndRemove(String key) {
|
||||
return map.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void push(Exchange exchange) {
|
||||
stack.push(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange pop() {
|
||||
if (!stack.isEmpty()) {
|
||||
return stack.pop();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
map.clear();
|
||||
stack.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
// noop
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.CamelContextAware;
|
||||
import org.apache.camel.spi.ClassResolver;
|
||||
import org.apache.camel.util.CastUtils;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* Default class resolver that uses regular class loader to load classes.
|
||||
*/
|
||||
public class DefaultClassResolver implements ClassResolver, CamelContextAware {
|
||||
|
||||
private CamelContext camelContext;
|
||||
|
||||
public DefaultClassResolver() {
|
||||
}
|
||||
|
||||
public DefaultClassResolver(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCamelContext(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CamelContext getCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> resolveClass(String name) {
|
||||
Class<?> answer = loadClass(name, DefaultClassResolver.class.getClassLoader());
|
||||
if (answer == null && getApplicationContextClassLoader() != null) {
|
||||
// fallback and use application context class loader
|
||||
answer = loadClass(name, getApplicationContextClassLoader());
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Class<T> resolveClass(String name, Class<T> type) {
|
||||
Class<T> answer = CastUtils.cast(loadClass(name, DefaultClassResolver.class.getClassLoader()));
|
||||
if (answer == null && getApplicationContextClassLoader() != null) {
|
||||
// fallback and use application context class loader
|
||||
answer = CastUtils.cast(loadClass(name, getApplicationContextClassLoader()));
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> resolveClass(String name, ClassLoader loader) {
|
||||
return loadClass(name, loader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Class<T> resolveClass(String name, Class<T> type, ClassLoader loader) {
|
||||
return CastUtils.cast(loadClass(name, loader));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> resolveMandatoryClass(String name) throws ClassNotFoundException {
|
||||
Class<?> answer = resolveClass(name);
|
||||
if (answer == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Class<T> resolveMandatoryClass(String name, Class<T> type) throws ClassNotFoundException {
|
||||
Class<T> answer = resolveClass(name, type);
|
||||
if (answer == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> resolveMandatoryClass(String name, ClassLoader loader) throws ClassNotFoundException {
|
||||
Class<?> answer = resolveClass(name, loader);
|
||||
if (answer == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Class<T> resolveMandatoryClass(String name, Class<T> type, ClassLoader loader) throws ClassNotFoundException {
|
||||
Class<T> answer = resolveClass(name, type, loader);
|
||||
if (answer == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream loadResourceAsStream(String uri) {
|
||||
return ObjectHelper.loadResourceAsStream(uri, getApplicationContextClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL loadResourceAsURL(String uri) {
|
||||
return ObjectHelper.loadResourceAsURL(uri, getApplicationContextClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> loadResourcesAsURL(String uri) {
|
||||
return loadAllResourcesAsURL(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> loadAllResourcesAsURL(String uri) {
|
||||
return ObjectHelper.loadResourcesAsURL(uri);
|
||||
}
|
||||
|
||||
protected Class<?> loadClass(String name, ClassLoader loader) {
|
||||
return ObjectHelper.loadClass(name, loader);
|
||||
}
|
||||
|
||||
protected ClassLoader getApplicationContextClassLoader() {
|
||||
return camelContext != null ? camelContext.getApplicationContextClassLoader() : null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.ExtendedCamelContext;
|
||||
import org.apache.camel.RuntimeCamelException;
|
||||
import org.apache.camel.spi.ComponentNameResolver;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class DefaultComponentNameResolver implements ComponentNameResolver {
|
||||
|
||||
public static final String RESOURCE_PATH = "META-INF/services/org/apache/camel/component/*";
|
||||
|
||||
@Override
|
||||
public Set<String> resolveNames(CamelContext camelContext) {
|
||||
// remove leading path to only keep name
|
||||
Set<String> sorted = new TreeSet<>();
|
||||
|
||||
try {
|
||||
Set<String> locations = camelContext.adapt(ExtendedCamelContext.class).getPackageScanResourceResolver().findResourceNames(RESOURCE_PATH);
|
||||
locations.forEach(l -> sorted.add(l.substring(l.lastIndexOf('/') + 1)));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeCamelException(e);
|
||||
}
|
||||
|
||||
return sorted;
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.Component;
|
||||
import org.apache.camel.ExtendedCamelContext;
|
||||
import org.apache.camel.NoFactoryAvailableException;
|
||||
import org.apache.camel.spi.ComponentResolver;
|
||||
import org.apache.camel.spi.FactoryFinder;
|
||||
import org.apache.camel.support.ResolverHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The default implementation of {@link ComponentResolver} which tries to find
|
||||
* components by using the URI scheme prefix and searching for a file of the URI
|
||||
* scheme name in the <b>META-INF/services/org/apache/camel/component/</b>
|
||||
* directory on the classpath.
|
||||
*/
|
||||
public class DefaultComponentResolver implements ComponentResolver {
|
||||
|
||||
public static final String RESOURCE_PATH = "META-INF/services/org/apache/camel/component/";
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultComponentResolver.class);
|
||||
|
||||
private FactoryFinder factoryFinder;
|
||||
|
||||
@Override
|
||||
public Component resolveComponent(String name, CamelContext context) {
|
||||
// lookup in registry first
|
||||
Component componentReg = ResolverHelper.lookupComponentInRegistryWithFallback(context, name);
|
||||
if (componentReg != null) {
|
||||
return componentReg;
|
||||
}
|
||||
|
||||
// not in registry then use component factory
|
||||
Class<?> type;
|
||||
try {
|
||||
type = findComponent(name, context);
|
||||
if (type == null) {
|
||||
// not found
|
||||
return null;
|
||||
}
|
||||
} catch (NoFactoryAvailableException e) {
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Invalid URI, no Component registered for scheme: " + name, e);
|
||||
}
|
||||
|
||||
if (getLog().isDebugEnabled()) {
|
||||
getLog().debug("Found component: {} via type: {} via: {}{}", name, type.getName(), factoryFinder.getResourcePath(), name);
|
||||
}
|
||||
|
||||
// create the component
|
||||
if (Component.class.isAssignableFrom(type)) {
|
||||
return (Component) context.getInjector().newInstance(type, false);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Type is not a Component implementation. Found: " + type.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> findComponent(String name, CamelContext context) throws IOException {
|
||||
if (factoryFinder == null) {
|
||||
factoryFinder = context.adapt(ExtendedCamelContext.class).getFactoryFinder(RESOURCE_PATH);
|
||||
}
|
||||
return factoryFinder.findClass(name).orElse(null);
|
||||
}
|
||||
|
||||
protected Logger getLog() {
|
||||
return LOG;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.ExtendedCamelContext;
|
||||
import org.apache.camel.NoFactoryAvailableException;
|
||||
import org.apache.camel.spi.ConfigurerResolver;
|
||||
import org.apache.camel.spi.FactoryFinder;
|
||||
import org.apache.camel.spi.GeneratedPropertyConfigurer;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Default configurer resolver that looks for configurer factories in <b>META-INF/services/org/apache/camel/configurer/</b>.
|
||||
*/
|
||||
public class DefaultConfigurerResolver implements ConfigurerResolver {
|
||||
public static final String RESOURCE_PATH = "META-INF/services/org/apache/camel/configurer/";
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultConfigurerResolver.class);
|
||||
|
||||
protected FactoryFinder factoryFinder;
|
||||
|
||||
@Override
|
||||
public GeneratedPropertyConfigurer resolvePropertyConfigurer(String name, CamelContext context) {
|
||||
if (ObjectHelper.isEmpty(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// lookup in registry first
|
||||
GeneratedPropertyConfigurer configurer = context.getRegistry().lookupByNameAndType(name, GeneratedPropertyConfigurer.class);
|
||||
if (configurer != null) {
|
||||
return configurer;
|
||||
}
|
||||
|
||||
// clip -configurer from the name as that is not the name in META-INF
|
||||
if (name.endsWith("-configurer")) {
|
||||
name = name.substring(0, name.length() - 11);
|
||||
}
|
||||
|
||||
// not in registry then use configurer factory
|
||||
Class<?> type;
|
||||
try {
|
||||
type = findConfigurer(name, context);
|
||||
if (type == null) {
|
||||
// not found
|
||||
return null;
|
||||
}
|
||||
} catch (NoFactoryAvailableException e) {
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Invalid URI, no Configurer registered for scheme: " + name, e);
|
||||
}
|
||||
|
||||
if (getLog().isDebugEnabled()) {
|
||||
getLog().debug("Found configurer: {} via type: {} via: {}{}", name, type.getName(), factoryFinder.getResourcePath(), name);
|
||||
}
|
||||
|
||||
// create the component
|
||||
if (GeneratedPropertyConfigurer.class.isAssignableFrom(type)) {
|
||||
return (GeneratedPropertyConfigurer) context.getInjector().newInstance(type, false);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Type is not a GeneratedPropertyConfigurer implementation. Found: " + type.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> findConfigurer(String name, CamelContext context) throws IOException {
|
||||
if (factoryFinder == null) {
|
||||
factoryFinder = context.adapt(ExtendedCamelContext.class).getFactoryFinder(RESOURCE_PATH);
|
||||
}
|
||||
return factoryFinder.findClass(name).orElse(null);
|
||||
}
|
||||
|
||||
protected Logger getLog() {
|
||||
return LOG;
|
||||
}
|
||||
}
|
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.spi.ConsumerCache;
|
||||
import org.apache.camel.spi.EndpointUtilizationStatistics;
|
||||
import org.apache.camel.support.CamelContextHelper;
|
||||
import org.apache.camel.support.service.ServiceHelper;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* Cache containing created {@link Consumer}.
|
||||
*/
|
||||
public class DefaultConsumerCache extends ServiceSupport implements ConsumerCache {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultConsumerCache.class);
|
||||
|
||||
private final CamelContext camelContext;
|
||||
private final org.apache.camel.impl.engine.PollingConsumerServicePool consumers;
|
||||
private final Object source;
|
||||
|
||||
private EndpointUtilizationStatistics statistics;
|
||||
private boolean extendedStatistics;
|
||||
private int maxCacheSize;
|
||||
|
||||
public DefaultConsumerCache(Object source, CamelContext camelContext, int cacheSize) {
|
||||
this.source = source;
|
||||
this.camelContext = camelContext;
|
||||
this.maxCacheSize = cacheSize <= 0 ? CamelContextHelper.getMaximumCachePoolSize(camelContext) : cacheSize;
|
||||
this.consumers = createServicePool(camelContext, maxCacheSize);
|
||||
// only if JMX is enabled
|
||||
if (camelContext.getManagementStrategy().getManagementAgent() != null) {
|
||||
this.extendedStatistics = camelContext.getManagementStrategy().getManagementAgent().getStatisticsLevel().isExtended();
|
||||
} else {
|
||||
this.extendedStatistics = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected org.apache.camel.impl.engine.PollingConsumerServicePool createServicePool(CamelContext camelContext, int cacheSize) {
|
||||
return new PollingConsumerServicePool(Endpoint::createPollingConsumer, Consumer::getEndpoint, cacheSize);
|
||||
}
|
||||
|
||||
public boolean isExtendedStatistics() {
|
||||
return extendedStatistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether extended JMX statistics is enabled for {@link EndpointUtilizationStatistics}
|
||||
*/
|
||||
public void setExtendedStatistics(boolean extendedStatistics) {
|
||||
this.extendedStatistics = extendedStatistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases an acquired producer back after usage.
|
||||
*
|
||||
* @param endpoint the endpoint
|
||||
* @param pollingConsumer the pollingConsumer to release
|
||||
*/
|
||||
@Override
|
||||
public void releasePollingConsumer(Endpoint endpoint, PollingConsumer pollingConsumer) {
|
||||
consumers.release(endpoint, pollingConsumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires a pooled PollingConsumer which you <b>must</b> release back again after usage using the
|
||||
* {@link #releasePollingConsumer(Endpoint, PollingConsumer)} method.
|
||||
*
|
||||
* @param endpoint the endpoint
|
||||
* @return the PollingConsumer
|
||||
*/
|
||||
@Override
|
||||
public PollingConsumer acquirePollingConsumer(Endpoint endpoint) {
|
||||
try {
|
||||
PollingConsumer consumer = consumers.acquire(endpoint);
|
||||
if (statistics != null) {
|
||||
statistics.onHit(endpoint.getEndpointUri());
|
||||
}
|
||||
return consumer;
|
||||
} catch (Throwable e) {
|
||||
throw new FailedToCreateConsumerException(endpoint, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange receive(Endpoint endpoint) {
|
||||
if (camelContext.isStopped()) {
|
||||
throw new RejectedExecutionException("CamelContext is stopped");
|
||||
}
|
||||
|
||||
LOG.debug("<<<< {}", endpoint);
|
||||
PollingConsumer consumer = null;
|
||||
try {
|
||||
consumer = acquirePollingConsumer(endpoint);
|
||||
return consumer.receive();
|
||||
} finally {
|
||||
if (consumer != null) {
|
||||
releasePollingConsumer(endpoint, consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange receive(Endpoint endpoint, long timeout) {
|
||||
if (camelContext.isStopped()) {
|
||||
throw new RejectedExecutionException("CamelContext is stopped");
|
||||
}
|
||||
|
||||
LOG.debug("<<<< {}", endpoint);
|
||||
PollingConsumer consumer = null;
|
||||
try {
|
||||
consumer = acquirePollingConsumer(endpoint);
|
||||
return consumer.receive(timeout);
|
||||
} finally {
|
||||
if (consumer != null) {
|
||||
releasePollingConsumer(endpoint, consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange receiveNoWait(Endpoint endpoint) {
|
||||
if (camelContext.isStopped()) {
|
||||
throw new RejectedExecutionException("CamelContext is stopped");
|
||||
}
|
||||
|
||||
LOG.debug("<<<< {}", endpoint);
|
||||
PollingConsumer consumer = null;
|
||||
try {
|
||||
consumer = acquirePollingConsumer(endpoint);
|
||||
return consumer.receiveNoWait();
|
||||
} finally {
|
||||
if (consumer != null) {
|
||||
releasePollingConsumer(endpoint, consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CamelContext getCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source which uses this cache
|
||||
*
|
||||
* @return the source
|
||||
*/
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum cache size (capacity).
|
||||
*
|
||||
* @return the capacity
|
||||
*/
|
||||
@Override
|
||||
public int getCapacity() {
|
||||
return maxCacheSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current size of the cache
|
||||
*
|
||||
* @return the current size
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
int size = consumers.size();
|
||||
LOG.trace("size = {}", size);
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges this cache
|
||||
*/
|
||||
@Override
|
||||
public synchronized void purge() {
|
||||
try {
|
||||
consumers.stop();
|
||||
consumers.start();
|
||||
} catch (Exception e) {
|
||||
LOG.debug("Error restarting consumer pool", e);
|
||||
}
|
||||
if (statistics != null) {
|
||||
statistics.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
consumers.cleanUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EndpointUtilizationStatistics getEndpointUtilizationStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConsumerCache for source: " + source + ", capacity: " + getCapacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() throws Exception {
|
||||
if (extendedStatistics) {
|
||||
int max = maxCacheSize == 0 ? CamelContextHelper.getMaximumCachePoolSize(camelContext) : maxCacheSize;
|
||||
statistics = new DefaultEndpointUtilizationStatistics(max);
|
||||
}
|
||||
ServiceHelper.initService(consumers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception {
|
||||
if (statistics != null) {
|
||||
statistics.clear();
|
||||
}
|
||||
ServiceHelper.startService(consumers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception {
|
||||
ServiceHelper.stopService(consumers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doShutdown() throws Exception {
|
||||
ServiceHelper.stopAndShutdownServices(consumers);
|
||||
}
|
||||
}
|
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.spi.ConsumerCache;
|
||||
import org.apache.camel.spi.Synchronization;
|
||||
import org.apache.camel.support.CamelContextHelper;
|
||||
import org.apache.camel.support.UnitOfWorkHelper;
|
||||
import org.apache.camel.support.service.ServiceHelper;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.camel.RuntimeCamelException.wrapRuntimeCamelException;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ConsumerTemplate}.
|
||||
*/
|
||||
public class DefaultConsumerTemplate extends ServiceSupport implements ConsumerTemplate {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultConsumerTemplate.class);
|
||||
|
||||
private final CamelContext camelContext;
|
||||
private ConsumerCache consumerCache;
|
||||
private int maximumCacheSize;
|
||||
|
||||
public DefaultConsumerTemplate(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumCacheSize() {
|
||||
return maximumCacheSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaximumCacheSize(int maximumCacheSize) {
|
||||
this.maximumCacheSize = maximumCacheSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentCacheSize() {
|
||||
if (consumerCache == null) {
|
||||
return 0;
|
||||
}
|
||||
return consumerCache.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
if (consumerCache != null) {
|
||||
consumerCache.cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CamelContext getCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange receive(String endpointUri) {
|
||||
Endpoint endpoint = resolveMandatoryEndpoint(endpointUri);
|
||||
return getConsumerCache().receive(endpoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange receive(Endpoint endpoint) {
|
||||
return receive(endpoint.getEndpointUri());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange receive(String endpointUri, long timeout) {
|
||||
Endpoint endpoint = resolveMandatoryEndpoint(endpointUri);
|
||||
return getConsumerCache().receive(endpoint, timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange receive(Endpoint endpoint, long timeout) {
|
||||
return receive(endpoint.getEndpointUri(), timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange receiveNoWait(String endpointUri) {
|
||||
Endpoint endpoint = resolveMandatoryEndpoint(endpointUri);
|
||||
return getConsumerCache().receiveNoWait(endpoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange receiveNoWait(Endpoint endpoint) {
|
||||
return receiveNoWait(endpoint.getEndpointUri());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object receiveBody(String endpointUri) {
|
||||
return receiveBody(receive(endpointUri));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object receiveBody(Endpoint endpoint) {
|
||||
return receiveBody(endpoint.getEndpointUri());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object receiveBody(String endpointUri, long timeout) {
|
||||
return receiveBody(receive(endpointUri, timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object receiveBody(Endpoint endpoint, long timeout) {
|
||||
return receiveBody(endpoint.getEndpointUri(), timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object receiveBodyNoWait(String endpointUri) {
|
||||
return receiveBody(receiveNoWait(endpointUri));
|
||||
}
|
||||
|
||||
private Object receiveBody(Exchange exchange) {
|
||||
Object answer;
|
||||
try {
|
||||
answer = extractResultBody(exchange);
|
||||
} finally {
|
||||
doneUoW(exchange);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object receiveBodyNoWait(Endpoint endpoint) {
|
||||
return receiveBodyNoWait(endpoint.getEndpointUri());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T receiveBody(String endpointUri, Class<T> type) {
|
||||
Object answer;
|
||||
Exchange exchange = receive(endpointUri);
|
||||
try {
|
||||
answer = extractResultBody(exchange);
|
||||
answer = camelContext.getTypeConverter().convertTo(type, exchange, answer);
|
||||
} finally {
|
||||
doneUoW(exchange);
|
||||
}
|
||||
return (T) answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T receiveBody(Endpoint endpoint, Class<T> type) {
|
||||
return receiveBody(endpoint.getEndpointUri(), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T receiveBody(String endpointUri, long timeout, Class<T> type) {
|
||||
Object answer;
|
||||
Exchange exchange = receive(endpointUri, timeout);
|
||||
try {
|
||||
answer = extractResultBody(exchange);
|
||||
answer = camelContext.getTypeConverter().convertTo(type, exchange, answer);
|
||||
} finally {
|
||||
doneUoW(exchange);
|
||||
}
|
||||
return (T) answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T receiveBody(Endpoint endpoint, long timeout, Class<T> type) {
|
||||
return receiveBody(endpoint.getEndpointUri(), timeout, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T receiveBodyNoWait(String endpointUri, Class<T> type) {
|
||||
Object answer;
|
||||
Exchange exchange = receiveNoWait(endpointUri);
|
||||
try {
|
||||
answer = extractResultBody(exchange);
|
||||
answer = camelContext.getTypeConverter().convertTo(type, exchange, answer);
|
||||
} finally {
|
||||
doneUoW(exchange);
|
||||
}
|
||||
return (T) answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T receiveBodyNoWait(Endpoint endpoint, Class<T> type) {
|
||||
return receiveBodyNoWait(endpoint.getEndpointUri(), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doneUoW(Exchange exchange) {
|
||||
try {
|
||||
// The receiveBody method will get a null exchange
|
||||
if (exchange == null) {
|
||||
return;
|
||||
}
|
||||
if (exchange.getUnitOfWork() == null) {
|
||||
// handover completions and done them manually to ensure they are being executed
|
||||
List<Synchronization> synchronizations = exchange.adapt(ExtendedExchange.class).handoverCompletions();
|
||||
UnitOfWorkHelper.doneSynchronizations(exchange, synchronizations, LOG);
|
||||
} else {
|
||||
// done the unit of work
|
||||
exchange.getUnitOfWork().done(exchange);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOG.warn("Exception occurred during done UnitOfWork for Exchange: " + exchange
|
||||
+ ". This exception will be ignored.", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected Endpoint resolveMandatoryEndpoint(String endpointUri) {
|
||||
return CamelContextHelper.getMandatoryEndpoint(camelContext, endpointUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the body from the given result.
|
||||
* <p/>
|
||||
* If the exchange pattern is provided it will try to honor it and retrieve the body
|
||||
* from either IN or OUT according to the pattern.
|
||||
*
|
||||
* @param result the result
|
||||
* @return the result, can be <tt>null</tt>.
|
||||
*/
|
||||
protected Object extractResultBody(Exchange result) {
|
||||
Object answer = null;
|
||||
if (result != null) {
|
||||
// rethrow if there was an exception
|
||||
if (result.getException() != null) {
|
||||
throw wrapRuntimeCamelException(result.getException());
|
||||
}
|
||||
|
||||
// okay no fault then return the response
|
||||
answer = result.getMessage().getBody();
|
||||
|
||||
// in a very seldom situation then getBody can cause an exception to be set on the exchange
|
||||
// rethrow if there was an exception during execution
|
||||
if (result.getException() != null) {
|
||||
throw wrapRuntimeCamelException(result.getException());
|
||||
}
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
private ConsumerCache getConsumerCache() {
|
||||
if (!isStarted()) {
|
||||
throw new IllegalStateException("ConsumerTemplate has not been started");
|
||||
}
|
||||
return consumerCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() throws Exception {
|
||||
if (consumerCache == null) {
|
||||
consumerCache = new DefaultConsumerCache(this, camelContext, maximumCacheSize);
|
||||
}
|
||||
ServiceHelper.initService(consumerCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception {
|
||||
ServiceHelper.startService(consumerCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception {
|
||||
ServiceHelper.stopService(consumerCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doShutdown() throws Exception {
|
||||
// we should shutdown the services as this is our intention, to not re-use the services anymore
|
||||
ServiceHelper.stopAndShutdownService(consumerCache);
|
||||
consumerCache = null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.ExtendedCamelContext;
|
||||
import org.apache.camel.spi.DataFormat;
|
||||
import org.apache.camel.spi.DataFormatFactory;
|
||||
import org.apache.camel.spi.DataFormatResolver;
|
||||
import org.apache.camel.spi.FactoryFinder;
|
||||
import org.apache.camel.support.ResolverHelper;
|
||||
|
||||
/**
|
||||
* Default data format resolver
|
||||
*/
|
||||
public class DefaultDataFormatResolver implements DataFormatResolver {
|
||||
public static final String DATAFORMAT_RESOURCE_PATH = "META-INF/services/org/apache/camel/dataformat/";
|
||||
|
||||
private FactoryFinder dataformatFactory;
|
||||
|
||||
@Override
|
||||
public DataFormat resolveDataFormat(String name, CamelContext context) {
|
||||
// lookup in registry first
|
||||
DataFormat dataFormat = ResolverHelper.lookupDataFormatInRegistryWithFallback(context, name);
|
||||
|
||||
if (dataFormat == null) {
|
||||
// If not found in the registry, try to create a new instance using
|
||||
// a DataFormatFactory or from resources
|
||||
dataFormat = createDataFormat(name, context);
|
||||
}
|
||||
|
||||
return dataFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFormat createDataFormat(String name, CamelContext context) {
|
||||
DataFormat dataFormat = null;
|
||||
|
||||
// lookup in registry first
|
||||
DataFormatFactory dataFormatFactory = ResolverHelper.lookupDataFormatFactoryInRegistryWithFallback(context, name);
|
||||
if (dataFormatFactory != null) {
|
||||
dataFormat = dataFormatFactory.newInstance();
|
||||
}
|
||||
|
||||
if (dataFormat == null) {
|
||||
dataFormat = createDataFormatFromResource(name, context);
|
||||
}
|
||||
|
||||
return dataFormat;
|
||||
}
|
||||
|
||||
private DataFormat createDataFormatFromResource(String name, CamelContext context) {
|
||||
DataFormat dataFormat = null;
|
||||
|
||||
Class<?> type;
|
||||
try {
|
||||
if (dataformatFactory == null) {
|
||||
dataformatFactory = context.adapt(ExtendedCamelContext.class).getFactoryFinder(DATAFORMAT_RESOURCE_PATH);
|
||||
}
|
||||
type = dataformatFactory.findClass(name).orElse(null);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Invalid URI, no DataFormat registered for scheme: " + name, e);
|
||||
}
|
||||
|
||||
if (type == null) {
|
||||
type = context.getClassResolver().resolveClass(name);
|
||||
}
|
||||
|
||||
if (type != null) {
|
||||
if (DataFormat.class.isAssignableFrom(type)) {
|
||||
dataFormat = (DataFormat) context.getInjector().newInstance(type, false);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Resolving dataformat: " + name + " detected type conflict: Not a DataFormat implementation. Found: " + type.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return dataFormat;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.Endpoint;
|
||||
import org.apache.camel.Producer;
|
||||
import org.apache.camel.processor.EventNotifierProducer;
|
||||
import org.apache.camel.processor.UnitOfWorkProducer;
|
||||
import org.apache.camel.spi.DeferServiceFactory;
|
||||
|
||||
public final class DefaultDeferServiceFactory implements DeferServiceFactory {
|
||||
|
||||
@Override
|
||||
public Producer createProducer(Endpoint endpoint) throws Exception {
|
||||
Producer producer = new DeferProducer(endpoint);
|
||||
producer = new UnitOfWorkProducer(producer);
|
||||
producer = new EventNotifierProducer(producer);
|
||||
endpoint.getCamelContext().deferStartService(producer, true);
|
||||
return producer;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.Endpoint;
|
||||
import org.apache.camel.spi.EndpointRegistry;
|
||||
import org.apache.camel.support.CamelContextHelper;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link EndpointRegistry}
|
||||
*/
|
||||
public class DefaultEndpointRegistry extends AbstractDynamicRegistry<EndpointKey, Endpoint> implements EndpointRegistry<EndpointKey> {
|
||||
|
||||
public DefaultEndpointRegistry(CamelContext context) {
|
||||
super(context, CamelContextHelper.getMaximumEndpointCacheSize(context));
|
||||
}
|
||||
|
||||
public DefaultEndpointRegistry(CamelContext context, Map<EndpointKey, Endpoint> endpoints) {
|
||||
this(context);
|
||||
putAll(endpoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStatic(String key) {
|
||||
return isStatic(new EndpointKey(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDynamic(String key) {
|
||||
return isDynamic(new EndpointKey(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EndpointRegistry for " + context.getName() + ", capacity: " + maxCacheSize;
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.spi.EndpointUtilizationStatistics;
|
||||
import org.apache.camel.support.LRUCacheFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class DefaultEndpointUtilizationStatistics implements EndpointUtilizationStatistics {
|
||||
|
||||
private final int maxCapacity;
|
||||
private final Map<String, Long> map;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public DefaultEndpointUtilizationStatistics(int maxCapacity) {
|
||||
this.map = LRUCacheFactory.newLRUCache(16, maxCapacity, false);
|
||||
this.maxCapacity = maxCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int maxCapacity() {
|
||||
return maxCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHit(String uri) {
|
||||
map.compute(uri, (key, current) -> {
|
||||
if (current == null) {
|
||||
return 1L;
|
||||
} else {
|
||||
return ++current;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String uri) {
|
||||
map.remove(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Long> getStatistics() {
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
map.clear();
|
||||
}
|
||||
}
|
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.RuntimeCamelException;
|
||||
import org.apache.camel.spi.ClassResolver;
|
||||
import org.apache.camel.spi.FactoryFinder;
|
||||
import org.apache.camel.support.ObjectHelper;
|
||||
import org.apache.camel.util.IOHelper;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Default factory finder.
|
||||
*/
|
||||
public class DefaultFactoryFinder implements FactoryFinder {
|
||||
|
||||
private final ConcurrentMap<String, Class<?>> classMap = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<String, Boolean> classesNotFound = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<String, Exception> classesNotFoundExceptions = new ConcurrentHashMap<>();
|
||||
private final ClassResolver classResolver;
|
||||
private final String path;
|
||||
|
||||
public DefaultFactoryFinder(ClassResolver classResolver, String resourcePath) {
|
||||
this.classResolver = classResolver;
|
||||
this.path = resourcePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourcePath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> newInstance(String key) {
|
||||
return Optional.ofNullable(doNewInstance(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> newInstance(String key, Class<T> type) {
|
||||
Object obj = doNewInstance(key);
|
||||
return Optional.ofNullable(type.cast(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Class<?>> findClass(String key) {
|
||||
final String classKey = key;
|
||||
|
||||
Class<?> clazz = addToClassMap(classKey, () -> {
|
||||
Properties prop = doFindFactoryProperties(key);
|
||||
if (prop != null) {
|
||||
return doNewInstance(prop, true).orElse(null);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
return Optional.ofNullable(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Class<?>> findOptionalClass(String key) {
|
||||
final String classKey = key;
|
||||
|
||||
Class<?> clazz = addToClassMap(classKey, () -> {
|
||||
Properties prop = doFindFactoryProperties(key);
|
||||
if (prop != null) {
|
||||
return doNewInstance(prop, false).orElse(null);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
return Optional.ofNullable(clazz);
|
||||
}
|
||||
|
||||
private Object doNewInstance(String key) {
|
||||
Optional<Class<?>> clazz = findClass(key);
|
||||
return clazz.map(ObjectHelper::newInstance).orElse(null);
|
||||
}
|
||||
|
||||
private Optional<Class<?>> doNewInstance(Properties properties, boolean mandatory) throws IOException {
|
||||
String className = properties.getProperty("class");
|
||||
if (className == null && mandatory) {
|
||||
throw new IOException("Expected property is missing: class");
|
||||
} else if (className == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Class<?> clazz = classResolver.resolveClass(className);
|
||||
return Optional.ofNullable(clazz);
|
||||
}
|
||||
|
||||
private Properties doFindFactoryProperties(String key) throws IOException {
|
||||
String uri = path + key;
|
||||
|
||||
InputStream in = classResolver.loadResourceAsStream(uri);
|
||||
if (in == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// lets load the file
|
||||
BufferedInputStream reader = null;
|
||||
try {
|
||||
reader = IOHelper.buffered(in);
|
||||
Properties properties = new Properties();
|
||||
properties.load(reader);
|
||||
return properties;
|
||||
} finally {
|
||||
IOHelper.close(reader, key, null);
|
||||
IOHelper.close(in, key, null);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a wrapper function to deal with exceptions in lambdas: the exception
|
||||
* is wrapped by a runtime exception (WrappedRuntimeException) which we catch
|
||||
* later on with the only purpose to re-throw the original exception.
|
||||
*/
|
||||
protected Class<?> addToClassMap(String key, ClassSupplier mappingFunction) {
|
||||
if (classesNotFound.containsKey(key) || classesNotFoundExceptions.containsKey(key)) {
|
||||
Exception e = classesNotFoundExceptions.get(key);
|
||||
if (e == null) {
|
||||
return null;
|
||||
} else {
|
||||
throw RuntimeCamelException.wrapRuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> suppliedClass = classMap.computeIfAbsent(key, new Function<String, Class<?>>() {
|
||||
@Override
|
||||
public Class<?> apply(String classKey) {
|
||||
try {
|
||||
return mappingFunction.get();
|
||||
} catch (Exception e) {
|
||||
classesNotFoundExceptions.put(key, e);
|
||||
throw RuntimeCamelException.wrapRuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (suppliedClass == null) {
|
||||
// mark the key as non-resolvable to prevent pointless searching
|
||||
classesNotFound.put(key, Boolean.TRUE);
|
||||
}
|
||||
|
||||
return suppliedClass;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected interface ClassSupplier {
|
||||
Class<?> get() throws Exception;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.spi.ClassResolver;
|
||||
import org.apache.camel.spi.FactoryFinder;
|
||||
import org.apache.camel.spi.FactoryFinderResolver;
|
||||
|
||||
/**
|
||||
* Default factory finder.
|
||||
*/
|
||||
public class DefaultFactoryFinderResolver implements FactoryFinderResolver {
|
||||
|
||||
@Override
|
||||
public FactoryFinder resolveFactoryFinder(ClassResolver classResolver, String resourcePath) {
|
||||
return new DefaultFactoryFinder(classResolver, resourcePath);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,407 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.processor.ConvertBodyProcessor;
|
||||
import org.apache.camel.support.ExchangeHelper;
|
||||
import org.apache.camel.support.service.ServiceHelper;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class DefaultFluentProducerTemplate extends ServiceSupport implements FluentProducerTemplate {
|
||||
|
||||
// transient state of endpoint, headers and body which needs to be thread local scoped to be thread-safe
|
||||
private final ThreadLocal<Map<String, Object>> headers = new ThreadLocal<>();
|
||||
private final ThreadLocal<Object> body = new ThreadLocal<>();
|
||||
private final ThreadLocal<Endpoint> endpoint = new ThreadLocal<>();
|
||||
private final ThreadLocal<Supplier<Exchange>> exchangeSupplier = new ThreadLocal<>();
|
||||
private final ThreadLocal<Supplier<Processor>> processorSupplier = new ThreadLocal<>();
|
||||
private final ThreadLocal<Consumer<ProducerTemplate>> templateCustomizer = new ThreadLocal<>();
|
||||
|
||||
private final CamelContext context;
|
||||
private final ClassValue<ConvertBodyProcessor> resultProcessors;
|
||||
private Endpoint defaultEndpoint;
|
||||
private int maximumCacheSize;
|
||||
private boolean eventNotifierEnabled;
|
||||
private volatile ProducerTemplate template;
|
||||
|
||||
public DefaultFluentProducerTemplate(CamelContext context) {
|
||||
this.context = context;
|
||||
this.eventNotifierEnabled = true;
|
||||
this.resultProcessors = new ClassValue<ConvertBodyProcessor>() {
|
||||
@Override
|
||||
protected ConvertBodyProcessor computeValue(Class<?> type) {
|
||||
return new ConvertBodyProcessor(type);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public CamelContext getCamelContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentCacheSize() {
|
||||
if (template == null) {
|
||||
return 0;
|
||||
}
|
||||
return template.getCurrentCacheSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
if (template != null) {
|
||||
template.cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultEndpointUri(String endpointUri) {
|
||||
setDefaultEndpoint(getCamelContext().getEndpoint(endpointUri));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Endpoint getDefaultEndpoint() {
|
||||
return defaultEndpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultEndpoint(Endpoint defaultEndpoint) {
|
||||
this.defaultEndpoint = defaultEndpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumCacheSize() {
|
||||
return maximumCacheSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaximumCacheSize(int maximumCacheSize) {
|
||||
this.maximumCacheSize = maximumCacheSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEventNotifierEnabled() {
|
||||
return eventNotifierEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEventNotifierEnabled(boolean eventNotifierEnabled) {
|
||||
this.eventNotifierEnabled = eventNotifierEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate clearAll() {
|
||||
clearBody();
|
||||
clearHeaders();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate withHeader(String key, Object value) {
|
||||
Map<String, Object> map = headers.get();
|
||||
if (map == null) {
|
||||
map = new LinkedHashMap<>();
|
||||
headers.set(map);
|
||||
}
|
||||
|
||||
map.put(key, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate clearHeaders() {
|
||||
headers.remove();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate withBody(Object body) {
|
||||
this.body.set(body);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate withBodyAs(Object body, Class<?> type) {
|
||||
Object b = type != null
|
||||
? context.getTypeConverter().convertTo(type, body)
|
||||
: body;
|
||||
|
||||
this.body.set(b);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate clearBody() {
|
||||
body.remove();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate withTemplateCustomizer(final Consumer<ProducerTemplate> templateCustomizer) {
|
||||
this.templateCustomizer.set(templateCustomizer);
|
||||
|
||||
if (template != null) {
|
||||
// need to re-initialize template since we have a customizer
|
||||
ServiceHelper.stopService(template);
|
||||
templateCustomizer.accept(template);
|
||||
ServiceHelper.startService(template);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate withExchange(final Exchange exchange) {
|
||||
return withExchange(() -> exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate withExchange(final Supplier<Exchange> exchangeSupplier) {
|
||||
this.exchangeSupplier.set(exchangeSupplier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate withProcessor(final Processor processor) {
|
||||
return withProcessor(() -> processor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate withProcessor(final Supplier<Processor> processorSupplier) {
|
||||
this.processorSupplier.set(processorSupplier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate to(String endpointUri) {
|
||||
return to(context.getEndpoint(endpointUri));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentProducerTemplate to(Endpoint endpoint) {
|
||||
this.endpoint.set(endpoint);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ************************
|
||||
// REQUEST
|
||||
// ************************
|
||||
|
||||
@Override
|
||||
public Object request() throws CamelExecutionException {
|
||||
return request(Object.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T request(Class<T> type) throws CamelExecutionException {
|
||||
if (exchangeSupplier.get() != null) {
|
||||
throw new IllegalArgumentException("withExchange not supported on FluentProducerTemplate.request method. Use send method instead.");
|
||||
}
|
||||
|
||||
// Determine the target endpoint
|
||||
final Endpoint target = target();
|
||||
|
||||
// Create the default processor if not provided.
|
||||
final Processor processorSupplier = this.processorSupplier.get() != null ? this.processorSupplier.get().get() : defaultProcessor();
|
||||
|
||||
T result;
|
||||
if (type == Exchange.class) {
|
||||
result = (T)template().request(target, processorSupplier);
|
||||
} else if (type == Message.class) {
|
||||
Exchange exchange = template().request(target, processorSupplier);
|
||||
result = (T)exchange.getMessage();
|
||||
} else {
|
||||
Exchange exchange = template().send(
|
||||
target,
|
||||
ExchangePattern.InOut,
|
||||
processorSupplier,
|
||||
resultProcessors.get(type)
|
||||
);
|
||||
|
||||
result = context.getTypeConverter().convertTo(
|
||||
type,
|
||||
ExchangeHelper.extractResultBody(exchange, exchange.getPattern())
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Object> asyncRequest() {
|
||||
return asyncRequest(Object.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> asyncRequest(Class<T> type) {
|
||||
// Determine the target endpoint
|
||||
final Endpoint target = target();
|
||||
|
||||
Future<T> result;
|
||||
if (ObjectHelper.isNotEmpty(headers.get())) {
|
||||
// Make a copy of the headers and body so that async processing won't
|
||||
// be invalidated by subsequent reuse of the template
|
||||
final Map<String, Object> headersCopy = new HashMap<>(headers.get());
|
||||
final Object bodyCopy = body.get();
|
||||
|
||||
result = template().asyncRequestBodyAndHeaders(target, bodyCopy, headersCopy, type);
|
||||
} else {
|
||||
// Make a copy of the and body so that async processing won't be
|
||||
// invalidated by subsequent reuse of the template
|
||||
final Object bodyCopy = body.get();
|
||||
|
||||
result = template().asyncRequestBody(target, bodyCopy, type);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ************************
|
||||
// SEND
|
||||
// ************************
|
||||
|
||||
@Override
|
||||
public Exchange send() throws CamelExecutionException {
|
||||
// Determine the target endpoint
|
||||
final Endpoint target = target();
|
||||
|
||||
Exchange exchange = exchangeSupplier.get() != null ? exchangeSupplier.get().get() : null;
|
||||
if (exchange != null) {
|
||||
return template().send(target, exchange);
|
||||
} else {
|
||||
Processor processor = processorSupplier.get() != null ? processorSupplier.get().get() : defaultProcessor();
|
||||
return template().send(target, processor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Exchange> asyncSend() {
|
||||
// Determine the target endpoint
|
||||
final Endpoint target = target();
|
||||
|
||||
Exchange exchange = exchangeSupplier.get() != null ? exchangeSupplier.get().get() : null;
|
||||
if (exchange != null) {
|
||||
return template().asyncSend(target, exchange);
|
||||
} else {
|
||||
Processor processor = processorSupplier.get() != null ? processorSupplier.get().get() : defaultAsyncProcessor();
|
||||
return template().asyncSend(target, processor);
|
||||
}
|
||||
}
|
||||
|
||||
// ************************
|
||||
// HELPERS
|
||||
// ************************
|
||||
|
||||
/**
|
||||
* Create the FluentProducerTemplate by setting the camel context
|
||||
*
|
||||
* @param context the camel context
|
||||
*/
|
||||
public static FluentProducerTemplate on(CamelContext context) {
|
||||
DefaultFluentProducerTemplate fluent = new DefaultFluentProducerTemplate(context);
|
||||
fluent.start();
|
||||
return fluent;
|
||||
}
|
||||
|
||||
private ProducerTemplate template() {
|
||||
return template;
|
||||
}
|
||||
|
||||
private Processor defaultProcessor() {
|
||||
return exchange -> {
|
||||
ObjectHelper.ifNotEmpty(headers.get(), exchange.getIn().getHeaders()::putAll);
|
||||
ObjectHelper.ifNotEmpty(body.get(), exchange.getIn()::setBody);
|
||||
};
|
||||
}
|
||||
|
||||
private Processor defaultAsyncProcessor() {
|
||||
final Map<String, Object> headersCopy = ObjectHelper.isNotEmpty(this.headers.get()) ? new HashMap<>(this.headers.get()) : null;
|
||||
final Object bodyCopy = this.body.get();
|
||||
|
||||
return exchange -> {
|
||||
ObjectHelper.ifNotEmpty(headersCopy, exchange.getIn().getHeaders()::putAll);
|
||||
ObjectHelper.ifNotEmpty(bodyCopy, exchange.getIn()::setBody);
|
||||
};
|
||||
}
|
||||
|
||||
private Endpoint target() {
|
||||
if (endpoint.get() != null) {
|
||||
return endpoint.get();
|
||||
}
|
||||
if (defaultEndpoint != null) {
|
||||
return defaultEndpoint;
|
||||
}
|
||||
|
||||
if (template != null && template.getDefaultEndpoint() != null) {
|
||||
return template.getDefaultEndpoint();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("No endpoint configured on FluentProducerTemplate. You can configure an endpoint with to(uri)");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() throws Exception {
|
||||
ObjectHelper.notNull(context, "CamelContext");
|
||||
template = context.createProducerTemplate(maximumCacheSize);
|
||||
if (defaultEndpoint != null) {
|
||||
template.setDefaultEndpoint(defaultEndpoint);
|
||||
}
|
||||
template.setEventNotifierEnabled(eventNotifierEnabled);
|
||||
if (templateCustomizer.get() != null) {
|
||||
templateCustomizer.get().accept(template);
|
||||
}
|
||||
ServiceHelper.initService(template);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception {
|
||||
ServiceHelper.startService(template);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception {
|
||||
clearAll();
|
||||
this.endpoint.remove();
|
||||
this.exchangeSupplier.remove();
|
||||
this.processorSupplier.remove();
|
||||
this.templateCustomizer.remove();
|
||||
ServiceHelper.stopService(template);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doShutdown() throws Exception {
|
||||
ServiceHelper.stopAndShutdownService(template);
|
||||
template = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.GlobalEndpointConfiguration;
|
||||
|
||||
public final class DefaultGlobalEndpointConfiguration implements GlobalEndpointConfiguration {
|
||||
|
||||
private boolean lazyStartProducer;
|
||||
private boolean bridgeErrorHandler;
|
||||
private boolean basicPropertyBinding;
|
||||
|
||||
@Override
|
||||
public boolean isLazyStartProducer() {
|
||||
return lazyStartProducer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLazyStartProducer(boolean lazyStartProducer) {
|
||||
this.lazyStartProducer = lazyStartProducer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBridgeErrorHandler() {
|
||||
return bridgeErrorHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBridgeErrorHandler(boolean bridgeErrorHandler) {
|
||||
this.bridgeErrorHandler = bridgeErrorHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBasicPropertyBinding() {
|
||||
return basicPropertyBinding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBasicPropertyBinding(boolean basicPropertyBinding) {
|
||||
this.basicPropertyBinding = basicPropertyBinding;
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.spi.HeadersMapFactory;
|
||||
import org.apache.camel.util.CaseInsensitiveMap;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Default {@link HeadersMapFactory} which uses the {@link CaseInsensitiveMap CaseInsensitiveMap}.
|
||||
* This implementation uses a {@link CaseInsensitiveMap} storing the headers.
|
||||
* This allows us to be able to lookup headers using case insensitive keys, making it easier for end users
|
||||
* as they do not have to be worried about using exact keys.
|
||||
* See more details at {@link CaseInsensitiveMap}.
|
||||
*/
|
||||
public class DefaultHeadersMapFactory implements HeadersMapFactory {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> newMap() {
|
||||
return new CaseInsensitiveMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> newMap(Map<String, Object> map) {
|
||||
return new CaseInsensitiveMap(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstanceOf(Map<String, Object> map) {
|
||||
return map instanceof CaseInsensitiveMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCaseInsensitive() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.ExtendedExchange;
|
||||
import org.apache.camel.MessageHistory;
|
||||
import org.apache.camel.spi.InflightRepository;
|
||||
import org.apache.camel.support.ExchangeHelper;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Default {@link InflightRepository}.
|
||||
*/
|
||||
public class DefaultInflightRepository extends ServiceSupport implements InflightRepository {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultInflightRepository.class);
|
||||
|
||||
private final AtomicInteger size = new AtomicInteger();
|
||||
private final ConcurrentMap<String, Exchange> inflight = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<String, AtomicInteger> routeCount = new ConcurrentHashMap<>();
|
||||
private boolean inflightExchangeEnabled;
|
||||
|
||||
@Override
|
||||
public void add(Exchange exchange) {
|
||||
size.incrementAndGet();
|
||||
|
||||
if (inflightExchangeEnabled) {
|
||||
inflight.put(exchange.getExchangeId(), exchange);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Exchange exchange) {
|
||||
size.decrementAndGet();
|
||||
|
||||
if (inflightExchangeEnabled) {
|
||||
inflight.remove(exchange.getExchangeId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Exchange exchange, String routeId) {
|
||||
AtomicInteger existing = routeCount.get(routeId);
|
||||
if (existing != null) {
|
||||
existing.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Exchange exchange, String routeId) {
|
||||
AtomicInteger existing = routeCount.get(routeId);
|
||||
if (existing != null) {
|
||||
existing.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRoute(String routeId) {
|
||||
routeCount.putIfAbsent(routeId, new AtomicInteger());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRoute(String routeId) {
|
||||
routeCount.remove(routeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size(String routeId) {
|
||||
AtomicInteger existing = routeCount.get(routeId);
|
||||
return existing != null ? existing.get() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInflightBrowseEnabled() {
|
||||
return inflightExchangeEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInflightBrowseEnabled(boolean inflightBrowseEnabled) {
|
||||
this.inflightExchangeEnabled = inflightBrowseEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<InflightExchange> browse() {
|
||||
return browse(null, -1, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<InflightExchange> browse(String fromRouteId) {
|
||||
return browse(fromRouteId, -1, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<InflightExchange> browse(int limit, boolean sortByLongestDuration) {
|
||||
return browse(null, limit, sortByLongestDuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<InflightExchange> browse(String fromRouteId, int limit, boolean sortByLongestDuration) {
|
||||
if (!inflightExchangeEnabled) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Stream<Exchange> values;
|
||||
if (fromRouteId == null) {
|
||||
// all values
|
||||
values = inflight.values().stream();
|
||||
} else {
|
||||
// only if route match
|
||||
values = inflight.values().stream()
|
||||
.filter(e -> fromRouteId.equals(e.getFromRouteId()));
|
||||
}
|
||||
|
||||
if (sortByLongestDuration) {
|
||||
// sort by duration and grab the first
|
||||
values = values.sorted((e1, e2) -> {
|
||||
long d1 = getExchangeDuration(e1);
|
||||
long d2 = getExchangeDuration(e2);
|
||||
// need the biggest number first
|
||||
return -1 * Long.compare(d1, d2);
|
||||
});
|
||||
} else {
|
||||
// else sort by exchange id
|
||||
values = values.sorted(Comparator.comparing(Exchange::getExchangeId));
|
||||
}
|
||||
|
||||
if (limit > 0) {
|
||||
values = values.limit(limit);
|
||||
}
|
||||
|
||||
List<InflightExchange> answer = values.map(InflightExchangeEntry::new).collect(Collectors.toList());
|
||||
return Collections.unmodifiableCollection(answer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InflightExchange oldest(String fromRouteId) {
|
||||
if (!inflightExchangeEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Stream<Exchange> values;
|
||||
|
||||
if (fromRouteId == null) {
|
||||
// all values
|
||||
values = inflight.values().stream();
|
||||
} else {
|
||||
// only if route match
|
||||
values = inflight.values().stream()
|
||||
.filter(e -> fromRouteId.equals(e.getFromRouteId()));
|
||||
}
|
||||
|
||||
// sort by duration and grab the first
|
||||
Exchange first = values.sorted((e1, e2) -> {
|
||||
long d1 = getExchangeDuration(e1);
|
||||
long d2 = getExchangeDuration(e2);
|
||||
// need the biggest number first
|
||||
return -1 * Long.compare(d1, d2);
|
||||
}).findFirst().orElse(null);
|
||||
|
||||
if (first != null) {
|
||||
return new InflightExchangeEntry(first);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception {
|
||||
int count = size();
|
||||
if (count > 0) {
|
||||
LOG.warn("Shutting down while there are still {} inflight exchanges.", count);
|
||||
} else {
|
||||
LOG.debug("Shutting down with no inflight exchanges.");
|
||||
}
|
||||
routeCount.clear();
|
||||
}
|
||||
|
||||
private static long getExchangeDuration(Exchange exchange) {
|
||||
return System.currentTimeMillis() - exchange.getCreated();
|
||||
}
|
||||
|
||||
private static final class InflightExchangeEntry implements InflightExchange {
|
||||
|
||||
private final Exchange exchange;
|
||||
|
||||
private InflightExchangeEntry(Exchange exchange) {
|
||||
this.exchange = exchange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange getExchange() {
|
||||
return exchange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return DefaultInflightRepository.getExchangeDuration(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public long getElapsed() {
|
||||
// this can only be calculate if message history is enabled
|
||||
LinkedList<MessageHistory> list = exchange.getProperty(Exchange.MESSAGE_HISTORY, LinkedList.class);
|
||||
if (list == null || list.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get latest entry
|
||||
MessageHistory history = list.getLast();
|
||||
if (history != null) {
|
||||
long elapsed = history.getElapsed();
|
||||
if (elapsed == 0 && history.getTime() > 0) {
|
||||
// still in progress, so lets compute it via the start time
|
||||
elapsed = System.currentTimeMillis() - history.getTime();
|
||||
}
|
||||
return elapsed;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public String getNodeId() {
|
||||
return exchange.adapt(ExtendedExchange.class).getHistoryNodeId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFromRouteId() {
|
||||
return exchange.getFromRouteId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public String getAtRouteId() {
|
||||
return ExchangeHelper.getAtRouteId(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InflightExchangeEntry[exchangeId=" + exchange.getExchangeId() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.ExtendedCamelContext;
|
||||
import org.apache.camel.RuntimeCamelException;
|
||||
import org.apache.camel.spi.CamelBeanPostProcessor;
|
||||
import org.apache.camel.spi.Injector;
|
||||
import org.apache.camel.support.ObjectHelper;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
/**
|
||||
* A default implementation of {@link Injector} which just uses reflection to
|
||||
* instantiate new objects using their zero argument constructor,
|
||||
* and then performing bean post processing using {@link CamelBeanPostProcessor}.
|
||||
*/
|
||||
public class DefaultInjector implements Injector {
|
||||
|
||||
// use the reflection injector
|
||||
private final CamelBeanPostProcessor postProcessor;
|
||||
|
||||
public DefaultInjector(CamelContext context) {
|
||||
postProcessor = context.adapt(ExtendedCamelContext.class).getBeanPostProcessor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T newInstance(Class<T> type) {
|
||||
return newInstance(type, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T newInstance(Class<T> type, String factoryMethod) {
|
||||
T answer = null;
|
||||
try {
|
||||
// lookup factory method
|
||||
Method fm = type.getMethod(factoryMethod);
|
||||
if (Modifier.isStatic(fm.getModifiers()) && Modifier.isPublic(fm.getModifiers()) && fm.getReturnType() == type) {
|
||||
Object obj = fm.invoke(null);
|
||||
answer = type.cast(obj);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeCamelException("Error invoking factory method: " + factoryMethod + " on class: " + type, e);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T newInstance(Class<T> type, boolean postProcessBean) {
|
||||
T answer = ObjectHelper.newInstance(type);
|
||||
if (answer != null && postProcessBean) {
|
||||
try {
|
||||
postProcessor.postProcessBeforeInitialization(answer, answer.getClass().getName());
|
||||
postProcessor.postProcessAfterInitialization(answer, answer.getClass().getName());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeCamelException("Error during post processing of bean: " + answer, e);
|
||||
}
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAutoWiring() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.spi.InterceptSendToEndpoint;
|
||||
import org.apache.camel.support.service.ServiceHelper;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is an endpoint when sending to it, is intercepted and is routed in a detour (before and optionally after).
|
||||
*/
|
||||
public class DefaultInterceptSendToEndpoint implements InterceptSendToEndpoint, ShutdownableService {
|
||||
|
||||
private final Endpoint delegate;
|
||||
private Processor before;
|
||||
private Processor after;
|
||||
private boolean skip;
|
||||
|
||||
/**
|
||||
* Intercepts sending to the given endpoint
|
||||
*
|
||||
* @param destination the original endpoint
|
||||
* @param skip <tt>true</tt> to skip sending after the detour to the original endpoint
|
||||
*/
|
||||
public DefaultInterceptSendToEndpoint(final Endpoint destination, boolean skip) {
|
||||
this.delegate = destination;
|
||||
this.skip = skip;
|
||||
}
|
||||
|
||||
public void setBefore(Processor before) {
|
||||
this.before = before;
|
||||
}
|
||||
|
||||
public void setAfter(Processor after) {
|
||||
this.after = after;
|
||||
}
|
||||
|
||||
public void setSkip(boolean skip) {
|
||||
this.skip = skip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Processor getBefore() {
|
||||
return before;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Processor getAfter() {
|
||||
return after;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Endpoint getOriginalEndpoint() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkip() {
|
||||
return skip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEndpointUri() {
|
||||
return delegate.getEndpointUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEndpointBaseUri() {
|
||||
return delegate.getEndpointBaseUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEndpointKey() {
|
||||
return delegate.getEndpointKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange createExchange() {
|
||||
return delegate.createExchange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange createExchange(ExchangePattern pattern) {
|
||||
return delegate.createExchange(pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CamelContext getCamelContext() {
|
||||
return delegate.getCamelContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Producer createProducer() throws Exception {
|
||||
return createAsyncProducer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncProducer createAsyncProducer() throws Exception {
|
||||
AsyncProducer producer = delegate.createAsyncProducer();
|
||||
return new InterceptSendToEndpointProcessor(this, delegate, producer, skip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer createConsumer(Processor processor) throws Exception {
|
||||
return delegate.createConsumer(processor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PollingConsumer createPollingConsumer() throws Exception {
|
||||
return delegate.createPollingConsumer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureProperties(Map<String, Object> options) {
|
||||
delegate.configureProperties(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCamelContext(CamelContext context) {
|
||||
delegate.setCamelContext(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLenientProperties() {
|
||||
return delegate.isLenientProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return delegate.isSingleton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
ServiceHelper.startService(before, delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
ServiceHelper.stopService(delegate, before);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
ServiceHelper.stopAndShutdownServices(delegate, before);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.ExtendedCamelContext;
|
||||
import org.apache.camel.NoFactoryAvailableException;
|
||||
import org.apache.camel.NoSuchLanguageException;
|
||||
import org.apache.camel.spi.FactoryFinder;
|
||||
import org.apache.camel.spi.Language;
|
||||
import org.apache.camel.spi.LanguageResolver;
|
||||
import org.apache.camel.support.ResolverHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Default language resolver that looks for language factories in <b>META-INF/services/org/apache/camel/language/</b> and
|
||||
* language resolvers in <b>META-INF/services/org/apache/camel/language/resolver/</b>.
|
||||
*/
|
||||
public class DefaultLanguageResolver implements LanguageResolver {
|
||||
public static final String LANGUAGE_RESOURCE_PATH = "META-INF/services/org/apache/camel/language/";
|
||||
public static final String LANGUAGE_RESOLVER_RESOURCE_PATH = LANGUAGE_RESOURCE_PATH + "resolver/";
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultLanguageResolver.class);
|
||||
|
||||
protected FactoryFinder languageFactory;
|
||||
protected FactoryFinder languageResolver;
|
||||
|
||||
@Override
|
||||
public Language resolveLanguage(String name, CamelContext context) {
|
||||
// lookup in registry first
|
||||
Language languageReg = ResolverHelper.lookupLanguageInRegistryWithFallback(context, name);
|
||||
if (languageReg != null) {
|
||||
return languageReg;
|
||||
}
|
||||
|
||||
Class<?> type = null;
|
||||
try {
|
||||
type = findLanguage(name, context);
|
||||
} catch (NoFactoryAvailableException e) {
|
||||
// ignore
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Invalid URI, no Language registered for scheme: " + name, e);
|
||||
}
|
||||
|
||||
if (type != null) {
|
||||
if (Language.class.isAssignableFrom(type)) {
|
||||
return (Language) context.getInjector().newInstance(type, false);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Resolving language: " + name + " detected type conflict: Not a Language implementation. Found: " + type.getName());
|
||||
}
|
||||
} else {
|
||||
// no specific language found then try fallback
|
||||
return noSpecificLanguageFound(name, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected Language noSpecificLanguageFound(String name, CamelContext context) {
|
||||
Class<?> type = null;
|
||||
try {
|
||||
type = findLanguageResolver("default", context);
|
||||
} catch (NoFactoryAvailableException e) {
|
||||
// ignore
|
||||
} catch (ClassNotFoundException e) {
|
||||
// ignore
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Invalid URI, no LanguageResolver registered for scheme: " + name, e);
|
||||
}
|
||||
if (type != null) {
|
||||
if (LanguageResolver.class.isAssignableFrom(type)) {
|
||||
LanguageResolver resolver = (LanguageResolver) context.getInjector().newInstance(type, false);
|
||||
return resolver.resolveLanguage(name, context);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Resolving language: " + name + " detected type conflict: Not a LanguageResolver implementation. Found: " + type.getName());
|
||||
}
|
||||
}
|
||||
throw new NoSuchLanguageException(name);
|
||||
}
|
||||
|
||||
protected Class<?> findLanguage(String name, CamelContext context) throws Exception {
|
||||
if (languageFactory == null) {
|
||||
languageFactory = context.adapt(ExtendedCamelContext.class).getFactoryFinder(LANGUAGE_RESOURCE_PATH);
|
||||
}
|
||||
return languageFactory.findClass(name).orElse(null);
|
||||
}
|
||||
|
||||
protected Class<?> findLanguageResolver(String name, CamelContext context) throws Exception {
|
||||
if (languageResolver == null) {
|
||||
languageResolver = context.adapt(ExtendedCamelContext.class).getFactoryFinder(LANGUAGE_RESOLVER_RESOURCE_PATH);
|
||||
}
|
||||
return languageResolver.findClass(name).orElse(null);
|
||||
}
|
||||
|
||||
protected Logger getLog() {
|
||||
return LOG;
|
||||
}
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.spi.ManagementNameStrategy;
|
||||
import org.apache.camel.util.StringHelper;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ManagementNameStrategy}
|
||||
* <p/>
|
||||
* This implementation will by default use a name pattern as <tt>#name#</tt> and in case
|
||||
* of a clash, then the pattern will fallback to be using the counter as <tt>#name#-#counter#</tt>.
|
||||
*/
|
||||
public class DefaultManagementNameStrategy implements ManagementNameStrategy {
|
||||
|
||||
private static final Pattern INVALID_PATTERN = Pattern.compile(".*#\\w+#.*");
|
||||
private static final AtomicLong NAME_COUNTER = new AtomicLong();
|
||||
|
||||
private final CamelContext camelContext;
|
||||
private final String defaultPattern;
|
||||
private final String nextPattern;
|
||||
private String name;
|
||||
private String namePattern;
|
||||
|
||||
public DefaultManagementNameStrategy(CamelContext camelContext) {
|
||||
this(camelContext, null, "#name#-#counter#");
|
||||
}
|
||||
|
||||
public DefaultManagementNameStrategy(CamelContext camelContext, String defaultPattern, String nextPattern) {
|
||||
this.camelContext = camelContext;
|
||||
this.defaultPattern = defaultPattern;
|
||||
this.nextPattern = nextPattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamePattern() {
|
||||
return namePattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNamePattern(String namePattern) {
|
||||
this.namePattern = namePattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
if (name == null) {
|
||||
String pattern = getNamePattern();
|
||||
if (pattern == null) {
|
||||
// fallback and use the default pattern which is the same name as the CamelContext has been given
|
||||
pattern = defaultPattern != null ? defaultPattern : camelContext.getManagementStrategy().getManagementAgent().getManagementNamePattern();
|
||||
}
|
||||
name = resolveManagementName(pattern, camelContext.getName(), true);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNextName() {
|
||||
if (isFixedName()) {
|
||||
// use the fixed name
|
||||
return getName();
|
||||
} else {
|
||||
// or resolve a new name
|
||||
String pattern = getNamePattern();
|
||||
if (pattern == null) {
|
||||
// use a pattern that has a counter to ensure unique next name
|
||||
pattern = nextPattern;
|
||||
}
|
||||
return resolveManagementName(pattern, camelContext.getName(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFixedName() {
|
||||
// the name will be fixed unless there is a counter token
|
||||
String pattern = getNamePattern();
|
||||
if (pattern == null) {
|
||||
// we are not fixed by default
|
||||
return false;
|
||||
}
|
||||
return !pattern.contains("#counter#");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new management name with the given pattern
|
||||
*
|
||||
* @param pattern the pattern
|
||||
* @param name the name
|
||||
* @return the management name
|
||||
* @throws IllegalArgumentException if the pattern or name is invalid or empty
|
||||
*/
|
||||
@Override
|
||||
public String resolveManagementName(String pattern, String name, boolean invalidCheck) {
|
||||
StringHelper.notEmpty(pattern, "pattern");
|
||||
StringHelper.notEmpty(name, "name");
|
||||
|
||||
// replace tokens
|
||||
String answer = pattern;
|
||||
if (pattern.contains("#counter#")) {
|
||||
// only increment the counter on-demand
|
||||
answer = StringHelper.replaceAll(pattern, "#counter#", "" + nextNameCounter());
|
||||
}
|
||||
// camelId and name is the same tokens
|
||||
answer = StringHelper.replaceAll(answer, "#camelId#", name);
|
||||
answer = StringHelper.replaceAll(answer, "#name#", name);
|
||||
|
||||
// allow custom name resolution as well. For example with camel-core-osgi we have a custom
|
||||
// name strategy that supports OSGI specific tokens such as #bundleId# etc.
|
||||
answer = customResolveManagementName(pattern, answer);
|
||||
|
||||
// are there any #word# combos left, if so they should be considered invalid tokens
|
||||
if (invalidCheck && INVALID_PATTERN.matcher(answer).matches()) {
|
||||
throw new IllegalArgumentException("Pattern is invalid: " + pattern);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy to do any custom resolution of the name
|
||||
*
|
||||
* @param pattern the pattern
|
||||
* @param answer the current answer, which may have custom patterns still to be resolved
|
||||
* @return the resolved name
|
||||
*/
|
||||
protected String customResolveManagementName(String pattern, String answer) {
|
||||
return answer;
|
||||
}
|
||||
|
||||
private static long nextNameCounter() {
|
||||
// we want to be 1-based, so increment first
|
||||
return NAME_COUNTER.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* To reset the counter, should only be used for testing purposes.
|
||||
*
|
||||
* @param value the counter value
|
||||
*/
|
||||
public static void setCounter(int value) {
|
||||
NAME_COUNTER.set(value);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.CamelContextAware;
|
||||
import org.apache.camel.ExtendedCamelContext;
|
||||
import org.apache.camel.NamedNode;
|
||||
import org.apache.camel.impl.event.DefaultEventFactory;
|
||||
import org.apache.camel.spi.*;
|
||||
import org.apache.camel.support.service.ServiceHelper;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* A default management strategy that does <b>not</b> manage.
|
||||
* <p/>
|
||||
* This is default only used if Camel detects that it cannot use the JMX capable
|
||||
* {@link org.apache.camel.management.JmxManagementStrategy} strategy. Then Camel will
|
||||
* fallback to use this instead that is basically a simple and <tt>noop</tt> strategy.
|
||||
* <p/>
|
||||
* This class can also be used to extend your custom management implement. In fact the JMX capable
|
||||
* provided by Camel extends this class as well.
|
||||
*
|
||||
* @see org.apache.camel.management.JmxManagementStrategy
|
||||
*/
|
||||
public class DefaultManagementStrategy extends ServiceSupport implements ManagementStrategy, CamelContextAware {
|
||||
|
||||
private final List<EventNotifier> eventNotifiers = new CopyOnWriteArrayList<>();
|
||||
private EventFactory eventFactory = new DefaultEventFactory();
|
||||
private ManagementObjectNameStrategy managementObjectNameStrategy;
|
||||
private ManagementObjectStrategy managementObjectStrategy;
|
||||
private ManagementAgent managementAgent;
|
||||
private CamelContext camelContext;
|
||||
|
||||
public DefaultManagementStrategy() {
|
||||
}
|
||||
|
||||
public DefaultManagementStrategy(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
}
|
||||
|
||||
public DefaultManagementStrategy(CamelContext camelContext, ManagementAgent managementAgent) {
|
||||
this.camelContext = camelContext;
|
||||
this.managementAgent = managementAgent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EventNotifier> getEventNotifiers() {
|
||||
return eventNotifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEventNotifier(EventNotifier eventNotifier) {
|
||||
this.eventNotifiers.add(eventNotifier);
|
||||
if (getCamelContext() != null) {
|
||||
// okay we have an event notifier so its applicable
|
||||
getCamelContext().adapt(ExtendedCamelContext.class).setEventNotificationApplicable(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeEventNotifier(EventNotifier eventNotifier) {
|
||||
return eventNotifiers.remove(eventNotifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventFactory getEventFactory() {
|
||||
return eventFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEventFactory(EventFactory eventFactory) {
|
||||
this.eventFactory = eventFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManagementObjectNameStrategy getManagementObjectNameStrategy() {
|
||||
return managementObjectNameStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setManagementObjectNameStrategy(ManagementObjectNameStrategy managementObjectNameStrategy) {
|
||||
this.managementObjectNameStrategy = managementObjectNameStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManagementObjectStrategy getManagementObjectStrategy() {
|
||||
return managementObjectStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setManagementObjectStrategy(ManagementObjectStrategy managementObjectStrategy) {
|
||||
this.managementObjectStrategy = managementObjectStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManagementAgent getManagementAgent() {
|
||||
return managementAgent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setManagementAgent(ManagementAgent managementAgent) {
|
||||
this.managementAgent = managementAgent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean manageProcessor(NamedNode definition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void manageObject(Object managedObject) throws Exception {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unmanageObject(Object managedObject) throws Exception {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isManaged(Object managedObject) {
|
||||
// noop
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isManagedName(Object name) {
|
||||
// noop
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CamelContext getCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCamelContext(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(CamelEvent event) throws Exception {
|
||||
if (!eventNotifiers.isEmpty()) {
|
||||
for (EventNotifier notifier : eventNotifiers) {
|
||||
if (notifier.isEnabled(event)) {
|
||||
notifier.notify(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() throws Exception {
|
||||
ObjectHelper.notNull(getCamelContext(), "CamelContext", this);
|
||||
if (!getEventNotifiers().isEmpty()) {
|
||||
getCamelContext().adapt(ExtendedCamelContext.class).setEventNotificationApplicable(true);
|
||||
}
|
||||
for (EventNotifier notifier : eventNotifiers) {
|
||||
// inject CamelContext if the service is aware
|
||||
if (notifier instanceof CamelContextAware) {
|
||||
CamelContextAware aware = (CamelContextAware) notifier;
|
||||
aware.setCamelContext(camelContext);
|
||||
}
|
||||
}
|
||||
ServiceHelper.initService(eventNotifiers, managementAgent);
|
||||
|
||||
if (managementObjectStrategy == null) {
|
||||
managementObjectStrategy = createManagementObjectStrategy();
|
||||
}
|
||||
if (managementObjectStrategy instanceof CamelContextAware) {
|
||||
((CamelContextAware) managementObjectStrategy).setCamelContext(getCamelContext());
|
||||
}
|
||||
|
||||
if (managementObjectNameStrategy == null) {
|
||||
managementObjectNameStrategy = createManagementObjectNameStrategy();
|
||||
}
|
||||
if (managementObjectNameStrategy instanceof CamelContextAware) {
|
||||
((CamelContextAware) managementObjectNameStrategy).setCamelContext(getCamelContext());
|
||||
}
|
||||
ServiceHelper.initService(managementObjectStrategy, managementObjectNameStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception {
|
||||
ServiceHelper.startService(eventNotifiers, managementAgent, managementObjectStrategy, managementObjectNameStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception {
|
||||
ServiceHelper.stopService(managementObjectNameStrategy, managementObjectStrategy, managementAgent, eventNotifiers);
|
||||
}
|
||||
|
||||
protected ManagementObjectNameStrategy createManagementObjectNameStrategy(String domain) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected ManagementObjectStrategy createManagementObjectStrategy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected ManagementObjectNameStrategy createManagementObjectNameStrategy() {
|
||||
String domain = managementAgent != null ? managementAgent.getMBeanObjectDomainName() : null;
|
||||
return createManagementObjectNameStrategy(domain);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.spi.LifecycleStrategy;
|
||||
import org.apache.camel.spi.ManagementStrategy;
|
||||
import org.apache.camel.spi.ManagementStrategyFactory;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Factory for creating non JMX {@link ManagementStrategy}.
|
||||
*/
|
||||
public class DefaultManagementStrategyFactory implements ManagementStrategyFactory {
|
||||
|
||||
@Override
|
||||
public ManagementStrategy create(CamelContext context, Map<String, Object> properties) throws Exception {
|
||||
return new DefaultManagementStrategy(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LifecycleStrategy createLifecycle(CamelContext context) throws Exception {
|
||||
// not in use for non JMX
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupManagement(CamelContext camelContext, ManagementStrategy strategy, LifecycleStrategy lifecycle) {
|
||||
camelContext.setManagementStrategy(strategy);
|
||||
|
||||
if (!camelContext.getLifecycleStrategies().isEmpty()) {
|
||||
// camel-spring/camel-blueprint may re-initialize JMX during startup,
|
||||
// so remove any previous JMX initialized lifecycle strategy
|
||||
camelContext.getLifecycleStrategies().removeIf(s -> s.getClass().getName().startsWith("org.apache.camel.management"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.api.management.ManagedAttribute;
|
||||
import org.apache.camel.api.management.ManagedResource;
|
||||
import org.apache.camel.spi.MessageHistoryFactory;
|
||||
import org.apache.camel.support.DefaultMessageHistory;
|
||||
import org.apache.camel.support.PatternHelper;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
|
||||
@ManagedResource(description = "Managed MessageHistoryFactory")
|
||||
public class DefaultMessageHistoryFactory extends ServiceSupport implements MessageHistoryFactory {
|
||||
|
||||
private CamelContext camelContext;
|
||||
private boolean copyMessage;
|
||||
private String nodePattern;
|
||||
private volatile String[] nodePatternParts;
|
||||
|
||||
@Override
|
||||
public CamelContext getCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCamelContext(CamelContext camelContext) {
|
||||
this.camelContext = camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageHistory newMessageHistory(String routeId, NamedNode node, long timestamp, Exchange exchange) {
|
||||
if (nodePatternParts != null) {
|
||||
String name = node.getShortName();
|
||||
for (String part : nodePatternParts) {
|
||||
boolean match = PatternHelper.matchPattern(name, part);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Message msg = null;
|
||||
if (copyMessage) {
|
||||
msg = exchange.getMessage().copy();
|
||||
}
|
||||
|
||||
return new DefaultMessageHistory(routeId, node, timestamp, msg);
|
||||
}
|
||||
|
||||
@ManagedAttribute(description = "Whether message history is enabled")
|
||||
public boolean isEnabled() {
|
||||
return camelContext != null ? camelContext.isMessageHistory() : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ManagedAttribute(description = "Whether a copy of the message is included in the message history")
|
||||
public boolean isCopyMessage() {
|
||||
return copyMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ManagedAttribute(description = "Whether a copy of the message is included in the message history")
|
||||
public void setCopyMessage(boolean copyMessage) {
|
||||
this.copyMessage = copyMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ManagedAttribute(description = "Pattern to filter EIPs")
|
||||
public String getNodePattern() {
|
||||
return nodePattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ManagedAttribute(description = "Pattern to filter EIPs")
|
||||
public void setNodePattern(String nodePattern) {
|
||||
this.nodePattern = nodePattern;
|
||||
if (nodePattern != null) {
|
||||
this.nodePatternParts = nodePattern.split(",");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.NamedNode;
|
||||
import org.apache.camel.spi.NodeIdFactory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Default id factory.
|
||||
*/
|
||||
public class DefaultNodeIdFactory implements NodeIdFactory {
|
||||
|
||||
protected static Map<String, AtomicInteger> nodeCounters = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public String createId(NamedNode definition) {
|
||||
String key = definition.getShortName();
|
||||
return key + getNodeCounter(key).incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the counter for the given node key, lazily creating one if necessary
|
||||
*/
|
||||
protected static AtomicInteger getNodeCounter(String key) {
|
||||
return nodeCounters.computeIfAbsent(key, k -> new AtomicInteger(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for test purposes that allows tests to start clean (made protected
|
||||
* to ensure that it is not called accidentally)
|
||||
*/
|
||||
protected static void resetAllCounters() {
|
||||
for (AtomicInteger counter : nodeCounters.values()) {
|
||||
counter.set(0);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,446 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.NonManagedService;
|
||||
import org.apache.camel.impl.scan.AnnotatedWithAnyPackageScanFilter;
|
||||
import org.apache.camel.impl.scan.AnnotatedWithPackageScanFilter;
|
||||
import org.apache.camel.impl.scan.AssignableToPackageScanFilter;
|
||||
import org.apache.camel.impl.scan.CompositePackageScanFilter;
|
||||
import org.apache.camel.spi.PackageScanClassResolver;
|
||||
import org.apache.camel.spi.PackageScanFilter;
|
||||
import org.apache.camel.support.LRUCacheFactory;
|
||||
import org.apache.camel.util.IOHelper;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
/**
|
||||
* Default implement of {@link PackageScanClassResolver}
|
||||
*/
|
||||
public class DefaultPackageScanClassResolver extends BasePackageScanResolver implements PackageScanClassResolver, NonManagedService {
|
||||
|
||||
private volatile Map<String, List<String>> jarCache;
|
||||
private Set<PackageScanFilter> scanFilters;
|
||||
|
||||
@Override
|
||||
public void addFilter(PackageScanFilter filter) {
|
||||
if (scanFilters == null) {
|
||||
scanFilters = new LinkedHashSet<>();
|
||||
}
|
||||
scanFilters.add(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFilter(PackageScanFilter filter) {
|
||||
if (scanFilters != null) {
|
||||
scanFilters.remove(filter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> findAnnotated(Class<? extends Annotation> annotation, String... packageNames) {
|
||||
if (packageNames == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Searching for annotations of {} in packages: {}", annotation.getName(), Arrays.asList(packageNames));
|
||||
}
|
||||
|
||||
PackageScanFilter test = getCompositeFilter(new AnnotatedWithPackageScanFilter(annotation, true));
|
||||
Set<Class<?>> classes = new LinkedHashSet<>();
|
||||
for (String pkg : packageNames) {
|
||||
find(test, pkg, classes);
|
||||
}
|
||||
|
||||
log.debug("Found: {}", classes);
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> findAnnotated(Set<Class<? extends Annotation>> annotations, String... packageNames) {
|
||||
if (packageNames == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Searching for annotations of {} in packages: {}", annotations, Arrays.asList(packageNames));
|
||||
}
|
||||
|
||||
PackageScanFilter test = getCompositeFilter(new AnnotatedWithAnyPackageScanFilter(annotations, true));
|
||||
Set<Class<?>> classes = new LinkedHashSet<>();
|
||||
for (String pkg : packageNames) {
|
||||
find(test, pkg, classes);
|
||||
}
|
||||
|
||||
log.debug("Found: {}", classes);
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> findImplementations(Class<?> parent, String... packageNames) {
|
||||
if (packageNames == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Searching for implementations of {} in packages: {}", parent.getName(), Arrays.asList(packageNames));
|
||||
}
|
||||
|
||||
PackageScanFilter test = getCompositeFilter(new AssignableToPackageScanFilter(parent));
|
||||
Set<Class<?>> classes = new LinkedHashSet<>();
|
||||
for (String pkg : packageNames) {
|
||||
find(test, pkg, classes);
|
||||
}
|
||||
|
||||
log.debug("Found: {}", classes);
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> findByFilter(PackageScanFilter filter, String... packageNames) {
|
||||
if (packageNames == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<Class<?>> classes = new LinkedHashSet<>();
|
||||
for (String pkg : packageNames) {
|
||||
find(filter, pkg, classes);
|
||||
}
|
||||
|
||||
log.debug("Found: {}", classes);
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
protected void find(PackageScanFilter test, String packageName, Set<Class<?>> classes) {
|
||||
packageName = packageName.replace('.', '/');
|
||||
|
||||
Set<ClassLoader> set = getClassLoaders();
|
||||
|
||||
for (ClassLoader classLoader : set) {
|
||||
find(test, packageName, classLoader, classes);
|
||||
}
|
||||
}
|
||||
|
||||
protected void find(PackageScanFilter test, String packageName, ClassLoader loader, Set<Class<?>> classes) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Searching for: {} in package: {} using classloader: {}",
|
||||
new Object[]{test, packageName, loader.getClass().getName()});
|
||||
}
|
||||
|
||||
Enumeration<URL> urls;
|
||||
try {
|
||||
urls = getResources(loader, packageName);
|
||||
if (!urls.hasMoreElements()) {
|
||||
log.trace("No URLs returned by classloader");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
log.warn("Cannot read package: {}", packageName, ioe);
|
||||
return;
|
||||
}
|
||||
|
||||
while (urls.hasMoreElements()) {
|
||||
URL url = null;
|
||||
try {
|
||||
url = urls.nextElement();
|
||||
log.trace("URL from classloader: {}", url);
|
||||
|
||||
url = customResourceLocator(url);
|
||||
|
||||
String urlPath = url.getFile();
|
||||
urlPath = URLDecoder.decode(urlPath, "UTF-8");
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Decoded urlPath: {} with protocol: {}", urlPath, url.getProtocol());
|
||||
}
|
||||
|
||||
// If it's a file in a directory, trim the stupid file: spec
|
||||
if (urlPath.startsWith("file:")) {
|
||||
// file path can be temporary folder which uses characters that the URLDecoder decodes wrong
|
||||
// for example + being decoded to something else (+ can be used in temp folders on Mac OS)
|
||||
// to remedy this then create new path without using the URLDecoder
|
||||
try {
|
||||
urlPath = new URI(url.getFile()).getPath();
|
||||
} catch (URISyntaxException e) {
|
||||
// fallback to use as it was given from the URLDecoder
|
||||
// this allows us to work on Windows if users have spaces in paths
|
||||
}
|
||||
|
||||
if (urlPath.startsWith("file:")) {
|
||||
urlPath = urlPath.substring(5);
|
||||
}
|
||||
}
|
||||
|
||||
// osgi bundles should be skipped
|
||||
if (url.toString().startsWith("bundle:") || urlPath.startsWith("bundle:")) {
|
||||
log.trace("Skipping OSGi bundle: {}", url);
|
||||
continue;
|
||||
}
|
||||
|
||||
// bundle resource should be skipped
|
||||
if (url.toString().startsWith("bundleresource:") || urlPath.startsWith("bundleresource:")) {
|
||||
log.trace("Skipping bundleresource: {}", url);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Else it's in a JAR, grab the path to the jar
|
||||
if (urlPath.indexOf('!') > 0) {
|
||||
urlPath = urlPath.substring(0, urlPath.indexOf('!'));
|
||||
}
|
||||
|
||||
log.trace("Scanning for classes in: {} matching criteria: {}", urlPath, test);
|
||||
|
||||
File file = new File(urlPath);
|
||||
if (file.isDirectory()) {
|
||||
log.trace("Loading from directory using file: {}", file);
|
||||
loadImplementationsInDirectory(test, packageName, file, classes);
|
||||
} else {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
if (urlPath.startsWith("http:") || urlPath.startsWith("https:")
|
||||
|| urlPath.startsWith("sonicfs:")
|
||||
|| isAcceptableScheme(urlPath)) {
|
||||
// load resources using http/https, sonicfs and other acceptable scheme
|
||||
// sonic ESB requires to be loaded using a regular URLConnection
|
||||
log.trace("Loading from jar using url: {}", urlPath);
|
||||
URL urlStream = new URL(urlPath);
|
||||
URLConnection con = urlStream.openConnection();
|
||||
// disable cache mainly to avoid jar file locking on Windows
|
||||
con.setUseCaches(false);
|
||||
stream = con.getInputStream();
|
||||
} else {
|
||||
log.trace("Loading from jar using file: {}", file);
|
||||
stream = new FileInputStream(file);
|
||||
}
|
||||
|
||||
// only create jar cache on-demand when needed
|
||||
if (jarCache == null) {
|
||||
// use a soft cache so it can be claimed if needed
|
||||
jarCache = LRUCacheFactory.newLRUSoftCache(1000);
|
||||
}
|
||||
|
||||
loadImplementationsInJar(test, packageName, stream, urlPath, classes, jarCache);
|
||||
} finally {
|
||||
IOHelper.close(stream);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// use debug logging to avoid being to noisy in logs
|
||||
log.debug("Cannot read entries in url: {}", url, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PackageScanFilter getCompositeFilter(PackageScanFilter filter) {
|
||||
if (scanFilters != null) {
|
||||
CompositePackageScanFilter composite = new CompositePackageScanFilter(scanFilters);
|
||||
composite.addFilter(filter);
|
||||
return composite;
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds matches in a physical directory on a filesystem. Examines all files
|
||||
* within a directory - if the File object is not a directory, and ends with
|
||||
* <i>.class</i> the file is loaded and tested to see if it is acceptable
|
||||
* according to the Test. Operates recursively to find classes within a
|
||||
* folder structure matching the package structure.
|
||||
*
|
||||
* @param test a Test used to filter the classes that are discovered
|
||||
* @param parent the package name up to this directory in the package
|
||||
* hierarchy. E.g. if /classes is in the classpath and we wish to
|
||||
* examine files in /classes/org/apache then the values of
|
||||
* <i>parent</i> would be <i>org/apache</i>
|
||||
* @param location a File object representing a directory
|
||||
*/
|
||||
private void loadImplementationsInDirectory(PackageScanFilter test, String parent, File location, Set<Class<?>> classes) {
|
||||
File[] files = location.listFiles();
|
||||
StringBuilder builder;
|
||||
|
||||
for (File file : files) {
|
||||
builder = new StringBuilder(100);
|
||||
String name = file.getName();
|
||||
if (name != null) {
|
||||
name = name.trim();
|
||||
builder.append(parent).append("/").append(name);
|
||||
String packageOrClass = parent == null ? name : builder.toString();
|
||||
|
||||
if (file.isDirectory()) {
|
||||
loadImplementationsInDirectory(test, packageOrClass, file, classes);
|
||||
} else if (name.endsWith(".class")) {
|
||||
addIfMatching(test, packageOrClass, classes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds matching classes within a jar files that contains a folder
|
||||
* structure matching the package structure. If the File is not a JarFile or
|
||||
* does not exist a warning will be logged, but no error will be raised.
|
||||
*
|
||||
* @param test a Test used to filter the classes that are discovered
|
||||
* @param parent the parent package under which classes must be in order to
|
||||
* be considered
|
||||
* @param stream the inputstream of the jar file to be examined for classes
|
||||
* @param urlPath the url of the jar file to be examined for classes
|
||||
* @param classes to add found and matching classes
|
||||
* @param jarCache cache for JARs to speedup loading
|
||||
*/
|
||||
private void loadImplementationsInJar(PackageScanFilter test, String parent, InputStream stream,
|
||||
String urlPath, Set<Class<?>> classes, Map<String, List<String>> jarCache) {
|
||||
ObjectHelper.notNull(classes, "classes");
|
||||
|
||||
List<String> entries = jarCache != null ? jarCache.get(urlPath) : null;
|
||||
if (entries == null) {
|
||||
entries = doLoadJarClassEntries(stream, urlPath);
|
||||
if (jarCache != null) {
|
||||
jarCache.put(urlPath, entries);
|
||||
log.trace("Cached {} JAR with {} entries", urlPath, entries.size());
|
||||
}
|
||||
} else {
|
||||
log.trace("Using cached {} JAR with {} entries", urlPath, entries.size());
|
||||
}
|
||||
|
||||
doLoadImplementationsInJar(test, parent, entries, classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all the class entries from the JAR.
|
||||
*
|
||||
* @param stream the inputstream of the jar file to be examined for classes
|
||||
* @param urlPath the url of the jar file to be examined for classes
|
||||
* @return all the .class entries from the JAR
|
||||
*/
|
||||
protected List<String> doLoadJarClassEntries(InputStream stream, String urlPath) {
|
||||
List<String> entries = new ArrayList<>();
|
||||
|
||||
JarInputStream jarStream = null;
|
||||
try {
|
||||
jarStream = new JarInputStream(stream);
|
||||
|
||||
JarEntry entry;
|
||||
while ((entry = jarStream.getNextJarEntry()) != null) {
|
||||
String name = entry.getName();
|
||||
if (name != null) {
|
||||
name = name.trim();
|
||||
if (!entry.isDirectory() && name.endsWith(".class")) {
|
||||
entries.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
log.warn("Cannot search jar file '" + urlPath + " due to an IOException: " + ioe.getMessage(), ioe);
|
||||
} finally {
|
||||
IOHelper.close(jarStream, urlPath, log);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all the matching implementations from from the JAR entries to the classes.
|
||||
*
|
||||
* @param test a Test used to filter the classes that are discovered
|
||||
* @param parent the parent package under which classes must be in order to be considered
|
||||
* @param entries the .class entries from the JAR
|
||||
* @param classes to add found and matching classes
|
||||
*/
|
||||
private void doLoadImplementationsInJar(PackageScanFilter test, String parent, List<String> entries, Set<Class<?>> classes) {
|
||||
for (String entry : entries) {
|
||||
if (entry.startsWith(parent)) {
|
||||
addIfMatching(test, entry, classes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the class designated by the fully qualified class name provided to
|
||||
* the set of resolved classes if and only if it is approved by the Test
|
||||
* supplied.
|
||||
*
|
||||
* @param test the test used to determine if the class matches
|
||||
* @param fqn the fully qualified name of a class
|
||||
*/
|
||||
protected void addIfMatching(PackageScanFilter test, String fqn, Set<Class<?>> classes) {
|
||||
try {
|
||||
String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
|
||||
Set<ClassLoader> set = getClassLoaders();
|
||||
boolean found = false;
|
||||
for (ClassLoader classLoader : set) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Testing for class {} matches criteria [{}] using classloader: {}", externalName, test, classLoader);
|
||||
}
|
||||
try {
|
||||
Class<?> type = classLoader.loadClass(externalName);
|
||||
log.trace("Loaded the class: {} in classloader: {}", type, classLoader);
|
||||
if (test.matches(type)) {
|
||||
log.trace("Found class: {} which matches the filter in classloader: {}", type, classLoader);
|
||||
classes.add(type);
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
} catch (ClassNotFoundException e) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Cannot find class '" + fqn + "' in classloader: " + classLoader
|
||||
+ ". Reason: " + e.getMessage(), e);
|
||||
}
|
||||
} catch (NoClassDefFoundError e) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Cannot find the class definition '" + fqn + "' in classloader: " + classLoader
|
||||
+ ". Reason: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
log.debug("Cannot find class '{}' in any classloaders: {}", fqn, set);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("Cannot examine class '" + fqn + "' due to a " + e.getClass().getName()
|
||||
+ " with message: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache() {
|
||||
if (jarCache != null) {
|
||||
jarCache.clear();
|
||||
jarCache = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception {
|
||||
clearCache();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.CamelContextAware;
|
||||
import org.apache.camel.NonManagedService;
|
||||
import org.apache.camel.spi.PackageScanResourceResolver;
|
||||
import org.apache.camel.support.ResourceHelper;
|
||||
import org.apache.camel.util.AntPathMatcher;
|
||||
import org.apache.camel.util.IOHelper;
|
||||
import org.apache.camel.util.KeyValueHolder;
|
||||
import org.apache.camel.util.ObjectHelper;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Default implement of {@link PackageScanResourceResolver}
|
||||
*/
|
||||
public class DefaultPackageScanResourceResolver extends BasePackageScanResolver implements PackageScanResourceResolver, NonManagedService, CamelContextAware {
|
||||
|
||||
private static final AntPathMatcher PATH_MATCHER = AntPathMatcher.INSTANCE;
|
||||
|
||||
@Override
|
||||
public Set<String> findResourceNames(String location) throws Exception {
|
||||
Set<KeyValueHolder<String, InputStream>> answer = new LinkedHashSet<>();
|
||||
doFindResources(location, answer);
|
||||
return answer.stream().map(KeyValueHolder::getKey).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Set<InputStream> findResources(String location) throws Exception {
|
||||
Set<KeyValueHolder<String, InputStream>> answer = new LinkedHashSet<>();
|
||||
doFindResources(location, answer);
|
||||
return answer.stream().map(KeyValueHolder::getValue).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
protected void doFindResources(String location, Set<KeyValueHolder<String, InputStream>> resources) throws Exception {
|
||||
// if its a pattern then we need to scan its root path and find
|
||||
// all matching resources using the sub pattern
|
||||
if (PATH_MATCHER.isPattern(location)) {
|
||||
String root = PATH_MATCHER.determineRootDir(location);
|
||||
String subPattern = location.substring(root.length());
|
||||
|
||||
String scheme = ResourceHelper.getScheme(location);
|
||||
if ("file:".equals(scheme)) {
|
||||
// file based scanning
|
||||
root = root.substring(scheme.length());
|
||||
findInFileSystem(new File(root), resources, subPattern);
|
||||
} else {
|
||||
if ("classpath:".equals(scheme)) {
|
||||
root = root.substring(scheme.length());
|
||||
}
|
||||
// assume classpath based scan from root path and find all resources
|
||||
findInClasspath(root, resources, subPattern);
|
||||
}
|
||||
} else {
|
||||
// its a single resource so load it directly
|
||||
InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(getCamelContext(), location);
|
||||
resources.add(new KeyValueHolder<>(location, is));
|
||||
}
|
||||
}
|
||||
|
||||
protected void findInFileSystem(File dir, Set<KeyValueHolder<String, InputStream>> resources, String subPattern) throws Exception {
|
||||
ResourceHelper.findInFileSystem(dir.toPath(), subPattern).forEach(f -> {
|
||||
try {
|
||||
String location = f.toString();
|
||||
resources.add(new KeyValueHolder<>(location, Files.newInputStream(f)));
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void findInClasspath(String packageName, Set<KeyValueHolder<String, InputStream>> resources, String subPattern) {
|
||||
packageName = packageName.replace('.', '/');
|
||||
// If the URL is a jar, the URLClassloader.getResources() seems to require a trailing slash.
|
||||
// The trailing slash is harmless for other URLs
|
||||
if (!packageName.endsWith("/")) {
|
||||
packageName = packageName + "/";
|
||||
}
|
||||
|
||||
Set<ClassLoader> set = getClassLoaders();
|
||||
|
||||
for (ClassLoader classLoader : set) {
|
||||
doFind(packageName, classLoader, resources, subPattern);
|
||||
}
|
||||
}
|
||||
|
||||
protected void doFind(String packageName, ClassLoader classLoader, Set<KeyValueHolder<String, InputStream>> resources, String subPattern) {
|
||||
Enumeration<URL> urls;
|
||||
try {
|
||||
urls = getResources(classLoader, packageName);
|
||||
if (!urls.hasMoreElements()) {
|
||||
log.trace("No URLs returned by classloader");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
log.warn("Cannot read package: {}", packageName, ioe);
|
||||
return;
|
||||
}
|
||||
|
||||
while (urls.hasMoreElements()) {
|
||||
URL url = null;
|
||||
try {
|
||||
url = urls.nextElement();
|
||||
log.trace("URL from classloader: {}", url);
|
||||
|
||||
url = customResourceLocator(url);
|
||||
|
||||
String urlPath = url.getFile();
|
||||
urlPath = URLDecoder.decode(urlPath, "UTF-8");
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Decoded urlPath: {} with protocol: {}", urlPath, url.getProtocol());
|
||||
}
|
||||
|
||||
// If it's a file in a directory, trim the stupid file: spec
|
||||
if (urlPath.startsWith("file:")) {
|
||||
// file path can be temporary folder which uses characters that the URLDecoder decodes wrong
|
||||
// for example + being decoded to something else (+ can be used in temp folders on Mac OS)
|
||||
// to remedy this then create new path without using the URLDecoder
|
||||
try {
|
||||
urlPath = new URI(url.getFile()).getPath();
|
||||
} catch (URISyntaxException e) {
|
||||
// fallback to use as it was given from the URLDecoder
|
||||
// this allows us to work on Windows if users have spaces in paths
|
||||
}
|
||||
|
||||
if (urlPath.startsWith("file:")) {
|
||||
urlPath = urlPath.substring(5);
|
||||
}
|
||||
}
|
||||
|
||||
// osgi bundles should be skipped
|
||||
if (url.toString().startsWith("bundle:") || urlPath.startsWith("bundle:")) {
|
||||
log.trace("Skipping OSGi bundle: {}", url);
|
||||
continue;
|
||||
}
|
||||
|
||||
// bundle resource should be skipped
|
||||
if (url.toString().startsWith("bundleresource:") || urlPath.startsWith("bundleresource:")) {
|
||||
log.trace("Skipping bundleresource: {}", url);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Else it's in a JAR, grab the path to the jar
|
||||
if (urlPath.indexOf('!') > 0) {
|
||||
urlPath = urlPath.substring(0, urlPath.indexOf('!'));
|
||||
}
|
||||
|
||||
log.trace("Scanning for resources in: {} matching pattern: {}", urlPath, subPattern);
|
||||
|
||||
File file = new File(urlPath);
|
||||
if (file.isDirectory()) {
|
||||
log.trace("Loading from directory using file: {}", file);
|
||||
loadImplementationsInDirectory(subPattern, packageName, file, resources);
|
||||
} else {
|
||||
InputStream stream;
|
||||
if (urlPath.startsWith("http:") || urlPath.startsWith("https:")
|
||||
|| urlPath.startsWith("sonicfs:")
|
||||
|| isAcceptableScheme(urlPath)) {
|
||||
// load resources using http/https, sonicfs and other acceptable scheme
|
||||
// sonic ESB requires to be loaded using a regular URLConnection
|
||||
log.trace("Loading from jar using url: {}", urlPath);
|
||||
URL urlStream = new URL(urlPath);
|
||||
URLConnection con = urlStream.openConnection();
|
||||
// disable cache mainly to avoid jar file locking on Windows
|
||||
con.setUseCaches(false);
|
||||
stream = con.getInputStream();
|
||||
} else {
|
||||
log.trace("Loading from jar using file: {}", file);
|
||||
stream = new FileInputStream(file);
|
||||
}
|
||||
|
||||
loadImplementationsInJar(packageName, subPattern, stream, urlPath, resources);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// use debug logging to avoid being to noisy in logs
|
||||
log.debug("Cannot read entries in url: {}", url, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds matching classes within a jar files that contains a folder
|
||||
* structure matching the package structure. If the File is not a JarFile or
|
||||
* does not exist a warning will be logged, but no error will be raised.
|
||||
*
|
||||
* @param stream the inputstream of the jar file to be examined for classes
|
||||
* @param urlPath the url of the jar file to be examined for classes
|
||||
*/
|
||||
private void loadImplementationsInJar(String packageName, String subPattern, InputStream stream,
|
||||
String urlPath, Set<KeyValueHolder<String, InputStream>> resources) {
|
||||
List<String> entries = new ArrayList<>();
|
||||
|
||||
JarInputStream jarStream = null;
|
||||
try {
|
||||
jarStream = new JarInputStream(stream);
|
||||
|
||||
JarEntry entry;
|
||||
while ((entry = jarStream.getNextJarEntry()) != null) {
|
||||
String name = entry.getName();
|
||||
if (name != null) {
|
||||
name = name.trim();
|
||||
if (!entry.isDirectory() && !name.endsWith(".class")) {
|
||||
// name is FQN so it must start with package name
|
||||
if (name.startsWith(packageName)) {
|
||||
entries.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
log.warn("Cannot search jar file '" + urlPath + " due to an IOException: " + ioe.getMessage(), ioe);
|
||||
} finally {
|
||||
IOHelper.close(jarStream, urlPath, log);
|
||||
}
|
||||
|
||||
for (String name : entries) {
|
||||
String shortName = name.substring(packageName.length());
|
||||
boolean match = PATH_MATCHER.match(subPattern, shortName);
|
||||
log.debug("Found resource: {} matching pattern: {} -> {}", shortName, subPattern, match);
|
||||
if (match) {
|
||||
// use fqn name to load resource
|
||||
InputStream is = getCamelContext().getClassResolver().loadResourceAsStream(name);
|
||||
if (is != null) {
|
||||
resources.add(new KeyValueHolder<>(name, is));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds matches in a physical directory on a filesystem. Examines all files
|
||||
* within a directory - if the File object is not a directory, and ends with
|
||||
* <i>.class</i> the file is loaded and tested to see if it is acceptable
|
||||
* according to the Test. Operates recursively to find classes within a
|
||||
* folder structure matching the package structure.
|
||||
*
|
||||
* @param parent the package name up to this directory in the package
|
||||
* hierarchy. E.g. if /classes is in the classpath and we wish to
|
||||
* examine files in /classes/org/apache then the values of
|
||||
* <i>parent</i> would be <i>org/apache</i>
|
||||
* @param location a File object representing a directory
|
||||
*/
|
||||
private void loadImplementationsInDirectory(String subPattern, String parent, File location, Set<KeyValueHolder<String, InputStream>> resources) throws FileNotFoundException {
|
||||
File[] files = location.listFiles();
|
||||
if (files == null || files.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder builder;
|
||||
for (File file : files) {
|
||||
builder = new StringBuilder(100);
|
||||
String name = file.getName();
|
||||
name = name.trim();
|
||||
builder.append(parent).append("/").append(name);
|
||||
String packageOrClass = parent == null ? name : builder.toString();
|
||||
|
||||
if (file.isDirectory()) {
|
||||
loadImplementationsInDirectory(subPattern, packageOrClass, file, resources);
|
||||
} else if (file.isFile() && file.exists() && !name.endsWith(".class")) {
|
||||
boolean match = PATH_MATCHER.match(subPattern, name);
|
||||
log.debug("Found resource: {} matching pattern: {} -> {}", name, subPattern, match);
|
||||
if (match) {
|
||||
InputStream is = new FileInputStream(file);
|
||||
resources.add(new KeyValueHolder<>(name, is));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() throws Exception {
|
||||
ObjectHelper.notNull(getCamelContext(), "CamelContext", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception {
|
||||
// noop
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.processor.SendDynamicProcessor;
|
||||
import org.apache.camel.processor.UnitOfWorkProducer;
|
||||
import org.apache.camel.spi.FactoryFinder;
|
||||
import org.apache.camel.spi.ProcessorFactory;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Default {@link ProcessorFactory} that supports using 3rd party Camel components to implement the EIP {@link Processor}.
|
||||
* <p/>
|
||||
* The component should use the {@link FactoryFinder} SPI to specify a file with the name of the EIP model in the
|
||||
* directory of {@link #RESOURCE_PATH}. The file should contain a property with key <tt>class</tt> that refers
|
||||
* to the name of the {@link ProcessorFactory} the Camel component implement, which gets called for creating
|
||||
* the {@link Processor}s for the EIP.
|
||||
* <p/>
|
||||
* The Hystrix EIP is such an example where the circuit breaker EIP (CircuitBreakerDefinition) is implemented
|
||||
* in the <tt>camel-hystrix</tt> component.
|
||||
*/
|
||||
public class DefaultProcessorFactory implements ProcessorFactory {
|
||||
|
||||
public static final String RESOURCE_PATH = "META-INF/services/org/apache/camel/model/";
|
||||
|
||||
@Override
|
||||
public Processor createChildProcessor(Route route, NamedNode definition, boolean mandatory) throws Exception {
|
||||
String name = definition.getClass().getSimpleName();
|
||||
FactoryFinder finder = route.getCamelContext().adapt(ExtendedCamelContext.class).getFactoryFinder(RESOURCE_PATH);
|
||||
try {
|
||||
if (finder != null) {
|
||||
Object object = finder.newInstance(name);
|
||||
if (object instanceof ProcessorFactory) {
|
||||
ProcessorFactory pc = (ProcessorFactory) object;
|
||||
return pc.createChildProcessor(route, definition, mandatory);
|
||||
}
|
||||
}
|
||||
} catch (NoFactoryAvailableException e) {
|
||||
// ignore there is no custom factory
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Processor createProcessor(Route route, NamedNode definition) throws Exception {
|
||||
String name = definition.getClass().getSimpleName();
|
||||
FactoryFinder finder = route.getCamelContext().adapt(ExtendedCamelContext.class).getFactoryFinder(RESOURCE_PATH);
|
||||
if (finder != null) {
|
||||
ProcessorFactory pc = finder.newInstance(name, ProcessorFactory.class).orElse(null);
|
||||
if (pc != null) {
|
||||
return pc.createProcessor(route, definition);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Processor createProcessor(CamelContext camelContext, String definitionName, Map<String, Object> args) throws Exception {
|
||||
if ("SendDynamicProcessor".equals(definitionName)) {
|
||||
String uri = (String) args.get("uri");
|
||||
Expression expression = (Expression) args.get("expression");
|
||||
ExchangePattern exchangePattern = (ExchangePattern) args.get("exchangePattern");
|
||||
SendDynamicProcessor processor = new SendDynamicProcessor(uri, expression);
|
||||
processor.setCamelContext(camelContext);
|
||||
if (exchangePattern != null) {
|
||||
processor.setPattern(exchangePattern);
|
||||
}
|
||||
return processor;
|
||||
} else if ("UnitOfWorkProducer".equals(definitionName)) {
|
||||
Producer producer = (Producer) args.get("producer");
|
||||
return new UnitOfWorkProducer(producer);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,418 @@
|
||||
/*
|
||||
* 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.impl.engine;
|
||||
|
||||
import org.apache.camel.*;
|
||||
import org.apache.camel.processor.CamelInternalProcessor;
|
||||
import org.apache.camel.processor.SharedCamelInternalProcessor;
|
||||
import org.apache.camel.spi.EndpointUtilizationStatistics;
|
||||
import org.apache.camel.spi.ProducerCache;
|
||||
import org.apache.camel.support.CamelContextHelper;
|
||||
import org.apache.camel.support.EventHelper;
|
||||
import org.apache.camel.support.service.ServiceHelper;
|
||||
import org.apache.camel.support.service.ServiceSupport;
|
||||
import org.apache.camel.util.StopWatch;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ProducerCache}.
|
||||
*/
|
||||
public class DefaultProducerCache extends ServiceSupport implements ProducerCache {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultProducerCache.class);
|
||||
private static final long ACQUIRE_WAIT_TIME = 30000;
|
||||
|
||||
private final ExtendedCamelContext camelContext;
|
||||
private final ProducerServicePool producers;
|
||||
private final Object source;
|
||||
private final SharedCamelInternalProcessor internalProcessor;
|
||||
|
||||
private EndpointUtilizationStatistics statistics;
|
||||
private boolean eventNotifierEnabled = true;
|
||||
private boolean extendedStatistics;
|
||||
private int maxCacheSize;
|
||||
|
||||
public DefaultProducerCache(Object source, CamelContext camelContext, int cacheSize) {
|
||||
this.source = source;
|
||||
this.camelContext = (ExtendedCamelContext) camelContext;
|
||||
this.maxCacheSize = cacheSize <= 0 ? CamelContextHelper.getMaximumCachePoolSize(camelContext) : cacheSize;
|
||||
if (cacheSize >= 0) {
|
||||
this.producers = createServicePool(camelContext, maxCacheSize);
|
||||
} else {
|
||||
// no cache then empty
|
||||
this.producers = null;
|
||||
}
|
||||
|
||||
// only if JMX is enabled
|
||||
if (camelContext.getManagementStrategy() != null && camelContext.getManagementStrategy().getManagementAgent() != null) {
|
||||
this.extendedStatistics = camelContext.getManagementStrategy().getManagementAgent().getStatisticsLevel().isExtended();
|
||||
} else {
|
||||
this.extendedStatistics = false;
|
||||
}
|
||||
|
||||
// internal processor used for sending
|
||||
internalProcessor = new SharedCamelInternalProcessor(camelContext, new CamelInternalProcessor.UnitOfWorkProcessorAdvice(null, camelContext));
|
||||
}
|
||||
|
||||
protected ProducerServicePool createServicePool(CamelContext camelContext, int cacheSize) {
|
||||
return new ProducerServicePool(Endpoint::createAsyncProducer, Producer::getEndpoint, cacheSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEventNotifierEnabled() {
|
||||
return eventNotifierEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEventNotifierEnabled(boolean eventNotifierEnabled) {
|
||||
this.eventNotifierEnabled = eventNotifierEnabled;
|
||||
}
|
||||
|
||||
public boolean isExtendedStatistics() {
|
||||
return extendedStatistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether extended JMX statistics is enabled for {@link EndpointUtilizationStatistics}
|
||||
*/
|
||||
public void setExtendedStatistics(boolean extendedStatistics) {
|
||||
this.extendedStatistics = extendedStatistics;
|
||||
}
|
||||
|
||||
public CamelContext getCamelContext() {
|
||||
return camelContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncProducer acquireProducer(Endpoint endpoint) {
|
||||
try {
|
||||
AsyncProducer producer = producers.acquire(endpoint);
|
||||
if (statistics != null) {
|
||||
statistics.onHit(endpoint.getEndpointUri());
|
||||
}
|
||||
// if producer is starting then wait for it to be ready
|
||||
if (producer instanceof StatefulService) {
|
||||
StatefulService ss = (StatefulService) producer;
|
||||
if (ss.isStarting()) {
|
||||
LOG.trace("Waiting for producer to finish starting: {}", producer);
|
||||
StopWatch watch = new StopWatch();
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
done = !ss.isStarting() || watch.taken() > ACQUIRE_WAIT_TIME;
|
||||
if (!done) {
|
||||
Thread.sleep(5);
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Waiting {} ms for producer to finish starting: {} state: {}", watch.taken(), producer, ss.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Waited {} ms for producer to finish starting: {} state: {}", watch.taken(), producer, ss.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
return producer;
|
||||
} catch (Throwable e) {
|
||||
throw new FailedToCreateProducerException(endpoint, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseProducer(Endpoint endpoint, AsyncProducer producer) {
|
||||
producers.release(endpoint, producer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange send(Endpoint endpoint, Exchange exchange, Processor resultProcessor) {
|
||||
if (camelContext.isStopped()) {
|
||||
exchange.setException(new RejectedExecutionException("CamelContext is stopped"));
|
||||
return exchange;
|
||||
}
|
||||
|
||||
AsyncProducer producer = acquireProducer(endpoint);
|
||||
try {
|
||||
// now lets dispatch
|
||||
LOG.debug(">>>> {} {}", endpoint, exchange);
|
||||
|
||||
// set property which endpoint we send to
|
||||
exchange.setProperty(Exchange.TO_ENDPOINT, endpoint.getEndpointUri());
|
||||
|
||||
// send the exchange using the processor
|
||||
StopWatch watch = null;
|
||||
try {
|
||||
if (eventNotifierEnabled && camelContext.isEventNotificationApplicable()) {
|
||||
boolean sending = EventHelper.notifyExchangeSending(exchange.getContext(), exchange, endpoint);
|
||||
if (sending) {
|
||||
watch = new StopWatch();
|
||||
}
|
||||
}
|
||||
|
||||
// invoke the synchronous method
|
||||
internalProcessor.process(exchange, producer, resultProcessor);
|
||||
|
||||
} catch (Throwable e) {
|
||||
// ensure exceptions is caught and set on the exchange
|
||||
exchange.setException(e);
|
||||
} finally {
|
||||
// emit event that the exchange was sent to the endpoint
|
||||
if (watch != null) {
|
||||
long timeTaken = watch.taken();
|
||||
EventHelper.notifyExchangeSent(exchange.getContext(), exchange, endpoint, timeTaken);
|
||||
}
|
||||
}
|
||||
return exchange;
|
||||
} finally {
|
||||
releaseProducer(endpoint, producer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously sends an exchange to an endpoint using a supplied
|
||||
* {@link Processor} to populate the exchange
|
||||
* <p>
|
||||
* This method will <b>neither</b> throw an exception <b>nor</b> complete future exceptionally.
|
||||
* If processing of the given Exchange failed then the exception is stored on the return Exchange
|
||||
*
|
||||
* @param endpoint the endpoint to send the exchange to
|
||||
* @param pattern the message {@link ExchangePattern} such as
|
||||
* {@link ExchangePattern#InOnly} or {@link ExchangePattern#InOut}
|
||||
* @param processor the transformer used to populate the new exchange
|
||||
* @param resultProcessor a processor to process the exchange when the send is complete.
|
||||
* @param future the preexisting future to complete when processing is done or null if to create new one
|
||||
* @return future that completes with exchange when processing is done. Either passed into future parameter
|
||||
* or new one if parameter was null
|
||||
*/
|
||||
@Deprecated
|
||||
public CompletableFuture<Exchange> asyncSend(Endpoint endpoint,
|
||||
ExchangePattern pattern,
|
||||
Processor processor,
|
||||
Processor resultProcessor,
|
||||
CompletableFuture<Exchange> future) {
|
||||
return asyncSendExchange(endpoint, pattern, processor, resultProcessor, null, future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Exchange> asyncSendExchange(Endpoint endpoint,
|
||||
ExchangePattern pattern,
|
||||
Processor processor,
|
||||
Processor resultProcessor,
|
||||
Exchange exchange,
|
||||
CompletableFuture<Exchange> future) {
|
||||
if (exchange == null) {
|
||||
exchange = pattern != null ? endpoint.createExchange(pattern) : endpoint.createExchange();
|
||||
}
|
||||
return doAsyncSendExchange(endpoint, processor, resultProcessor, exchange, future);
|
||||
}
|
||||
|
||||
protected CompletableFuture<Exchange> doAsyncSendExchange(Endpoint endpoint,
|
||||
Processor processor,
|
||||
Processor resultProcessor,
|
||||
Exchange exchange,
|
||||
CompletableFuture<Exchange> f) {
|
||||
CompletableFuture<Exchange> future = f != null ? f : new CompletableFuture<>();
|
||||
AsyncProducerCallback cb = (p, e, c) -> asyncDispatchExchange(endpoint, p, resultProcessor, e, c);
|
||||
try {
|
||||
if (processor instanceof AsyncProcessor) {
|
||||
((AsyncProcessor) processor).process(exchange,
|
||||
doneSync -> doInAsyncProducer(endpoint, exchange, ds -> future.complete(exchange), cb));
|
||||
} else {
|
||||
if (processor != null) {
|
||||
processor.process(exchange);
|
||||
}
|
||||
doInAsyncProducer(endpoint, exchange, ds -> future.complete(exchange), cb);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// populate failed so return
|
||||
exchange.setException(e);
|
||||
future.complete(exchange);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doInAsyncProducer(Endpoint endpoint,
|
||||
Exchange exchange,
|
||||
AsyncCallback callback,
|
||||
AsyncProducerCallback producerCallback) {
|
||||
|
||||
AsyncProducer producer;
|
||||
try {
|
||||
// get the producer and we do not mind if its pooled as we can handle returning it back to the pool
|
||||
producer = acquireProducer(endpoint);
|
||||
|
||||
if (producer == null) {
|
||||
if (isStopped()) {
|
||||
LOG.warn("Ignoring exchange sent after processor is stopped: {}", exchange);
|
||||
callback.done(true);
|
||||
return true;
|
||||
} else {
|
||||
exchange.setException(new IllegalStateException("No producer, this processor has not been started: " + this));
|
||||
callback.done(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
exchange.setException(e);
|
||||
callback.done(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
// record timing for sending the exchange using the producer
|
||||
StopWatch watch;
|
||||
if (eventNotifierEnabled && camelContext.isEventNotificationApplicable()) {
|
||||
boolean sending = EventHelper.notifyExchangeSending(exchange.getContext(), exchange, endpoint);
|
||||
if (sending) {
|
||||
watch = new StopWatch();
|
||||
} else {
|
||||
watch = null;
|
||||
}
|
||||
} else {
|
||||
watch = null;
|
||||
}
|
||||
|
||||
// invoke the callback
|
||||
return producerCallback.doInAsyncProducer(producer, exchange, doneSync -> {
|
||||
try {
|
||||
if (watch != null) {
|
||||
long timeTaken = watch.taken();
|
||||
// emit event that the exchange was sent to the endpoint
|
||||
EventHelper.notifyExchangeSent(exchange.getContext(), exchange, endpoint, timeTaken);
|
||||
}
|
||||
|
||||
// release back to the pool
|
||||
releaseProducer(endpoint, producer);
|
||||
} finally {
|
||||
callback.done(doneSync);
|
||||
}
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
// ensure exceptions is caught and set on the exchange
|
||||
if (exchange != null) {
|
||||
exchange.setException(e);
|
||||
}
|
||||
callback.done(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean asyncDispatchExchange(Endpoint endpoint, AsyncProducer producer,
|
||||
Processor resultProcessor, Exchange exchange, AsyncCallback callback) {
|
||||
// now lets dispatch
|
||||
LOG.debug(">>>> {} {}", endpoint, exchange);
|
||||
|
||||
// set property which endpoint we send to
|
||||
exchange.setProperty(Exchange.TO_ENDPOINT, endpoint.getEndpointUri());
|
||||
|
||||
// send the exchange using the processor
|
||||
try {
|
||||
if (eventNotifierEnabled && camelContext.isEventNotificationApplicable()) {
|
||||
callback = new EventNotifierCallback(callback, exchange, endpoint);
|
||||
}
|
||||
// invoke the asynchronous method
|
||||
return internalProcessor.process(exchange, callback, producer, resultProcessor);
|
||||
} catch (Throwable e) {
|
||||
// ensure exceptions is caught and set on the exchange
|
||||
exchange.setException(e);
|
||||
callback.done(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() throws Exception {
|
||||
if (extendedStatistics) {
|
||||
int max = maxCacheSize == 0 ? CamelContextHelper.getMaximumCachePoolSize(camelContext) : maxCacheSize;
|
||||
statistics = new DefaultEndpointUtilizationStatistics(max);
|
||||
}
|
||||
ServiceHelper.initService(producers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception {
|
||||
if (statistics != null) {
|
||||
statistics.clear();
|
||||
}
|
||||
ServiceHelper.startService(producers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception {
|
||||
ServiceHelper.stopService(producers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doShutdown() throws Exception {
|
||||
ServiceHelper.stopAndShutdownServices(producers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
int size = producers != null ? producers.size() : 0;
|
||||
|
||||
LOG.trace("size = {}", size);
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCapacity() {
|
||||
return maxCacheSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void purge() {
|
||||
try {
|
||||
if (producers != null) {
|
||||
producers.stop();
|
||||
producers.start();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.debug("Error restarting producers", e);
|
||||
}
|
||||
if (statistics != null) {
|
||||
statistics.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
if (producers != null) {
|
||||
producers.cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EndpointUtilizationStatistics getEndpointUtilizationStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProducerCache for source: " + source + ", capacity: " + getCapacity();
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user