SLING-6183 - add Sling Model Exporter feature

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1767030 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/models/annotations/Exporter.java b/src/main/java/org/apache/sling/models/annotations/Exporter.java
new file mode 100644
index 0000000..6470f91
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/annotations/Exporter.java
@@ -0,0 +1,51 @@
+/*
+ * 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.sling.models.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to declare an model exporter servlet mapping.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Exporter {
+
+    /**
+     * Exporter name.
+     */
+    String name();
+
+    /**
+     * The selector name under which the exporter servlet will be registered.
+     */
+    String selector() default "model";
+
+    /**
+     * Extensions under which the exporter servlet will be registered.
+     */
+    String[] extensions();
+
+    /**
+     * Optional list of exporter options.
+     */
+    ExporterOption[] options() default {};
+
+}
diff --git a/src/main/java/org/apache/sling/models/annotations/ExporterOption.java b/src/main/java/org/apache/sling/models/annotations/ExporterOption.java
new file mode 100644
index 0000000..d67813d
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/annotations/ExporterOption.java
@@ -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.sling.models.annotations;
+
+/**
+ * Annotation to specify options on an model's export operation.
+ */
+public @interface ExporterOption {
+
+    /**
+     * Name of the exporter option.
+     */
+    String name();
+
+    /**
+     * Value of the exporter option.
+     */
+    String value();
+}
diff --git a/src/main/java/org/apache/sling/models/annotations/Exporters.java b/src/main/java/org/apache/sling/models/annotations/Exporters.java
new file mode 100644
index 0000000..b38943f
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/annotations/Exporters.java
@@ -0,0 +1,35 @@
+/*
+ * 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.sling.models.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to declare multiple exporter servlets for a model.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Exporters {
+
+    /**
+     * List of exporters.
+     */
+    Exporter[] value();
+}
diff --git a/src/main/java/org/apache/sling/models/annotations/package-info.java b/src/main/java/org/apache/sling/models/annotations/package-info.java
index 8da3547..1cf7e55 100644
--- a/src/main/java/org/apache/sling/models/annotations/package-info.java
+++ b/src/main/java/org/apache/sling/models/annotations/package-info.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.3.0")
+@Version("1.4.0")
 package org.apache.sling.models.annotations;
 
 import aQute.bnd.annotation.Version;
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/export/spi/ModelExporter.java b/src/main/java/org/apache/sling/models/export/spi/ModelExporter.java
new file mode 100644
index 0000000..919ebb5
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/export/spi/ModelExporter.java
@@ -0,0 +1,58 @@
+/*
+ * 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.sling.models.export.spi;
+
+import aQute.bnd.annotation.ConsumerType;
+import org.apache.sling.models.factory.ExportException;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import java.util.Map;
+
+/**
+ * SPI interface for model exporters.
+ */
+@ConsumerType
+public interface ModelExporter {
+
+    /**
+     * Check if the result class is supported by this exporter.
+     *
+     * @param clazz the result class
+     * @return true if the result class is supported
+     */
+    boolean isSupported(@Nonnull Class<?> clazz);
+
+    /**
+     * Export the provided model to the defined class using the options.
+     *
+     * @param model the model class
+     * @param clazz the export type
+     * @param options export options
+     * @param <T> the export type
+     * @return an exported object
+     * @throws ExportException if the export is not successful
+     */
+    @CheckForNull <T> T export(@Nonnull Object model, @Nonnull Class<T> clazz, @Nonnull Map<String, String> options) throws ExportException;
+
+    /**
+     * The name of the exporter.
+     * @return the name of the exporter
+     */
+    @Nonnull String getName();
+
+}
diff --git a/src/main/java/org/apache/sling/models/export/spi/package-info.java b/src/main/java/org/apache/sling/models/export/spi/package-info.java
new file mode 100644
index 0000000..292b494
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/export/spi/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+@Version("1.0.0")
+package org.apache.sling.models.export.spi;
+
+import aQute.bnd.annotation.Version;
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/factory/ExportException.java b/src/main/java/org/apache/sling/models/factory/ExportException.java
new file mode 100644
index 0000000..1d71bb2
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/factory/ExportException.java
@@ -0,0 +1,31 @@
+/*
+ * 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.sling.models.factory;
+
+import aQute.bnd.annotation.ProviderType;
+
+@ProviderType
+@SuppressWarnings("serial")
+public class ExportException extends Exception {
+    public ExportException(final String message) {
+        super(message);
+    }
+
+    public ExportException(final Throwable e) {
+        super(e);
+    }
+}
diff --git a/src/main/java/org/apache/sling/models/factory/MissingExporterException.java b/src/main/java/org/apache/sling/models/factory/MissingExporterException.java
new file mode 100644
index 0000000..1cb7717
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/factory/MissingExporterException.java
@@ -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.
+ */
+package org.apache.sling.models.factory;
+
+import aQute.bnd.annotation.ProviderType;
+
+@ProviderType
+@SuppressWarnings("serial")
+public class MissingExporterException extends Exception {
+    public MissingExporterException(final String name, final Class<?> targetClass) {
+        super(String.format("No exporter named %s supports %s.", name, targetClass.getName()));
+    }
+}
diff --git a/src/main/java/org/apache/sling/models/factory/ModelFactory.java b/src/main/java/org/apache/sling/models/factory/ModelFactory.java
index 3717625..e061796 100644
--- a/src/main/java/org/apache/sling/models/factory/ModelFactory.java
+++ b/src/main/java/org/apache/sling/models/factory/ModelFactory.java
@@ -25,6 +25,8 @@
 
 import aQute.bnd.annotation.ProviderType;
 
