Merge branch 'master' into feature/sling-cpconverter-maven-plugin
diff --git a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/JcrPersist.java b/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/JcrPersist.java
deleted file mode 100755
index de608ed..0000000
--- a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/JcrPersist.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.apache.sling.models.persist;
-
-import javax.jcr.RepositoryException;
-import org.apache.sling.api.resource.PersistenceException;
-import org.apache.sling.api.resource.ResourceResolver;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * Definition of JCR Persist service
- */
-public interface JcrPersist {
-
- void persist(@NotNull Object object, @NotNull ResourceResolver resourceResolver) throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException;
-
- void persist(@NotNull Object object, @NotNull ResourceResolver resourceResolver, boolean deepPersist) throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException;
-
- void persist(String nodePath, @NotNull Object object, @NotNull ResourceResolver resourceResolver) throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException;
-
- void persist(String nodePath, @NotNull Object object, @NotNull ResourceResolver resourceResolver, boolean deepPersist) throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException;
-
-}
diff --git a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/annotations/ChildType.java b/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/annotations/ChildType.java
deleted file mode 100755
index da454d2..0000000
--- a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/annotations/ChildType.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * #%L
- * ACS AEM Commons Bundle
- * %%
- * Copyright (C) 2016 Adobe
- * %%
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * #L%
- */
-package org.apache.sling.models.persist.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
-public @interface ChildType {
- String value() default "";
-}
diff --git a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/annotations/DirectDescendants.java b/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/annotations/DirectDescendants.java
deleted file mode 100755
index 498f50f..0000000
--- a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/annotations/DirectDescendants.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * #%L
- * ACS AEM Commons Bundle
- * %%
- * Copyright (C) 2016 Adobe
- * %%
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * #L%
- */
-
-package org.apache.sling.models.persist.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Marker annotation to indicate that collection elements
- * are direct descendants of this node and do not have an extra
- * wrapper child node around.
- *
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ ElementType.FIELD, ElementType.METHOD })
-public @interface DirectDescendants {
-
-}
diff --git a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/annotations/Ignore.java b/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/annotations/Ignore.java
deleted file mode 100755
index e998dc1..0000000
--- a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/annotations/Ignore.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * #%L
- * ACS AEM Commons Bundle
- * %%
- * Copyright (C) 2016 Adobe
- * %%
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * #L%
- */
-
-package org.apache.sling.models.persist.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Marker annotation to signify that the attribute be excluded
- * from all serialization/deserialization workflows.
- *
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ ElementType.FIELD, ElementType.METHOD })
-public @interface Ignore {
-
-}
diff --git a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/annotations/ResourceType.java b/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/annotations/ResourceType.java
deleted file mode 100755
index 69b19b9..0000000
--- a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/annotations/ResourceType.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * #%L
- * ACS AEM Commons Bundle
- * %%
- * Copyright (C) 2016 Adobe
- * %%
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * #L%
- */
-package org.apache.sling.models.persist.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
-public @interface ResourceType {
- String value() default "";
-}
diff --git a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/impl/util/AssertUtils.java b/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/impl/util/AssertUtils.java
deleted file mode 100755
index f994b23..0000000
--- a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/impl/util/AssertUtils.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * #%L
- * ACS AEM Commons Bundle
- * %%
- * Copyright (C) 2016 Adobe
- * %%
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * #L%
- */
-package org.apache.sling.models.persist.impl.util;
-
-/**
- * Simple assertion functions.
- *
- * Part of the code of this class has been borrowed from the open-source project
- * <code>jerry-core</code> from https://github.com/sangupta/jerry-core.
- *
- */
-public class AssertUtils {
- private AssertUtils() {
- // Utility class cannot be instantiated
- }
-
- public static boolean isEmpty(String str) {
- return str == null || str.isEmpty();
- }
-
- public static boolean isEmpty(Object[] array) {
- return array == null || array.length == 0;
- }
-
- public static boolean isNotEmpty(String str) {
- return !isEmpty(str);
- }
-
- public static boolean isNotEmpty(Object[] array) {
- return !isEmpty(array);
- }
-
-}
diff --git a/SlingJCRPersist/src/test/java/org/apache/sling/models/injectors/BeanWithDirectMappedChildren.java b/SlingJCRPersist/src/test/java/org/apache/sling/models/injectors/BeanWithDirectMappedChildren.java
deleted file mode 100755
index b6272b4..0000000
--- a/SlingJCRPersist/src/test/java/org/apache/sling/models/injectors/BeanWithDirectMappedChildren.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.apache.sling.models.injectors;
-
-import java.util.HashMap;
-import java.util.Map;
-import javax.inject.Inject;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.models.annotations.DefaultInjectionStrategy;
-import org.apache.sling.models.annotations.Model;
-import org.apache.sling.models.persist.annotations.DirectDescendants;
-
-/**
- * Expresses a sling model which has child nodes as a map
- */
-@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
-public class BeanWithDirectMappedChildren {
-
- public transient String path;
-
- @DirectDescendants
- @Inject
- public Map<String, Person> people = new HashMap<>();
-
- public void addPerson(String firstName, String lastName) {
- Person p = new Person();
- String name = lastName + '-' + firstName;
- p.firstName = firstName;
- p.lastName = lastName;
- p.path = "./" + name;
- people.put(name, p);
- }
-
- @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
- public static class Person {
-
- transient String path;
-
- @Inject
- String firstName;
-
- @Inject
- String lastName;
- }
-}
diff --git a/SlingJCRPersist/src/test/java/org/apache/sling/models/injectors/BeanWithMappedChildren.java b/SlingJCRPersist/src/test/java/org/apache/sling/models/injectors/BeanWithMappedChildren.java
deleted file mode 100755
index 96370fb..0000000
--- a/SlingJCRPersist/src/test/java/org/apache/sling/models/injectors/BeanWithMappedChildren.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.apache.sling.models.injectors;
-
-import java.util.HashMap;
-import java.util.Map;
-import javax.inject.Inject;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.models.annotations.DefaultInjectionStrategy;
-import org.apache.sling.models.annotations.Model;
-
-/**
- * Expresses a sling model which has child nodes as a map
- */
-@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
-public class BeanWithMappedChildren {
-
- public transient String path;
-
- @Inject
- public Map<String, Person> people = new HashMap<>();
-
- public void addPerson(String firstName, String lastName) {
- Person p = new Person();
- String name = lastName + '-' + firstName;
- p.firstName = firstName;
- p.lastName = lastName;
- p.path = "./" + name;
- people.put(name, p);
- }
-
- @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
- public static class Person {
-
- transient String path;
-
- @Inject
- String firstName;
-
- @Inject
- String lastName;
- }
-}
diff --git a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithAnnotatedPathField.java b/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithAnnotatedPathField.java
deleted file mode 100755
index 1958f84..0000000
--- a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithAnnotatedPathField.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.apache.sling.models.persist.bean;
-
-import javax.inject.Inject;
-import org.apache.sling.models.annotations.Path;
-
-/**
- * Example of bean with getPath method that stores the path in a field using an annotation marker
- */
-public class BeanWithAnnotatedPathField {
- @Inject
- public String prop1 = "testValue";
-
- public String path = "/test/WRONG-path";
-
- @Path
- public String correctPath = "/test/annotated-field-path";
-
- public String getPath() {
- return path;
- }
-}
diff --git a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithAnnotatedPathGetter.java b/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithAnnotatedPathGetter.java
deleted file mode 100755
index 5010077..0000000
--- a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithAnnotatedPathGetter.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.apache.sling.models.persist.bean;
-
-import javax.inject.Inject;
-import org.apache.sling.models.annotations.Path;
-
-/**
- * Example of bean with getPath method that stores the path in a field using an annotation marker
- */
-public class BeanWithAnnotatedPathGetter {
- @Inject
- public String prop1 = "testValue";
-
- public String path = "/test/WRONG-path";
-
- public String correctPath = "/test/annotated-getter-path";
-
- public String getPath() {
- return path;
- }
-
- @Path
- public String getCorrectPath() {
- return path;
- }
-}
diff --git a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithPathField.java b/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithPathField.java
deleted file mode 100755
index 0b5e853..0000000
--- a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithPathField.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.apache.sling.models.persist.bean;
-
-import javax.inject.Inject;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.models.annotations.Model;
-import org.apache.sling.models.persist.annotations.ChildType;
-
-/**
- * Example of bean with getPath method that stores the path in a field
- */
-@Model(adaptables = Resource.class, resourceType = "test/testBean")
-@ChildType("test/testBean/field-path")
-public class BeanWithPathField {
- @Inject
- public String prop1 = "testValue";
-
- public String path = "/test/field-path";
-}
diff --git a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithPathGetter.java b/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithPathGetter.java
deleted file mode 100755
index ee3274f..0000000
--- a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithPathGetter.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.apache.sling.models.persist.bean;
-
-import javax.inject.Inject;
-
-/**
- * Example of bean with getPath method that renders path of the bean [possible] dynamically.
- */
-public class BeanWithPathGetter {
- @Inject
- public String prop1 = "testValue";
-
- // This provides the path of the bean, which could also be some kind of dynamic business logic.
- public String getPath() {
- return "/test/dynamic-path";
- }
-}
diff --git a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/MappedChildren.java b/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/MappedChildren.java
deleted file mode 100755
index d93180e..0000000
--- a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/MappedChildren.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.apache.sling.models.persist.bean;
-
-import java.util.EnumMap;
-import java.util.HashMap;
-import java.util.Map;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.models.annotations.DefaultInjectionStrategy;
-import org.apache.sling.models.annotations.Model;
-
-/**
- * Bean with children arranged in maps (enumeration map and also string keys)
- */
-@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
-public class MappedChildren {
- public static enum KEYS{ONE,TWO,THREE};
-
- public String name;
-
- public Map<String, Child> stringKeys = new HashMap<>();
-
- public EnumMap<KEYS, Child> enumKeys = new EnumMap<>(KEYS.class);
-
- @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
- public static class Child {
- public String name;
- public String testValue;
- }
-
- public MappedChildren() {
- }
-
- public MappedChildren(Resource resource) {
- if (resource != null) {
- name = resource.getName();
- }
- }
-
-
-}
diff --git a/SlingJCRPersist/pom.xml b/SlingModelPersist/pom.xml
old mode 100755
new mode 100644
similarity index 92%
rename from SlingJCRPersist/pom.xml
rename to SlingModelPersist/pom.xml
index e02efb6..9626769
--- a/SlingJCRPersist/pom.xml
+++ b/SlingModelPersist/pom.xml
@@ -1,176 +1,183 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <groupId>org.apache.sling</groupId>
- <artifactId>SlingJCRPersist</artifactId>
- <version>1.0-SNAPSHOT</version>
- <packaging>bundle</packaging>
-
- <name>Sling JCRPersist OSGi Bundle</name>
-
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <maven.compiler.source>1.8</maven.compiler.source>
- <maven.compiler.target>1.8</maven.compiler.target>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
- <version>5.0.0</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.jcr.api</artifactId>
- <version>2.4.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.api</artifactId>
- <version>2.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.component.annotations</artifactId>
- <version>1.4.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- <version>3.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.models.api</artifactId>
- <version>1.3.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.commons.log</artifactId>
- <version>2.1.2</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>javax.inject</groupId>
- <artifactId>javax.inject</artifactId>
- <version>1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-collections</groupId>
- <artifactId>commons-collections</artifactId>
- <version>3.2.2</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>com.drewnoakes</groupId>
- <artifactId>metadata-extractor</artifactId>
- <version>2.6.2</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>javax.servlet-api</artifactId>
- <version>3.1.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.testing.sling-mock</artifactId>
- <version>2.3.4</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.testing.jcr-mock</artifactId>
- <version>1.4.4</version>
- <scope>test</scope>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
- <scope>test</scope>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.11.1</version>
- <scope>test</scope>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- <version>2.9.6</version>
- <scope>test</scope>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
- <version>2.9.0</version>
- <scope>test</scope>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <version>2.9.6</version>
- <scope>test</scope>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.compendium</artifactId>
- <version>5.0.0</version>
- <scope>test</scope>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>osgi.core</artifactId>
- <version>5.0.0</version>
- <scope>test</scope>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>javax.jcr</groupId>
- <artifactId>jcr</artifactId>
- <version>2.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.jetbrains</groupId>
- <artifactId>annotations</artifactId>
- <version>16.0.2</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <version>4.2.0</version>
- <extensions>true</extensions>
- <configuration>
- <instructions>
- <Bundle-Activator>org.apache.sling.jcrpersist.Activator</Bundle-Activator>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
-</project>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.models.persistor</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <name>Sling Model Persistor</name>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>5.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.jcr.api</artifactId>
+ <version>2.4.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.api</artifactId>
+ <version>2.16.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.service.component.annotations</artifactId>
+ <version>1.4.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.9</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.models.api</artifactId>
+ <version>1.3.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.log</artifactId>
+ <version>2.1.2</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ <version>1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <version>3.2.2</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>com.drewnoakes</groupId>
+ <artifactId>metadata-extractor</artifactId>
+ <version>2.6.2</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <version>3.1.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.sling-mock</artifactId>
+ <version>2.3.4</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.jcr-mock</artifactId>
+ <version>1.4.4</version>
+ <scope>test</scope>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.11.1</version>
+ <scope>test</scope>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <version>2.9.6</version>
+ <scope>test</scope>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ <version>2.9.0</version>
+ <scope>test</scope>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>2.9.6</version>
+ <scope>test</scope>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>5.0.0</version>
+ <scope>test</scope>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.core</artifactId>
+ <version>5.0.0</version>
+ <scope>test</scope>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>javax.jcr</groupId>
+ <artifactId>jcr</artifactId>
+ <version>2.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains</groupId>
+ <artifactId>annotations</artifactId>
+ <version>16.0.2</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.annotation</groupId>
+ <artifactId>javax.annotation-api</artifactId>
+ <version>1.3.2</version>
+ <scope>test</scope>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>4.2.0</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Activator>org.apache.sling.models.persist.Activator</Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/SlingJCRPersist/src/main/assembly/felix.xml b/SlingModelPersist/src/main/assembly/felix.xml
old mode 100755
new mode 100644
similarity index 100%
rename from SlingJCRPersist/src/main/assembly/felix.xml
rename to SlingModelPersist/src/main/assembly/felix.xml
diff --git a/SlingJCRPersist/src/main/java/org/apache/sling/models/injectors/MapOfChildResourcesInjector.java b/SlingModelPersist/src/main/java/org/apache/sling/models/injectors/MapOfChildResourcesInjector.java
old mode 100755
new mode 100644
similarity index 84%
rename from SlingJCRPersist/src/main/java/org/apache/sling/models/injectors/MapOfChildResourcesInjector.java
rename to SlingModelPersist/src/main/java/org/apache/sling/models/injectors/MapOfChildResourcesInjector.java
index 3a517dc..0082c23
--- a/SlingJCRPersist/src/main/java/org/apache/sling/models/injectors/MapOfChildResourcesInjector.java
+++ b/SlingModelPersist/src/main/java/org/apache/sling/models/injectors/MapOfChildResourcesInjector.java
@@ -1,113 +1,131 @@
-package org.apache.sling.models.injectors;
-
-import com.drew.lang.annotations.NotNull;
-import java.lang.reflect.AnnotatedElement;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.util.AbstractMap;
-import java.util.Collections;
-import java.util.EnumMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
-import org.apache.commons.lang3.EnumUtils;
-import org.apache.sling.api.SlingHttpServletRequest;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.models.persist.annotations.DirectDescendants;
-import org.apache.sling.models.spi.AcceptsNullName;
-import org.apache.sling.models.spi.DisposalCallbackRegistry;
-import org.apache.sling.models.spi.Injector;
-import org.osgi.service.component.annotations.Component;
-
-@Component(service = Injector.class)
-public class MapOfChildResourcesInjector implements Injector, AcceptsNullName {
-
- @Override
- public @NotNull
- String getName() {
- return "map-of-child-resources";
- }
-
- @Override
- public Object getValue(@NotNull Object adaptable, String name, @NotNull Type declaredType, @NotNull AnnotatedElement element,
- @NotNull DisposalCallbackRegistry callbackRegistry) {
- if (adaptable instanceof Resource) {
- boolean directDescendants = element.getAnnotation(DirectDescendants.class) != null;
- Resource source = ((Resource) adaptable);
- if (!directDescendants) {
- source = source.getChild(name);
- }
- return createMap(source != null ? source.getChildren() : Collections.EMPTY_LIST, declaredType);
- } else if (adaptable instanceof SlingHttpServletRequest) {
- return getValue(((SlingHttpServletRequest) adaptable).getResource(), name, declaredType, element, callbackRegistry);
- }
- return null;
- }
-
- // TODO: Clean up and refactor this method
- private Object createMap(Iterable<Resource> children, Type declaredType) {
- if (declaredType instanceof ParameterizedType) {
- ParameterizedType type = (ParameterizedType) declaredType;
- if (type.getRawType().equals(Map.class)) {
- Type keyType = type.getActualTypeArguments()[0];
- Type valueType = type.getActualTypeArguments()[1];
- if (keyType.equals(String.class) && valueType instanceof Class) {
- Class<?> valueClass = (Class) valueType;
- return StreamSupport.stream(children.spliterator(), false).map(r ->
- buildSimpleMapEntry(valueClass, r)
- ).filter(o -> o != null).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new));
- }
- } else if (type.getRawType().equals(EnumMap.class)) {
- Class keyType = (Class) type.getActualTypeArguments()[0];
- Map<String, Object> enumKeys = EnumUtils.getEnumMap(keyType);
- Type valueType = type.getActualTypeArguments()[1];
- Boolean isSingleValue = valueType instanceof Class;
- Class<?> valueClass = isSingleValue
- ? (Class) valueType
- : (Class) ((ParameterizedType) valueType).getActualTypeArguments()[0];
- EnumMap out = new EnumMap(keyType);
- StreamSupport.stream(children.spliterator(), false).map(r
- -> buildEnumMapEntry(enumKeys, r, valueClass, isSingleValue)
- )
- .filter(o -> o != null)
- .forEach(e -> out.put(e.getKey(), e.getValue()));
- return out;
- }
- }
- return null;
- }
-
- private Map.Entry buildSimpleMapEntry(Class<?> valueClass, Resource r) {
- if (valueClass.equals(Resource.class)) {
- return new AbstractMap.SimpleEntry<>(r.getName(), r);
- } else {
- Object adapted = r.adaptTo(valueClass);
- if (adapted == null) {
- return null;
- } else {
- return new AbstractMap.SimpleEntry<>(r.getName(), adapted);
- }
- }
- }
-
- private Map.Entry buildEnumMapEntry(Map<String, Object> enumKeys, Resource r, Class<?> valueClass, Boolean isSingleValue) {
- Object key = enumKeys.get(r.getName());
- if (valueClass.equals(Resource.class)) {
- return new AbstractMap.SimpleEntry<>(key, r);
- } else if (isSingleValue) {
- Object adapted = r.adaptTo(valueClass);
- if (adapted == null) {
- return null;
- } else {
- return new AbstractMap.SimpleEntry<>(key, adapted);
- }
- } else {
- List values = StreamSupport.stream(r.getChildren().spliterator(), false)
- .map(c -> c.adaptTo(valueClass))
- .collect(Collectors.toList());
- return new AbstractMap.SimpleEntry<>(key, values);
- }
- }
-}
+/*
+ * 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.injectors;
+
+import com.drew.lang.annotations.NotNull;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.AbstractMap;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+import org.apache.commons.lang3.EnumUtils;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.persistor.annotations.DirectDescendants;
+import org.apache.sling.models.spi.AcceptsNullName;
+import org.apache.sling.models.spi.DisposalCallbackRegistry;
+import org.apache.sling.models.spi.Injector;
+import org.osgi.service.component.annotations.Component;
+
+@Component(service = Injector.class)
+public class MapOfChildResourcesInjector implements Injector, AcceptsNullName {
+
+ @Override
+ public @NotNull
+ String getName() {
+ return "map-of-child-resources";
+ }
+
+ @Override
+ public Object getValue(@NotNull Object adaptable, String name, @NotNull Type declaredType, @NotNull AnnotatedElement element,
+ @NotNull DisposalCallbackRegistry callbackRegistry) {
+ if (adaptable instanceof Resource) {
+ boolean directDescendants = element.getAnnotation(DirectDescendants.class) != null;
+ Resource source = ((Resource) adaptable);
+ if (!directDescendants) {
+ source = source.getChild(name);
+ }
+ return createMap(source != null ? source.getChildren() : Collections.EMPTY_LIST, declaredType);
+ } else if (adaptable instanceof SlingHttpServletRequest) {
+ return getValue(((SlingHttpServletRequest) adaptable).getResource(), name, declaredType, element, callbackRegistry);
+ }
+ return null;
+ }
+
+ // TODO: Clean up and refactor this method
+ private Object createMap(Iterable<Resource> children, Type declaredType) {
+ if (declaredType instanceof ParameterizedType) {
+ ParameterizedType type = (ParameterizedType) declaredType;
+ if (type.getRawType().equals(Map.class)) {
+ Type keyType = type.getActualTypeArguments()[0];
+ Type valueType = type.getActualTypeArguments()[1];
+ if (keyType.equals(String.class) && valueType instanceof Class) {
+ Class<?> valueClass = (Class) valueType;
+ return StreamSupport.stream(children.spliterator(), false).map(r ->
+ buildSimpleMapEntry(valueClass, r)
+ ).filter(o -> o != null).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new));
+ }
+ } else if (type.getRawType().equals(EnumMap.class)) {
+ Class keyType = (Class) type.getActualTypeArguments()[0];
+ Map<String, Object> enumKeys = EnumUtils.getEnumMap(keyType);
+ Type valueType = type.getActualTypeArguments()[1];
+ Boolean isSingleValue = valueType instanceof Class;
+ Class<?> valueClass = isSingleValue
+ ? (Class) valueType
+ : (Class) ((ParameterizedType) valueType).getActualTypeArguments()[0];
+ EnumMap out = new EnumMap(keyType);
+ StreamSupport.stream(children.spliterator(), false).map(r
+ -> buildEnumMapEntry(enumKeys, r, valueClass, isSingleValue)
+ )
+ .filter(o -> o != null)
+ .forEach(e -> out.put(e.getKey(), e.getValue()));
+ return out;
+ }
+ }
+ return null;
+ }
+
+ private Map.Entry buildSimpleMapEntry(Class<?> valueClass, Resource r) {
+ if (valueClass.equals(Resource.class)) {
+ return new AbstractMap.SimpleEntry<>(r.getName(), r);
+ } else {
+ Object adapted = r.adaptTo(valueClass);
+ if (adapted == null) {
+ return null;
+ } else {
+ return new AbstractMap.SimpleEntry<>(r.getName(), adapted);
+ }
+ }
+ }
+
+ private Map.Entry buildEnumMapEntry(Map<String, Object> enumKeys, Resource r, Class<?> valueClass, Boolean isSingleValue) {
+ Object key = enumKeys.get(r.getName());
+ if (valueClass.equals(Resource.class)) {
+ return new AbstractMap.SimpleEntry<>(key, r);
+ } else if (isSingleValue) {
+ Object adapted = r.adaptTo(valueClass);
+ if (adapted == null) {
+ return null;
+ } else {
+ return new AbstractMap.SimpleEntry<>(key, adapted);
+ }
+ } else {
+ List values = StreamSupport.stream(r.getChildren().spliterator(), false)
+ .map(c -> c.adaptTo(valueClass))
+ .collect(Collectors.toList());
+ return new AbstractMap.SimpleEntry<>(key, values);
+ }
+ }
+}
diff --git a/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/ModelPersistor.java b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/ModelPersistor.java
new file mode 100644
index 0000000..7c5c9f4
--- /dev/null
+++ b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/ModelPersistor.java
@@ -0,0 +1,39 @@
+/*
+ * 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.persistor;
+
+import javax.jcr.RepositoryException;
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Definition of Model Persistor service
+ */
+public interface ModelPersistor {
+
+ void persist(@NotNull Object object, @NotNull ResourceResolver resourceResolver) throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException;
+
+ void persist(@NotNull Object object, @NotNull ResourceResolver resourceResolver, boolean deepPersist) throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException;
+
+ void persist(String nodePath, @NotNull Object object, @NotNull ResourceResolver resourceResolver) throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException;
+
+ void persist(String nodePath, @NotNull Object object, @NotNull ResourceResolver resourceResolver, boolean deepPersist) throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException;
+
+}
diff --git a/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/annotations/ChildType.java b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/annotations/ChildType.java
new file mode 100644
index 0000000..2fa9b32
--- /dev/null
+++ b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/annotations/ChildType.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.models.persistor.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
+public @interface ChildType {
+ String value() default "";
+}
diff --git a/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/annotations/DirectDescendants.java b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/annotations/DirectDescendants.java
new file mode 100644
index 0000000..8a79e6d
--- /dev/null
+++ b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/annotations/DirectDescendants.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.models.persistor.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marker annotation to indicate that collection elements
+ * are direct descendants of this node and do not have an extra
+ * wrapper child node around.
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.FIELD, ElementType.METHOD })
+public @interface DirectDescendants {
+
+}
diff --git a/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/annotations/Ignore.java b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/annotations/Ignore.java
new file mode 100644
index 0000000..c0aad48
--- /dev/null
+++ b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/annotations/Ignore.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.persistor.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marker annotation to signify that the attribute be excluded
+ * from all serialization/deserialization workflows.
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.FIELD, ElementType.METHOD })
+public @interface Ignore {
+
+}
diff --git a/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/annotations/ResourceType.java b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/annotations/ResourceType.java
new file mode 100644
index 0000000..7c8e177
--- /dev/null
+++ b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/annotations/ResourceType.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.models.persistor.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
+public @interface ResourceType {
+ String value() default "";
+}
diff --git a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/impl/JcrPersistImpl.java b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/impl/ModelPersistorImpl.java
old mode 100755
new mode 100644
similarity index 75%
rename from SlingJCRPersist/src/main/java/org/apache/sling/models/persist/impl/JcrPersistImpl.java
rename to SlingModelPersist/src/main/java/org/apache/sling/models/persistor/impl/ModelPersistorImpl.java
index 1ebe23e..d2c5c42
--- a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/impl/JcrPersistImpl.java
+++ b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/impl/ModelPersistorImpl.java
@@ -1,297 +1,296 @@
-/*
- * #%L
- * ACS AEM Commons Bundle
- * %%
- * Copyright (C) 2016 Adobe
- * %%
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * #L%
- */
-package org.apache.sling.models.persist.impl;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.logging.Level;
-import java.util.stream.StreamSupport;
-import javax.jcr.RepositoryException;
-import org.apache.commons.lang3.reflect.FieldUtils;
-import org.apache.commons.lang3.reflect.MethodUtils;
-import org.apache.sling.api.resource.ModifiableValueMap;
-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.ResourceUtil;
-import org.apache.sling.models.annotations.Path;
-import org.apache.sling.models.persist.annotations.DirectDescendants;
-import org.apache.sling.models.persist.impl.util.AssertUtils;
-import org.apache.sling.models.persist.impl.util.ReflectionUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.sling.models.persist.JcrPersist;
-import org.osgi.service.component.annotations.Component;
-
-
-import org.jetbrains.annotations.NotNull;
-
-import static org.apache.sling.models.persist.impl.util.ReflectionUtils.getAnnotatedValue;
-
-/**
- * Code to persist a given object instance to a JCR node.
- *
- */
-@Component(service =JcrPersist.class)
-public class JcrPersistImpl implements JcrPersist {
-
- public JcrPersistImpl() {
- // Utility class, cannot be instantiated
- }
-
- /**
- * My private logger
- */
- private static final Logger LOGGER = LoggerFactory.getLogger(JcrPersistImpl.class);
-
- static final Map<String, Object> RESOURCE_TYPE_NT_UNSTRUCTURED = new HashMap<>();
-
- static final String JCR_PRIMARYTYPE = "jcr:primaryType";
- static final String NT_UNSTRUCTURED = "nt:unstructured";
- static final String JCR_CONTENT = "jcr:content";
-
- static {
- RESOURCE_TYPE_NT_UNSTRUCTURED.put(JCR_PRIMARYTYPE, NT_UNSTRUCTURED);
- }
-
- public void persist(final @NotNull Object instance, @NotNull ResourceResolver resourceResolver) throws PersistenceException, IllegalArgumentException, IllegalAccessException, RepositoryException {
- persist (instance, resourceResolver, true);
- }
-
- public void persist(final @NotNull Object instance, @NotNull ResourceResolver resourceResolver, boolean deepPersist) throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
- String path = getJcrPath(instance);
- persist(path, instance, resourceResolver, deepPersist);
- }
-
- public void persist(final @NotNull String nodePath, final @NotNull Object instance,
- @NotNull ResourceResolver resourceResolver) throws PersistenceException, IllegalArgumentException, IllegalAccessException, RepositoryException {
- persist (nodePath, instance, resourceResolver, true);
- }
-
-
- public void persist(final @NotNull String nodePath, final @NotNull Object instance,
- @NotNull ResourceResolver resourceResolver, boolean deepPersist)
- throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
- if (nodePath == null || nodePath.trim().isEmpty()) {
- throw new IllegalArgumentException("Node path cannot be null/empty");
- }
-
- if (instance == null) {
- throw new IllegalArgumentException("Object to save cannot be null");
- }
-
- if (resourceResolver == null) {
- throw new IllegalArgumentException("ResourceResolver cannot be null");
- }
-
- Resource resource;
- final ResourceTypeKey resourceType = ResourceTypeKey.fromObject(instance);
-
- // let's create the resource first
- LOGGER.debug("Creating node at: {} of type: {}", nodePath, resourceType.primaryType);
- resource = ResourceUtil.getOrCreateResource(resourceResolver, nodePath, resourceType.primaryType, NT_UNSTRUCTURED, true);
- if (AssertUtils.isNotEmpty(resourceType.childType)) {
- LOGGER.debug("Needs a child node, creating node at: {} of type: {}", nodePath, resourceType.childType);
- resource = ResourceUtil.getOrCreateResource(resourceResolver, nodePath + "/" + JCR_CONTENT, resourceType.childType, NT_UNSTRUCTURED, true);
- }
-
- if (ReflectionUtils.isArrayOrCollection(instance)) {
- persistComplexValue(instance, true, nodePath, "dunno", resource);
- } else {
- // find properties to be saved
- List<Field> fields = ReflectionUtils.getAllFields(instance.getClass());
- if (fields == null || fields.isEmpty()) {
- // TODO: remove all properties
- } else {
- Resource r = resource;
- fields.stream()
- .filter(ReflectionUtils::isNotTransient)
- .filter(ReflectionUtils::isSupportedType)
- .filter(f -> ReflectionUtils.hasNoTransientGetter(f.getName(), instance.getClass()))
- .forEach(field -> persistField(r, instance, field, deepPersist));
- }
- }
-
- // save back
- resourceResolver.commit();
- }
-
- private void persistField(@NotNull Resource resource, @NotNull Object instance, Field field, boolean deepPersist) {
- try {
- // read the existing resource map
- ModifiableValueMap values = resource.adaptTo(ModifiableValueMap.class);
- String nodePath = resource.getPath();
-
- // find the type of field
- final Class<?> fieldType = field.getType();
- final String fieldName = ReflectionUtils.getFieldName(field);
-
- // set accessible
- field.setAccessible(true);
-
- // handle the value as primitive first
- if (ReflectionUtils.isPrimitiveFieldType(fieldType)) {
- Object value = field.get(instance);
-
- // remove the attribute that is null, or remove in case it changes type
- values.remove(fieldName);
- if (value != null) {
- values.put(fieldName, value);
- }
- } else if (deepPersist) {
- boolean directDescendents = field.getAnnotation(DirectDescendants.class) != null;
- persistComplexValue(field.get(instance), directDescendents, nodePath, fieldName, resource);
- }
- } catch (IllegalAccessException | RepositoryException | PersistenceException ex) {
- LOGGER.error("Error when persisting content to " + resource.getPath(), ex);
- }
- }
-
- private void persistComplexValue(Object obj, Boolean implicitCollection, String nodePath, final String fieldName, Resource resource) throws RepositoryException, IllegalAccessException, IllegalArgumentException, PersistenceException {
- if (obj == null) {
- return;
- }
- if (Collection.class.isAssignableFrom(obj.getClass())) {
- Collection collection = (Collection) obj;
- if (!collection.isEmpty()) {
- String childrenRoot = buildChildrenRoot(nodePath, fieldName, resource.getResourceResolver(), implicitCollection);
- persistCollection(childrenRoot, collection, resource.getResourceResolver());
- }
- } else if (Map.class.isAssignableFrom(obj.getClass())) {
- Map map = (Map) obj;
- if (!map.isEmpty()) {
- String childrenRoot = buildChildrenRoot(nodePath, fieldName, resource.getResourceResolver(), implicitCollection);
- persistMap(childrenRoot, map, resource.getResourceResolver());
- }
- } else {
- // this is a single compound object
- // create a child node and persist all its values
- persist(nodePath + "/" + fieldName, obj, resource.getResourceResolver(), true);
- }
- }
-
- private void persistCollection(final String collectionRoot, final Collection collection, ResourceResolver resourceResolver) throws PersistenceException, RepositoryException, IllegalArgumentException, IllegalAccessException {
- // now for each child in the collection - create a new node
- Set<String> childNodes = new HashSet<>();
- if (collection != null) {
- for (Object childObject : collection) {
- String childName = null;
- String childPath = getJcrPath(childObject);
-
- if (childPath != null) {
- childName = extractNodeNameFromPath(childPath);
- } else {
- childName = UUID.randomUUID().toString();
- }
-
- childPath = collectionRoot + "/" + childName;
- childNodes.add(childPath);
- persist(childPath, childObject, resourceResolver, true);
- }
- }
- deleteOrphanNodes(resourceResolver, collectionRoot, childNodes);
- }
-
- private <K, V> void persistMap(final String collectionRoot, final Map<K,V> collection, ResourceResolver resourceResolver) throws PersistenceException, RepositoryException, IllegalArgumentException, IllegalAccessException {
- // now for each child in the collection - create a new node
- Set<String> childNodes = new HashSet<>();
- if (collection != null) {
- for (Map.Entry<K,V> childObject : collection.entrySet()) {
- String childName = String.valueOf(childObject.getKey());
- String childPath = collectionRoot + "/" + childName;
- childNodes.add(childPath);
- persist(childPath, childObject.getValue(), resourceResolver, true);
- }
- }
- deleteOrphanNodes(resourceResolver, collectionRoot, childNodes);
- }
-
- private String buildChildrenRoot(String nodePath, String fieldName, ResourceResolver rr, boolean implicitCollection) throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
- if (implicitCollection) {
- return nodePath;
- } else {
- // create a child collection of required type
- // and persist all instances
-
- // create the first child node first
- persist(nodePath + "/" + fieldName, new Object(), rr, false);
-
- // update collection root
- return nodePath + "/" + fieldName;
- }
- }
-
- private static void deleteOrphanNodes(ResourceResolver resourceResolver, final String collectionRoot, Set<String> childNodes) {
- // Delete any children that are not in the list
- Resource collectionParent = resourceResolver.getResource(collectionRoot);
- if (collectionParent != null) {
- StreamSupport
- .stream(resourceResolver.getChildren(collectionParent).spliterator(), false)
- .filter(r -> !childNodes.contains(r.getPath()))
- .forEach(r -> {
- try {
- resourceResolver.delete(r);
- } catch (PersistenceException ex) {
- LOGGER.error("Unable to remove stale resource at " + r.getPath(), ex);
- }
- });
- }
- }
-
- private static String extractNodeNameFromPath(String pathValue) throws IllegalArgumentException, IllegalAccessException {
- int lastIndex = pathValue.lastIndexOf('/');
- if (lastIndex >= 0) {
- pathValue = pathValue.substring(lastIndex + 1);
- }
-
- return pathValue;
- }
-
- private static String getJcrPath(Object obj) {
- String path = (String) getAnnotatedValue(obj, Path.class);
- if (path != null) {
- return path;
- }
-
- try {
- Method pathGetter = MethodUtils.getMatchingMethod(obj.getClass(), "getPath");
- if (pathGetter != null) {
- return (String) MethodUtils.invokeMethod(obj, "getPath");
- }
-
- Field pathField = FieldUtils.getDeclaredField(obj.getClass(), "path");
- if (pathField != null) {
- return (String) FieldUtils.readField(pathField, obj, true);
- }
- } catch (IllegalArgumentException | NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
- LOGGER.warn("exception caught",ex);
- }
- LOGGER.warn("Object of type {} does NOT contain a Path attribute or a path property - multiple instances may conflict", obj.getClass());
- return null;
- }
-}
+/*
+ * 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.persistor.impl;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.StreamSupport;
+import javax.jcr.RepositoryException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.commons.lang3.reflect.MethodUtils;
+import org.apache.sling.api.resource.ModifiableValueMap;
+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.ResourceUtil;
+import org.apache.sling.models.annotations.Path;
+import org.apache.sling.models.persistor.ModelPersistor;
+import org.apache.sling.models.persistor.annotations.DirectDescendants;
+import org.apache.sling.models.persistor.impl.util.ReflectionUtils;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.sling.models.persistor.impl.util.ReflectionUtils.getAnnotatedValue;
+
+/**
+ * Code to persist a given object graph to a sling resource tree.
+ *
+ */
+@Component(service = ModelPersistor.class)
+public class ModelPersistorImpl implements ModelPersistor {
+
+ public ModelPersistorImpl() {
+ // Utility class, cannot be instantiated
+ }
+
+ /**
+ * My private logger
+ */
+ private static final Logger LOGGER = LoggerFactory.getLogger(ModelPersistorImpl.class);
+
+ static final Map<String, Object> RESOURCE_TYPE_NT_UNSTRUCTURED = new HashMap<>();
+
+ static final String JCR_PRIMARYTYPE = "jcr:primaryType";
+ static final String NT_UNSTRUCTURED = "nt:unstructured";
+ static final String JCR_CONTENT = "jcr:content";
+
+ static {
+ RESOURCE_TYPE_NT_UNSTRUCTURED.put(JCR_PRIMARYTYPE, NT_UNSTRUCTURED);
+ }
+
+ @Override
+ public void persist(final @NotNull Object instance, @NotNull ResourceResolver resourceResolver) throws PersistenceException, IllegalArgumentException, IllegalAccessException, RepositoryException {
+ persist(instance, resourceResolver, true);
+ }
+
+ @Override
+ public void persist(final @NotNull Object instance, @NotNull ResourceResolver resourceResolver, boolean deepPersist) throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
+ String path = getJcrPath(instance);
+ persist(path, instance, resourceResolver, deepPersist);
+ }
+
+ @Override
+ public void persist(final @NotNull String nodePath, final @NotNull Object instance,
+ @NotNull ResourceResolver resourceResolver) throws PersistenceException, IllegalArgumentException, IllegalAccessException, RepositoryException {
+ persist(nodePath, instance, resourceResolver, true);
+ }
+
+ @Override
+ public void persist(final @NotNull String nodePath, final @NotNull Object instance,
+ @NotNull ResourceResolver resourceResolver, boolean deepPersist)
+ throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
+ if (StringUtils.isBlank(nodePath)) {
+ throw new IllegalArgumentException("Node path cannot be null/empty");
+ }
+
+ if (instance == null) {
+ throw new IllegalArgumentException("Object to save cannot be null");
+ }
+
+ if (resourceResolver == null) {
+ throw new IllegalArgumentException("ResourceResolver cannot be null");
+ }
+
+ Resource resource;
+ final ResourceTypeKey resourceType = ResourceTypeKey.fromObject(instance);
+
+ // let's create the resource first
+ LOGGER.debug("Creating node at: {} of type: {}", nodePath, resourceType.primaryType);
+ resource = ResourceUtil.getOrCreateResource(resourceResolver, nodePath, resourceType.primaryType, NT_UNSTRUCTURED, true);
+ if (StringUtils.isNotEmpty(resourceType.childType)) {
+ LOGGER.debug("Needs a child node, creating node at: {} of type: {}", nodePath, resourceType.childType);
+ resource = ResourceUtil.getOrCreateResource(resourceResolver, nodePath + "/" + JCR_CONTENT, resourceType.childType, NT_UNSTRUCTURED, true);
+ }
+
+ if (ReflectionUtils.isArrayOrCollection(instance)) {
+ persistComplexValue(instance, true, nodePath, "dunno", resource);
+ } else {
+ // find properties to be saved
+ List<Field> fields = ReflectionUtils.getAllFields(instance.getClass());
+ if (fields == null || fields.isEmpty()) {
+ // TODO: remove all properties
+ } else {
+ Resource r = resource;
+ fields.stream()
+ .filter(ReflectionUtils::isNotTransient)
+ .filter(ReflectionUtils::isSupportedType)
+ .filter(f -> ReflectionUtils.hasNoTransientGetter(f.getName(), instance.getClass()))
+ .forEach(field -> persistField(r, instance, field, deepPersist));
+ }
+ }
+
+ // save back
+ resourceResolver.commit();
+ }
+
+ private void persistField(@NotNull Resource resource, @NotNull Object instance, Field field, boolean deepPersist) {
+ try {
+ // read the existing resource map
+ ModifiableValueMap values = resource.adaptTo(ModifiableValueMap.class);
+ String nodePath = resource.getPath();
+
+ // find the type of field
+ final Class<?> fieldType = field.getType();
+ final String fieldName = ReflectionUtils.getFieldName(field);
+
+ // set accessible
+ field.setAccessible(true);
+
+ // handle the value as primitive first
+ if (ReflectionUtils.isPrimitiveFieldType(fieldType)) {
+ Object value = field.get(instance);
+
+ // remove the attribute that is null, or remove in case it changes type
+ values.remove(fieldName);
+ if (value != null) {
+ values.put(fieldName, value);
+ }
+ } else if (deepPersist) {
+ boolean directDescendents = field.getAnnotation(DirectDescendants.class) != null;
+ persistComplexValue(field.get(instance), directDescendents, nodePath, fieldName, resource);
+ }
+ } catch (IllegalAccessException | RepositoryException | PersistenceException ex) {
+ LOGGER.error("Error when persisting content to " + resource.getPath(), ex);
+ }
+ }
+
+ private void persistComplexValue(Object obj, Boolean implicitCollection, String nodePath, final String fieldName, Resource resource) throws RepositoryException, IllegalAccessException, IllegalArgumentException, PersistenceException {
+ if (obj == null) {
+ return;
+ }
+ if (Collection.class.isAssignableFrom(obj.getClass())) {
+ Collection collection = (Collection) obj;
+ if (!collection.isEmpty()) {
+ String childrenRoot = buildChildrenRoot(nodePath, fieldName, resource.getResourceResolver(), implicitCollection);
+ persistCollection(childrenRoot, collection, resource.getResourceResolver());
+ }
+ } else if (Map.class.isAssignableFrom(obj.getClass())) {
+ Map map = (Map) obj;
+ if (!map.isEmpty()) {
+ String childrenRoot = buildChildrenRoot(nodePath, fieldName, resource.getResourceResolver(), implicitCollection);
+ persistMap(childrenRoot, map, resource.getResourceResolver());
+ }
+ } else {
+ // this is a single compound object
+ // create a child node and persist all its values
+ persist(nodePath + "/" + fieldName, obj, resource.getResourceResolver(), true);
+ }
+ }
+
+ private void persistCollection(final String collectionRoot, final Collection collection, ResourceResolver resourceResolver) throws PersistenceException, RepositoryException, IllegalArgumentException, IllegalAccessException {
+ // now for each child in the collection - create a new node
+ Set<String> childNodes = new HashSet<>();
+ if (collection != null) {
+ for (Object childObject : collection) {
+ String childName = null;
+ String childPath = getJcrPath(childObject);
+
+ if (childPath != null) {
+ childName = extractNodeNameFromPath(childPath);
+ } else {
+ childName = UUID.randomUUID().toString();
+ }
+
+ childPath = collectionRoot + "/" + childName;
+ childNodes.add(childPath);
+ persist(childPath, childObject, resourceResolver, true);
+ }
+ }
+ deleteOrphanNodes(resourceResolver, collectionRoot, childNodes);
+ }
+
+ private <K, V> void persistMap(final String collectionRoot, final Map<K, V> collection, ResourceResolver resourceResolver) throws PersistenceException, RepositoryException, IllegalArgumentException, IllegalAccessException {
+ // now for each child in the collection - create a new node
+ Set<String> childNodes = new HashSet<>();
+ if (collection != null) {
+ for (Map.Entry<K, V> childObject : collection.entrySet()) {
+ String childName = String.valueOf(childObject.getKey());
+ String childPath = collectionRoot + "/" + childName;
+ childNodes.add(childPath);
+ persist(childPath, childObject.getValue(), resourceResolver, true);
+ }
+ }
+ deleteOrphanNodes(resourceResolver, collectionRoot, childNodes);
+ }
+
+ private String buildChildrenRoot(String nodePath, String fieldName, ResourceResolver rr, boolean implicitCollection) throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
+ if (implicitCollection) {
+ return nodePath;
+ } else {
+ // create a child collection of required type
+ // and persist all instances
+
+ // create the first child node first
+ persist(nodePath + "/" + fieldName, new Object(), rr, false);
+
+ // update collection root
+ return nodePath + "/" + fieldName;
+ }
+ }
+
+ private static void deleteOrphanNodes(ResourceResolver resourceResolver, final String collectionRoot, Set<String> childNodes) {
+ // Delete any children that are not in the list
+ Resource collectionParent = resourceResolver.getResource(collectionRoot);
+ if (collectionParent != null) {
+ StreamSupport
+ .stream(resourceResolver.getChildren(collectionParent).spliterator(), false)
+ .filter(r -> !childNodes.contains(r.getPath()))
+ .forEach(r -> {
+ try {
+ resourceResolver.delete(r);
+ } catch (PersistenceException ex) {
+ LOGGER.error("Unable to remove stale resource at " + r.getPath(), ex);
+ }
+ });
+ }
+ }
+
+ private static String extractNodeNameFromPath(String pathValue) throws IllegalArgumentException, IllegalAccessException {
+ int lastIndex = pathValue.lastIndexOf('/');
+ if (lastIndex >= 0) {
+ pathValue = pathValue.substring(lastIndex + 1);
+ }
+
+ return pathValue;
+ }
+
+ private static String getJcrPath(Object obj) {
+ String path = (String) getAnnotatedValue(obj, Path.class);
+ if (path != null) {
+ return path;
+ }
+
+ try {
+ Method pathGetter = MethodUtils.getMatchingMethod(obj.getClass(), "getPath");
+ if (pathGetter != null) {
+ return (String) MethodUtils.invokeMethod(obj, "getPath");
+ }
+
+ Field pathField = FieldUtils.getDeclaredField(obj.getClass(), "path");
+ if (pathField != null) {
+ return (String) FieldUtils.readField(pathField, obj, true);
+ }
+ } catch (IllegalArgumentException | NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
+ LOGGER.warn("exception caught", ex);
+ }
+ LOGGER.warn("Object of type {} does NOT contain a Path attribute or a path property - multiple instances may conflict", obj.getClass());
+ return null;
+ }
+}
diff --git a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/impl/ResourceTypeKey.java b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/impl/ResourceTypeKey.java
old mode 100755
new mode 100644
similarity index 61%
rename from SlingJCRPersist/src/main/java/org/apache/sling/models/persist/impl/ResourceTypeKey.java
rename to SlingModelPersist/src/main/java/org/apache/sling/models/persistor/impl/ResourceTypeKey.java
index a1f08fa..108038b
--- a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/impl/ResourceTypeKey.java
+++ b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/impl/ResourceTypeKey.java
@@ -1,59 +1,71 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.apache.sling.models.persist.impl;
-
-import java.util.HashMap;
-import java.util.Map;
-import org.apache.sling.models.annotations.Model;
-import org.apache.sling.models.persist.annotations.ChildType;
-import org.apache.sling.models.persist.annotations.ResourceType;
-
-import static org.apache.sling.models.persist.impl.util.ReflectionUtils.getAnnotatedValue;
-
-/**
- * Represents primary/child pair type
- */
-public class ResourceTypeKey {
-
- static final Map<Class<?>, ResourceTypeKey> NODE_TYPE_FOR_CLASS = new HashMap<>();
- static public final ResourceTypeKey NT_UNSTRUCTURED = new ResourceTypeKey("nt:unstructured", null);
-
- final public String primaryType;
- final public String childType;
-
- public ResourceTypeKey(String primaryType, String childType) {
- this.primaryType = primaryType;
- this.childType = childType;
- }
-
- public static ResourceTypeKey fromObject(Object obj) {
- if (obj == null) {
- return ResourceTypeKey.NT_UNSTRUCTURED;
- }
-
- if (NODE_TYPE_FOR_CLASS.containsKey(obj.getClass())) {
- return NODE_TYPE_FOR_CLASS.get(obj.getClass());
- }
-
- Model modelAnnotation = obj.getClass().getAnnotation(Model.class);
- String primaryType = (String) getAnnotatedValue(obj, ResourceType.class);
- // Use the model annotation for resource type as needed
- if (primaryType == null
- && modelAnnotation != null
- && modelAnnotation.resourceType() != null
- && modelAnnotation.resourceType().length == 1) {
- primaryType = modelAnnotation.resourceType()[0];
- }
- String childType = (String) getAnnotatedValue(obj, ChildType.class);
- if (primaryType != null) {
- ResourceTypeKey key = new ResourceTypeKey(primaryType, childType);
- NODE_TYPE_FOR_CLASS.put(obj.getClass(), key);
- return key;
- } else {
- return ResourceTypeKey.NT_UNSTRUCTURED;
- }
- }
-}
+/*
+ * 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.persistor.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.persistor.annotations.ChildType;
+import org.apache.sling.models.persistor.annotations.ResourceType;
+
+import static org.apache.sling.models.persistor.impl.util.ReflectionUtils.getAnnotatedValue;
+
+/**
+ * Represents primary/child pair type
+ */
+public class ResourceTypeKey {
+
+ static final Map<Class<?>, ResourceTypeKey> NODE_TYPE_FOR_CLASS = new HashMap<>();
+ static public final ResourceTypeKey NT_UNSTRUCTURED = new ResourceTypeKey("nt:unstructured", null);
+
+ final public String primaryType;
+ final public String childType;
+
+ public ResourceTypeKey(String primaryType, String childType) {
+ this.primaryType = primaryType;
+ this.childType = childType;
+ }
+
+ public static ResourceTypeKey fromObject(Object obj) {
+ if (obj == null) {
+ return ResourceTypeKey.NT_UNSTRUCTURED;
+ }
+
+ if (NODE_TYPE_FOR_CLASS.containsKey(obj.getClass())) {
+ return NODE_TYPE_FOR_CLASS.get(obj.getClass());
+ }
+
+ Model modelAnnotation = obj.getClass().getAnnotation(Model.class);
+ String primaryType = (String) getAnnotatedValue(obj, ResourceType.class);
+ // Use the model annotation for resource type as needed
+ if (primaryType == null
+ && modelAnnotation != null
+ && modelAnnotation.resourceType() != null
+ && modelAnnotation.resourceType().length == 1) {
+ primaryType = modelAnnotation.resourceType()[0];
+ }
+ String childType = (String) getAnnotatedValue(obj, ChildType.class);
+ if (primaryType != null) {
+ ResourceTypeKey key = new ResourceTypeKey(primaryType, childType);
+ NODE_TYPE_FOR_CLASS.put(obj.getClass(), key);
+ return key;
+ } else {
+ return ResourceTypeKey.NT_UNSTRUCTURED;
+ }
+ }
+}
diff --git a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/impl/util/ReflectionUtils.java b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/impl/util/ReflectionUtils.java
old mode 100755
new mode 100644
similarity index 89%
rename from SlingJCRPersist/src/main/java/org/apache/sling/models/persist/impl/util/ReflectionUtils.java
rename to SlingModelPersist/src/main/java/org/apache/sling/models/persistor/impl/util/ReflectionUtils.java
index 4bb46cc..1d1eda1
--- a/SlingJCRPersist/src/main/java/org/apache/sling/models/persist/impl/util/ReflectionUtils.java
+++ b/SlingModelPersist/src/main/java/org/apache/sling/models/persistor/impl/util/ReflectionUtils.java
@@ -1,303 +1,303 @@
-/*
- * #%L
- * ACS AEM Commons Bundle
- * %%
- * Copyright (C) 2016 Adobe
- * %%
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * #L%
- */
-package org.apache.sling.models.persist.impl.util;
-
-import java.beans.IntrospectionException;
-import java.beans.PropertyDescriptor;
-import java.beans.Transient;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.math.BigDecimal;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.inject.Named;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang3.reflect.FieldUtils;
-import org.apache.commons.lang3.reflect.MethodUtils;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.models.annotations.Via;
-import org.apache.sling.models.persist.JcrPersist;
-import org.apache.sling.models.persist.annotations.Ignore;
-
-/**
- * Utility methods around object reflection.
- *
- */
-public class ReflectionUtils {
-
- private ReflectionUtils() {
- // Utility class cannot be instantiated
- }
-
- /**
- * Classes that correspond to basic parameter types, which are handled
- * directly in code
- */
- static final Set<Class<?>> BASIC_PARAMS = new HashSet<>();
-
- static final Set<Class<?>> UNSUPPORTED_CLASSES = new HashSet<>();
- static final Set<String> UNSUPPORTED_PACKAGES = new HashSet<>();
-
- /**
- * Add default values
- */
- static {
- // primitives
- BASIC_PARAMS.add(byte.class);
- BASIC_PARAMS.add(char.class);
- BASIC_PARAMS.add(short.class);
- BASIC_PARAMS.add(int.class);
- BASIC_PARAMS.add(long.class);
- BASIC_PARAMS.add(float.class);
- BASIC_PARAMS.add(double.class);
- BASIC_PARAMS.add(boolean.class);
-
- // primitive boxed
- BASIC_PARAMS.add(Byte.class);
- BASIC_PARAMS.add(Character.class);
- BASIC_PARAMS.add(Short.class);
- BASIC_PARAMS.add(Integer.class);
- BASIC_PARAMS.add(Long.class);
- BASIC_PARAMS.add(Float.class);
- BASIC_PARAMS.add(Double.class);
- BASIC_PARAMS.add(Boolean.class);
-
- // other basic types
- BASIC_PARAMS.add(String.class);
- BASIC_PARAMS.add(Calendar.class);
- BASIC_PARAMS.add(Date.class);
- BASIC_PARAMS.add(URI.class);
- BASIC_PARAMS.add(BigDecimal.class);
-
- // basic type arrays
- BASIC_PARAMS.add(String[].class);
- BASIC_PARAMS.add(Calendar[].class);
- BASIC_PARAMS.add(Date[].class);
- BASIC_PARAMS.add(URI[].class);
- BASIC_PARAMS.add(BigDecimal[].class);
-
- // primitives array
- BASIC_PARAMS.add(byte[].class);
- BASIC_PARAMS.add(char[].class);
- BASIC_PARAMS.add(short[].class);
- BASIC_PARAMS.add(int[].class);
- BASIC_PARAMS.add(long[].class);
- BASIC_PARAMS.add(float[].class);
- BASIC_PARAMS.add(double[].class);
- BASIC_PARAMS.add(boolean[].class);
-
- // primitive boxed arrays
- BASIC_PARAMS.add(Byte[].class);
- BASIC_PARAMS.add(Character[].class);
- BASIC_PARAMS.add(Short[].class);
- BASIC_PARAMS.add(Integer[].class);
- BASIC_PARAMS.add(Long[].class);
- BASIC_PARAMS.add(Float[].class);
- BASIC_PARAMS.add(Double[].class);
- BASIC_PARAMS.add(Boolean[].class);
-
- // Any field with this type will be ignored
- UNSUPPORTED_CLASSES.add(Resource.class);
- UNSUPPORTED_CLASSES.add(JcrPersist.class);
- UNSUPPORTED_PACKAGES.add("javax.jcr");
- UNSUPPORTED_PACKAGES.add("com.day.cq");
- UNSUPPORTED_PACKAGES.add("org.apache.sling.api");
- UNSUPPORTED_PACKAGES.add("com.adobe.acs.commons.mcp");
- }
-
- public static Collection<Class<?>> getSupportedPropertyTypes() {
- return BASIC_PARAMS;
- }
-
- public static boolean isArrayOrCollection(Object instance) {
- return instance.getClass().isArray() || instance instanceof Collection;
- }
-
- /**
- * Check if a given field is transient. A field is considered transient if
- * and only if the field is marked with `transient` keyword and no
- * annotation of type {@link Named} exists over the field; or if the field
- * is marked with {@link Ignore} annotation.
- *
- * @param field the non-<code>null</code> field to check.
- *
- * @return <code>false</code> if field is to be considered transient,
- * <code>true</code> otherwise
- */
- public static boolean isNotTransient(Field field) {
- if (field != null && Modifier.isTransient(field.getModifiers())) {
- // if property is covered using @Named annotation it shall not be excluded
- Named aemProperty = field.getAnnotation(Named.class);
- return aemProperty != null;
- } else {
- // is the property annotated with @Ignore?
- return field == null || field.getAnnotation(Ignore.class) == null;
- }
- }
-
- public static boolean hasNoTransientGetter(String fieldName, Class clazz) {
- PropertyDescriptor desc;
- try {
- desc = new PropertyDescriptor(fieldName, clazz);
- if (desc.getReadMethod() != null && desc.getReadMethod().getAnnotation(Transient.class) != null) {
- return false;
- }
- } catch (IntrospectionException ex) {
- // Do nothing
- }
- return true;
- }
-
- public static boolean isSupportedType(Field field) {
- Class clazz = field.getType();
- if (Map.class.isAssignableFrom(clazz)) {
- ParameterizedType p = (ParameterizedType) field.getGenericType();
- Type paramType = p.getActualTypeArguments()[1];
- try {
- // In case the value type is a collection of something, check to be safe
- if (!Class.class.isAssignableFrom(paramType.getClass())) {
- paramType = ((ParameterizedType) paramType).getActualTypeArguments()[0];
- }
- // Assume for now that we've narrowed down to the final object type to confirm
- clazz = (Class) paramType;
- } catch (ClassCastException ex) {
- return false;
- }
- }
- if (UNSUPPORTED_CLASSES.contains(clazz)) {
- return false;
- }
- Package pkg = clazz.isArray() ? clazz.getComponentType().getPackage() : clazz.getPackage();
- if (pkg == null) {
- return true;
- } else {
- String packageName = pkg.getName();
- return UNSUPPORTED_PACKAGES
- .stream()
- .noneMatch(packageName::startsWith);
- }
- }
-
- /**
- * Return all fields including all-private and all-inherited fields for the
- * given class.
- *
- * @param clazz the class for which fields are needed
- *
- * @return the {@link List} of {@link Field} objects in no certain order
- *
- * @throws IllegalArgumentException if given class is <code>null</code>
- */
- public static List<Field> getAllFields(Class<?> clazz) {
- List<Field> fields = new ArrayList<>();
- populateAllFields(clazz, fields);
- return fields;
- }
-
- public static void populateAllFields(Class<?> clazz, List<Field> fields) {
- if (clazz == null) {
- return;
- }
-
- Field[] array = clazz.getDeclaredFields();
- if (AssertUtils.isNotEmpty(array)) {
- fields.addAll(Arrays.asList(array));
- }
-
- if (clazz.getSuperclass() == null) {
- return;
- }
-
- populateAllFields(clazz.getSuperclass(), fields);
- }
-
- // Utility function common to reading/writing start here
- /**
- * Returns the name of the field to look for in JCR.
- *
- * @param field
- * @return
- */
- public static String getFieldName(Field field) {
- Named namedAnnotation = field.getAnnotation(Named.class);
- Via viaAnnotation = field.getAnnotation(Via.class);
- if (namedAnnotation != null) {
- return namedAnnotation.value();
- } else if (viaAnnotation != null && viaAnnotation.value() != null) {
- return viaAnnotation.value();
- } else {
- return field.getName();
- }
- }
-
- public static boolean isPrimitiveFieldType(Class<?> fieldType) {
- return getSupportedPropertyTypes().contains(fieldType);
- }
-
- /**
- * Get the value defined on an annotation, if it is a class annotation, or a
- * method or field-level member which has that annotation
- *
- * @param obj Object which has the given annotation as a class, method, or
- * field annotation
- * @param annotatedType desired annotation type class
- * @return Value if found otherwise null
- */
- public static Object getAnnotatedValue(Object obj, Class annotatedType) {
- if (obj == null) {
- return null;
- }
- Annotation a = obj.getClass().getAnnotation(annotatedType);
- try {
- if (a != null) {
- String value = (String) MethodUtils.invokeMethod(a, "value");
- if (value != null && !value.isEmpty()) {
- return value;
- }
- }
- } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
- // Do nothing, it didn't have a value defined on that annotation
- }
- List<Field> fields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), annotatedType);
- try {
- if (fields == null || fields.isEmpty()) {
- List<Method> methods = MethodUtils.getMethodsListWithAnnotation(obj.getClass(), annotatedType);
- return CollectionUtils.isNotEmpty(methods) ? methods.get(0).invoke(obj) : null;
- } else {
- return fields.get(0).get(obj);
- }
- } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
- return null;
- }
- }
-}
+/*
+ * 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.persistor.impl.util;
+
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.beans.Transient;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Named;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.commons.lang3.reflect.MethodUtils;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Via;
+import org.apache.sling.models.persistor.annotations.Ignore;
+import org.apache.sling.models.persistor.ModelPersistor;
+
+/**
+ * Utility methods around object reflection.
+ *
+ */
+public class ReflectionUtils {
+
+ private ReflectionUtils() {
+ // Utility class cannot be instantiated
+ }
+
+ /**
+ * Classes that correspond to basic parameter types, which are handled
+ * directly in code
+ */
+ static final Set<Class<?>> BASIC_PARAMS = new HashSet<>();
+
+ static final Set<Class<?>> UNSUPPORTED_CLASSES = new HashSet<>();
+ static final Set<String> UNSUPPORTED_PACKAGES = new HashSet<>();
+
+ /**
+ * Add default values
+ */
+ static {
+ // primitives
+ BASIC_PARAMS.add(byte.class);
+ BASIC_PARAMS.add(char.class);
+ BASIC_PARAMS.add(short.class);
+ BASIC_PARAMS.add(int.class);
+ BASIC_PARAMS.add(long.class);
+ BASIC_PARAMS.add(float.class);
+ BASIC_PARAMS.add(double.class);
+ BASIC_PARAMS.add(boolean.class);
+
+ // primitive boxed
+ BASIC_PARAMS.add(Byte.class);
+ BASIC_PARAMS.add(Character.class);
+ BASIC_PARAMS.add(Short.class);
+ BASIC_PARAMS.add(Integer.class);
+ BASIC_PARAMS.add(Long.class);
+ BASIC_PARAMS.add(Float.class);
+ BASIC_PARAMS.add(Double.class);
+ BASIC_PARAMS.add(Boolean.class);
+
+ // other basic types
+ BASIC_PARAMS.add(String.class);
+ BASIC_PARAMS.add(Calendar.class);
+ BASIC_PARAMS.add(Date.class);
+ BASIC_PARAMS.add(URI.class);
+ BASIC_PARAMS.add(BigDecimal.class);
+
+ // basic type arrays
+ BASIC_PARAMS.add(String[].class);
+ BASIC_PARAMS.add(Calendar[].class);
+ BASIC_PARAMS.add(Date[].class);
+ BASIC_PARAMS.add(URI[].class);
+ BASIC_PARAMS.add(BigDecimal[].class);
+
+ // primitives array
+ BASIC_PARAMS.add(byte[].class);
+ BASIC_PARAMS.add(char[].class);
+ BASIC_PARAMS.add(short[].class);
+ BASIC_PARAMS.add(int[].class);
+ BASIC_PARAMS.add(long[].class);
+ BASIC_PARAMS.add(float[].class);
+ BASIC_PARAMS.add(double[].class);
+ BASIC_PARAMS.add(boolean[].class);
+
+ // primitive boxed arrays
+ BASIC_PARAMS.add(Byte[].class);
+ BASIC_PARAMS.add(Character[].class);
+ BASIC_PARAMS.add(Short[].class);
+ BASIC_PARAMS.add(Integer[].class);
+ BASIC_PARAMS.add(Long[].class);
+ BASIC_PARAMS.add(Float[].class);
+ BASIC_PARAMS.add(Double[].class);
+ BASIC_PARAMS.add(Boolean[].class);
+
+ // Any field with this type will be ignored
+ UNSUPPORTED_CLASSES.add(Resource.class);
+ UNSUPPORTED_CLASSES.add(ModelPersistor.class);
+ UNSUPPORTED_PACKAGES.add("javax.jcr");
+ UNSUPPORTED_PACKAGES.add("com.day.cq");
+ UNSUPPORTED_PACKAGES.add("org.apache.sling.api");
+ UNSUPPORTED_PACKAGES.add("com.adobe.acs.commons.mcp");
+ }
+
+ public static Collection<Class<?>> getSupportedPropertyTypes() {
+ return BASIC_PARAMS;
+ }
+
+ public static boolean isArrayOrCollection(Object instance) {
+ return instance.getClass().isArray() || instance instanceof Collection;
+ }
+
+ /**
+ * Check if a given field is transient. A field is considered transient if
+ * and only if the field is marked with `transient` keyword and no
+ * annotation of type {@link Named} exists over the field; or if the field
+ * is marked with {@link Ignore} annotation.
+ *
+ * @param field the non-<code>null</code> field to check.
+ *
+ * @return <code>false</code> if field is to be considered transient,
+ * <code>true</code> otherwise
+ */
+ public static boolean isNotTransient(Field field) {
+ if (field != null && Modifier.isTransient(field.getModifiers())) {
+ // if property is covered using @Named annotation it shall not be excluded
+ Named aemProperty = field.getAnnotation(Named.class);
+ return aemProperty != null;
+ } else {
+ // is the property annotated with @Ignore?
+ return field == null || field.getAnnotation(Ignore.class) == null;
+ }
+ }
+
+ public static boolean hasNoTransientGetter(String fieldName, Class clazz) {
+ PropertyDescriptor desc;
+ try {
+ desc = new PropertyDescriptor(fieldName, clazz);
+ if (desc.getReadMethod() != null && desc.getReadMethod().getAnnotation(Transient.class) != null) {
+ return false;
+ }
+ } catch (IntrospectionException ex) {
+ // Do nothing
+ }
+ return true;
+ }
+
+ public static boolean isSupportedType(Field field) {
+ Class clazz = field.getType();
+ if (Map.class.isAssignableFrom(clazz)) {
+ ParameterizedType p = (ParameterizedType) field.getGenericType();
+ Type paramType = p.getActualTypeArguments()[1];
+ try {
+ // In case the value type is a collection of something, check to be safe
+ if (!Class.class.isAssignableFrom(paramType.getClass())) {
+ paramType = ((ParameterizedType) paramType).getActualTypeArguments()[0];
+ }
+ // Assume for now that we've narrowed down to the final object type to confirm
+ clazz = (Class) paramType;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+ }
+ if (UNSUPPORTED_CLASSES.contains(clazz)) {
+ return false;
+ }
+ Package pkg = clazz.isArray() ? clazz.getComponentType().getPackage() : clazz.getPackage();
+ if (pkg == null) {
+ return true;
+ } else {
+ String packageName = pkg.getName();
+ return UNSUPPORTED_PACKAGES
+ .stream()
+ .noneMatch(packageName::startsWith);
+ }
+ }
+
+ /**
+ * Return all fields including all-private and all-inherited fields for the
+ * given class.
+ *
+ * @param clazz the class for which fields are needed
+ *
+ * @return the {@link List} of {@link Field} objects in no certain order
+ *
+ * @throws IllegalArgumentException if given class is <code>null</code>
+ */
+ public static List<Field> getAllFields(Class<?> clazz) {
+ List<Field> fields = new ArrayList<>();
+ populateAllFields(clazz, fields);
+ return fields;
+ }
+
+ public static void populateAllFields(Class<?> clazz, List<Field> fields) {
+ if (clazz == null) {
+ return;
+ }
+
+ Field[] array = clazz.getDeclaredFields();
+ if (ArrayUtils.isNotEmpty(array)) {
+ fields.addAll(Arrays.asList(array));
+ }
+
+ if (clazz.getSuperclass() == null) {
+ return;
+ }
+
+ populateAllFields(clazz.getSuperclass(), fields);
+ }
+
+ // Utility function common to reading/writing start here
+ /**
+ * Returns the name of the field to look for in JCR.
+ *
+ * @param field
+ * @return
+ */
+ public static String getFieldName(Field field) {
+ Named namedAnnotation = field.getAnnotation(Named.class);
+ Via viaAnnotation = field.getAnnotation(Via.class);
+ if (namedAnnotation != null) {
+ return namedAnnotation.value();
+ } else if (viaAnnotation != null && viaAnnotation.value() != null) {
+ return viaAnnotation.value();
+ } else {
+ return field.getName();
+ }
+ }
+
+ public static boolean isPrimitiveFieldType(Class<?> fieldType) {
+ return getSupportedPropertyTypes().contains(fieldType);
+ }
+
+ /**
+ * Get the value defined on an annotation, if it is a class annotation, or a
+ * method or field-level member which has that annotation
+ *
+ * @param obj Object which has the given annotation as a class, method, or
+ * field annotation
+ * @param annotatedType desired annotation type class
+ * @return Value if found otherwise null
+ */
+ public static Object getAnnotatedValue(Object obj, Class annotatedType) {
+ if (obj == null) {
+ return null;
+ }
+ Annotation a = obj.getClass().getAnnotation(annotatedType);
+ try {
+ if (a != null) {
+ String value = (String) MethodUtils.invokeMethod(a, "value");
+ if (value != null && !value.isEmpty()) {
+ return value;
+ }
+ }
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
+ // Do nothing, it didn't have a value defined on that annotation
+ }
+ List<Field> fields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), annotatedType);
+ try {
+ if (fields == null || fields.isEmpty()) {
+ List<Method> methods = MethodUtils.getMethodsListWithAnnotation(obj.getClass(), annotatedType);
+ return CollectionUtils.isNotEmpty(methods) ? methods.get(0).invoke(obj) : null;
+ } else {
+ return fields.get(0).get(obj);
+ }
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+ return null;
+ }
+ }
+}
diff --git a/SlingModelPersist/src/test/java/org/apache/sling/models/injectors/BeanWithDirectMappedChildren.java b/SlingModelPersist/src/test/java/org/apache/sling/models/injectors/BeanWithDirectMappedChildren.java
new file mode 100644
index 0000000..2a9eecc
--- /dev/null
+++ b/SlingModelPersist/src/test/java/org/apache/sling/models/injectors/BeanWithDirectMappedChildren.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */package org.apache.sling.models.injectors;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.inject.Inject;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.DefaultInjectionStrategy;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.persistor.annotations.DirectDescendants;
+
+/**
+ * Expresses a sling model which has child nodes as a map
+ */
+@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
+public class BeanWithDirectMappedChildren {
+
+ public transient String path;
+
+ @DirectDescendants
+ @Inject
+ public Map<String, Person> people = new HashMap<>();
+
+ public void addPerson(String firstName, String lastName) {
+ Person p = new Person();
+ String name = lastName + '-' + firstName;
+ p.firstName = firstName;
+ p.lastName = lastName;
+ p.path = "./" + name;
+ people.put(name, p);
+ }
+
+ @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
+ public static class Person {
+
+ transient String path;
+
+ @Inject
+ String firstName;
+
+ @Inject
+ String lastName;
+ }
+}
diff --git a/SlingModelPersist/src/test/java/org/apache/sling/models/injectors/BeanWithMappedChildren.java b/SlingModelPersist/src/test/java/org/apache/sling/models/injectors/BeanWithMappedChildren.java
new file mode 100644
index 0000000..f50221c
--- /dev/null
+++ b/SlingModelPersist/src/test/java/org/apache/sling/models/injectors/BeanWithMappedChildren.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.injectors;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.inject.Inject;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.DefaultInjectionStrategy;
+import org.apache.sling.models.annotations.Model;
+
+/**
+ * Expresses a sling model which has child nodes as a map
+ */
+@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
+public class BeanWithMappedChildren {
+
+ public transient String path;
+
+ @Inject
+ public Map<String, Person> people = new HashMap<>();
+
+ public void addPerson(String firstName, String lastName) {
+ Person p = new Person();
+ String name = lastName + '-' + firstName;
+ p.firstName = firstName;
+ p.lastName = lastName;
+ p.path = "./" + name;
+ people.put(name, p);
+ }
+
+ @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
+ public static class Person {
+
+ transient String path;
+
+ @Inject
+ String firstName;
+
+ @Inject
+ String lastName;
+ }
+}
diff --git a/SlingJCRPersist/src/test/java/org/apache/sling/models/injectors/MapOfChildResourcesInjectorTest.java b/SlingModelPersist/src/test/java/org/apache/sling/models/injectors/MapOfChildResourcesInjectorTest.java
old mode 100755
new mode 100644
similarity index 76%
rename from SlingJCRPersist/src/test/java/org/apache/sling/models/injectors/MapOfChildResourcesInjectorTest.java
rename to SlingModelPersist/src/test/java/org/apache/sling/models/injectors/MapOfChildResourcesInjectorTest.java
index ccf7e39..8a2d772
--- a/SlingJCRPersist/src/test/java/org/apache/sling/models/injectors/MapOfChildResourcesInjectorTest.java
+++ b/SlingModelPersist/src/test/java/org/apache/sling/models/injectors/MapOfChildResourcesInjectorTest.java
@@ -1,96 +1,110 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.apache.sling.models.injectors;
-
-import javax.jcr.RepositoryException;
-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.models.persist.JcrPersist;
-import org.apache.sling.models.persist.impl.JcrPersistImpl;
-import org.apache.sling.models.spi.Injector;
-import org.apache.sling.testing.mock.sling.junit.SlingContext;
-import static org.junit.Assert.*;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- *
- * @author Brendan Robert
- */
-public class MapOfChildResourcesInjectorTest {
- @Rule
- public final SlingContext context = new SlingContext();
-
- ResourceResolver rr;
- JcrPersist jcrPersist;
-
- @Before
- public void setUp() {
- rr = context.resourceResolver();
- context.addModelsForPackage(this.getClass().getPackage().getName());
- context.registerService(Injector.class, new MapOfChildResourcesInjector());
- jcrPersist = new JcrPersistImpl();
- }
-
- @Test
- public void roundtripTest() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
- BeanWithMappedChildren source = new BeanWithMappedChildren();
- source.addPerson("joe", "schmoe");
- source.addPerson("john", "doe");
- source.addPerson("bob", "smith");
- jcrPersist.persist("/test/bean", source, rr);
-
- Resource targetResource = rr.getResource("/test/bean");
- assertNotNull("Bean should have been persisted");
-
- Resource personRes = rr.getResource("/test/bean/people/smith-bob");
- assertNotNull("Person should have persisted in repository", personRes);
-
- BeanWithMappedChildren target = targetResource.adaptTo(BeanWithMappedChildren.class);
- assertNotNull("Bean should deserialize", target);
-
- assertEquals("Should have 3 children", 3, target.people.size());
- }
-
- @Test
- public void roundtripEmpytTest() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
- BeanWithMappedChildren source = new BeanWithMappedChildren();
- jcrPersist.persist("/test/empty-bean", source, rr);
-
- Resource targetResource = rr.getResource("/test/empty-bean");
- assertNotNull("Bean should have been persisted");
-
- Resource personRes = rr.getResource("/test/empty-bean/people");
- assertNull("Person should not have persisted in repository", personRes);
-
- BeanWithMappedChildren target = targetResource.adaptTo(BeanWithMappedChildren.class);
- assertNotNull("Bean should deserialize", target);
-
- assertEquals("Should have 0 children", 0, target.people.size());
- }
-
- @Test
- public void roundtripTestDirectChildren() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
- BeanWithDirectMappedChildren source = new BeanWithDirectMappedChildren();
- source.addPerson("joe", "schmoe");
- source.addPerson("john", "doe");
- source.addPerson("bob", "smith");
- jcrPersist.persist("/test/bean", source, rr);
-
- Resource targetResource = rr.getResource("/test/bean");
- assertNotNull("Bean should have been persisted");
-
- Resource personRes = rr.getResource("/test/bean/smith-bob");
- assertNotNull("Person should have persisted in repository", personRes);
-
- BeanWithDirectMappedChildren target = targetResource.adaptTo(BeanWithDirectMappedChildren.class);
- assertNotNull("Bean should deserialize", target);
-
- assertEquals("Should have 3 children", 3, target.people.size());
- }
-}
+/*
+ * 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.injectors;
+
+import javax.jcr.RepositoryException;
+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.models.persistor.impl.ModelPersistorImpl;
+import org.apache.sling.models.spi.Injector;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+import org.apache.sling.models.persistor.ModelPersistor;
+
+/**
+ *
+ * @author Brendan Robert
+ */
+public class MapOfChildResourcesInjectorTest {
+ @Rule
+ public final SlingContext context = new SlingContext();
+
+ ResourceResolver rr;
+ ModelPersistor jcrPersist;
+
+ @Before
+ public void setUp() {
+ rr = context.resourceResolver();
+ context.addModelsForPackage(this.getClass().getPackage().getName());
+ context.registerService(Injector.class, new MapOfChildResourcesInjector());
+ jcrPersist = new ModelPersistorImpl();
+ }
+
+ @Test
+ public void roundtripTest() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
+ BeanWithMappedChildren source = new BeanWithMappedChildren();
+ source.addPerson("joe", "schmoe");
+ source.addPerson("john", "doe");
+ source.addPerson("bob", "smith");
+ jcrPersist.persist("/test/bean", source, rr);
+
+ Resource targetResource = rr.getResource("/test/bean");
+ assertNotNull("Bean should have been persisted");
+
+ Resource personRes = rr.getResource("/test/bean/people/smith-bob");
+ assertNotNull("Person should have persisted in repository", personRes);
+
+ BeanWithMappedChildren target = targetResource.adaptTo(BeanWithMappedChildren.class);
+ assertNotNull("Bean should deserialize", target);
+
+ assertEquals("Should have 3 children", 3, target.people.size());
+ }
+
+ @Test
+ public void roundtripEmpytTest() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
+ BeanWithMappedChildren source = new BeanWithMappedChildren();
+ jcrPersist.persist("/test/empty-bean", source, rr);
+
+ Resource targetResource = rr.getResource("/test/empty-bean");
+ assertNotNull("Bean should have been persisted");
+
+ Resource personRes = rr.getResource("/test/empty-bean/people");
+ assertNull("Person should not have persisted in repository", personRes);
+
+ BeanWithMappedChildren target = targetResource.adaptTo(BeanWithMappedChildren.class);
+ assertNotNull("Bean should deserialize", target);
+
+ assertEquals("Should have 0 children", 0, target.people.size());
+ }
+
+ @Test
+ public void roundtripTestDirectChildren() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
+ BeanWithDirectMappedChildren source = new BeanWithDirectMappedChildren();
+ source.addPerson("joe", "schmoe");
+ source.addPerson("john", "doe");
+ source.addPerson("bob", "smith");
+ jcrPersist.persist("/test/bean", source, rr);
+
+ Resource targetResource = rr.getResource("/test/bean");
+ assertNotNull("Bean should have been persisted");
+
+ Resource personRes = rr.getResource("/test/bean/smith-bob");
+ assertNotNull("Person should have persisted in repository", personRes);
+
+ BeanWithDirectMappedChildren target = targetResource.adaptTo(BeanWithDirectMappedChildren.class);
+ assertNotNull("Bean should deserialize", target);
+
+ assertEquals("Should have 3 children", 3, target.people.size());
+ }
+}
diff --git a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/JcrWriterTest.java b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/ModelPersistTest.java
old mode 100755
new mode 100644
similarity index 92%
rename from SlingJCRPersist/src/test/java/org/apache/sling/models/persist/JcrWriterTest.java
rename to SlingModelPersist/src/test/java/org/apache/sling/models/persist/ModelPersistTest.java
index 3215c44..5c0056d
--- a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/JcrWriterTest.java
+++ b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/ModelPersistTest.java
@@ -1,327 +1,341 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.apache.sling.models.persist;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.stream.StreamSupport;
-import javax.jcr.RepositoryException;
-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.ValueMap;
-import org.apache.sling.models.persist.bean.BeanWithAnnotatedPathField;
-import org.apache.sling.models.persist.bean.BeanWithAnnotatedPathGetter;
-import org.apache.sling.models.persist.bean.BeanWithMappedNames;
-import org.apache.sling.models.persist.bean.BeanWithPathField;
-import org.apache.sling.models.persist.bean.BeanWithPathGetter;
-import org.apache.sling.models.persist.bean.ComplexBean;
-import org.apache.sling.models.persist.bean.MappedChildren;
-import org.apache.sling.models.persist.impl.JcrPersistImpl;
-import org.apache.sling.testing.mock.sling.junit.SlingContext;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.*;
-
-/**
- * Test basic JCR persistence behaviors
- */
-public class JcrWriterTest {
-
- @Rule
- public final SlingContext context = new SlingContext();
-
- ResourceResolver rr;
- JcrPersist jcrPersist = new JcrPersistImpl();
-
- @Before
- public void setUp() {
- rr = context.resourceResolver();
- context.addModelsForClasses(
- BeanWithAnnotatedPathField.class,
- BeanWithAnnotatedPathGetter.class,
- BeanWithPathField.class,
- BeanWithPathGetter.class,
- BeanWithMappedNames.class,
- BeanWithMappedNames.ChildBean.class,
- MappedChildren.class,
- MappedChildren.Child.class,
- ComplexBean.class,
- ComplexBean.Level2Bean.class,
- ComplexBean.Level3Bean.class);
- }
-
- /**
- * Confirm that content is written to correct path indicated by either path
- * field, path getter, or path annotation.Also asserts that path annotation
- * takes precedence over any field or getter method.
- *
- * @throws javax.jcr.RepositoryException
- * @throws org.apache.sling.api.resource.PersistenceException
- * @throws java.lang.IllegalAccessException
- */
- @Test
- public void testPersistBeanPath() throws RepositoryException, PersistenceException, IllegalAccessException {
- BeanWithPathGetter bean1 = new BeanWithPathGetter();
- jcrPersist.persist(bean1, rr, false);
- Resource res = rr.getResource(bean1.getPath());
- assertNotNull("Resource not created at expected path", res);
- assertEquals("Expected property not found", bean1.prop1, res.getValueMap().get("prop1", "missing"));
-
- BeanWithPathField bean2 = new BeanWithPathField();
- jcrPersist.persist(bean2, rr, false);
- res = rr.getResource(bean2.path + "/jcr:content");
- assertNotNull("Resource not created at expected path", res);
- assertEquals("Expected property not found", bean2.prop1, res.getValueMap().get("prop1", "missing"));
-
- BeanWithAnnotatedPathField bean3 = new BeanWithAnnotatedPathField();
- jcrPersist.persist(bean3, rr);
- res = rr.getResource(bean3.correctPath);
- assertNotNull("Resource not created at expected path", res);
- assertEquals("Expected property not found", bean3.prop1, res.getValueMap().get("prop1", "missing"));
-
- BeanWithAnnotatedPathGetter bean4 = new BeanWithAnnotatedPathGetter();
- jcrPersist.persist(bean4, rr, true);
- res = rr.getResource(bean4.getCorrectPath());
- assertNotNull("Resource not created at expected path", res);
- assertEquals("Expected property not found", bean4.prop1, res.getValueMap().get("prop1", "missing"));
- }
-
- /**
- * Confirm that content is persisted at provided path even if it has a path
- * annotation or path getter, etc.
- *
- * @throws javax.jcr.RepositoryException
- * @throws org.apache.sling.api.resource.PersistenceException
- * @throws java.lang.IllegalAccessException
- */
- @Test
- public void testPersistProvidedPath() throws RepositoryException, PersistenceException, IllegalAccessException {
- String testPath = "/manual/path";
- BeanWithAnnotatedPathField bean = new BeanWithAnnotatedPathField();
- jcrPersist.persist(testPath, bean, rr, false);
- Resource res = rr.getResource(bean.correctPath);
- assertNull("Should not have stored content here", res);
- res = rr.getResource(testPath);
- assertNotNull("Resource not created at expected path", res);
- assertEquals("Expected property not found", bean.prop1, res.getValueMap().get("prop1", "missing"));
- }
-
- @Test
- public void testComplexObjectGraph() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
- // First create a bean with a complex structure and various object types buried in it
- ComplexBean sourceBean = new ComplexBean();
- sourceBean.name = "Complex-bean-test";
- sourceBean.arrayOfStrings = new String[]{"Value 1", "Value 2", "Value 3"};
- sourceBean.level2.name = "Complex-bean-level2";
- ComplexBean.Level3Bean l31 = new ComplexBean.Level3Bean();
- l31.value1 = "L3-1";
- l31.value2 = 123;
- l31.valueList = new String[]{"L31a", "L31b", "L31c", "L31d"};
- ComplexBean.Level3Bean l32 = new ComplexBean.Level3Bean();
- l32.value1 = "L3-2";
- l32.value2 = 456;
- l32.valueList = new String[]{"L32a", "L32b", "L32c", "L32d"};
- l32.path = "/test/complex-beans/Complex-bean-test/level2/level3/child-2";
- sourceBean.level2.level3.add(l31);
- sourceBean.level2.level3.add(l32);
-
- // Persist the bean
- jcrPersist.persist(sourceBean.getPath(), sourceBean, rr);
-
- // Now retrieve that object from the repository
- rr.refresh();
- Resource createdResource = rr.getResource(sourceBean.getPath());
- ComplexBean targetBean = createdResource.adaptTo(ComplexBean.class);
-
- assertNotNull(targetBean);
- assertNotEquals(sourceBean, targetBean);
- assertTrue("Expecing children of object to have been deserialized", targetBean.level2.level3 != null && targetBean.level2.level3.size() > 0);
- targetBean.level2.level3.get(0).path = l31.path;
- assertThat(targetBean).isEqualToComparingFieldByFieldRecursively(sourceBean);
- }
-
- @Test
- public void testChildObjectRemoval() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
- // First create a bean with a complex structure and various object types buried in it
- ComplexBean sourceBean = new ComplexBean();
- sourceBean.name = "Complex-bean-test";
- sourceBean.arrayOfStrings = new String[]{"Value 1", "Value 2", "Value 3"};
- sourceBean.level2.name = "Complex-bean-level2";
- ComplexBean.Level3Bean l31 = new ComplexBean.Level3Bean();
- l31.value1 = "L3-1";
- l31.value2 = 123;
- l31.valueList = new String[]{"L31a", "L31b", "L31c", "L31d"};
- ComplexBean.Level3Bean l32 = new ComplexBean.Level3Bean();
- l32.value1 = "L3-2";
- l32.value2 = 456;
- l32.valueList = new String[]{"L32a", "L32b", "L32c", "L32d"};
- l32.path = "/test/complex-beans/Complex-bean-test/level2/level3/child-2";
- sourceBean.level2.level3.add(l31);
- sourceBean.level2.level3.add(l32);
-
- // Persist the bean
- jcrPersist.persist(sourceBean, rr);
-
- // Child record should exist
- Resource existingResource = rr.getResource(l32.path);
- assertNotNull(existingResource);
-
- sourceBean.level2.level3.remove(l32);
- jcrPersist.persist(sourceBean, rr);
-
- // Child record should no longer exist
- Resource deletedResource = rr.getResource(l32.path);
- assertNull(deletedResource);
- }
-
- @Test
- public void testMappedNames() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException, JsonProcessingException {
- // Create test beans
- BeanWithMappedNames.ChildBean child1 = new BeanWithMappedNames.ChildBean();
- BeanWithMappedNames.ChildBean child2 = new BeanWithMappedNames.ChildBean();
- BeanWithMappedNames.ChildBean child3 = new BeanWithMappedNames.ChildBean();
- child1.setName("child-1");
- child2.setName("child-2");
- child3.setName("child-3");
-
- BeanWithMappedNames bean = new BeanWithMappedNames();
- bean.setWrong1("Name");
- bean.setWrong2(new String[]{"foo", "bar", "baz"});
- bean.setWrong3(child1);
- bean.setWrong4(Arrays.asList(child1, child2, child3));
- bean.setWrong5(new HashMap<String, BeanWithMappedNames.ChildBean>() {
- {
- put("child1", child1);
- put("child2", child2);
- put("child3", child3);
- }
- });
- bean.setWrong6(Boolean.TRUE);
-
- // Persist values
- jcrPersist.persist("/test/mapped", bean, rr);
-
- // Check that everything stored correctly
- Resource res = rr.getResource("/test/mapped");
- ValueMap properties = res.getValueMap();
- // Part 1: Simple property
- assertEquals("Name", properties.get("prop-1", String.class));
- assertNull(properties.get("wrong1"));
- // Part 2: Array property
- String[] prop2 = properties.get("prop-2", String[].class);
- assertArrayEquals(prop2, new String[]{"foo", "bar", "baz"});
- assertNull(properties.get("wrong2"));
- // Part 3: Object property
- assertNull(rr.getResource("/test/mapped/wrong3"));
- Resource childRes1 = rr.getResource("/test/mapped/child-1");
- assertNotNull(childRes1);
- assertEquals("child-1", childRes1.getValueMap().get("name"));
- // Part 4: Object list property
- assertNull(rr.getResource("/test/mapped/wrong4"));
- Resource childRes2 = rr.getResource("/test/mapped/child-2");
- assertNotNull(childRes2);
- assertEquals(StreamSupport
- .stream(childRes2.getChildren().spliterator(), false)
- .count(), 3L);
- // Part 5: Map-of-objects property
- assertNull(rr.getResource("/test/mapped/wrong5"));
- Resource childRes3 = rr.getResource("/test/mapped/child-3");
- assertNotNull(childRes3);
- assertEquals(StreamSupport
- .stream(childRes3.getChildren().spliterator(), false)
- .count(), 3L);
- // Part 6: Boolean property
- assertNull(properties.get("wrong6"));
- assertNull(properties.get("isWrong6"));
- assertTrue(properties.get("prop-3", Boolean.class));
-
- // Now confirm Jackson respects its mappings too
- ObjectMapper mapper = new ObjectMapper();
- String json = mapper.writeValueAsString(bean);
- assertFalse("Should not have wrong property names: " + json, json.contains("wrong"));
- assertTrue("Should have prop-1" + json, json.contains("prop-1"));
- assertTrue("Should have prop-2" + json, json.contains("prop-2"));
- assertTrue("Should have prop-3" + json, json.contains("prop-3"));
- assertTrue("Should have child-1" + json, json.contains("child-1"));
- assertTrue("Should have child-2" + json, json.contains("child-2"));
- assertTrue("Should have child-3" + json, json.contains("child-3"));
- }
-
- @Test
- /**
- * Test named map children with map<String, Object>
- *
- */
- public void testMapChildrenWithStringKeys() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
- // Create some values in the Map<String, Object> data structure
- MappedChildren bean = new MappedChildren();
- MappedChildren.Child child1 = new MappedChildren.Child();
- bean.stringKeys.put("one", child1);
- MappedChildren.Child child2 = new MappedChildren.Child();
- bean.stringKeys.put("two", child2);
- MappedChildren.Child child3 = new MappedChildren.Child();
- bean.stringKeys.put("three", child3);
- child1.name = "one";
- child1.testValue = "Test Value 1";
- child2.name = "two";
- child2.testValue = "Test Value 2";
- child3.name = "three";
- child3.testValue = "Test Value 3";
-
- // Attempt to save the data structure
- jcrPersist.persist("/test/path", bean, rr);
-
- // Confirm the children were saved in the expected places using the map key as the node name
- Resource r1 = rr.getResource("/test/path/stringKeys/one");
- assertNotNull(r1);
- Resource r2 = rr.getResource("/test/path/stringKeys/two");
- assertNotNull(r2);
- Resource r3 = rr.getResource("/test/path/stringKeys/three");
- assertNotNull(r3);
- }
-
- @Test
- /**
- * Test named map children with map<String, Object>
- *
- */
- public void testMapChildrenWithEnumerationKeys() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
- // Do same thing except using enumKeys map on the bean object
- // e.g. --> bean.enumKeys.put(MappedChildren.KEYS.ONE, child1);
-
- // Create some values in the Map<String, Object> data structure
- MappedChildren bean = new MappedChildren();
- MappedChildren.Child child1 = new MappedChildren.Child();
- bean.enumKeys.put(MappedChildren.KEYS.ONE, child1);
- MappedChildren.Child child2 = new MappedChildren.Child();
- bean.enumKeys.put(MappedChildren.KEYS.TWO, child2);
- MappedChildren.Child child3 = new MappedChildren.Child();
- bean.enumKeys.put(MappedChildren.KEYS.THREE, child3);
- child1.name = "one";
- child1.testValue = "Test Value 1";
- child2.name = "two";
- child2.testValue = "Test Value 2";
- child3.name = "three";
- child3.testValue = "Test Value 3";
-
- // Attempt to save the data structure
- jcrPersist.persist("/test/path", bean, rr);
-
- // Confirm the children were saved in the expected places using the map key as the node name
- Resource r1 = rr.getResource("/test/path/enumKeys/ONE");
- assertNotNull(r1);
- Resource r2 = rr.getResource("/test/path/enumKeys/TWO");
- assertNotNull(r2);
- Resource r3 = rr.getResource("/test/path/enumKeys/THREE");
- assertNotNull(r3);
- }
-}
+/*
+ * 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.persist;
+
+import org.apache.sling.models.persistor.ModelPersistor;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.stream.StreamSupport;
+import javax.jcr.RepositoryException;
+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.ValueMap;
+import org.apache.sling.models.persist.bean.BeanWithAnnotatedPathField;
+import org.apache.sling.models.persist.bean.BeanWithAnnotatedPathGetter;
+import org.apache.sling.models.persist.bean.BeanWithMappedNames;
+import org.apache.sling.models.persist.bean.BeanWithPathField;
+import org.apache.sling.models.persist.bean.BeanWithPathGetter;
+import org.apache.sling.models.persist.bean.ComplexBean;
+import org.apache.sling.models.persist.bean.MappedChildren;
+import org.apache.sling.models.persistor.impl.ModelPersistorImpl;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.*;
+
+/**
+ * Test basic JCR persistence behaviors
+ */
+public class ModelPersistTest {
+
+ @Rule
+ public final SlingContext context = new SlingContext();
+
+ ResourceResolver rr;
+ ModelPersistor jcrPersist = new ModelPersistorImpl();
+
+ @Before
+ public void setUp() {
+ rr = context.resourceResolver();
+ context.addModelsForClasses(
+ BeanWithAnnotatedPathField.class,
+ BeanWithAnnotatedPathGetter.class,
+ BeanWithPathField.class,
+ BeanWithPathGetter.class,
+ BeanWithMappedNames.class,
+ BeanWithMappedNames.ChildBean.class,
+ MappedChildren.class,
+ MappedChildren.Child.class,
+ ComplexBean.class,
+ ComplexBean.Level2Bean.class,
+ ComplexBean.Level3Bean.class);
+ }
+
+ /**
+ * Confirm that content is written to correct path indicated by either path
+ * field, path getter, or path annotation.Also asserts that path annotation
+ * takes precedence over any field or getter method.
+ *
+ * @throws javax.jcr.RepositoryException
+ * @throws org.apache.sling.api.resource.PersistenceException
+ * @throws java.lang.IllegalAccessException
+ */
+ @Test
+ public void testPersistBeanPath() throws RepositoryException, PersistenceException, IllegalAccessException {
+ BeanWithPathGetter bean1 = new BeanWithPathGetter();
+ jcrPersist.persist(bean1, rr, false);
+ Resource res = rr.getResource(bean1.getPath());
+ assertNotNull("Resource not created at expected path", res);
+ assertEquals("Expected property not found", bean1.prop1, res.getValueMap().get("prop1", "missing"));
+
+ BeanWithPathField bean2 = new BeanWithPathField();
+ jcrPersist.persist(bean2, rr, false);
+ res = rr.getResource(bean2.path + "/jcr:content");
+ assertNotNull("Resource not created at expected path", res);
+ assertEquals("Expected property not found", bean2.prop1, res.getValueMap().get("prop1", "missing"));
+
+ BeanWithAnnotatedPathField bean3 = new BeanWithAnnotatedPathField();
+ jcrPersist.persist(bean3, rr);
+ res = rr.getResource(bean3.correctPath);
+ assertNotNull("Resource not created at expected path", res);
+ assertEquals("Expected property not found", bean3.prop1, res.getValueMap().get("prop1", "missing"));
+
+ BeanWithAnnotatedPathGetter bean4 = new BeanWithAnnotatedPathGetter();
+ jcrPersist.persist(bean4, rr, true);
+ res = rr.getResource(bean4.getCorrectPath());
+ assertNotNull("Resource not created at expected path", res);
+ assertEquals("Expected property not found", bean4.prop1, res.getValueMap().get("prop1", "missing"));
+ }
+
+ /**
+ * Confirm that content is persisted at provided path even if it has a path
+ * annotation or path getter, etc.
+ *
+ * @throws javax.jcr.RepositoryException
+ * @throws org.apache.sling.api.resource.PersistenceException
+ * @throws java.lang.IllegalAccessException
+ */
+ @Test
+ public void testPersistProvidedPath() throws RepositoryException, PersistenceException, IllegalAccessException {
+ String testPath = "/manual/path";
+ BeanWithAnnotatedPathField bean = new BeanWithAnnotatedPathField();
+ jcrPersist.persist(testPath, bean, rr, false);
+ Resource res = rr.getResource(bean.correctPath);
+ assertNull("Should not have stored content here", res);
+ res = rr.getResource(testPath);
+ assertNotNull("Resource not created at expected path", res);
+ assertEquals("Expected property not found", bean.prop1, res.getValueMap().get("prop1", "missing"));
+ }
+
+ @Test
+ public void testComplexObjectGraph() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
+ // First create a bean with a complex structure and various object types buried in it
+ ComplexBean sourceBean = new ComplexBean();
+ sourceBean.name = "Complex-bean-test";
+ sourceBean.arrayOfStrings = new String[]{"Value 1", "Value 2", "Value 3"};
+ sourceBean.level2.name = "Complex-bean-level2";
+ ComplexBean.Level3Bean l31 = new ComplexBean.Level3Bean();
+ l31.value1 = "L3-1";
+ l31.value2 = 123;
+ l31.valueList = new String[]{"L31a", "L31b", "L31c", "L31d"};
+ ComplexBean.Level3Bean l32 = new ComplexBean.Level3Bean();
+ l32.value1 = "L3-2";
+ l32.value2 = 456;
+ l32.valueList = new String[]{"L32a", "L32b", "L32c", "L32d"};
+ l32.path = "/test/complex-beans/Complex-bean-test/level2/level3/child-2";
+ sourceBean.level2.level3.add(l31);
+ sourceBean.level2.level3.add(l32);
+
+ // Persist the bean
+ jcrPersist.persist(sourceBean.getPath(), sourceBean, rr);
+
+ // Now retrieve that object from the repository
+ rr.refresh();
+ Resource createdResource = rr.getResource(sourceBean.getPath());
+ ComplexBean targetBean = createdResource.adaptTo(ComplexBean.class);
+
+ assertNotNull(targetBean);
+ assertNotEquals(sourceBean, targetBean);
+ assertTrue("Expecing children of object to have been deserialized", targetBean.level2.level3 != null && targetBean.level2.level3.size() > 0);
+ targetBean.level2.level3.get(0).path = l31.path;
+ assertThat(targetBean).isEqualToComparingFieldByFieldRecursively(sourceBean);
+ }
+
+ @Test
+ public void testChildObjectRemoval() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
+ // First create a bean with a complex structure and various object types buried in it
+ ComplexBean sourceBean = new ComplexBean();
+ sourceBean.name = "Complex-bean-test";
+ sourceBean.arrayOfStrings = new String[]{"Value 1", "Value 2", "Value 3"};
+ sourceBean.level2.name = "Complex-bean-level2";
+ ComplexBean.Level3Bean l31 = new ComplexBean.Level3Bean();
+ l31.value1 = "L3-1";
+ l31.value2 = 123;
+ l31.valueList = new String[]{"L31a", "L31b", "L31c", "L31d"};
+ ComplexBean.Level3Bean l32 = new ComplexBean.Level3Bean();
+ l32.value1 = "L3-2";
+ l32.value2 = 456;
+ l32.valueList = new String[]{"L32a", "L32b", "L32c", "L32d"};
+ l32.path = "/test/complex-beans/Complex-bean-test/level2/level3/child-2";
+ sourceBean.level2.level3.add(l31);
+ sourceBean.level2.level3.add(l32);
+
+ // Persist the bean
+ jcrPersist.persist(sourceBean, rr);
+
+ // Child record should exist
+ Resource existingResource = rr.getResource(l32.path);
+ assertNotNull(existingResource);
+
+ sourceBean.level2.level3.remove(l32);
+ jcrPersist.persist(sourceBean, rr);
+
+ // Child record should no longer exist
+ Resource deletedResource = rr.getResource(l32.path);
+ assertNull(deletedResource);
+ }
+
+ @Test
+ public void testMappedNames() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException, JsonProcessingException {
+ // Create test beans
+ BeanWithMappedNames.ChildBean child1 = new BeanWithMappedNames.ChildBean();
+ BeanWithMappedNames.ChildBean child2 = new BeanWithMappedNames.ChildBean();
+ BeanWithMappedNames.ChildBean child3 = new BeanWithMappedNames.ChildBean();
+ child1.setName("child-1");
+ child2.setName("child-2");
+ child3.setName("child-3");
+
+ BeanWithMappedNames bean = new BeanWithMappedNames();
+ bean.setWrong1("Name");
+ bean.setWrong2(new String[]{"foo", "bar", "baz"});
+ bean.setWrong3(child1);
+ bean.setWrong4(Arrays.asList(child1, child2, child3));
+ bean.setWrong5(new HashMap<String, BeanWithMappedNames.ChildBean>() {
+ {
+ put("child1", child1);
+ put("child2", child2);
+ put("child3", child3);
+ }
+ });
+ bean.setWrong6(Boolean.TRUE);
+
+ // Persist values
+ jcrPersist.persist("/test/mapped", bean, rr);
+
+ // Check that everything stored correctly
+ Resource res = rr.getResource("/test/mapped");
+ ValueMap properties = res.getValueMap();
+ // Part 1: Simple property
+ assertEquals("Name", properties.get("prop-1", String.class));
+ assertNull(properties.get("wrong1"));
+ // Part 2: Array property
+ String[] prop2 = properties.get("prop-2", String[].class);
+ assertArrayEquals(prop2, new String[]{"foo", "bar", "baz"});
+ assertNull(properties.get("wrong2"));
+ // Part 3: Object property
+ assertNull(rr.getResource("/test/mapped/wrong3"));
+ Resource childRes1 = rr.getResource("/test/mapped/child-1");
+ assertNotNull(childRes1);
+ assertEquals("child-1", childRes1.getValueMap().get("name"));
+ // Part 4: Object list property
+ assertNull(rr.getResource("/test/mapped/wrong4"));
+ Resource childRes2 = rr.getResource("/test/mapped/child-2");
+ assertNotNull(childRes2);
+ assertEquals(StreamSupport
+ .stream(childRes2.getChildren().spliterator(), false)
+ .count(), 3L);
+ // Part 5: Map-of-objects property
+ assertNull(rr.getResource("/test/mapped/wrong5"));
+ Resource childRes3 = rr.getResource("/test/mapped/child-3");
+ assertNotNull(childRes3);
+ assertEquals(StreamSupport
+ .stream(childRes3.getChildren().spliterator(), false)
+ .count(), 3L);
+ // Part 6: Boolean property
+ assertNull(properties.get("wrong6"));
+ assertNull(properties.get("isWrong6"));
+ assertTrue(properties.get("prop-3", Boolean.class));
+
+ // Now confirm Jackson respects its mappings too
+ ObjectMapper mapper = new ObjectMapper();
+ String json = mapper.writeValueAsString(bean);
+ assertFalse("Should not have wrong property names: " + json, json.contains("wrong"));
+ assertTrue("Should have prop-1" + json, json.contains("prop-1"));
+ assertTrue("Should have prop-2" + json, json.contains("prop-2"));
+ assertTrue("Should have prop-3" + json, json.contains("prop-3"));
+ assertTrue("Should have child-1" + json, json.contains("child-1"));
+ assertTrue("Should have child-2" + json, json.contains("child-2"));
+ assertTrue("Should have child-3" + json, json.contains("child-3"));
+ }
+
+ @Test
+ /**
+ * Test named map children with map<String, Object>
+ *
+ */
+ public void testMapChildrenWithStringKeys() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
+ // Create some values in the Map<String, Object> data structure
+ MappedChildren bean = new MappedChildren();
+ MappedChildren.Child child1 = new MappedChildren.Child();
+ bean.stringKeys.put("one", child1);
+ MappedChildren.Child child2 = new MappedChildren.Child();
+ bean.stringKeys.put("two", child2);
+ MappedChildren.Child child3 = new MappedChildren.Child();
+ bean.stringKeys.put("three", child3);
+ child1.name = "one";
+ child1.testValue = "Test Value 1";
+ child2.name = "two";
+ child2.testValue = "Test Value 2";
+ child3.name = "three";
+ child3.testValue = "Test Value 3";
+
+ // Attempt to save the data structure
+ jcrPersist.persist("/test/path", bean, rr);
+
+ // Confirm the children were saved in the expected places using the map key as the node name
+ Resource r1 = rr.getResource("/test/path/stringKeys/one");
+ assertNotNull(r1);
+ Resource r2 = rr.getResource("/test/path/stringKeys/two");
+ assertNotNull(r2);
+ Resource r3 = rr.getResource("/test/path/stringKeys/three");
+ assertNotNull(r3);
+ }
+
+ @Test
+ /**
+ * Test named map children with map<String, Object>
+ *
+ */
+ public void testMapChildrenWithEnumerationKeys() throws RepositoryException, PersistenceException, IllegalArgumentException, IllegalAccessException {
+ // Do same thing except using enumKeys map on the bean object
+ // e.g. --> bean.enumKeys.put(MappedChildren.KEYS.ONE, child1);
+
+ // Create some values in the Map<String, Object> data structure
+ MappedChildren bean = new MappedChildren();
+ MappedChildren.Child child1 = new MappedChildren.Child();
+ bean.enumKeys.put(MappedChildren.KEYS.ONE, child1);
+ MappedChildren.Child child2 = new MappedChildren.Child();
+ bean.enumKeys.put(MappedChildren.KEYS.TWO, child2);
+ MappedChildren.Child child3 = new MappedChildren.Child();
+ bean.enumKeys.put(MappedChildren.KEYS.THREE, child3);
+ child1.name = "one";
+ child1.testValue = "Test Value 1";
+ child2.name = "two";
+ child2.testValue = "Test Value 2";
+ child3.name = "three";
+ child3.testValue = "Test Value 3";
+
+ // Attempt to save the data structure
+ jcrPersist.persist("/test/path", bean, rr);
+
+ // Confirm the children were saved in the expected places using the map key as the node name
+ Resource r1 = rr.getResource("/test/path/enumKeys/ONE");
+ assertNotNull(r1);
+ Resource r2 = rr.getResource("/test/path/enumKeys/TWO");
+ assertNotNull(r2);
+ Resource r3 = rr.getResource("/test/path/enumKeys/THREE");
+ assertNotNull(r3);
+ }
+}
diff --git a/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithAnnotatedPathField.java b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithAnnotatedPathField.java
new file mode 100644
index 0000000..6e60189
--- /dev/null
+++ b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithAnnotatedPathField.java
@@ -0,0 +1,38 @@
+/*
+ * 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.persist.bean;
+
+import javax.inject.Inject;
+import org.apache.sling.models.annotations.Path;
+
+/**
+ * Example of bean with getPath method that stores the path in a field using an annotation marker
+ */
+public class BeanWithAnnotatedPathField {
+ @Inject
+ public String prop1 = "testValue";
+
+ public String path = "/test/WRONG-path";
+
+ @Path
+ public String correctPath = "/test/annotated-field-path";
+
+ public String getPath() {
+ return path;
+ }
+}
diff --git a/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithAnnotatedPathGetter.java b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithAnnotatedPathGetter.java
new file mode 100644
index 0000000..43a597d
--- /dev/null
+++ b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithAnnotatedPathGetter.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */package org.apache.sling.models.persist.bean;
+
+import javax.inject.Inject;
+import org.apache.sling.models.annotations.Path;
+
+/**
+ * Example of bean with getPath method that stores the path in a field using an annotation marker
+ */
+public class BeanWithAnnotatedPathGetter {
+ @Inject
+ public String prop1 = "testValue";
+
+ public String path = "/test/WRONG-path";
+
+ public String correctPath = "/test/annotated-getter-path";
+
+ public String getPath() {
+ return path;
+ }
+
+ @Path
+ public String getCorrectPath() {
+ return path;
+ }
+}
diff --git a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithMappedNames.java b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithMappedNames.java
old mode 100755
new mode 100644
similarity index 78%
rename from SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithMappedNames.java
rename to SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithMappedNames.java
index 38db353..29b545c
--- a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithMappedNames.java
+++ b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithMappedNames.java
@@ -1,148 +1,164 @@
-package org.apache.sling.models.persist.bean;
-
-//import com.adobe.cq.export.json.ExporterConstants;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.List;
-import java.util.Map;
-import javax.inject.Inject;
-import javax.inject.Named;
-
-/**
- * every property has a different mapped name, designed to test the @Named annotation is respected.
- * fields are all named "wrong" because if we see that in the stored JCR values then the persist logic was wrong.
- */
-//@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
-public class BeanWithMappedNames {
- @Inject
- @Named("prop-1")
- @JsonProperty("prop-1")
- private String wrong1;
-
- @Inject
- @Named("prop-2")
- @JsonProperty("prop-2")
- private String[] wrong2;
-
- @Inject
- @Named("child-1")
- @JsonProperty("child-1")
- private ChildBean wrong3;
-
- @Inject
- @Named("child-2")
- @JsonProperty("child-2")
- private List<ChildBean> wrong4;
-
- @Inject
- @Named("child-3")
- @JsonProperty("child-3")
- private Map<String,ChildBean> wrong5;
-
- @Inject
- @Named("prop-3")
- @JsonProperty("prop-3")
- private Boolean wrong6;
-
- public static class ChildBean {
- @Inject
- private String name;
-
- /**
- * @return the name
- */
- public String getName() {
- return name;
- }
-
- /**
- * @param name the name to set
- */
- public void setName(String name) {
- this.name = name;
- }
- }
-
- /**
- * @return the wrong1
- */
- public String getWrong1() {
- return wrong1;
- }
-
- /**
- * @param wrong1 the wrong1 to set
- */
- public void setWrong1(String wrong1) {
- this.wrong1 = wrong1;
- }
-
- /**
- * @return the wrong2
- */
- public String[] getWrong2() {
- return wrong2;
- }
-
- /**
- * @param wrong2 the wrong2 to set
- */
- public void setWrong2(String[] wrong2) {
- this.wrong2 = wrong2;
- }
-
- /**
- * @return the wrong3
- */
- public ChildBean getWrong3() {
- return wrong3;
- }
-
- /**
- * @param wrong3 the wrong3 to set
- */
- public void setWrong3(ChildBean wrong3) {
- this.wrong3 = wrong3;
- }
-
- /**
- * @return the wrong4
- */
- public List<ChildBean> getWrong4() {
- return wrong4;
- }
-
- /**
- * @param wrong4 the wrong4 to set
- */
- public void setWrong4(List<ChildBean> wrong4) {
- this.wrong4 = wrong4;
- }
-
- /**
- * @return the wrong5
- */
- public Map<String,ChildBean> getWrong5() {
- return wrong5;
- }
-
- /**
- * @param wrong5 the wrong5 to set
- */
- public void setWrong5(Map<String,ChildBean> wrong5) {
- this.wrong5 = wrong5;
- }
-
- /**
- * @return the wrong6
- */
- public Boolean isWrong6() {
- return wrong6;
- }
-
- /**
- * @param wrong6 the wrong6 to set
- */
- public void setWrong6(Boolean wrong6) {
- this.wrong6 = wrong6;
- }
-}
+/*
+ * 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.persist.bean;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
+import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * every property has a different mapped name, designed to test the @Named annotation is respected.
+ * fields are all named "wrong" because if we see that in the stored JCR values then the persist logic was wrong.
+ */
+public class BeanWithMappedNames {
+ @Inject
+ @Named("prop-1")
+ @JsonProperty("prop-1")
+ private String wrong1;
+
+ @Inject
+ @Named("prop-2")
+ @JsonProperty("prop-2")
+ private String[] wrong2;
+
+ @Inject
+ @Named("child-1")
+ @JsonProperty("child-1")
+ private ChildBean wrong3;
+
+ @Inject
+ @Named("child-2")
+ @JsonProperty("child-2")
+ private List<ChildBean> wrong4;
+
+ @Inject
+ @Named("child-3")
+ @JsonProperty("child-3")
+ private Map<String,ChildBean> wrong5;
+
+ @Inject
+ @Named("prop-3")
+ @JsonProperty("prop-3")
+ private Boolean wrong6;
+
+ public static class ChildBean {
+ @Inject
+ private String name;
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+
+ /**
+ * @return the wrong1
+ */
+ public String getWrong1() {
+ return wrong1;
+ }
+
+ /**
+ * @param wrong1 the wrong1 to set
+ */
+ public void setWrong1(String wrong1) {
+ this.wrong1 = wrong1;
+ }
+
+ /**
+ * @return the wrong2
+ */
+ public String[] getWrong2() {
+ return wrong2;
+ }
+
+ /**
+ * @param wrong2 the wrong2 to set
+ */
+ public void setWrong2(String[] wrong2) {
+ this.wrong2 = wrong2;
+ }
+
+ /**
+ * @return the wrong3
+ */
+ public ChildBean getWrong3() {
+ return wrong3;
+ }
+
+ /**
+ * @param wrong3 the wrong3 to set
+ */
+ public void setWrong3(ChildBean wrong3) {
+ this.wrong3 = wrong3;
+ }
+
+ /**
+ * @return the wrong4
+ */
+ public List<ChildBean> getWrong4() {
+ return wrong4;
+ }
+
+ /**
+ * @param wrong4 the wrong4 to set
+ */
+ public void setWrong4(List<ChildBean> wrong4) {
+ this.wrong4 = wrong4;
+ }
+
+ /**
+ * @return the wrong5
+ */
+ public Map<String,ChildBean> getWrong5() {
+ return wrong5;
+ }
+
+ /**
+ * @param wrong5 the wrong5 to set
+ */
+ public void setWrong5(Map<String,ChildBean> wrong5) {
+ this.wrong5 = wrong5;
+ }
+
+ /**
+ * @return the wrong6
+ */
+ public Boolean isWrong6() {
+ return wrong6;
+ }
+
+ /**
+ * @param wrong6 the wrong6 to set
+ */
+ public void setWrong6(Boolean wrong6) {
+ this.wrong6 = wrong6;
+ }
+}
diff --git a/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithPathField.java b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithPathField.java
new file mode 100644
index 0000000..ecd9737
--- /dev/null
+++ b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithPathField.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.persist.bean;
+
+import javax.inject.Inject;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.persistor.annotations.ChildType;
+
+/**
+ * Example of bean with getPath method that stores the path in a field
+ */
+@Model(adaptables = Resource.class, resourceType = "test/testBean")
+@ChildType("test/testBean/field-path")
+public class BeanWithPathField {
+ @Inject
+ public String prop1 = "testValue";
+
+ public String path = "/test/field-path";
+}
diff --git a/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithPathGetter.java b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithPathGetter.java
new file mode 100644
index 0000000..8eb5166
--- /dev/null
+++ b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/BeanWithPathGetter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.persist.bean;
+
+import javax.inject.Inject;
+
+/**
+ * Example of bean with getPath method that renders path of the bean [possible] dynamically.
+ */
+public class BeanWithPathGetter {
+ @Inject
+ public String prop1 = "testValue";
+
+ // This provides the path of the bean, which could also be some kind of dynamic business logic.
+ public String getPath() {
+ return "/test/dynamic-path";
+ }
+}
diff --git a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/ComplexBean.java b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/ComplexBean.java
old mode 100755
new mode 100644
similarity index 69%
rename from SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/ComplexBean.java
rename to SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/ComplexBean.java
index 74d299f..f91c836
--- a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/bean/ComplexBean.java
+++ b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/ComplexBean.java
@@ -1,84 +1,97 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.apache.sling.models.persist.bean;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import javax.inject.Inject;
-import javax.inject.Named;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.models.annotations.DefaultInjectionStrategy;
-import org.apache.sling.models.annotations.Model;
-
-/**
- * Example of a model bean with an object graph of depth 4
- */
-@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
-public class ComplexBean {
- public ComplexBean() {
-
- }
-
- public ComplexBean(Resource resource) {
- if (resource != null) {
- name = resource.getName();
- }
- }
-
- public String name = "change-me";
-
- public String getPath() {
- return "/test/complex-beans/" + name;
- }
-
- // --- Serializable properties
- @Inject
- @Named("array-of-strings")
- public String[] arrayOfStrings = {"one", "two", "three", "four"};
-
- @Inject
- public Date now = new Date();
-
- @Inject
- public long nowLong = now.getTime();
-
- @Inject
- public Level2Bean level2 = new Level2Bean();
-
- @Model(adaptables = Resource.class)
- public static class Level2Bean {
- @Inject
- public String name = "level2";
-
- @Inject
- public List<Level3Bean> level3 = new ArrayList<>();
- }
-
- @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
- public static class Level3Bean {
- public Level3Bean() {
-
- }
-
- public Level3Bean(Resource resource) {
- if (resource != null) {
- path = resource.getPath();
- }
- }
-
- public String path;
-
- @Inject
- public String value1 = "val1";
-
- @Inject
- public int value2 = -1;
-
- @Inject
- public String[] valueList = {};
- }
-}
+/*
+ * 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.persist.bean;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.DefaultInjectionStrategy;
+import org.apache.sling.models.annotations.Model;
+
+/**
+ * Example of a model bean with an object graph of depth 4
+ */
+@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
+public class ComplexBean {
+ public ComplexBean() {
+
+ }
+
+ public ComplexBean(Resource resource) {
+ if (resource != null) {
+ name = resource.getName();
+ }
+ }
+
+ public String name = "change-me";
+
+ public String getPath() {
+ return "/test/complex-beans/" + name;
+ }
+
+ // --- Serializable properties
+ @Inject
+ @Named("array-of-strings")
+ public String[] arrayOfStrings = {"one", "two", "three", "four"};
+
+ @Inject
+ public Date now = new Date();
+
+ @Inject
+ public long nowLong = now.getTime();
+
+ @Inject
+ public Level2Bean level2 = new Level2Bean();
+
+ @Model(adaptables = Resource.class)
+ public static class Level2Bean {
+ @Inject
+ public String name = "level2";
+
+ @Inject
+ public List<Level3Bean> level3 = new ArrayList<>();
+ }
+
+ @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
+ public static class Level3Bean {
+ public Level3Bean() {
+
+ }
+
+ public Level3Bean(Resource resource) {
+ if (resource != null) {
+ path = resource.getPath();
+ }
+ }
+
+ public String path;
+
+ @Inject
+ public String value1 = "val1";
+
+ @Inject
+ public int value2 = -1;
+
+ @Inject
+ public String[] valueList = {};
+ }
+}
diff --git a/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/MappedChildren.java b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/MappedChildren.java
new file mode 100644
index 0000000..44a20be
--- /dev/null
+++ b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/bean/MappedChildren.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.models.persist.bean;
+
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.DefaultInjectionStrategy;
+import org.apache.sling.models.annotations.Model;
+
+/**
+ * Bean with children arranged in maps (enumeration map and also string keys)
+ */
+@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
+public class MappedChildren {
+ public static enum KEYS{ONE,TWO,THREE};
+
+ public String name;
+
+ public Map<String, Child> stringKeys = new HashMap<>();
+
+ public EnumMap<KEYS, Child> enumKeys = new EnumMap<>(KEYS.class);
+
+ @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
+ public static class Child {
+ public String name;
+ public String testValue;
+ }
+
+ public MappedChildren() {
+ }
+
+ public MappedChildren(Resource resource) {
+ if (resource != null) {
+ name = resource.getName();
+ }
+ }
+
+
+}
diff --git a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/impl/ResourceTypeKeyTest.java b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/impl/ResourceTypeKeyTest.java
old mode 100755
new mode 100644
similarity index 62%
rename from SlingJCRPersist/src/test/java/org/apache/sling/models/persist/impl/ResourceTypeKeyTest.java
rename to SlingModelPersist/src/test/java/org/apache/sling/models/persist/impl/ResourceTypeKeyTest.java
index b25713e..e0b5b4a
--- a/SlingJCRPersist/src/test/java/org/apache/sling/models/persist/impl/ResourceTypeKeyTest.java
+++ b/SlingModelPersist/src/test/java/org/apache/sling/models/persist/impl/ResourceTypeKeyTest.java
@@ -1,52 +1,66 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.apache.sling.models.persist.impl;
-
-import org.apache.sling.models.persist.bean.BeanWithPathField;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Test various behaviors of ResourceTypeKey class
- */
-public class ResourceTypeKeyTest {
-
- public ResourceTypeKeyTest() {
- }
-
- @Before
- public void setUp() {
- }
-
- /**
- * Test of fromObject method, of class ResourceTypeKey relying on class annotations
- */
- @Test
- public void testFromObject() {
- BeanWithPathField bean = new BeanWithPathField();
- ResourceTypeKey result = ResourceTypeKey.fromObject(bean);
- assertEquals("test/testBean", result.primaryType);
- assertEquals("test/testBean/field-path", result.childType);
- }
-
- @Test
- public void testFromObjectCache() {
- BeanWithPathField bean1 = new BeanWithPathField();
- BeanWithPathField bean2 = new BeanWithPathField();
- ResourceTypeKey result1 = ResourceTypeKey.fromObject(bean1);
- ResourceTypeKey result2 = ResourceTypeKey.fromObject(bean2);
- assertEquals("Should use the same object value", result1, result2);
- }
-
- @Test
- public void testNullObject() {
- ResourceTypeKey result = ResourceTypeKey.fromObject(null);
- assertNotNull("Should not return null", result);
- assertEquals("Should be NT UNSTRUCTURED", "nt:unstructured", result.primaryType);
- }
-}
+/*
+ * 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.persist.impl;
+
+import org.apache.sling.models.persistor.impl.ResourceTypeKey;
+import org.apache.sling.models.persist.bean.BeanWithPathField;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test various behaviors of ResourceTypeKey class
+ */
+public class ResourceTypeKeyTest {
+
+ public ResourceTypeKeyTest() {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ /**
+ * Test of fromObject method, of class ResourceTypeKey relying on class annotations
+ */
+ @Test
+ public void testFromObject() {
+ BeanWithPathField bean = new BeanWithPathField();
+ ResourceTypeKey result = ResourceTypeKey.fromObject(bean);
+ assertEquals("test/testBean", result.primaryType);
+ assertEquals("test/testBean/field-path", result.childType);
+ }
+
+ @Test
+ public void testFromObjectCache() {
+ BeanWithPathField bean1 = new BeanWithPathField();
+ BeanWithPathField bean2 = new BeanWithPathField();
+ ResourceTypeKey result1 = ResourceTypeKey.fromObject(bean1);
+ ResourceTypeKey result2 = ResourceTypeKey.fromObject(bean2);
+ assertEquals("Should use the same object value", result1, result2);
+ }
+
+ @Test
+ public void testNullObject() {
+ ResourceTypeKey result = ResourceTypeKey.fromObject(null);
+ assertNotNull("Should not return null", result);
+ assertEquals("Should be NT UNSTRUCTURED", "nt:unstructured", result.primaryType);
+ }
+}
diff --git a/feature-diff/pom.xml b/feature-diff/pom.xml
index 8531760..8ea030d 100644
--- a/feature-diff/pom.xml
+++ b/feature-diff/pom.xml
@@ -22,8 +22,8 @@
<parent>
<groupId>org.apache.sling</groupId>
- <artifactId>sling</artifactId>
- <version>34</version>
+ <artifactId>sling-bundle-parent</artifactId>
+ <version>35</version>
<relativePath />
</parent>
@@ -36,7 +36,7 @@
<properties>
<sling.java.version>8</sling.java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <jackson.version>2.9.8</jackson.version>
+ <bnd.baseline.skip>true</bnd.baseline.skip>
</properties>
<dependencies>
@@ -96,23 +96,4 @@
</dependency>
</dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- <configuration>
- <archive>
- <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
- </archive>
- </configuration>
- </plugin>
- <plugin>
- <groupId>biz.aQute.bnd</groupId>
- <artifactId>bnd-maven-plugin</artifactId>
- <version>4.1.0</version>
- </plugin>
- </plugins>
- </build>
-
</project>
diff --git a/runtime2feature/pom.xml b/runtime2feature/pom.xml
index 8b344df..d1ece0f 100644
--- a/runtime2feature/pom.xml
+++ b/runtime2feature/pom.xml
@@ -22,8 +22,8 @@
<parent>
<groupId>org.apache.sling</groupId>
- <artifactId>sling</artifactId>
- <version>34</version>
+ <artifactId>sling-bundle-parent</artifactId>
+ <version>35</version>
<relativePath />
</parent>
@@ -36,6 +36,7 @@
<properties>
<sling.java.version>8</sling.java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <bnd.baseline.skip>true</bnd.baseline.skip>
</properties>
<dependencies>
@@ -63,6 +64,7 @@
<artifactId>org.osgi.service.component.annotations</artifactId>
<scope>provided</scope>
</dependency>
+
<!--
| Apache Sling Feature APIs
-->
@@ -94,23 +96,4 @@
</dependency>
</dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- <configuration>
- <archive>
- <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
- </archive>
- </configuration>
- </plugin>
- <plugin>
- <groupId>biz.aQute.bnd</groupId>
- <artifactId>bnd-maven-plugin</artifactId>
- <version>4.1.0</version>
- </plugin>
- </plugins>
- </build>
-
</project>