You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2019/08/24 16:58:09 UTC

[camel] 01/03: CAMEL-13870: Generating fast component options configurer

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

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

commit b9e55dd08891dc50141d1d94953af445db214e4b
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Aug 24 16:30:10 2019 +0200

    CAMEL-13870: Generating fast component options configurer
---
 .../component/properties/PropertiesComponent.java  |  14 +-
 .../src/main/java/org/apache/camel/Component.java  |   9 ++
 .../core/xml/AbstractCamelContextFactoryBean.java  |   2 +-
 .../component/log/LogComponentOptionsTest.java     | 146 +++++++++++++++++++++
 .../org/apache/camel/support/DefaultComponent.java |  19 ++-
 .../org/apache/camel/support/DefaultEndpoint.java  |   4 +-
 .../camel/support/PropertyBindingSupport.java      |   9 +-
 .../tools/apt/EndpointAnnotationProcessor.java     |  75 +++++++----
 8 files changed, 248 insertions(+), 30 deletions(-)

diff --git a/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java b/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
index 13f57de..1613bab 100644
--- a/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
+++ b/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
@@ -106,10 +106,11 @@ public class PropertiesComponent extends DefaultComponent implements org.apache.
     private PropertiesParser propertiesParser = new DefaultPropertiesParser(this);
     private final PropertiesLookup propertiesLookup = new DefaultPropertiesLookup(this);
     private final List<PropertiesSource> sources = new ArrayList<>();
-
     private List<PropertiesLocation> locations = Collections.emptyList();
 
     @Metadata
+    private String location;
+    @Metadata
     private boolean ignoreMissingLocation;
     @Metadata
     private String encoding;
