sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jus...@apache.org
Subject svn commit: r1767030 - in /sling/trunk/bundles/extensions/models: ./ api/src/main/java/org/apache/sling/models/annotations/ api/src/main/java/org/apache/sling/models/export/ api/src/main/java/org/apache/sling/models/export/spi/ api/src/main/java/org/ap...
Date Fri, 28 Oct 2016 15:02:13 GMT
Author: justin
Date: Fri Oct 28 15:02:13 2016
New Revision: 1767030

URL: http://svn.apache.org/viewvc?rev=1767030&view=rev
Log:
SLING-6183 - add Sling Model Exporter feature

Added:
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/Exporter.java
      - copied, changed from r1767029, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/ExporterOption.java
      - copied, changed from r1767029, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/Exporters.java
      - copied, changed from r1767029, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/export/
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/export/spi/
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/export/spi/ModelExporter.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/export/spi/package-info.java
      - copied, changed from r1767029, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ExportException.java
      - copied, changed from r1767029, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/MissingExporterException.java
      - copied, changed from r1767029, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ExportServlet.java
    sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/
    sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/BaseComponent.java
    sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/ExporterTest.java
    sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/ExtendedComponent.java
    sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/FakeRequest.java
    sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/FakeResponse.java
    sling/trunk/bundles/extensions/models/jackson-exporter/
    sling/trunk/bundles/extensions/models/jackson-exporter/README.txt
    sling/trunk/bundles/extensions/models/jackson-exporter/pom.xml
    sling/trunk/bundles/extensions/models/jackson-exporter/src/
    sling/trunk/bundles/extensions/models/jackson-exporter/src/main/
    sling/trunk/bundles/extensions/models/jackson-exporter/src/main/java/
    sling/trunk/bundles/extensions/models/jackson-exporter/src/main/java/org/
    sling/trunk/bundles/extensions/models/jackson-exporter/src/main/java/org/apache/
    sling/trunk/bundles/extensions/models/jackson-exporter/src/main/java/org/apache/sling/
    sling/trunk/bundles/extensions/models/jackson-exporter/src/main/java/org/apache/sling/models/
    sling/trunk/bundles/extensions/models/jackson-exporter/src/main/java/org/apache/sling/models/jacksonexporter/
    sling/trunk/bundles/extensions/models/jackson-exporter/src/main/java/org/apache/sling/models/jacksonexporter/impl/
    sling/trunk/bundles/extensions/models/jackson-exporter/src/main/java/org/apache/sling/models/jacksonexporter/impl/JacksonExporter.java
Modified:
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ModelFactory.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelPackageBundleListener.java
    sling/trunk/bundles/extensions/models/integration-tests/pom.xml
    sling/trunk/bundles/extensions/models/pom.xml

Copied: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/Exporter.java (from r1767029, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java)
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/Exporter.java?p2=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/Exporter.java&p1=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java&r1=1767029&r2=1767030&rev=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java (original)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/Exporter.java Fri Oct 28 15:02:13 2016
@@ -14,7 +14,38 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.3.0")
 package org.apache.sling.models.annotations;
 
-import aQute.bnd.annotation.Version;
\ No newline at end of file
+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 {};
+
+}

Copied: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/ExporterOption.java (from r1767029, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java)
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/ExporterOption.java?p2=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/ExporterOption.java&p1=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java&r1=1767029&r2=1767030&rev=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java (original)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/ExporterOption.java Fri Oct 28 15:02:13 2016
@@ -14,7 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.3.0")
 package org.apache.sling.models.annotations;
 
