ENTAXY-248 release 1.8.1

This commit is contained in:
2022-02-28 15:20:38 +03:00
parent 4d274c4fcc
commit c826adf1db
1958 changed files with 195926 additions and 10280 deletions

201
underlying/LICENSE.txt Normal file
View 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.

View 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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

View File

@ -0,0 +1,2 @@
# Generated by camel build tools - do NOT edit this file!
class=org.apache.camel.component.properties.PropertiesComponent

View File

@ -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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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" : "");
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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>

View File

@ -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()]);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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.");
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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() + "]";
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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(",");
}
}
}

View File

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

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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