+import java.util.Map;
+
 /**
  * The ModelFactory instantiates Sling Model classes similar to #adaptTo but will throw an exception in case
  * instantiation fails for some reason.
@@ -126,4 +128,62 @@
     public Object getModelFromRequest(@Nonnull SlingHttpServletRequest request) throws MissingElementsException,
             InvalidAdaptableException, ModelClassException, PostConstructException, ValidationException, InvalidModelException;
 
+    /**
+     * Export the model object using the defined target class using the named exporter.
+     *
+     * @param model the model object
+     * @param exporterName the exporter name
+     * @param targetClass the target class
+     * @param options any exporter options
+     * @param <T> the target class
+     * @return an instance of the target class
+     * @throws ExportException if the export fails
+     * @throws MissingExporterException if the named exporter can't be found
+     */
+    public <T> T exportModel(Object model, String exporterName, Class<T> targetClass, Map<String, String> options) throws ExportException, MissingExporterException;
+
+    /**
+     * Export the model object registered to the resource's type using the defined target class using the named exporter.
+     *
+     * @param resource the resource
+     * @param exporterName the exporter name
+     * @param targetClass the target class
+     * @param options any exporter options
+     * @param <T> the target class
+     * @return an instance of the target class
+     * @throws MissingElementsException in case no injector was able to inject some required values with the given types
+     * @throws InvalidAdaptableException in case the given class cannot be instantiated from the given adaptable (different adaptable on the model annotation)
+     * @throws ModelClassException in case the model could not be instantiated because model annotation was missing, reflection failed, no valid constructor was found, model was not registered as adapter factory yet, or post-construct could not be called
+     * @throws PostConstructException in case the post-construct method has thrown an exception itself
+     * @throws ValidationException in case validation could not be performed for some reason (e.g. no validation information available)
+     * @throws InvalidModelException in case the given model type could not be validated through the model validation
+     * @throws ExportException if the export fails
+     * @throws MissingExporterException if the named exporter can't be found
+     */
+    public <T> T exportModelForResource(Resource resource, String exporterName, Class<T> targetClass, Map<String, String> options) throws MissingElementsException,
+            InvalidAdaptableException, ModelClassException, PostConstructException, ValidationException, InvalidModelException,
+            ExportException, MissingExporterException;
+
+    /**
+     * Export the model object registered to the request's resource's type using the defined target class using the named exporter.
+     *
+     * @param request the request
+     * @param exporterName the exporter name
+     * @param targetClass the target class
+     * @param options any exporter options
+     * @param <T> the target class
+     * @return an instance of the target class
+     * @throws MissingElementsException in case no injector was able to inject some required values with the given types
+     * @throws InvalidAdaptableException in case the given class cannot be instantiated from the given adaptable (different adaptable on the model annotation)
+     * @throws ModelClassException in case the model could not be instantiated because model annotation was missing, reflection failed, no valid constructor was found, model was not registered as adapter factory yet, or post-construct could not be called
+     * @throws PostConstructException in case the post-construct method has thrown an exception itself
+     * @throws ValidationException in case validation could not be performed for some reason (e.g. no validation information available)
+     * @throws InvalidModelException in case the given model type could not be validated through the model validation
+     * @throws ExportException if the export fails
+     * @throws MissingExporterException if the named exporter can't be found
+     */
+    public <T> T exportModelForRequest(SlingHttpServletRequest request, String exporterName, Class<T> targetClass, Map<String, String> options) throws MissingElementsException,
+            InvalidAdaptableException, ModelClassException, PostConstructException, ValidationException, InvalidModelException,
+            ExportException, MissingExporterException;
+
 }
diff --git a/src/main/java/org/apache/sling/models/factory/package-info.java b/src/main/java/org/apache/sling/models/factory/package-info.java
index 66be608..8ee2982 100644
--- a/src/main/java/org/apache/sling/models/factory/package-info.java
+++ b/src/main/java/org/apache/sling/models/factory/package-info.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.2.0")
+@Version("1.3.0")
 package org.apache.sling.models.factory;
 
 import aQute.bnd.annotation.Version;
\ No newline at end of file