-import aQute.bnd.annotation.Version;
\ No newline at end of file
+/**
+ * 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();
+}

Copied: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/Exporters.java (from r1767029, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java)
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/Exporters.java?p2=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/Exporters.java&p1=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java&r1=1767029&r2=1767030&rev=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java (original)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/Exporters.java Fri Oct 28 15:02:13 2016
@@ -14,7 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.3.0")
 package org.apache.sling.models.annotations;
 
-import aQute.bnd.annotation.Version;
\ No newline at end of file
+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();
+}

Modified: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java?rev=1767030&r1=1767029&r2=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java (original)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java Fri Oct 28 15:02:13 2016
@@ -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

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/export/spi/ModelExporter.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/export/spi/ModelExporter.java?rev=1767030&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/export/spi/ModelExporter.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/export/spi/ModelExporter.java Fri Oct 28 15:02:13 2016
@@ -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();
+
+}

Copied: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/export/spi/package-info.java (from r1767029, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java)
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/export/spi/package-info.java?p2=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/export/spi/package-info.java&p1=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java&r1=1767029&r2=1767030&rev=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/package-info.java (original)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/export/spi/package-info.java Fri Oct 28 15:02:13 2016
@@ -14,7 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.3.0")
-package org.apache.sling.models.annotations;
+
+@Version("1.0.0")
+package org.apache.sling.models.export.spi;
 
 import aQute.bnd.annotation.Version;
\ No newline at end of file

Copied: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ExportException.java (from r1767029, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java)
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ExportException.java?p2=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ExportException.java&p1=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java&r1=1767029&r2=1767030&rev=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java (original)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ExportException.java Fri Oct 28 15:02:13 2016
@@ -14,7 +14,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.2.0")
 package org.apache.sling.models.factory;
 
-import aQute.bnd.annotation.Version;
\ No newline at end of file
+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);
+    }
+}

Copied: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/MissingExporterException.java (from r1767029, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java)
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/MissingExporterException.java?p2=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/MissingExporterException.java&p1=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java&r1=1767029&r2=1767030&rev=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java (original)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/MissingExporterException.java Fri Oct 28 15:02:13 2016
@@ -14,7 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.2.0")
 package org.apache.sling.models.factory;
 
-import aQute.bnd.annotation.Version;
\ No newline at end of file
+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()));
+    }
+}

Modified: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ModelFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ModelFactory.java?rev=1767030&r1=1767029&r2=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ModelFactory.java (original)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ModelFactory.java Fri Oct 28 15:02:13 2016
@@ -25,6 +25,8 @@ import org.apache.sling.api.resource.Res
 
 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 interface ModelFactory {
     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;
+
 }

Modified: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java?rev=1767030&r1=1767029&r2=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java (original)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java Fri Oct 28 15:02:13 2016
@@ -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

Added: sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ExportServlet.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ExportServlet.java?rev=1767030&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ExportServlet.java (added)
+++ sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ExportServlet.java Fri Oct 28 15:02:13 2016
@@ -0,0 +1,121 @@
+/*
+ * 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.impl;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
+import org.apache.sling.models.factory.ExportException;
+import org.apache.sling.models.factory.MissingExporterException;
+import org.apache.sling.models.factory.ModelFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("serial")
+class ExportServlet extends SlingSafeMethodsServlet {
+
+    private static final Logger logger = LoggerFactory.getLogger(ExportServlet.class);
+
+    private final String exporterName;
+    private final String registeredSelector;
+    private final ModelFactory modelFactory;
+    private final ExportedObjectAccessor accessor;
+    private final Map<String, String> baseOptions;
+
+    public ExportServlet(ModelFactory modelFactory, String registeredSelector, String exporterName, ExportedObjectAccessor accessor,
+                         Map<String, String> baseOptions) {
+        this.modelFactory = modelFactory;
+        this.registeredSelector = registeredSelector;
+        this.exporterName = exporterName;
+        this.accessor = accessor;
+        this.baseOptions = baseOptions;
+    }
+
+    @Override
+    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
+            throws ServletException, IOException {
+        Map<String, String> options = createOptionMap(request);
+
+        String exported;
+        try {
+            exported = accessor.getExportedString(request, options, modelFactory, exporterName);
+        } catch (ExportException e) {
+            logger.error("Could not get serializer requested by model.");
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            return;
+        } catch (MissingExporterException e) {
+            logger.error("Could not get serialize model to JSON.", e);
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            return;
+        }
+        if (exported == null) {
+            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+            return;
+        }
+        response.setContentType(request.getResponseContentType());
+        response.getWriter().write(exported);
+    }
+
+    private Map<String, String> createOptionMap(SlingHttpServletRequest request) {
+        Map<String, String[]> parameterMap = request.getParameterMap();
+        String[] selectors = request.getRequestPathInfo().getSelectors();
+        Map<String, String> result = new HashMap<String, String>(baseOptions.size() + parameterMap.size() + selectors.length - 1);
+        result.putAll(baseOptions);
+        for (String selector : selectors) {
+            if (!selector.equals(registeredSelector)) {
+                result.put(selector, "true");
+            }
+        }
+
+        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
+            if (entry.getValue().length == 0) {
+                result.put(entry.getKey(), Boolean.TRUE.toString());
+            } else {
+                result.put(entry.getKey(), entry.getValue()[0]);
+            }
+        }
+        return result;
+    }
+
+    public interface ExportedObjectAccessor {
+        String getExportedString(SlingHttpServletRequest request, Map<String, String> options, ModelFactory modelFactory, String exporterName) throws ExportException, MissingExporterException;
+    }
+
+    public static final ExportedObjectAccessor RESOURCE = new ExportedObjectAccessor() {
+        @Override
+        public String getExportedString(SlingHttpServletRequest request, Map<String, String> options, ModelFactory modelFactory, String exporterName) throws ExportException, MissingExporterException {
+            return modelFactory.exportModelForResource(request.getResource(), exporterName, String.class, options);
+        }
+    };
+
+    public static final ExportedObjectAccessor REQUEST = new ExportedObjectAccessor() {
+        @Override
+        public String getExportedString(SlingHttpServletRequest request, Map<String, String> options, ModelFactory modelFactory, String exporterName) throws ExportException, MissingExporterException {
+            return modelFactory.exportModelForRequest(request, exporterName, String.class, options);
+        }
+    };
+
+}

Modified: sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java?rev=1767030&r1=1767029&r2=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java Fri Oct 28 15:02:13 2016
@@ -62,10 +62,13 @@ import org.apache.sling.commons.osgi.Pro
 import org.apache.sling.commons.osgi.RankedServices;
 import org.apache.sling.models.annotations.Model;
 import org.apache.sling.models.annotations.ValidationStrategy;
+import org.apache.sling.models.export.spi.ModelExporter;
+import org.apache.sling.models.factory.ExportException;
 import org.apache.sling.models.factory.InvalidAdaptableException;
 import org.apache.sling.models.factory.InvalidModelException;
 import org.apache.sling.models.factory.MissingElementException;
 import org.apache.sling.models.factory.MissingElementsException;
+import org.apache.sling.models.factory.MissingExporterException;
 import org.apache.sling.models.factory.ModelClassException;
 import org.apache.sling.models.factory.ModelFactory;
 import org.apache.sling.models.factory.PostConstructException;
@@ -173,6 +176,10 @@ public class ModelAdapterFactory impleme
     @Reference(cardinality=ReferenceCardinality.OPTIONAL_UNARY, policyOption=ReferencePolicyOption.GREEDY)
     private ModelValidation modelValidation = null;
 
+    @Reference(name = "modelExporter", cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC,
+            referenceInterface = ModelExporter.class)
+    private final @Nonnull RankedServices<ModelExporter> modelExporters = new RankedServices<ModelExporter>();
+
     ModelPackageBundleListener listener;
 
     final AdapterImplementations adapterImplementations = new AdapterImplementations();
@@ -1010,6 +1017,18 @@ public class ModelAdapterFactory impleme
         }
     }
 
+    protected void bindModelExporter(final ModelExporter s, final Map<String, Object> props) {
+        synchronized (modelExporters) {
+            modelExporters.bind(s, props);
+        }
+    }
+
+    protected void unbindModelExporter(final ModelExporter s, final Map<String, Object> props) {
+        synchronized (modelExporters) {
+            modelExporters.unbind(s, props);
+        }
+    }
+
     @Nonnull Collection<Injector> getInjectors() {
         return sortedInjectors.get();
     }
@@ -1066,4 +1085,58 @@ public class ModelAdapterFactory impleme
         }
     }
 
+    @Override
+    public <T> T exportModel(Object model, String name, Class<T> targetClass, Map<String, String> options)
+            throws ExportException, MissingExporterException {
+        for (ModelExporter exporter : modelExporters) {
+            if (exporter.getName().equals(name) && exporter.isSupported(targetClass)) {
+                T resultObject = exporter.export(model, targetClass, options);
+                return resultObject;
+            } else {
+                throw new MissingExporterException(name, targetClass);
+            }
+        }
+        throw new MissingExporterException(name, targetClass);
+    }
+
+    @Override
+    public <T> T exportModelForResource(Resource resource, String name, Class<T> targetClass, Map<String, String> options)
+            throws ExportException, MissingExporterException {
+        Class<?> clazz = this.adapterImplementations.getModelClassForResource(resource);
+        if (clazz == null) {
+            throw new ModelClassException("Could find model registered for resource type: " + resource.getResourceType());
+        }
+        Result<?> result = internalCreateModel(resource, clazz);
+        return handleAndExportResult(result, name, targetClass, options);
+    }
+
+    @Override
+    public <T> T exportModelForRequest(SlingHttpServletRequest request, String name, Class<T> targetClass, Map<String, String> options)
+            throws ExportException, MissingExporterException {
+        Class<?> clazz = this.adapterImplementations.getModelClassForRequest(request);
+        if (clazz == null) {
+            throw new ModelClassException("Could find model registered for request path: " + request.getServletPath());
+        }
+        Result<?> result = internalCreateModel(request, clazz);
+        handleAndExportResult(result, name, targetClass, options);
+        // unreachable
+        return null;
+    }
+
+    private <T> T handleAndExportResult(Result<?> result, String name, Class<T> targetClass, Map<String, String> options) throws ExportException, MissingExporterException {
+        if (result.wasSuccessful()) {
+            for (ModelExporter exporter : modelExporters) {
+                if (exporter.getName().equals(name) && exporter.isSupported(targetClass)) {
+                    T resultObject = exporter.export(result.getValue(), targetClass, options);
+                    return resultObject;
+                } else {
+                    throw new MissingExporterException(name, targetClass);
+                }
+            }
+            throw new MissingExporterException(name, targetClass);
+        } else {
+            throw result.getThrowable();
+        }
+    }
+
 }

Modified: sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelPackageBundleListener.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelPackageBundleListener.java?rev=1767030&r1=1767029&r2=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelPackageBundleListener.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelPackageBundleListener.java Fri Oct 28 15:02:13 2016
@@ -18,14 +18,22 @@ package org.apache.sling.models.impl;
 
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Dictionary;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.lang.StringUtils;
+import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.adapter.AdapterFactory;
+import org.apache.sling.api.resource.Resource;
 import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.models.annotations.Exporter;
+import org.apache.sling.models.annotations.ExporterOption;
+import org.apache.sling.models.annotations.Exporters;
 import org.apache.sling.models.annotations.Model;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -37,6 +45,8 @@ import org.osgi.util.tracker.BundleTrack
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.servlet.Servlet;
+
 public class ModelPackageBundleListener implements BundleTrackerCustomizer {
 
     static final String HEADER = "Sling-Model-Packages";
@@ -117,6 +127,23 @@ public class ModelPackageBundleListener
                                 if (StringUtils.isNotEmpty(resourceType)) {
                                     for (Class<?> adaptable : annotation.adaptables()) {
                                         adapterImplementations.registerModelToResourceType(bundle, resourceType, adaptable, implType);
+                                        ExportServlet.ExportedObjectAccessor accessor = null;
+                                        if (adaptable == Resource.class) {
+                                            accessor = ExportServlet.RESOURCE;
+                                        } else if (adaptable == SlingHttpServletRequest.class) {
+                                            accessor = ExportServlet.REQUEST;
+                                        }
+                                        Exporter exporterAnnotation = implType.getAnnotation(Exporter.class);
+                                        if (exporterAnnotation != null) {
+                                            registerExporter(bundle, implType, resourceType, exporterAnnotation, regs, accessor);
+                                        }
+                                        Exporters exportersAnnotation = implType.getAnnotation(Exporters.class);
+                                        if (exportersAnnotation != null) {
+                                            for (Exporter ann : exportersAnnotation.value()) {
+                                                registerExporter(bundle, implType, resourceType, ann, regs, accessor);
+                                            }
+                                        }
+
                                     }
                                 }
                             }
@@ -142,9 +169,11 @@ public class ModelPackageBundleListener
             for (ServiceRegistration reg : (ServiceRegistration[]) object) {
                 ServiceReference ref = reg.getReference();
                 String[] adapterTypeNames = PropertiesUtil.toStringArray(ref.getProperty(AdapterFactory.ADAPTER_CLASSES));
-                String implTypeName = PropertiesUtil.toString(ref.getProperty(PROP_IMPLEMENTATION_CLASS), null);
-                for (String adapterTypeName : adapterTypeNames) {
-                    adapterImplementations.remove(adapterTypeName, implTypeName);
+                if (adapterTypeNames != null) {
+                    String implTypeName = PropertiesUtil.toString(ref.getProperty(PROP_IMPLEMENTATION_CLASS), null);
+                    for (String adapterTypeName : adapterTypeNames) {
+                        adapterImplementations.remove(adapterTypeName, implTypeName);
+                    }
                 }
                 reg.unregister();
             }
@@ -210,5 +239,36 @@ public class ModelPackageBundleListener
         }
         return bundleContext.registerService(AdapterFactory.SERVICE_NAME, factory, registrationProps);
     }
-    
+
+
+    private void registerExporter(Bundle bundle, Class<?> implType, String resourceType, Exporter exporterAnnotation, List<ServiceRegistration> regs,
+                                  ExportServlet.ExportedObjectAccessor accessor) {
+        if (accessor != null) {
+            Map<String, String> baseOptions = getOptions(exporterAnnotation);
+            ExportServlet servlet = new ExportServlet(factory, exporterAnnotation.selector(), exporterAnnotation.name(), accessor, baseOptions);
+            Dictionary<String, Object> registrationProps = new Hashtable<String, Object>();
+            registrationProps.put("sling.servlet.resourceTypes", resourceType);
+            registrationProps.put("sling.servlet.selectors", exporterAnnotation.selector());
+            registrationProps.put("sling.servlet.extensions", exporterAnnotation.extensions());
+
+            log.info("registering servlet for {}, {}, {}", new Object[]{resourceType, exporterAnnotation.selector(), exporterAnnotation.extensions()});
+
+            ServiceRegistration reg = bundleContext.registerService(Servlet.class.getName(), servlet, registrationProps);
+            regs.add(reg);
+        }
+    }
+
+    private Map<String, String> getOptions(Exporter annotation) {
+        ExporterOption[] options = annotation.options();
+        if (options.length == 0) {
+            return Collections.emptyMap();
+        } else {
+            Map<String, String> map = new HashMap<String, String>(options.length);
+            for (ExporterOption option : options) {
+                map.put(option.name(), option.value());
+            }
+            return map;
+        }
+    }
+
 }

Modified: sling/trunk/bundles/extensions/models/integration-tests/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/integration-tests/pom.xml?rev=1767030&r1=1767029&r2=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/integration-tests/pom.xml (original)
+++ sling/trunk/bundles/extensions/models/integration-tests/pom.xml Fri Oct 28 15:02:13 2016
@@ -115,7 +115,8 @@
                             org.apache.sling.models.it.noclasses,
                             org.apache.sling.models.it.models,
                             org.apache.sling.models.it.rtbound,
-                            org.apache.sling.models.it.rtboundpicker
+                            org.apache.sling.models.it.rtboundpicker,
+                            org.apache.sling.models.it.exporter
                         </Sling-Model-Packages>
                         <Sling-Test-Regexp>.*Test</Sling-Test-Regexp>
                         <Export-Package>org.apache.sling.models.it</Export-Package>
@@ -242,7 +243,11 @@
                         <sling.additional.bundle.2>geronimo-atinject_1.0_spec</sling.additional.bundle.2>
                         <sling.additional.bundle.10>org.apache.sling.models.api</sling.additional.bundle.10>
                         <sling.additional.bundle.11>org.apache.sling.models.impl</sling.additional.bundle.11>
-                        <sling.additional.bundle.12>${project.build.finalName}.jar</sling.additional.bundle.12>
+                        <sling.additional.bundle.12>org.apache.sling.models.jacksonexporter</sling.additional.bundle.12>
+                        <sling.additional.bundle.13>jackson-annotations</sling.additional.bundle.13>
+                        <sling.additional.bundle.14>jackson-core</sling.additional.bundle.14>
+                        <sling.additional.bundle.15>jackson-databind</sling.additional.bundle.15>
+                        <sling.additional.bundle.16>${project.build.finalName}.jar</sling.additional.bundle.16>
                     </systemPropertyVariables>
                 </configuration>
             </plugin>
@@ -281,6 +286,12 @@
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.engine</artifactId>
+            <version>2.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.models.api</artifactId>
             <version>1.3.0-SNAPSHOT</version>
             <scope>provided</scope>
@@ -291,6 +302,31 @@
             <version>1.3.0-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.models.jacksonexporter</artifactId>
+            <version>0.0.99-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.3.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>2.3.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <version>2.3.2</version>
+            <scope>provided</scope>
+        </dependency>
+
         <!-- not part of launchpad 7 (see SLING-4710) -->
         <dependency>
             <groupId>org.apache.geronimo.specs</groupId>

Added: sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/BaseComponent.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/BaseComponent.java?rev=1767030&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/BaseComponent.java (added)
+++ sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/BaseComponent.java Fri Oct 28 15:02:13 2016
@@ -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.sling.models.it.exporter;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Exporter;
+import org.apache.sling.models.annotations.Model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Model(adaptables = { Resource.class }, resourceType = "sling/exp/base")
+@Exporter(name = "jackson", extensions = "json")
+public class BaseComponent {
+
+    private final Resource resource;
+
+    @Inject
+    private String sampleValue;
+
+    public BaseComponent(Resource resource) {
+        this.resource = resource;
+    }
+
+    public String getId() {
+        return this.resource.getPath();
+    }
+
+    public String getSampleValue() {
+        return sampleValue;
+    }
+
+    @JsonProperty(value="UPPER")
+    public String getSampleValueToUpperCase() {
+        return sampleValue.toUpperCase();
+    }
+}

Added: sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/ExporterTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/ExporterTest.java?rev=1767030&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/ExporterTest.java (added)
+++ sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/ExporterTest.java Fri Oct 28 15:02:13 2016
@@ -0,0 +1,190 @@
+/*
+ * 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.it.exporter;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.sling.api.SlingConstants;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.commons.json.JSONObject;
+import org.apache.sling.engine.SlingRequestProcessor;
+import org.apache.sling.junit.annotations.SlingAnnotationsTestRunner;
+import org.apache.sling.junit.annotations.TestReference;
+import org.apache.sling.models.factory.MissingExporterException;
+import org.apache.sling.models.factory.ModelFactory;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(SlingAnnotationsTestRunner.class)
+public class ExporterTest {
+
+    @TestReference
+    private ResourceResolverFactory rrFactory;
+
+    @TestReference
+    private ModelFactory modelFactory;
+
+    @TestReference
+    private SlingRequestProcessor slingRequestProcessor;
+
+    private final String baseComponentPath = "/content/exp/baseComponent";
+    private final String childComponentPath = "/content/exp/childComponent";
+    private final String extendedComponentPath = "/content/exp/extendedComponent";
+    private Calendar testDate;
+
+    @Before
+    public void setup() throws LoginException, PersistenceException {
+        ResourceResolver adminResolver = null;
+        try {
+            adminResolver = rrFactory.getAdministrativeResourceResolver(null);
+            Map<String, Object> properties = new HashMap<String, Object>();
+            properties.put("sampleValue", "baseTESTValue");
+            properties.put(SlingConstants.NAMESPACE_PREFIX + ":" + SlingConstants.PROPERTY_RESOURCE_TYPE,
+                    "sling/exp/base");
+            ResourceUtil.getOrCreateResource(adminResolver, baseComponentPath, properties, null, false);
+            properties.clear();
+
+            properties.put("sampleValue", "childTESTValue");
+            properties.put(SlingConstants.NAMESPACE_PREFIX + ":" + SlingConstants.PROPERTY_RESOURCE_TYPE,
+                    "sling/exp/child");
+            properties.put(SlingConstants.NAMESPACE_PREFIX + ":" + SlingConstants.PROPERTY_RESOURCE_SUPER_TYPE,
+                    "sling/exp/base");
+            ResourceUtil.getOrCreateResource(adminResolver, childComponentPath, properties, null, false);
+            properties.clear();
+
+            properties.put("sampleValue", "extendedTESTValue");
+            properties.put(SlingConstants.NAMESPACE_PREFIX + ":" + SlingConstants.PROPERTY_RESOURCE_TYPE,
+                    "sling/exp/extended");
+            testDate = Calendar.getInstance();
+            testDate.setTimeZone(TimeZone.getTimeZone("UTC"));
+            testDate.setTimeInMillis(0);
+            testDate.set(2015, 6, 29);
+            properties.put("date", testDate);
+            ResourceUtil.getOrCreateResource(adminResolver, extendedComponentPath, properties, null, false);
+            adminResolver.commit();
+        } finally {
+            if (adminResolver != null && adminResolver.isLive()) {
+                adminResolver.close();
+            }
+        }
+    }
+
+    @Test
+    public void testExportToJSON() throws Exception {
+        ResourceResolver resolver = null;
+        try {
+            resolver = rrFactory.getAdministrativeResourceResolver(null);
+            final Resource baseComponentResource = resolver.getResource(baseComponentPath);
+            Assert.assertNotNull(baseComponentResource);
+            String jsonData = modelFactory.exportModelForResource(baseComponentResource, "jackson", String.class,
+                    Collections.<String, String> emptyMap());
+            Assert.assertTrue("JSON Data should contain the property value",
+                    StringUtils.contains(jsonData, "baseTESTValue"));
+
+            final Resource extendedComponentResource = resolver.getResource(extendedComponentPath);
+            Assert.assertNotNull(extendedComponentResource);
+            jsonData = modelFactory.exportModelForResource(extendedComponentResource, "jackson", String.class,
+                    Collections.<String, String> emptyMap());
+            Assert.assertTrue("JSON Data should contain the property value",
+                    StringUtils.contains(jsonData, "extendedTESTValue"));
+        } finally {
+            if (resolver != null && resolver.isLive()) {
+                resolver.close();
+            }
+        }
+    }
+
+    @Test
+    public void testExportToMap() throws Exception {
+        ResourceResolver resolver = null;
+        try {
+            resolver = rrFactory.getAdministrativeResourceResolver(null);
+            final Resource baseComponentResource = resolver.getResource(baseComponentPath);
+            Assert.assertNotNull(baseComponentResource);
+            Map<String, Object> data = modelFactory.exportModelForResource(baseComponentResource, "jackson", Map.class,
+                    Collections.<String, String> emptyMap());
+            Assert.assertEquals("Should have resource value", "baseTESTValue", data.get("sampleValue"));
+            Assert.assertEquals("Should have resource value", "BASETESTVALUE", data.get("UPPER"));
+        } finally {
+            if (resolver != null && resolver.isLive()) {
+                resolver.close();
+            }
+        }
+    }
+
+    @Test
+    public void testServlets() throws Exception {
+        ResourceResolver resolver = null;
+        try {
+            resolver = rrFactory.getAdministrativeResourceResolver(null);
+            FakeResponse response = new FakeResponse();
+            slingRequestProcessor.processRequest(new FakeRequest(baseComponentPath + ".model.json"), response, resolver);
+            JSONObject obj = new JSONObject(response.getStringWriter().toString());
+            Assert.assertEquals("application/json", response.getContentType());
+            Assert.assertEquals("BASETESTVALUE", obj.getString("UPPER"));
+            Assert.assertEquals(baseComponentPath, obj.getString("id"));
+
+            response = new FakeResponse();
+            slingRequestProcessor.processRequest(new FakeRequest(extendedComponentPath + ".model.json"), response, resolver);
+            obj = new JSONObject(response.getStringWriter().toString());
+            Assert.assertEquals(extendedComponentPath, obj.getString("id"));
+            Assert.assertEquals(testDate.getTimeInMillis(), obj.getLong("date"));
+        } finally {
+            if (resolver != null && resolver.isLive()) {
+                resolver.close();
+            }
+        }
+    }
+
+    @Test
+    public void testFailedExport() throws Exception {
+        boolean thrown = false;
+        try {
+            ResourceResolver resolver = null;
+            try {
+                resolver = rrFactory.getAdministrativeResourceResolver(null);
+                final Resource baseComponentResource = resolver.getResource(baseComponentPath);
+                Assert.assertNotNull(baseComponentResource);
+                String data = modelFactory.exportModelForResource(baseComponentResource, "jaxb", String.class,
+                        Collections.<String, String>emptyMap());
+                Assert.fail("Should have thrown missing serializer error.");
+            } finally {
+                if (resolver != null && resolver.isLive()) {
+                    resolver.close();
+                }
+            }
+        } catch (MissingExporterException e) {
+            thrown = true;
+            Assert.assertEquals("No exporter named jaxb supports java.lang.String.", e.getMessage());
+        }
+        Assert.assertTrue(thrown);
+
+    }
+
+}

Added: sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/ExtendedComponent.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/ExtendedComponent.java?rev=1767030&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/ExtendedComponent.java (added)
+++ sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/ExtendedComponent.java Fri Oct 28 15:02:13 2016
@@ -0,0 +1,49 @@
+/*
+ * 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.it.exporter;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Exporter;
+import org.apache.sling.models.annotations.Model;
+
+import javax.inject.Inject;
+
+@Model(adaptables = { Resource.class }, resourceType = "sling/exp/extended")
+@Exporter(name = "jackson", extensions = "json")
+public class ExtendedComponent extends BaseComponent {
+
+    @Inject
+    private Date date;
+
+    public ExtendedComponent(Resource resource) {
+        super(resource);
+    }
+
+    public Calendar getDateByCalendar() {
+        Calendar cal = new GregorianCalendar();
+        cal.setTime(date);
+        return cal;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+}

Added: sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/FakeRequest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/FakeRequest.java?rev=1767030&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/FakeRequest.java (added)
+++ sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/FakeRequest.java Fri Oct 28 15:02:13 2016
@@ -0,0 +1,309 @@
+/*
+ * 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.it.exporter;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+class FakeRequest implements HttpServletRequest {
+
+    private final String path;
+
+    FakeRequest(String path) {
+        this.path = path;
+    }
+
+    @Override
+    public String getAuthType() {
+        return null;
+    }
+
+    @Override
+    public Cookie[] getCookies() {
+        return new Cookie[0];
+    }
+
+    @Override
+    public long getDateHeader(String name) {
+        return 0;
+    }
+
+    @Override
+    public String getHeader(String name) {
+        return null;
+    }
+
+    @Override
+    public Enumeration getHeaders(String name) {
+        return null;
+    }
+
+    @Override
+    public Enumeration getHeaderNames() {
+        return null;
+    }
+
+    @Override
+    public int getIntHeader(String name) {
+        return 0;
+    }
+
+    @Override
+    public String getMethod() {
+        return "GET";
+    }
+
+    @Override
+    public String getPathInfo() {
+        return path;
+    }
+
+    @Override
+    public String getPathTranslated() {
+        return null;
+    }
+
+    @Override
+    public String getContextPath() {
+        return null;
+    }
+
+    @Override
+    public String getQueryString() {
+        return null;
+    }
+
+    @Override
+    public String getRemoteUser() {
+        return null;
+    }
+
+    @Override
+    public boolean isUserInRole(String role) {
+        return false;
+    }
+
+    @Override
+    public Principal getUserPrincipal() {
+        return null;
+    }
+
+    @Override
+    public String getRequestedSessionId() {
+        return null;
+    }
+
+    @Override
+    public String getRequestURI() {
+        return null;
+    }
+
+    @Override
+    public StringBuffer getRequestURL() {
+        return null;
+    }
+
+    @Override
+    public String getServletPath() {
+        return "";
+    }
+
+    @Override
+    public HttpSession getSession(boolean create) {
+        return null;
+    }
+
+    @Override
+    public HttpSession getSession() {
+        return null;
+    }
+
+    @Override
+    public boolean isRequestedSessionIdValid() {
+        return false;
+    }
+
+    @Override
+    public boolean isRequestedSessionIdFromCookie() {
+        return false;
+    }
+
+    @Override
+    public boolean isRequestedSessionIdFromURL() {
+        return false;
+    }
+
+    @Override
+    public boolean isRequestedSessionIdFromUrl() {
+        return false;
+    }
+
+    @Override
+    public Object getAttribute(String name) {
+        return null;
+    }
+
+    @Override
+    public Enumeration getAttributeNames() {
+        return null;
+    }
+
+    @Override
+    public String getCharacterEncoding() {
+        return null;
+    }
+
+    @Override
+    public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
+
+    }
+
+    @Override
+    public int getContentLength() {
+        return 0;
+    }
+
+    @Override
+    public String getContentType() {
+        return null;
+    }
+
+    @Override
+    public ServletInputStream getInputStream() throws IOException {
+        return null;
+    }
+
+    @Override
+    public String getParameter(String name) {
+        return null;
+    }
+
+    @Override
+    public Enumeration getParameterNames() {
+        return null;
+    }
+
+    @Override
+    public String[] getParameterValues(String name) {
+        return new String[0];
+    }
+
+    @Override
+    public Map getParameterMap() {
+        return null;
+    }
+
+    @Override
+    public String getProtocol() {
+        return null;
+    }
+
+    @Override
+    public String getScheme() {
+        return null;
+    }
+
+    @Override
+    public String getServerName() {
+        return null;
+    }
+
+    @Override
+    public int getServerPort() {
+        return 0;
+    }
+
+    @Override
+    public BufferedReader getReader() throws IOException {
+        return null;
+    }
+
+    @Override
+    public String getRemoteAddr() {
+        return null;
+    }
+
+    @Override
+    public String getRemoteHost() {
+        return null;
+    }
+
+    @Override
+    public void setAttribute(String name, Object o) {
+
+    }
+
+    @Override
+    public void removeAttribute(String name) {
+
+    }
+
+    @Override
+    public Locale getLocale() {
+        return null;
+    }
+
+    @Override
+    public Enumeration getLocales() {
+        return null;
+    }
+
+    @Override
+    public boolean isSecure() {
+        return false;
+    }
+
+    @Override
+    public RequestDispatcher getRequestDispatcher(String path) {
+        return null;
+    }
+
+    @Override
+    public String getRealPath(String path) {
+        return null;
+    }
+
+    @Override
+    public int getRemotePort() {
+        return 0;
+    }
+
+    @Override
+    public String getLocalName() {
+        return null;
+    }
+
+    @Override
+    public String getLocalAddr() {
+        return null;
+    }
+
+    @Override
+    public int getLocalPort() {
+        return 0;
+    }
+}

Added: sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/FakeResponse.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/FakeResponse.java?rev=1767030&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/FakeResponse.java (added)
+++ sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/exporter/FakeResponse.java Fri Oct 28 15:02:13 2016
@@ -0,0 +1,194 @@
+/*
+ * 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.it.exporter;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Locale;
+
+class FakeResponse implements HttpServletResponse {
+
+    private StringWriter stringWriter = new StringWriter();
+    private String contentType = null;
+
+    @Override
+    public void addCookie(Cookie cookie) {
+    }
+
+    @Override
+    public boolean containsHeader(String name) {
+        return false;
+    }
+
+    @Override
+    public String encodeURL(String url) {
+        return null;
+    }
+
+    @Override
+    public String encodeRedirectURL(String url) {
+        return null;
+    }
+
+    @Override
+    public String encodeUrl(String url) {
+        return null;
+    }
+
+    @Override
+    public String encodeRedirectUrl(String url) {
+        return null;
+    }
+
+    @Override
+    public void sendError(int sc, String msg) throws IOException {
+
+    }
+
+    @Override
+    public void sendError(int sc) throws IOException {
+
+    }
+
+    @Override
+    public void sendRedirect(String location) throws IOException {
+
+    }
+
+    @Override
+    public void setDateHeader(String name, long date) {
+
+    }
+
+    @Override
+    public void addDateHeader(String name, long date) {
+
+    }
+
+    @Override
+    public void setHeader(String name, String value) {
+
+    }
+
+    @Override
+    public void addHeader(String name, String value) {
+
+    }
+
+    @Override
+    public void setIntHeader(String name, int value) {
+
+    }
+
+    @Override
+    public void addIntHeader(String name, int value) {
+
+    }
+
+    @Override
+    public void setStatus(int sc) {
+
+    }
+
+    @Override
+    public void setStatus(int sc, String sm) {
+
+    }
+
+    @Override
+    public String getCharacterEncoding() {
+        return null;
+    }
+
+    @Override
+    public String getContentType() {
+        return contentType;
+    }
+
+    @Override
+    public ServletOutputStream getOutputStream() throws IOException {
+        return null;
+    }
+
+    @Override
+    public PrintWriter getWriter() throws IOException {
+        return new PrintWriter(stringWriter);
+    }
+
+    @Override
+    public void setCharacterEncoding(String charset) {
+
+    }
+
+    @Override
+    public void setContentLength(int len) {
+
+    }
+
+    @Override
+    public void setContentType(String type) {
+        this.contentType = type;
+    }
+
+    @Override
+    public void setBufferSize(int size) {
+
+    }
+
+    @Override
+    public int getBufferSize() {
+        return 0;
+    }
+
+    @Override
+    public void flushBuffer() throws IOException {
+
+    }
+
+    @Override
+    public void resetBuffer() {
+
+    }
+
+    @Override
+    public boolean isCommitted() {
+        return false;
+    }
+
+    @Override
+    public void reset() {
+
+    }
+
+    @Override
+    public void setLocale(Locale loc) {
+
+    }
+
+    @Override
+    public Locale getLocale() {
+        return null;
+    }
+
+    public StringWriter getStringWriter() {
+        return stringWriter;
+    }
+}

Added: sling/trunk/bundles/extensions/models/jackson-exporter/README.txt
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/jackson-exporter/README.txt?rev=1767030&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/jackson-exporter/README.txt (added)
+++ sling/trunk/bundles/extensions/models/jackson-exporter/README.txt Fri Oct 28 15:02:13 2016
@@ -0,0 +1,24 @@
+Apache Sling Models Jackson Exporter
+
+Getting Started
+===============
+
+This component uses a Maven 2 (http://maven.apache.org/) build
+environment. It requires a Java 5 JDK (or higher) and Maven (http://maven.apache.org/)
+2.0.7 or later. We recommend to use the latest Maven version.
+
+If you have Maven 2 installed, you can compile and
+package the jar using the following command:
+
+    mvn package
+
+See the Maven 2 documentation for other build features.
+
+The latest source code for this component is available in the
+Subversion (http://subversion.tigris.org/) source repository of
+the Apache Software Foundation. If you have Subversion installed,
+you can checkout the latest source using the following command:
+
+    svn checkout http://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/jackson-exporter
+
+See the Subversion documentation for other source control features.

Added: sling/trunk/bundles/extensions/models/jackson-exporter/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/jackson-exporter/pom.xml?rev=1767030&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/jackson-exporter/pom.xml (added)
+++ sling/trunk/bundles/extensions/models/jackson-exporter/pom.xml Fri Oct 28 15:02:13 2016
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
+    license agreements. See the NOTICE file distributed with this work for additional 
+    information regarding copyright ownership. The ASF licenses this file to 
+    you under the Apache License, Version 2.0 (the "License"); you may not use 
+    this file except in compliance with the License. You may obtain a copy of 
+    the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required 
+    by applicable law or agreed to in writing, software distributed under the 
+    License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 
+    OF ANY KIND, either express or implied. See the License for the specific 
+    language governing permissions and limitations under the License. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>25</version>
+        <relativePath />
+    </parent>
+
+    <artifactId>org.apache.sling.models.jacksonexporter</artifactId>
+    <version>0.0.99-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    <name>Apache Sling Models Model Exporter - Jackson</name>
+    <description>Apache Sling Models Client Model Exporter - Jackson</description>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/jackson-exporter</connection>
+        <developerConnection> scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/jackson-exporter</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/jackson-exporter</url>
+    </scm>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Embed-Dependency>*;scope=compile</Embed-Dependency>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.models.api</artifactId>
+            <version>1.3.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.9.5</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+            <version>3.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <version>2.6</version>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!-- *************************************************************** -->
+        <!-- JACKSON -->
+        <!-- *************************************************************** -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.3.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>2.3.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <version>2.3.2</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>

Added: sling/trunk/bundles/extensions/models/jackson-exporter/src/main/java/org/apache/sling/models/jacksonexporter/impl/JacksonExporter.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/jackson-exporter/src/main/java/org/apache/sling/models/jacksonexporter/impl/JacksonExporter.java?rev=1767030&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/jackson-exporter/src/main/java/org/apache/sling/models/jacksonexporter/impl/JacksonExporter.java (added)
+++ sling/trunk/bundles/extensions/models/jackson-exporter/src/main/java/org/apache/sling/models/jacksonexporter/impl/JacksonExporter.java Fri Oct 28 15:02:13 2016
@@ -0,0 +1,139 @@
+/*
+ * 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.jacksonexporter.impl;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.MapperFeature;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.models.export.spi.ModelExporter;
+import org.apache.sling.models.factory.ExportException;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.SerializableString;
+import com.fasterxml.jackson.core.io.CharacterEscapes;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+@Service
+public class JacksonExporter implements ModelExporter {
+
+    private static final Logger log = LoggerFactory.getLogger(JacksonExporter.class);
+
+    private static final String SERIALIZATION_FEATURE_PREFIX = SerializationFeature.class.getSimpleName() + ".";
+
+    private static final int SERIALIZATION_FEATURE_PREFIX_LENGTH = SERIALIZATION_FEATURE_PREFIX.length();
+
+    private static final String MAPPER_FEATURE_PREFIX = MapperFeature.class.getSimpleName() + ".";
+
+    private static final int MAPPER_FEATURE_PREFIX_LENGTH = MAPPER_FEATURE_PREFIX.length();
+
+    @Override
+    public boolean isSupported(Class<?> clazz) {
+        return clazz.equals(String.class) || clazz.equals(Map.class);
+    }
+
+    @Override
+    public <T> T export(Object model, Class<T> clazz, Map<String, String> options)
+            throws ExportException {
+        ObjectMapper mapper = new ObjectMapper();
+        for (Map.Entry<String, String> optionEntry : options.entrySet()) {
+            String key = optionEntry.getKey();
+            if (key.startsWith(SERIALIZATION_FEATURE_PREFIX)) {
+                String enumName = key.substring(SERIALIZATION_FEATURE_PREFIX_LENGTH);
+                try {
+                    SerializationFeature feature = SerializationFeature.valueOf(enumName);
+                    mapper.configure(feature, Boolean.valueOf(optionEntry.getValue()));
+                } catch (IllegalArgumentException e) {
+                    log.warn("Bad SerializationFeature option");
+                }
+            } else if (key.startsWith(MAPPER_FEATURE_PREFIX)) {
+                String enumName = key.substring(MAPPER_FEATURE_PREFIX_LENGTH);
+                try {
+                    MapperFeature feature = MapperFeature.valueOf(enumName);
+                    mapper.configure(feature, Boolean.valueOf(optionEntry.getValue()));
+                } catch (IllegalArgumentException e) {
+                    log.warn("Bad SerializationFeature option");
+                }
+            }
+        }
+
+        if (clazz.equals(Map.class)) {
+            return (T) mapper.convertValue(model, Map.class);
+        } else if (clazz.equals(String.class)) {
+            final JsonFactory f = new JsonFactory();
+            f.setCharacterEscapes(new EscapeCloseScriptBlocks());
+            StringWriter writer = new StringWriter();
+            JsonGenerator jgen;
+            final boolean printTidy;
+            if (options.containsKey("tidy")) {
+                printTidy = Boolean.valueOf(options.get("tidy"));
+            } else {
+                printTidy = false;
+            }
+            try {
+                jgen = f.createGenerator(writer);
+                if (printTidy) {
+                    mapper.writerWithDefaultPrettyPrinter().writeValue(jgen, model);
+                } else {
+                    mapper.writeValue(jgen, model);
+                }
+            } catch (final IOException e) {
+                throw new ExportException(e);
+            }
+            return (T) writer.toString();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public String getName() {
+        return "jackson";
+    }
+
+    private static class EscapeCloseScriptBlocks extends CharacterEscapes {
+        private final int[] escapes;
+
+        public EscapeCloseScriptBlocks() {
+            int[] baseEscapes = standardAsciiEscapesForJSON();
+            baseEscapes['<'] = CharacterEscapes.ESCAPE_STANDARD;
+            baseEscapes['>'] = CharacterEscapes.ESCAPE_STANDARD;
+            escapes = baseEscapes;
+        }
+
+        @Override
+        public int[] getEscapeCodesForAscii() {
+            return escapes;
+        }
+
+        @Override
+        public SerializableString getEscapeSequence(final int arg0) {
+            return null;
+        }
+    }
+}

Modified: sling/trunk/bundles/extensions/models/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/pom.xml?rev=1767030&r1=1767029&r2=1767030&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/pom.xml (original)
+++ sling/trunk/bundles/extensions/models/pom.xml Fri Oct 28 15:02:13 2016
@@ -42,6 +42,7 @@
     <modules>
         <module>api</module>
         <module>impl</module>
+        <module>jackson-exporter</module>
         <module>integration-tests</module>
     </modules>
 



Mime
View raw message