@@ -282,7 +283,7 @@ public class PropertiesComponent extends DefaultComponent implements org.apache.
      * 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) {
+    private void setLocations(List<PropertiesLocation> locations) {
         // reset locations
         locations = parseLocations(locations);
         this.locations = Collections.unmodifiableList(locations);
@@ -324,6 +325,10 @@ public class PropertiesComponent extends DefaultComponent implements org.apache.
         setLocations(locations);
     }
 
+    public void addLocation(PropertiesLocation location) {
+        this.locations.add(location);
+    }
+
     @Override
     public void addLocation(String location) {
         if (location != null) {
@@ -345,11 +350,16 @@ public class PropertiesComponent extends DefaultComponent implements org.apache.
      */
     @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;
diff --git a/core/camel-api/src/main/java/org/apache/camel/Component.java b/core/camel-api/src/main/java/org/apache/camel/Component.java
index 32683c8..809c90e 100644
--- a/core/camel-api/src/main/java/org/apache/camel/Component.java
+++ b/core/camel-api/src/main/java/org/apache/camel/Component.java
@@ -73,6 +73,15 @@ public interface Component extends CamelContextAware, Service {
     boolean useRawUri();
 
     /**
+     * Gets the component {@link PropertyConfigurer}.
+     *
+     * @return the configurer, or <tt>null</tt> if the component does not support using property configurer.
+     */
+    default PropertyConfigurer getComponentPropertyConfigurer() {
+        return null;
+    }
+
+    /**
      * Gets the endpoint {@link PropertyConfigurer}.
      *
      * @return the configurer, or <tt>null</tt> if the endpoint does not support using property configurer.
diff --git a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
index 8fbc61d..abdbbd2 100644
--- a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
+++ b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
@@ -627,7 +627,7 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
             }
 
             PropertiesComponent pc = new PropertiesComponent();
-            pc.setLocations(locations);
+            locations.forEach(pc::addLocation);
             pc.setEncoding(def.getEncoding());
 
             if (def.isIgnoreMissingLocation() != null) {
diff --git a/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentOptionsTest.java b/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentOptionsTest.java
new file mode 100644
index 0000000..df492e1
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentOptionsTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.log;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.support.PropertyBindingSupport;
+import org.apache.camel.support.processor.DefaultExchangeFormatter;
+import org.junit.Test;
+
+public class LogComponentOptionsTest extends ContextTestSupport {
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    @Test
+    public void testFastLogComponentOptions() throws Exception {
+        context.start();
+
+        long before = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+        DefaultExchangeFormatter myFormatter = new DefaultExchangeFormatter();
+
+        LogComponent log = context.getComponent("log", LogComponent.class);
+        assertNull(log.getExchangeFormatter());
+
+        new PropertyBindingSupport.Builder().withCamelContext(context).withTarget(log)
+                .withConfigurer(log.getComponentPropertyConfigurer())
+                .withProperty("exchangeFormatter", myFormatter).bind();
+
+        assertSame(myFormatter, log.getExchangeFormatter());
+
+        long after = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+        assertEquals("Should not use Java reflection", before, after);
+    }
+
+    @Test
+    public void testFastLogComponentNestedOptions() throws Exception {
+        context.start();
+
+        long before = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+        DefaultExchangeFormatter myFormatter = new DefaultExchangeFormatter();
+
+        LogComponent log = context.getComponent("log", LogComponent.class);
+        assertNull(log.getExchangeFormatter());
+
+        new PropertyBindingSupport.Builder().withCamelContext(context).withTarget(log)
+                .withConfigurer(log.getComponentPropertyConfigurer())
+                .withProperty("exchangeFormatter", myFormatter)
+                .withProperty("exchangeFormatter.showExchangeId", "true").bind();
+
+        assertSame(myFormatter, log.getExchangeFormatter());
+
+        long after = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+        assertTrue("Should use Java reflection", after > before);
+    }
+
+    @Test
+    public void testFastLogComponentOptionsLookupRegistry() throws Exception {
+        context.start();
+
+        long before = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+        DefaultExchangeFormatter myFormatter = new DefaultExchangeFormatter();
+        context.getRegistry().bind("myGreatFormatter", myFormatter);
+
+        LogComponent log = context.getComponent("log", LogComponent.class);
+        assertNull(log.getExchangeFormatter());
+
+        new PropertyBindingSupport.Builder().withCamelContext(context).withTarget(log)
+                .withConfigurer(log.getComponentPropertyConfigurer())
+                .withProperty("exchangeFormatter", "#bean:myGreatFormatter").bind();
+
+        assertSame(myFormatter, log.getExchangeFormatter());
+
+        long after = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+        assertEquals("Should not use Java reflection", before, after);
+    }
+
+    @Test
+    public void testSlowLogComponentOptions() throws Exception {
+        context.start();
+
+        long before = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+        DefaultExchangeFormatter myFormatter = new DefaultExchangeFormatter();
+
+        LogComponent log = context.getComponent("log", LogComponent.class);
+        assertNull(log.getExchangeFormatter());
+
+        new PropertyBindingSupport.Builder().withCamelContext(context).withTarget(log)
+                .withProperty("exchangeFormatter", myFormatter)
+                .withProperty("exchangeFormatter.showExchangeId", "true").bind();
+
+        assertSame(myFormatter, log.getExchangeFormatter());
+        assertTrue(myFormatter.isShowExchangeId());
+
+        long after = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+        assertTrue("Should use reflection", after > before);
+    }
+
+    @Test
+    public void testSlowLogComponentOptionsLookupRegistry() throws Exception {
+        context.start();
+
+        long before = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+        DefaultExchangeFormatter myFormatter = new DefaultExchangeFormatter();
+        context.getRegistry().bind("myGreatFormatter", myFormatter);
+
+        LogComponent log = context.getComponent("log", LogComponent.class);
+        assertNull(log.getExchangeFormatter());
+
+        new PropertyBindingSupport.Builder().withCamelContext(context).withTarget(log)
+                .withProperty("exchangeFormatter", "#bean:myGreatFormatter")
+                .withProperty("exchangeFormatter.showExchangeId", "true").bind();
+
+        assertSame(myFormatter, log.getExchangeFormatter());
+        assertTrue(myFormatter.isShowExchangeId());
+
+        long after = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().getInvokedCounter();
+
+        assertTrue("Should use reflection", after > before);
+    }
+}
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/DefaultComponent.java b/core/camel-support/src/main/java/org/apache/camel/support/DefaultComponent.java
index d7fc017..4c6aa71 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/DefaultComponent.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/DefaultComponent.java
@@ -59,6 +59,7 @@ public abstract class DefaultComponent extends ServiceSupport implements Compone
 
     private static final String RESOURCE_PATH = "META-INF/services/org/apache/camel/configurer/";
 
+    private volatile TriPropertyConfigurer componentPropertyConfigurer;
     private volatile TriPropertyConfigurer endpointPropertyConfigurer;
     private final List<Supplier<ComponentExtension>> extensions = new ArrayList<>();
     private CamelContext camelContext;
@@ -339,8 +340,15 @@ public abstract class DefaultComponent extends ServiceSupport implements Compone
                 name = StringHelper.before(name, ",");
             }
             try {
-                log.trace("Discovering optional endpoint property configurer class for component: {}", name);
+                log.trace("Discovering optional component property configurer class for component: {}", name);
                 Optional<Class<?>> clazz = getCamelContext().adapt(ExtendedCamelContext.class).getFactoryFinder(RESOURCE_PATH)
+                        .findOptionalClass(name + "-component", null);
+                clazz.ifPresent(c -> componentPropertyConfigurer = org.apache.camel.support.ObjectHelper.newInstance(c, TriPropertyConfigurer.class));
+                if (log.isDebugEnabled() && componentPropertyConfigurer != null) {
+                    log.debug("Discovered component property configurer: {} -> {}", name, componentPropertyConfigurer);
+                }
+                log.trace("Discovering optional endpoint property configurer class for component: {}", name);
+                clazz = getCamelContext().adapt(ExtendedCamelContext.class).getFactoryFinder(RESOURCE_PATH)
                         .findOptionalClass(name + "-endpoint", null);
                 clazz.ifPresent(c -> endpointPropertyConfigurer = org.apache.camel.support.ObjectHelper.newInstance(c, TriPropertyConfigurer.class));
                 if (log.isDebugEnabled() && endpointPropertyConfigurer != null) {
@@ -407,7 +415,9 @@ public abstract class DefaultComponent extends ServiceSupport implements Compone
                     .bind(camelContext, bean, parameters);
         } else {
             PropertyConfigurer configurer = null;
-            if (bean instanceof Endpoint) {
+            if (bean instanceof Component) {
+                configurer = getComponentPropertyConfigurer();
+            } else if (bean instanceof Endpoint) {
                 configurer = getEndpointPropertyConfigurer();
             } else if (bean instanceof PropertyConfigurerAware) {
                 configurer = ((PropertyConfigurerAware) bean).getPropertyConfigurer(bean);
@@ -418,6 +428,11 @@ public abstract class DefaultComponent extends ServiceSupport implements Compone
     }
 
     @Override
+    public PropertyConfigurer getComponentPropertyConfigurer() {
+        return componentPropertyConfigurer;
+    }
+
+    @Override
     public PropertyConfigurer getEndpointPropertyConfigurer() {
         return endpointPropertyConfigurer;
     }
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/DefaultEndpoint.java b/core/camel-support/src/main/java/org/apache/camel/support/DefaultEndpoint.java
index 2041a5f..ff7b490 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/DefaultEndpoint.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/DefaultEndpoint.java
@@ -423,7 +423,9 @@ public abstract class DefaultEndpoint extends ServiceSupport implements Endpoint
                     .bind(camelContext, bean, parameters);
         } else {
             PropertyConfigurer configurer = null;
-            if (bean instanceof Endpoint) {
+            if (bean instanceof Component) {
+                configurer = getComponent().getComponentPropertyConfigurer();
+            } else if (bean instanceof Endpoint) {
                 configurer = getComponent().getEndpointPropertyConfigurer();
             } else if (bean instanceof PropertyConfigurerAware) {
                 configurer = ((PropertyConfigurerAware) bean).getPropertyConfigurer(bean);
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
index bdce66f..f45f557 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
@@ -465,7 +465,12 @@ public final class PropertyBindingSupport {
                 Map.Entry<String, Object> entry = iter.next();
                 String key = entry.getKey();
                 Object value = entry.getValue();
-                if (writeProperties.containsKey(key)) {
+                boolean valid = true;
+                if (nesting) {
+                    // property configurer does not support nested names so skip if the name has a dot
+                    valid = key.indexOf('.') == -1;
+                }
+                if (valid && writeProperties.containsKey(key)) {
                     writeProperties.get(key).accept(camelContext, target, value);
                     if (removeParameter) {
                         iter.remove();
@@ -641,6 +646,8 @@ public final class PropertyBindingSupport {
             }
         }
 
+        // TODO: Move configurer here so the value can have done all its magic
+
         boolean hit = context.adapt(ExtendedCamelContext.class).getBeanIntrospection().setProperty(context, context.getTypeConverter(), target, name, value, refName, fluentBuilder, allowPrivateSetter, ignoreCase);
         if (!hit && mandatory) {
             // there is no setter with this given name, so lets report this as a problem
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 8d7d7d6..bd396e1 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
@@ -163,39 +163,70 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
             }
         }
 
+        // component options
         TypeElement componentClassElement = findTypeElement(processingEnv, roundEnv, componentModel.getJavaType());
         if (componentClassElement != null) {
             findComponentClassProperties(writer, roundEnv, componentModel, componentOptions, componentClassElement, "", parentData, null, null);
         }
+        generateComponentConfigurer(roundEnv, uriEndpoint, scheme, schemes, componentModel, componentOptions);
 
+        // endpoint options
         findClassProperties(writer, roundEnv, componentModel, endpointPaths, endpointOptions, classElement, "", uriEndpoint.excludeProperties(), parentData, null, null);
-
         String json = createParameterJsonSchema(componentModel, componentOptions, endpointPaths, endpointOptions, schemes, parentData);
         writer.println(json);
+        generateEndpointConfigurer(roundEnv, classElement, uriEndpoint, scheme, schemes, componentModel, endpointOptions);
+    }
 
-        // special for activemq and amqp scheme which should reuse jms
+    private void generateComponentConfigurer(RoundEnvironment roundEnv, UriEndpoint uriEndpoint, String scheme, String[] schemes,
+                                             ComponentModel componentModel, Set<ComponentOption> componentOptions) {
+        TypeElement parent;
         if ("activemq".equals(scheme) || "amqp".equals(scheme)) {
-            TypeElement parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.component.jms.JmsEndpointConfigurer");
-            String fqen = classElement.getQualifiedName().toString();
-            String pn = fqen.substring(0, fqen.lastIndexOf('.'));
-            String en = classElement.getSimpleName().toString();
-            String cn = en + "Configurer";
-            String fqn = pn + "." + cn;
-
-            EndpointPropertyConfigurerGenerator.generateExtendConfigurer(processingEnv, parent, pn, cn, fqn);
-            EndpointPropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-endpoint", fqn);
-        } else if (uriEndpoint.generateConfigurer() && !endpointOptions.isEmpty()) {
-            TypeElement parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.spi.TriPropertyConfigurer");
-            String fqen = classElement.getQualifiedName().toString();
-            String pn = fqen.substring(0, fqen.lastIndexOf('.'));
-            String en = classElement.getSimpleName().toString();
-            String cn = en + "Configurer";
-            String fqn = pn + "." + cn;
+            // special for activemq and amqp scheme which should reuse jms
+            parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.component.jms.JmsComponentConfigurer");
+        } else {
+            parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.spi.TriPropertyConfigurer");
+        }
+        String fqComponentClassName = componentModel.getJavaType();
+        String componentClassName = fqComponentClassName.substring(fqComponentClassName.lastIndexOf('.') + 1);
+        String className = componentClassName + "Configurer";
+        String packageName = fqComponentClassName.substring(0, fqComponentClassName.lastIndexOf('.'));
+        String fqClassName = packageName + "." + className;
 
+        if ("activemq".equals(scheme) || "amqp".equals(scheme)) {
+            ComponentPropertyConfigurerGenerator.generateExtendConfigurer(processingEnv, parent, packageName, className, fqClassName);
+            ComponentPropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-component", fqClassName);
+        } else if (uriEndpoint.generateConfigurer() && !componentOptions.isEmpty()) {
             // only generate this once for the first scheme
             if (schemes == null || schemes[0].equals(scheme)) {
-                EndpointPropertyConfigurerGenerator.generatePropertyConfigurer(processingEnv, parent, pn, cn, fqn, en, endpointOptions);
-                EndpointPropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-endpoint", fqn);
+                ComponentPropertyConfigurerGenerator.generatePropertyConfigurer(processingEnv, parent, packageName, className, fqClassName, componentClassName, componentOptions);
+                ComponentPropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-component", fqClassName);
+            }
+        }
+    }
+
+    private void generateEndpointConfigurer(RoundEnvironment roundEnv, TypeElement classElement, UriEndpoint uriEndpoint, String scheme, String[] schemes,
+                                            ComponentModel componentModel, Set<EndpointOption> endpointOptions) {
+        TypeElement parent;
+        if ("activemq".equals(scheme) || "amqp".equals(scheme)) {
+            // special for activemq and amqp scheme which should reuse jms
+            parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.component.jms.JmsEndpointConfigurer");
+        } else {
+            parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.spi.TriPropertyConfigurer");
+        }
+        String fqEndpointClassName = classElement.getQualifiedName().toString();
+        String packageName = fqEndpointClassName.substring(0, fqEndpointClassName.lastIndexOf('.'));
+        String endpointClassName = classElement.getSimpleName().toString();
+        String className = endpointClassName + "Configurer";
+        String fqClassName = packageName + "." + className;
+
+        if ("activemq".equals(scheme) || "amqp".equals(scheme)) {
+            EndpointPropertyConfigurerGenerator.generateExtendConfigurer(processingEnv, parent, packageName, className, fqClassName);
+            EndpointPropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-endpoint", fqClassName);
+        } else if (uriEndpoint.generateConfigurer() && !endpointOptions.isEmpty()) {
+            // only generate this once for the first scheme
+            if (schemes == null || schemes[0].equals(scheme)) {
+                EndpointPropertyConfigurerGenerator.generatePropertyConfigurer(processingEnv, parent, packageName, className, fqClassName, endpointClassName, endpointOptions);
+                EndpointPropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-endpoint", fqClassName);
             }
         }
     }
@@ -542,11 +573,9 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                     continue;
                 }
 
-                // must be a getter/setter pair
+                // we usually favor putting the @Metadata annotation on the field instead of the setter, so try to use it if its there
                 String fieldName = methodName.substring(3);
                 fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
-
-                // we usually favor putting the @Metadata annotation on the field instead of the setter, so try to use it if its there
                 VariableElement field = findFieldElement(classElement, fieldName);
                 if (field != null && metadata == null) {
                     metadata = field.getAnnotation(Metadata.class);