You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by gn...@apache.org on 2019/01/11 22:09:14 UTC

[camel] 09/13: Clean up a bit resource processors

This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git

commit c59a50d434aabeec16e46430905a53a27a9fd44d
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Fri Jan 11 10:37:36 2019 +0100

    Clean up a bit resource processors
---
 .../apt/AbstractCamelAnnotationProcessor.java      |  52 ++++
 .../camel/tools/apt/AnnotationProcessorHelper.java |  58 +---
 .../apache/camel/tools/apt/ConverterProcessor.java | 342 +++++++++------------
 .../apt/CoreEipAnnotationProcessorHelper.java      |  11 +-
 .../camel/tools/apt/DocumentationHelper.java       |  44 +--
 .../tools/apt/EndpointAnnotationProcessor.java     |  40 +--
 .../java/org/apache/camel/tools/apt/Func1.java     |  26 --
 .../camel/tools/apt/ModelAnnotationProcessor.java  |  48 +--
 .../tools/apt/SpringAnnotationProcessorHelper.java |  11 +-
 .../camel/tools/apt/TypeConverterProcessor.java    |  69 ++---
 10 files changed, 276 insertions(+), 425 deletions(-)

diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/AbstractCamelAnnotationProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/AbstractCamelAnnotationProcessor.java
new file mode 100644
index 0000000..0d73d87
--- /dev/null
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/AbstractCamelAnnotationProcessor.java
@@ -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.tools.apt;
+
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic.Kind;
+
+import static org.apache.camel.tools.apt.AnnotationProcessorHelper.dumpExceptionToErrorFile;
+
+public abstract class AbstractCamelAnnotationProcessor extends AbstractProcessor {
+
+    @Override
+    public SourceVersion getSupportedSourceVersion() {
+        return SourceVersion.latest();
+    }
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        try {
+            if (roundEnv.processingOver()) {
+                return false;
+            }
+            doProcess(annotations, roundEnv);
+        } catch (Throwable e) {
+            processingEnv.getMessager().printMessage(Kind.ERROR, "Unable to process annotated elements in " + getClass().getSimpleName() + ": " + e.getMessage());
+            dumpExceptionToErrorFile("camel-apt-error.log", "Error processing annotation in " + getClass().getSimpleName(), e);
+        }
+        return false;
+    }
+
+    protected abstract void doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws Exception;
+
+}
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/AnnotationProcessorHelper.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/AnnotationProcessorHelper.java
index d68ecac..66e4e86 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/AnnotationProcessorHelper.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/AnnotationProcessorHelper.java
@@ -16,18 +16,19 @@
  */
 package org.apache.camel.tools.apt;
 
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
+import java.io.BufferedWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.io.Writer;
-import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Consumer;
+
 import javax.annotation.processing.Filer;
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
@@ -319,40 +320,15 @@ public final class AnnotationProcessorHelper {
     /**
      * Helper method to produce class output text file using the given handler
      */
-    public static void processFile(ProcessingEnvironment processingEnv, String packageName, String fileName, Func1<PrintWriter, Void> handler) {
-        PrintWriter writer = null;
+    public static void processFile(ProcessingEnvironment processingEnv, String packageName, String fileName, Consumer<PrintWriter> handler) {
         try {
-            Writer out;
             Filer filer = processingEnv.getFiler();
-            FileObject resource;
-            try {
-                resource = filer.getResource(StandardLocation.CLASS_OUTPUT, packageName, fileName);
-            } catch (Throwable e) {
-                resource = filer.createResource(StandardLocation.CLASS_OUTPUT, packageName, fileName);
-            }
-            URI uri = resource.toUri();
-            File file = null;
-            if (uri != null) {
-                try {
-                    file = new File(uri.getPath());
-                } catch (Exception e) {
-                    warning(processingEnv, "Cannot convert output directory resource URI to a file " + e);
-                }
-            }
-            if (file == null) {
-                warning(processingEnv, "No class output directory could be found!");
-            } else {
-                file.getParentFile().mkdirs();
-                out = new FileWriter(file);
-                writer = new PrintWriter(out);
-                handler.call(writer);
+            FileObject resource = filer.createResource(StandardLocation.CLASS_OUTPUT, packageName, fileName);
+            try (Writer w = resource.openWriter(); PrintWriter writer = new PrintWriter(w)) {
+                handler.accept(writer);
             }
         } catch (IOException e) {
             log(processingEnv, e);
-        } finally {
-            if (writer != null) {
-                writer.close();
-            }
         }
     }
 
@@ -403,18 +379,12 @@ public final class AnnotationProcessorHelper {
     }
 
     public static void dumpExceptionToErrorFile(String fileName, String message, Throwable e) {
-        File file = new File(fileName);
-        try {
-            FileOutputStream fos = new FileOutputStream(file);
-            StringWriter sw = new StringWriter();
-            PrintWriter pw = new PrintWriter(sw);
+        try (BufferedWriter w = Files.newBufferedWriter(Paths.get(fileName))) {
+            w.append(message);
+            w.append("\n\n");
+            PrintWriter pw = new PrintWriter(w);
             e.printStackTrace(pw);
-            fos.write(message.getBytes());
-            fos.write("\n\n".getBytes());
-            fos.write(sw.toString().getBytes());
-            pw.close();
-            sw.close();
-            fos.close();
+            pw.flush();
         } catch (Throwable t) {
             // ignore
         }
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java
index bf19948..68d105f 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java
@@ -16,26 +16,17 @@
  */
 package org.apache.camel.tools.apt;
 
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-import java.io.StringWriter;
 import java.io.Writer;
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 
-import javax.annotation.processing.AbstractProcessor;
 import javax.annotation.processing.RoundEnvironment;
 import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.annotation.processing.SupportedSourceVersion;
-import javax.lang.model.SourceVersion;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.Element;
@@ -45,205 +36,192 @@ import javax.lang.model.element.Modifier;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.type.TypeMirror;
 import javax.tools.Diagnostic;
-import javax.tools.Diagnostic.Kind;
 import javax.tools.JavaFileObject;
 
 @SupportedAnnotationTypes({"org.apache.camel.Converter"})
-@SupportedSourceVersion(SourceVersion.RELEASE_8)
-public class ConverterProcessor extends AbstractProcessor {
+public class ConverterProcessor extends AbstractCamelAnnotationProcessor {
 
     @Override
-    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
-        try {
-            if (roundEnv.processingOver()) {
-                return false;
-            }
-
-            if (this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.impl.converter.CoreStaticTypeConverterLoader") != null) {
-                return false;
-            }
-
-            // We're in tests, do not generate anything
-            if (this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.converter.ObjectConverter") == null) {
-                return false;
-            }
+    protected void doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws Exception {
+        if (this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.impl.converter.CoreStaticTypeConverterLoader") != null) {
+            return;
+        }
 
-            Comparator<TypeMirror> comparator = (o1, o2) -> processingEnv.getTypeUtils().isAssignable(o1, o2)
-                    ? -1 : processingEnv.getTypeUtils().isAssignable(o2, o1) ? +1 : o1.toString().compareTo(o2.toString());
+        // We're in tests, do not generate anything
+        if (this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.converter.ObjectConverter") == null) {
+            return;
+        }
 
-            Map<String, Map<TypeMirror, ExecutableElement>> converters = new TreeMap<>();
-            TypeElement converterAnnotationType = this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.Converter");
-            for (Element element : roundEnv.getElementsAnnotatedWith(converterAnnotationType)) {
-                if (element.getKind() == ElementKind.METHOD) {
-                    ExecutableElement ee = (ExecutableElement) element;
-                    TypeMirror to = ee.getReturnType();
-                    TypeMirror from = ee.getParameters().get(0).asType();
-                    String fromStr = toString(from);
-                    if (!fromStr.endsWith("[]")) {
-                        TypeElement e = this.processingEnv.getElementUtils().getTypeElement(fromStr);
-                        if (e != null) {
-                            from = e.asType();
-                        } else {
-                            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Could not retrieve type element for " + fromStr);
-                        }
+        Comparator<TypeMirror> comparator = (o1, o2) -> processingEnv.getTypeUtils().isAssignable(o1, o2)
+                ? -1 : processingEnv.getTypeUtils().isAssignable(o2, o1) ? +1 : o1.toString().compareTo(o2.toString());
 
+        Map<String, Map<TypeMirror, ExecutableElement>> converters = new TreeMap<>();
+        TypeElement converterAnnotationType = this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.Converter");
+        for (Element element : roundEnv.getElementsAnnotatedWith(converterAnnotationType)) {
+            if (element.getKind() == ElementKind.METHOD) {
+                ExecutableElement ee = (ExecutableElement) element;
+                TypeMirror to = ee.getReturnType();
+                TypeMirror from = ee.getParameters().get(0).asType();
+                String fromStr = toString(from);
+                if (!fromStr.endsWith("[]")) {
+                    TypeElement e = this.processingEnv.getElementUtils().getTypeElement(fromStr);
+                    if (e != null) {
+                        from = e.asType();
+                    } else {
+                        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Could not retrieve type element for " + fromStr);
                     }
-                    converters.computeIfAbsent(toString(to), c -> new TreeMap<>(comparator)).put(from, ee);
+
                 }
+                converters.computeIfAbsent(toString(to), c -> new TreeMap<>(comparator)).put(from, ee);
             }
-            TypeElement fallbackAnnotationType = this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.FallbackConverter");
-            List<ExecutableElement> fallbackConverters = new ArrayList<>();
-            for (Element element : roundEnv.getElementsAnnotatedWith(fallbackAnnotationType)) {
-                if (element.getKind() == ElementKind.METHOD) {
-                    ExecutableElement ee = (ExecutableElement) element;
-                    fallbackConverters.add(ee);
-                }
+        }
+        TypeElement fallbackAnnotationType = this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.FallbackConverter");
+        List<ExecutableElement> fallbackConverters = new ArrayList<>();
+        for (Element element : roundEnv.getElementsAnnotatedWith(fallbackAnnotationType)) {
+            if (element.getKind() == ElementKind.METHOD) {
+                ExecutableElement ee = (ExecutableElement) element;
+                fallbackConverters.add(ee);
             }
+        }
 
-            String p = "org.apache.camel.impl.converter";
-            String c = "CoreStaticTypeConverterLoader";
-            JavaFileObject jfo = processingEnv.getFiler().createSourceFile(p + "." + c);
-            Set<String> converterClasses = new LinkedHashSet<>();
-            try (Writer writer = jfo.openWriter()) {
+        String p = "org.apache.camel.impl.converter";
+        String c = "CoreStaticTypeConverterLoader";
+        JavaFileObject jfo = processingEnv.getFiler().createSourceFile(p + "." + c);
+        Set<String> converterClasses = new LinkedHashSet<>();
+        try (Writer writer = jfo.openWriter()) {
 
-                writer.append("package ").append(p).append(";\n");
-                writer.append("\n");
-                writer.append("import org.apache.camel.Exchange;\n");
-                writer.append("import org.apache.camel.TypeConversionException;\n");
-                writer.append("import org.apache.camel.TypeConverterLoaderException;\n");
-                writer.append("import org.apache.camel.spi.TypeConverterLoader;\n");
-                writer.append("import org.apache.camel.spi.TypeConverterRegistry;\n");
-                writer.append("import org.apache.camel.support.TypeConverterSupport;\n");
-                writer.append("\n");
-                writer.append("@SuppressWarnings(\"unchecked\")\n");
-                writer.append("public class ").append(c).append(" implements TypeConverterLoader {\n");
-                writer.append("\n");
-                writer.append("    public static final CoreStaticTypeConverterLoader INSTANCE = new CoreStaticTypeConverterLoader();\n");
-                writer.append("\n");
-                writer.append("    static abstract class SimpleTypeConverter extends TypeConverterSupport {\n");
-                writer.append("        private final boolean allowNull;\n");
-                writer.append("\n");
-                writer.append("        public SimpleTypeConverter(boolean allowNull) {\n");
-                writer.append("            this.allowNull = allowNull;\n");
-                writer.append("        }\n");
-                writer.append("\n");
-                writer.append("        @Override\n");
-                writer.append("        public boolean allowNull() {\n");
-                writer.append("            return allowNull;\n");
-                writer.append("        }\n");
-                writer.append("\n");
-                writer.append("        @Override\n");
-                writer.append("        public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException {\n");
-                writer.append("            try {\n");
-                writer.append("                return (T) doConvert(exchange, value);\n");
-                writer.append("            } catch (TypeConversionException e) {\n");
-                writer.append("                throw e;\n");
-                writer.append("            } catch (Exception e) {\n");
-                writer.append("                throw new TypeConversionException(value, type, e);\n");
-                writer.append("            }\n");
-                writer.append("        }\n");
-                writer.append("        protected abstract Object doConvert(Exchange exchange, Object value) throws Exception;\n");
-                writer.append("    };\n");
-                writer.append("\n");
-                writer.append("    private DoubleMap<Class<?>, Class<?>, SimpleTypeConverter> converters = new DoubleMap<>(256);\n");
-                writer.append("\n");
-                writer.append("    private ").append(c).append("() {\n");
+            writer.append("package ").append(p).append(";\n");
+            writer.append("\n");
+            writer.append("import org.apache.camel.Exchange;\n");
+            writer.append("import org.apache.camel.TypeConversionException;\n");
+            writer.append("import org.apache.camel.TypeConverterLoaderException;\n");
+            writer.append("import org.apache.camel.spi.TypeConverterLoader;\n");
+            writer.append("import org.apache.camel.spi.TypeConverterRegistry;\n");
+            writer.append("import org.apache.camel.support.TypeConverterSupport;\n");
+            writer.append("\n");
+            writer.append("@SuppressWarnings(\"unchecked\")\n");
+            writer.append("public class ").append(c).append(" implements TypeConverterLoader {\n");
+            writer.append("\n");
+            writer.append("    public static final CoreStaticTypeConverterLoader INSTANCE = new CoreStaticTypeConverterLoader();\n");
+            writer.append("\n");
+            writer.append("    static abstract class SimpleTypeConverter extends TypeConverterSupport {\n");
+            writer.append("        private final boolean allowNull;\n");
+            writer.append("\n");
+            writer.append("        public SimpleTypeConverter(boolean allowNull) {\n");
+            writer.append("            this.allowNull = allowNull;\n");
+            writer.append("        }\n");
+            writer.append("\n");
+            writer.append("        @Override\n");
+            writer.append("        public boolean allowNull() {\n");
+            writer.append("            return allowNull;\n");
+            writer.append("        }\n");
+            writer.append("\n");
+            writer.append("        @Override\n");
+            writer.append("        public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException {\n");
+            writer.append("            try {\n");
+            writer.append("                return (T) doConvert(exchange, value);\n");
+            writer.append("            } catch (TypeConversionException e) {\n");
+            writer.append("                throw e;\n");
+            writer.append("            } catch (Exception e) {\n");
+            writer.append("                throw new TypeConversionException(value, type, e);\n");
+            writer.append("            }\n");
+            writer.append("        }\n");
+            writer.append("        protected abstract Object doConvert(Exchange exchange, Object value) throws Exception;\n");
+            writer.append("    };\n");
+            writer.append("\n");
+            writer.append("    private DoubleMap<Class<?>, Class<?>, SimpleTypeConverter> converters = new DoubleMap<>(256);\n");
+            writer.append("\n");
+            writer.append("    private ").append(c).append("() {\n");
 
-                for (Map.Entry<String, Map<TypeMirror, ExecutableElement>> to : converters.entrySet()) {
-                    for (Map.Entry<TypeMirror, ExecutableElement> from : to.getValue().entrySet()) {
-                        boolean allowNull = false;
-                        for (AnnotationMirror ann : from.getValue().getAnnotationMirrors()) {
-                            if (ann.getAnnotationType().asElement() == converterAnnotationType) {
-                                for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : ann.getElementValues().entrySet()) {
-                                    switch (entry.getKey().getSimpleName().toString()) {
-                                        case "allowNull":
-                                            allowNull = (Boolean) entry.getValue().getValue();
-                                            break;
-                                        default:
-                                            throw new IllegalStateException();
-                                    }
-                                }
-                            }
-                        }
-                        writer.append("        converters.put(").append(to.getKey()).append(".class").append(", ")
-                                .append(toString(from.getKey())).append(".class, new SimpleTypeConverter(")
-                                .append(Boolean.toString(allowNull)).append(") {\n");
-                        writer.append("            @Override\n");
-                        writer.append("            public Object doConvert(Exchange exchange, Object value) throws Exception {\n");
-                        writer.append("                return ").append(toJava(from.getValue(), converterClasses)).append(";\n");
-                        writer.append("            }\n");
-                        writer.append("        });\n");
-                    }
-                }
-                writer.append("    }\n");
-                writer.append("\n");
-                writer.append("    @Override\n");
-                writer.append("    public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {\n");
-                writer.append("        converters.forEach((k, v, c) -> registry.addTypeConverter(k, v, c));\n");
-                for (ExecutableElement ee : fallbackConverters) {
+            for (Map.Entry<String, Map<TypeMirror, ExecutableElement>> to : converters.entrySet()) {
+                for (Map.Entry<TypeMirror, ExecutableElement> from : to.getValue().entrySet()) {
                     boolean allowNull = false;
-                    boolean canPromote = false;
-                    for (AnnotationMirror ann : ee.getAnnotationMirrors()) {
-                        if (ann.getAnnotationType().asElement() == fallbackAnnotationType) {
+                    for (AnnotationMirror ann : from.getValue().getAnnotationMirrors()) {
+                        if (ann.getAnnotationType().asElement() == converterAnnotationType) {
                             for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : ann.getElementValues().entrySet()) {
                                 switch (entry.getKey().getSimpleName().toString()) {
                                     case "allowNull":
                                         allowNull = (Boolean) entry.getValue().getValue();
                                         break;
-                                    case "canPromote":
-                                        canPromote = (Boolean) entry.getValue().getValue();
-                                        break;
                                     default:
                                         throw new IllegalStateException();
                                 }
                             }
                         }
                     }
-                    writer.append("        registry.addFallbackTypeConverter(new TypeConverterSupport() {\n");
+                    writer.append("        converters.put(").append(to.getKey()).append(".class").append(", ")
+                            .append(toString(from.getKey())).append(".class, new SimpleTypeConverter(")
+                            .append(Boolean.toString(allowNull)).append(") {\n");
                     writer.append("            @Override\n");
-                    writer.append("            public boolean allowNull() {\n");
-                    writer.append("                return ").append(Boolean.toString(allowNull)).append(";\n");
+                    writer.append("            public Object doConvert(Exchange exchange, Object value) throws Exception {\n");
+                    writer.append("                return ").append(toJava(from.getValue(), converterClasses)).append(";\n");
                     writer.append("            }\n");
-                    writer.append("            @Override\n");
-                    writer.append("            public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException {\n");
-                    writer.append("                try {\n");
-                    writer.append("                    return (T) ").append(toJavaFallback(ee, converterClasses)).append(";\n");
-                    writer.append("                } catch (TypeConversionException e) {\n");
-                    writer.append("                    throw e;\n");
-                    writer.append("                } catch (Exception e) {\n");
-                    writer.append("                    throw new TypeConversionException(value, type, e);\n");
-                    writer.append("                }\n");
-                    writer.append("            }\n");
-                    writer.append("        }, ").append(Boolean.toString(canPromote)).append(");\n");
+                    writer.append("        });\n");
                 }
-                writer.append("    }\n");
-                writer.append("\n");
-
-                for (String f : converterClasses) {
-                    String s = f.substring(f.lastIndexOf('.') + 1);
-                    String v = s.substring(0, 1).toLowerCase() + s.substring(1);
-                    writer.append("    private volatile ").append(f).append(" ").append(v).append(";\n");
-                    writer.append("    private ").append(f).append(" get").append(s).append("() {\n");
-                    writer.append("        if (").append(v).append(" == null) {\n");
-                    writer.append("            synchronized (this) {\n");
-                    writer.append("                if (").append(v).append(" == null) {\n");
-                    writer.append("                    ").append(v).append(" = new ").append(f).append("();\n");
-                    writer.append("                }\n");
-                    writer.append("            }\n");
-                    writer.append("        }\n");
-                    writer.append("        return ").append(v).append(";\n");
-                    writer.append("    }\n");
+            }
+            writer.append("    }\n");
+            writer.append("\n");
+            writer.append("    @Override\n");
+            writer.append("    public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {\n");
+            writer.append("        converters.forEach((k, v, c) -> registry.addTypeConverter(k, v, c));\n");
+            for (ExecutableElement ee : fallbackConverters) {
+                boolean allowNull = false;
+                boolean canPromote = false;
+                for (AnnotationMirror ann : ee.getAnnotationMirrors()) {
+                    if (ann.getAnnotationType().asElement() == fallbackAnnotationType) {
+                        for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : ann.getElementValues().entrySet()) {
+                            switch (entry.getKey().getSimpleName().toString()) {
+                                case "allowNull":
+                                    allowNull = (Boolean) entry.getValue().getValue();
+                                    break;
+                                case "canPromote":
+                                    canPromote = (Boolean) entry.getValue().getValue();
+                                    break;
+                                default:
+                                    throw new IllegalStateException();
+                            }
+                        }
+                    }
                 }
+                writer.append("        registry.addFallbackTypeConverter(new TypeConverterSupport() {\n");
+                writer.append("            @Override\n");
+                writer.append("            public boolean allowNull() {\n");
+                writer.append("                return ").append(Boolean.toString(allowNull)).append(";\n");
+                writer.append("            }\n");
+                writer.append("            @Override\n");
+                writer.append("            public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException {\n");
+                writer.append("                try {\n");
+                writer.append("                    return (T) ").append(toJavaFallback(ee, converterClasses)).append(";\n");
+                writer.append("                } catch (TypeConversionException e) {\n");
+                writer.append("                    throw e;\n");
+                writer.append("                } catch (Exception e) {\n");
+                writer.append("                    throw new TypeConversionException(value, type, e);\n");
+                writer.append("                }\n");
+                writer.append("            }\n");
+                writer.append("        }, ").append(Boolean.toString(canPromote)).append(");\n");
+            }
+            writer.append("    }\n");
+            writer.append("\n");
 
-                writer.append("}\n");
-                writer.flush();
+            for (String f : converterClasses) {
+                String s = f.substring(f.lastIndexOf('.') + 1);
+                String v = s.substring(0, 1).toLowerCase() + s.substring(1);
+                writer.append("    private volatile ").append(f).append(" ").append(v).append(";\n");
+                writer.append("    private ").append(f).append(" get").append(s).append("() {\n");
+                writer.append("        if (").append(v).append(" == null) {\n");
+                writer.append("            synchronized (this) {\n");
+                writer.append("                if (").append(v).append(" == null) {\n");
+                writer.append("                    ").append(v).append(" = new ").append(f).append("();\n");
+                writer.append("                }\n");
+                writer.append("            }\n");
+                writer.append("        }\n");
+                writer.append("        return ").append(v).append(";\n");
+                writer.append("    }\n");
             }
 
-        } catch (Throwable e) {
-            processingEnv.getMessager().printMessage(Kind.ERROR, "Unable to process elements annotated with @Converter: " + e.getMessage());
-            dumpExceptionToErrorFile("camel-apt-error.log", "Error processing @Converter", e);
+            writer.append("}\n");
+            writer.flush();
         }
-        return false;
     }
 
     private String toString(TypeMirror type) {
@@ -276,22 +254,4 @@ public class ConverterProcessor extends AbstractProcessor {
         return pfx + "(type, " + (converter.getParameters().size() == 4 ? "exchange, " : "") + cast + "value" + ", registry)";
     }
 
-    public static void dumpExceptionToErrorFile(String fileName, String message, Throwable e) {
-        File file = new File(fileName);
-        try {
-            FileOutputStream fos = new FileOutputStream(file);
-            StringWriter sw = new StringWriter();
-            PrintWriter pw = new PrintWriter(sw);
-            e.printStackTrace(pw);
-            fos.write(message.getBytes());
-            fos.write("\n\n".getBytes());
-            fos.write(sw.toString().getBytes());
-            pw.close();
-            sw.close();
-            fos.close();
-        } catch (Throwable t) {
-            // ignore
-        }
-    }
-
 }
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreEipAnnotationProcessorHelper.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreEipAnnotationProcessorHelper.java
index 92a7a64..dd61673 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreEipAnnotationProcessorHelper.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreEipAnnotationProcessorHelper.java
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.stream.Collectors;
+
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
 import javax.lang.model.element.ElementKind;
@@ -128,14 +129,8 @@ public class CoreEipAnnotationProcessorHelper {
         }
 
         // write json schema
-        Func1<PrintWriter, Void> handler = new Func1<PrintWriter, Void>() {
-            @Override
-            public Void call(PrintWriter writer) {
-                writeJSonSchemeDocumentation(processingEnv, writer, roundEnv, classElement, rootElement, javaTypeName, name);
-                return null;
-            }
-        };
-        processFile(processingEnv, packageName, fileName, handler);
+        processFile(processingEnv, packageName, fileName,
+                writer -> writeJSonSchemeDocumentation(processingEnv, writer, roundEnv, classElement, rootElement, javaTypeName, name));
     }
 
     protected void writeJSonSchemeDocumentation(ProcessingEnvironment processingEnv, PrintWriter writer, RoundEnvironment roundEnv,
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/DocumentationHelper.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/DocumentationHelper.java
index 4f207d6..48be0e4 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/DocumentationHelper.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/DocumentationHelper.java
@@ -16,18 +16,12 @@
  */
 package org.apache.camel.tools.apt;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.LineNumberReader;
+import java.nio.file.Files;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.camel.tools.apt.helper.IOHelper;
-
 import static org.apache.camel.tools.apt.helper.JsonSchemaHelper.parseJsonSchema;
 
 /**
@@ -42,16 +36,12 @@ public final class DocumentationHelper {
     public static String findComponentJavaDoc(String scheme, String extendsScheme, String fieldName) {
         File file = jsonFile(scheme, extendsScheme);
         if (file != null) {
-            FileInputStream fis = null;
             try {
-                fis = new FileInputStream(file);
-                String json = loadText(fis);
+                String json = loadText(file);
                 List<Map<String, String>> rows = parseJsonSchema("componentProperties", json, true);
                 return getPropertyDescription(rows, fieldName);
             } catch (Exception e) {
                 // ignore
-            } finally {
-                IOHelper.close(fis);
             }
         }
 
@@ -62,16 +52,12 @@ public final class DocumentationHelper {
     public static String findEndpointJavaDoc(String scheme, String extendsScheme, String fieldName) {
         File file = jsonFile(scheme, extendsScheme);
         if (file != null) {
-            FileInputStream fis = null;
             try {
-                fis = new FileInputStream(file);
-                String json = loadText(fis);
+                String json = loadText(file);
                 List<Map<String, String>> rows = parseJsonSchema("properties", json, true);
                 return getPropertyDescription(rows, fieldName);
             } catch (Exception e) {
                 // ignore
-            } finally {
-                IOHelper.close(fis);
             }
         }
 
@@ -135,25 +121,13 @@ public final class DocumentationHelper {
      * <p/>
      * Warning, don't use for crazy big streams :)
      */
-    private static String loadText(InputStream in) throws IOException {
+    private static String loadText(File file) throws IOException {
         StringBuilder builder = new StringBuilder();
-        InputStreamReader isr = new InputStreamReader(in);
-        try {
-            BufferedReader reader = new LineNumberReader(isr);
-            while (true) {
-                String line = reader.readLine();
-                if (line != null) {
-                    builder.append(line);
-                    builder.append("\n");
-                } else {
-                    break;
-                }
-            }
-            return builder.toString();
-        } finally {
-            isr.close();
-            in.close();
-        }
+        Files.readAllLines(file.toPath()).forEach(line -> {
+            builder.append(line);
+            builder.append("\n");
+        });
+        return builder.toString();
     }
 
 }
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java
index 3c4be09..cca1496 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java
@@ -26,10 +26,8 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
-import javax.annotation.processing.AbstractProcessor;
 import javax.annotation.processing.RoundEnvironment;
 import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ElementKind;
 import javax.lang.model.element.ExecutableElement;
@@ -40,7 +38,6 @@ import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.ElementFilter;
 import javax.lang.model.util.Elements;
-import javax.tools.Diagnostic.Kind;
 
 import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.UriEndpoint;
@@ -56,7 +53,6 @@ import org.apache.camel.tools.apt.model.ComponentOption;
 import org.apache.camel.tools.apt.model.EndpointOption;
 import org.apache.camel.tools.apt.model.EndpointPath;
 
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.dumpExceptionToErrorFile;
 import static org.apache.camel.tools.apt.AnnotationProcessorHelper.findFieldElement;
 import static org.apache.camel.tools.apt.AnnotationProcessorHelper.findJavaDoc;
 import static org.apache.camel.tools.apt.AnnotationProcessorHelper.findTypeElement;
@@ -72,33 +68,19 @@ import static org.apache.camel.tools.apt.helper.Strings.isNullOrEmpty;
  * Processes all Camel {@link UriEndpoint}s and generate json schema documentation for the endpoint/component.
  */
 @SupportedAnnotationTypes({"org.apache.camel.spi.*"})
-public class EndpointAnnotationProcessor extends AbstractProcessor {
+public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcessor {
 
     // CHECKSTYLE:OFF
 
     private static final String HEADER_FILTER_STRATEGY_JAVADOC = "To use a custom HeaderFilterStrategy to filter header to and from Camel message.";
 
-    public boolean process(Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
-        try {
-            if (roundEnv.processingOver()) {
-                return true;
-            }
-            Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(UriEndpoint.class);
-            for (Element element : elements) {
-                if (element instanceof TypeElement) {
-                    processEndpointClass(roundEnv, (TypeElement) element);
-                }
+    protected void doProcess(Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) throws Exception {
+        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(UriEndpoint.class);
+        for (Element element : elements) {
+            if (element instanceof TypeElement) {
+                processEndpointClass(roundEnv, (TypeElement) element);
             }
-        } catch (Throwable e) {
-            processingEnv.getMessager().printMessage(Kind.ERROR, "Unable to process elements annotated with @UriEndpoint: " + e.getMessage());
-            dumpExceptionToErrorFile("camel-apt-error.log", "Error processing @UriEndpoint", e);
         }
-        return true;
-    }
-
-    @Override
-    public SourceVersion getSupportedSourceVersion() {
-        return SourceVersion.latest();
     }
 
     private void processEndpointClass(final RoundEnvironment roundEnv, final TypeElement classElement) {
@@ -129,14 +111,8 @@ public class EndpointAnnotationProcessor extends AbstractProcessor {
                     String name = canonicalClassName(classElement.getQualifiedName().toString());
                     String packageName = name.substring(0, name.lastIndexOf("."));
                     String fileName = alias + ".json";
-                    Func1<PrintWriter, Void> handler = new Func1<PrintWriter, Void>() {
-                        @Override
-                        public Void call(PrintWriter writer) {
-                            writeJSonSchemeDocumentation(writer, roundEnv, classElement, uriEndpoint, aliasTitle, alias, extendsAlias, label, schemes);
-                            return null;
-                        }
-                    };
-                    processFile(processingEnv, packageName, fileName, handler);
+                    processFile(processingEnv, packageName, fileName,
+                            writer -> writeJSonSchemeDocumentation(writer, roundEnv, classElement, uriEndpoint, aliasTitle, alias, extendsAlias, label, schemes));
                 }
             }
         }
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/Func1.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/Func1.java
deleted file mode 100644
index 6c6ab8d..0000000
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/Func1.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * 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.tools.apt;
-
-/**
- * Represents a function with 1 argument
- */
-interface Func1<T1, R> {
-
-    R call(T1 t1);
-
-}
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/ModelAnnotationProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/ModelAnnotationProcessor.java
index 7254db9..25fee69 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/ModelAnnotationProcessor.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/ModelAnnotationProcessor.java
@@ -17,59 +17,41 @@
 package org.apache.camel.tools.apt;
 
 import java.util.Set;
-import javax.annotation.processing.AbstractProcessor;
+
 import javax.annotation.processing.RoundEnvironment;
 import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic.Kind;
 import javax.xml.bind.annotation.XmlRootElement;
 
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.dumpExceptionToErrorFile;
 import static org.apache.camel.tools.apt.helper.Strings.canonicalClassName;
 
 /**
  * APT compiler plugin to generate JSon Schema for all EIP models and camel-spring's <camelContext> types.
  */
 @SupportedAnnotationTypes({"javax.xml.bind.annotation.*", "org.apache.camel.spi.Label"})
-public class ModelAnnotationProcessor extends AbstractProcessor {
+public class ModelAnnotationProcessor extends AbstractCamelAnnotationProcessor {
 
     private CoreEipAnnotationProcessorHelper coreProcessor = new CoreEipAnnotationProcessorHelper();
     private SpringAnnotationProcessorHelper springProcessor = new SpringAnnotationProcessorHelper();
 
     @Override
-    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
-        try {
-            if (roundEnv.processingOver()) {
-                return true;
-            }
-
-            Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(XmlRootElement.class);
-            for (Element element : elements) {
-                if (element instanceof TypeElement) {
-                    TypeElement classElement = (TypeElement) element;
-
-                    final String javaTypeName = canonicalClassName(classElement.getQualifiedName().toString());
-                    boolean core = javaTypeName.startsWith("org.apache.camel.model");
-                    boolean spring = javaTypeName.startsWith("org.apache.camel.spring") || javaTypeName.startsWith("org.apache.camel.core.xml");
-                    if (core) {
-                        coreProcessor.processModelClass(processingEnv, roundEnv, classElement);
-                    } else if (spring) {
-                        springProcessor.processModelClass(processingEnv, roundEnv, classElement);
-                    }
+    protected void doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws Exception {
+        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(XmlRootElement.class);
+        for (Element element : elements) {
+            if (element instanceof TypeElement) {
+                TypeElement classElement = (TypeElement) element;
+
+                final String javaTypeName = canonicalClassName(classElement.getQualifiedName().toString());
+                boolean core = javaTypeName.startsWith("org.apache.camel.model");
+                boolean spring = javaTypeName.startsWith("org.apache.camel.spring") || javaTypeName.startsWith("org.apache.camel.core.xml");
+                if (core) {
+                    coreProcessor.processModelClass(processingEnv, roundEnv, classElement);
+                } else if (spring) {
+                    springProcessor.processModelClass(processingEnv, roundEnv, classElement);
                 }
             }
-        } catch (Throwable e) {
-            processingEnv.getMessager().printMessage(Kind.ERROR, "Unable to process elements annotated with @XmlRootElement: " + e.getMessage());
-            dumpExceptionToErrorFile("camel-apt-error.log", "Error processing", e);
         }
-
-        return true;
     }
 
-    @Override
-    public SourceVersion getSupportedSourceVersion() {
-        return SourceVersion.latest();
-    }
 }
\ No newline at end of file
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/SpringAnnotationProcessorHelper.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/SpringAnnotationProcessorHelper.java
index 58c6277..f8d4418 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/SpringAnnotationProcessorHelper.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/SpringAnnotationProcessorHelper.java
@@ -22,6 +22,7 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
+
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
 import javax.lang.model.element.ElementKind;
@@ -85,14 +86,8 @@ public class SpringAnnotationProcessorHelper {
         }
 
         // write json schema
-        Func1<PrintWriter, Void> handler = new Func1<PrintWriter, Void>() {
-            @Override
-            public Void call(PrintWriter writer) {
-                writeJSonSchemeDocumentation(processingEnv, writer, roundEnv, classElement, rootElement, javaTypeName, name);
-                return null;
-            }
-        };
-        processFile(processingEnv, packageName, fileName, handler);
+        processFile(processingEnv, packageName, fileName,
+                writer -> writeJSonSchemeDocumentation(processingEnv, writer, roundEnv, classElement, rootElement, javaTypeName, name));
     }
 
     protected void writeJSonSchemeDocumentation(ProcessingEnvironment processingEnv, PrintWriter writer, RoundEnvironment roundEnv,
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/TypeConverterProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/TypeConverterProcessor.java
index 966c0a5..816facb 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/TypeConverterProcessor.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/TypeConverterProcessor.java
@@ -16,77 +16,50 @@
  */
 package org.apache.camel.tools.apt;
 
-import java.io.IOException;
 import java.io.Writer;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.TreeSet;
 
-import javax.annotation.processing.AbstractProcessor;
 import javax.annotation.processing.Filer;
 import javax.annotation.processing.RoundEnvironment;
 import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.annotation.processing.SupportedSourceVersion;
-import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic.Kind;
 import javax.tools.FileObject;
 import javax.tools.StandardLocation;
-import javax.xml.bind.annotation.XmlRootElement;
 
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.dumpExceptionToErrorFile;
 import static org.apache.camel.tools.apt.helper.Strings.canonicalClassName;
 
-/**
- * APT compiler plugin to generate JSon Schema for all EIP models and camel-spring's <camelContext> types.
- */
 @SupportedAnnotationTypes({"org.apache.camel.Converter"})
-@SupportedSourceVersion(SourceVersion.RELEASE_8)
-public class TypeConverterProcessor extends AbstractProcessor {
+public class TypeConverterProcessor extends AbstractCamelAnnotationProcessor {
 
     @Override
-    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
-        try {
-            if (roundEnv.processingOver()) {
-                return false;
-            }
+    protected void doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws Exception {
+        TypeElement converterAnnotationType = this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.Converter");
+        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(converterAnnotationType);
+        Map<String, Element> converterClasses = new TreeMap<>();
+        for (Element element : elements) {
+            if (element instanceof TypeElement) {
+                TypeElement classElement = (TypeElement) element;
 
-            TypeElement converterAnnotationType = this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.Converter");
-            Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(converterAnnotationType);
-            Map<String, Element> converterClasses = new TreeMap<>();
-            for (Element element : elements) {
-                if (element instanceof TypeElement) {
-                    TypeElement classElement = (TypeElement) element;
-
-                    final String javaTypeName = canonicalClassName(classElement.getQualifiedName().toString());
-                    converterClasses.put(javaTypeName, element);
-                }
+                final String javaTypeName = canonicalClassName(classElement.getQualifiedName().toString());
+                converterClasses.put(javaTypeName, element);
             }
-            if (!converterClasses.isEmpty()
-                    && !converterClasses.containsKey("org.apache.camel.converter.IOConverter")) {
-                Filer filer = processingEnv.getFiler();
-                FileObject resource = filer.createResource(StandardLocation.CLASS_OUTPUT,
-                        "", "META-INF/services/org/apache/camel/TypeConverter",
-                        converterClasses.values().toArray(new Element[0]));
-                try (Writer w = resource.openWriter()) {
-                    w.append("# Generated by camel annotation processor\n");
-                    for (String s : converterClasses.keySet()) {
-                        w.append(s).append("\n");
-                    }
+        }
+        if (!converterClasses.isEmpty()
+                && !converterClasses.containsKey("org.apache.camel.converter.IOConverter")) {
+            Filer filer = processingEnv.getFiler();
+            FileObject resource = filer.createResource(StandardLocation.CLASS_OUTPUT,
+                    "", "META-INF/services/org/apache/camel/TypeConverter",
+                    converterClasses.values().toArray(new Element[0]));
+            try (Writer w = resource.openWriter()) {
+                w.append("# Generated by camel annotation processor\n");
+                for (String s : converterClasses.keySet()) {
+                    w.append(s).append("\n");
                 }
             }
-        } catch (Throwable e) {
-            processingEnv.getMessager().printMessage(Kind.ERROR, "Unable to process elements annotated with @Converter: " + e.getMessage());
-            dumpExceptionToErrorFile("camel-apt-error.log", "Error processing", e);
         }
-
-        return false;
     }
 
-    @Override
-    public SourceVersion getSupportedSourceVersion() {
-        return SourceVersion.latest();
-    }
 }
\ No newline at end of file