This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.models.impl-1.0.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-impl.git
commit 58786a9c52601c84b140b31cbe5a8f4cc196ed02 Author: Justin Edelson <[email protected]> AuthorDate: Wed Jan 22 17:36:40 2014 +0000 SLING-3313 - adding initial version of Sling Models (nee YAMF) git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/impl@1560437 13f79535-47bb-0310-9956-ffa450edef68 --- README.txt | 24 + pom.xml | 142 +++++ .../sling/models/impl/ModelAdapterFactory.java | 615 +++++++++++++++++++++ .../models/impl/ModelPackageBundleListener.java | 130 +++++ .../models/impl/injectors/BindingsInjector.java | 75 +++ .../impl/injectors/ChildResourceInjector.java | 46 ++ .../models/impl/injectors/OSGiServiceInjector.java | 194 +++++++ .../impl/injectors/RequestAttributeInjector.java | 60 ++ .../models/impl/injectors/ValueMapInjector.java | 65 +++ .../org/apache/sling/models/impl/DefaultTest.java | 70 +++ .../sling/models/impl/MultipleInjectorTest.java | 128 +++++ .../sling/models/impl/OSGiInjectionTest.java | 209 +++++++ .../sling/models/impl/PostConstructTest.java | 57 ++ .../sling/models/impl/RequestInjectionTest.java | 76 +++ .../models/impl/ResourceModelClassesTest.java | 163 ++++++ .../models/impl/ResourceModelInterfacesTest.java | 96 ++++ .../java/org/apache/sling/models/impl/ViaTest.java | 78 +++ .../models/testmodels/classes/ArrayOSGiModel.java | 35 ++ .../models/testmodels/classes/BindingsModel.java | 37 ++ .../models/testmodels/classes/ChildModel.java | 33 ++ .../testmodels/classes/CollectionOSGiModel.java | 37 ++ .../testmodels/classes/DefaultStringModel.java | 43 ++ .../models/testmodels/classes/ListOSGiModel.java | 37 ++ .../testmodels/classes/RequestOSGiModel.java | 35 ++ .../classes/ResourceModelWithRequiredField.java | 34 ++ .../models/testmodels/classes/SetOSGiModel.java | 37 ++ .../models/testmodels/classes/SimpleOSGiModel.java | 35 ++ .../testmodels/classes/SimplePropertyModel.java | 71 +++ .../sling/models/testmodels/classes/SubClass.java | 43 ++ .../models/testmodels/classes/SuperClass.java | 34 ++ .../sling/models/testmodels/classes/ViaModel.java | 36 ++ .../testmodels/interfaces/ChildResourceModel.java | 30 + .../testmodels/interfaces/ChildValueMapModel.java | 31 ++ .../models/testmodels/interfaces/ParentModel.java | 30 + .../interfaces/ResourceModelWithRequiredField.java | 30 + .../testmodels/interfaces/ServiceInterface.java | 21 + .../testmodels/interfaces/SimplePropertyModel.java | 43 ++ 37 files changed, 2960 insertions(+) diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..657f1c7 --- /dev/null +++ b/README.txt @@ -0,0 +1,24 @@ +Apache Sling Models Implementation + +Getting Started +=============== + +This component uses a Maven 2 (http://maven.apache.org/) build +environment. It requires a Java 5 JDK (or higher) and Maven (http://maven.apache.org/) +2.0.7 or later. We recommend to use the latest Maven version. + +If you have Maven 2 installed, you can compile and +package the jar using the following command: + + mvn package + +See the Maven 2 documentation for other build features. + +The latest source code for this component is available in the +Subversion (http://subversion.tigris.org/) source repository of +the Apache Software Foundation. If you have Subversion installed, +you can checkout the latest source using the following command: + + svn checkout http://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/settings + +See the Subversion documentation for other source control features. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..079b51c --- /dev/null +++ b/pom.xml @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.sling</groupId> + <artifactId>sling</artifactId> + <version>18</version> + <relativePath>../../../../parent/pom.xml</relativePath> + </parent> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.models.impl</artifactId> + <packaging>bundle</packaging> + <version>0.0.1-SNAPSHOT</version> + <name>Sling Models Implementation</name> + <description>Sling Models Implementation</description> + <properties> + <sling.java.version>6</sling.java.version> + </properties> + <scm> + <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/impl</connection> + <developerConnection> scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/impl</developerConnection> + <url>http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl</url> + </scm> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-scr-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Embed-Dependency> + *;scope=compile, + org.osgi.compendium;inline="org/osgi/util/tracker/*"</Embed-Dependency> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.models.api</artifactId> + <version>0.0.1-SNAPSHOT</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>servlet-api</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + <version>4.2.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.api</artifactId> + <version>2.2.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>javax.inject</groupId> + <artifactId>javax.inject</artifactId> + <version>1</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <version>1.9.5</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.osgi</artifactId> + <version>2.2.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <version>2.5</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + <version>1.8.3</version> + <scope>compile</scope> + </dependency> + </dependencies> +</project> diff --git a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java new file mode 100644 index 0000000..39bce98 --- /dev/null +++ b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java @@ -0,0 +1,615 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl; + +import java.lang.annotation.Annotation; +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import javax.inject.Named; + +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.ReferencePolicy; +import org.apache.sling.api.adapter.Adaptable; +import org.apache.sling.api.adapter.AdapterFactory; +import org.apache.sling.commons.osgi.ServiceUtil; +import org.apache.sling.models.annotations.Default; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.Optional; +import org.apache.sling.models.annotations.Via; +import org.apache.sling.models.annotations.Source; +import org.apache.sling.models.spi.DisposalCallback; +import org.apache.sling.models.spi.DisposalCallbackRegistry; +import org.apache.sling.models.spi.Injector; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component +public class ModelAdapterFactory implements AdapterFactory, Runnable { + + public static class DisposalCallbackRegistryImpl implements DisposalCallbackRegistry { + + private List<DisposalCallback> callbacks = new ArrayList<DisposalCallback>(); + + @Override + public void addDisposalCallback(DisposalCallback callback) { + callbacks.add(callback); + } + + private void lock() { + callbacks = Collections.unmodifiableList(callbacks); + } + + private void onDisposed() { + for (DisposalCallback callback : callbacks) { + callback.onDisposed(); + } + } + + } + + private ReferenceQueue<Object> queue; + + private ConcurrentMap<java.lang.ref.Reference<Object>, DisposalCallbackRegistryImpl> disposalCallbacks; + + public static class MapBackedInvocationHandler implements InvocationHandler { + + private Map<Method, Object> methods; + + public MapBackedInvocationHandler(Map<Method, Object> methods) { + this.methods = methods; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return methods.get(method); + } + + } + + @Override + public void run() { + java.lang.ref.Reference<? extends Object> ref = queue.poll(); + if (ref != null) { + log.info("calling disposal for " + ref.toString()); + DisposalCallbackRegistryImpl registry = disposalCallbacks.remove(ref); + registry.onDisposed(); + } + } + + private static final Logger log = LoggerFactory.getLogger(ModelAdapterFactory.class); + + @Reference(name = "injector", referenceInterface = Injector.class, + cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC) + private final Map<Object, Injector> injectors = new TreeMap<Object, Injector>(); + + private volatile Injector[] sortedInjectors = new Injector[0]; + + private ModelPackageBundleListener listener; + + private ServiceRegistration jobRegistration; + + @SuppressWarnings("unchecked") + public <AdapterType> AdapterType getAdapter(Object adaptable, Class<AdapterType> type) { + Model modelAnnotation = type.getAnnotation(Model.class); + if (modelAnnotation == null) { + return null; + } + boolean isAdaptable = false; + + Class<?>[] declaredAdaptable = modelAnnotation.adaptables(); + for (Class<?> clazz : declaredAdaptable) { + if (clazz.isInstance(adaptable)) { + isAdaptable = true; + } + } + if (!isAdaptable) { + return null; + } + + if (type.isInterface()) { + InvocationHandler handler = createInvocationHandler(adaptable, type); + if (handler != null) { + return (AdapterType) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type }, + handler); + } else { + return null; + } + } else { + try { + return createObject(adaptable, type); + } catch (Exception e) { + log.error("unable to create object", e); + return null; + } + } + } + + private Set<Field> collectInjectableFields(Class<?> type) { + Set<Field> result = new HashSet<Field>(); + while (type != null) { + Field[] fields = type.getDeclaredFields(); + for (Field field : fields) { + Inject injection = field.getAnnotation(Inject.class); + if (injection != null) { + result.add(field); + } + } + type = type.getSuperclass(); + } + return result; + } + + private Set<Method> collectInjectableMethods(Class<?> type) { + Set<Method> result = new HashSet<Method>(); + while (type != null) { + Method[] methods = type.getDeclaredMethods(); + for (Method method : methods) { + Inject injection = method.getAnnotation(Inject.class); + if (injection != null) { + result.add(method); + } + } + type = type.getSuperclass(); + } + return result; + } + + private InvocationHandler createInvocationHandler(final Object adaptable, final Class<?> type) { + Set<Method> injectableMethods = collectInjectableMethods(type); + Map<Method, Object> methods = new HashMap<Method, Object>(); + MapBackedInvocationHandler handler = new MapBackedInvocationHandler(methods); + + DisposalCallbackRegistryImpl registry = createAndRegisterCallbackRegistry(handler); + + for (Injector injector : sortedInjectors) { + Iterator<Method> it = injectableMethods.iterator(); + while (it.hasNext()) { + Method method = it.next(); + String source = getSource(method); + if (source == null || source.equals(injector.getName())) { + String name = getName(method); + Type returnType = mapPrimitiveClasses(method.getGenericReturnType()); + Object injectionAdaptable = getAdaptable(adaptable, method); + if (injectionAdaptable != null) { + Object value = injector.getValue(injectionAdaptable, name, returnType, method, registry); + if (setMethod(method, methods, value)) { + it.remove(); + } + } + } + } + } + + Iterator<Method> it = injectableMethods.iterator(); + while (it.hasNext()) { + Method method = it.next(); + Default defaultAnnotation = method.getAnnotation(Default.class); + if (defaultAnnotation != null) { + Type returnType = mapPrimitiveClasses(method.getGenericReturnType()); + Object value = getDefaultValue(defaultAnnotation, returnType); + if (setMethod(method, methods, value)) { + it.remove(); + } + } + } + + if (injectableMethods.isEmpty()) { + return handler; + } else { + Set<Method> requiredMethods = new HashSet<Method>(); + for (Method method : injectableMethods) { + if (method.getAnnotation(Optional.class) == null) { + requiredMethods.add(method); + } + } + + if (!requiredMethods.isEmpty()) { + log.warn("Required methods {} on model class {} were not able to be injected.", requiredMethods, + type); + return null; + } else { + return handler; + } + } + } + + private DisposalCallbackRegistryImpl createAndRegisterCallbackRegistry(Object object) { + PhantomReference<Object> reference = new PhantomReference<Object>(object, queue); + DisposalCallbackRegistryImpl registry = new DisposalCallbackRegistryImpl(); + disposalCallbacks.put(reference, registry); + return registry; + } + + private String getSource(AnnotatedElement element) { + Source source = element.getAnnotation(Source.class); + if (source != null) { + return source.value(); + } else { + for (Annotation ann : element.getAnnotations()) { + source = ann.annotationType().getAnnotation(Source.class); + if (source != null) { + return source.value(); + } + } + } + return null; + } + + private <AdapterType> AdapterType createObject(Object adaptable, Class<AdapterType> type) + throws InstantiationException, IllegalAccessException { + Set<Field> injectableFields = collectInjectableFields(type); + + AdapterType object = type.newInstance(); + + DisposalCallbackRegistryImpl registry = createAndRegisterCallbackRegistry(object); + + for (Injector injector : sortedInjectors) { + Iterator<Field> it = injectableFields.iterator(); + while (it.hasNext()) { + Field field = it.next(); + String source = getSource(field); + if (source == null || source.equals(injector.getName())) { + String name = getName(field); + Type fieldType = mapPrimitiveClasses(field.getGenericType()); + Object injectionAdaptable = getAdaptable(adaptable, field); + if (injectionAdaptable != null) { + Object value = injector.getValue(injectionAdaptable, name, fieldType, field, registry); + if (setField(field, object, value)) { + it.remove(); + } + } + } + } + } + + Iterator<Field> it = injectableFields.iterator(); + while (it.hasNext()) { + Field field = it.next(); + Default defaultAnnotation = field.getAnnotation(Default.class); + if (defaultAnnotation != null) { + Type fieldType = mapPrimitiveClasses(field.getGenericType()); + Object value = getDefaultValue(defaultAnnotation, fieldType); + if (setField(field, object, value)) { + it.remove(); + } + } + } + + if (injectableFields.isEmpty()) { + try { + invokePostConstruct(object); + return object; + } catch (Exception e) { + log.error("Unable to invoke post construct method.", e); + return null; + } + } else { + Set<Field> requiredFields = new HashSet<Field>(); + for (Field field : injectableFields) { + if (field.getAnnotation(Optional.class) == null) { + requiredFields.add(field); + } + } + + if (!requiredFields.isEmpty()) { + log.warn("Required properties {} on model class {} were not able to be injected.", requiredFields, + type); + return null; + } else { + try { + invokePostConstruct(object); + return object; + } catch (Exception e) { + log.error("Unable to invoke post construct method.", e); + return null; + } + } + } + } + + private Object getDefaultValue(Default defaultAnnotation, Type type) { + if (type instanceof Class) { + Class<?> injectedClass = (Class<?>) type; + if (injectedClass.isArray()) { + Class<?> componentType = injectedClass.getComponentType(); + if (componentType == String.class) { + return defaultAnnotation.values(); + } + if (componentType == Integer.TYPE) { + return defaultAnnotation.intValues(); + } + if (componentType == Long.TYPE) { + return defaultAnnotation.longValues(); + } + if (componentType == Boolean.TYPE) { + return defaultAnnotation.booleanValues(); + } + if (componentType == Short.TYPE) { + return defaultAnnotation.shortValues(); + } + if (componentType == Float.TYPE) { + return defaultAnnotation.floatValues(); + } + if (componentType == Double.TYPE) { + return defaultAnnotation.doubleValues(); + } + + log.warn("Default values for {} are not supported", componentType); + return null; + } else { + if (injectedClass == String.class) { + return defaultAnnotation.values()[0]; + } + if (injectedClass == Integer.TYPE) { + return defaultAnnotation.intValues()[0]; + } + if (injectedClass == Long.TYPE) { + return defaultAnnotation.longValues()[0]; + } + if (injectedClass == Boolean.TYPE) { + return defaultAnnotation.booleanValues()[0]; + } + if (injectedClass == Short.TYPE) { + return defaultAnnotation.shortValues()[0]; + } + if (injectedClass == Float.TYPE) { + return defaultAnnotation.floatValues()[0]; + } + if (injectedClass == Double.TYPE) { + return defaultAnnotation.doubleValues()[0]; + } + + log.warn("Default values for {} are not supported", injectedClass); + return null; + } + } else { + log.warn("Cannot provide default for {}", type); + return null; + } + } + + private Object getAdaptable(Object adaptable, AnnotatedElement point) { + Via viaAnnotation = point.getAnnotation(Via.class); + if (viaAnnotation == null) { + return adaptable; + } + String viaPropertyName = viaAnnotation.value(); + try { + return PropertyUtils.getProperty(adaptable, viaPropertyName); + } catch (Exception e) { + log.error("Unable to execution projection " + viaPropertyName, e); + return null; + } + } + + private String getName(Field field) { + Named named = field.getAnnotation(Named.class); + if (named != null) { + return named.value(); + } + return field.getName(); + } + + private String getName(Method method) { + Named named = method.getAnnotation(Named.class); + if (named != null) { + return named.value(); + } + String methodName = method.getName(); + if (methodName.startsWith("get")) { + return methodName.substring(3, 4).toLowerCase() + methodName.substring(4); + } else if (methodName.startsWith("is")) { + return methodName.substring(2, 3).toLowerCase() + methodName.substring(3); + } else { + return methodName; + } + } + + private void invokePostConstruct(Object object) throws Exception { + Class<?> clazz = object.getClass(); + List<Method> postConstructMethods = new ArrayList<Method>(); + while (clazz != null) { + Method[] methods = clazz.getDeclaredMethods(); + for (Method method : methods) { + if (method.isAnnotationPresent(PostConstruct.class)) { + postConstructMethods.add(method); + } + } + clazz = clazz.getSuperclass(); + } + Collections.reverse(postConstructMethods); + for (Method method : postConstructMethods) { + boolean accessible = method.isAccessible(); + try { + if (!accessible) { + method.setAccessible(true); + } + method.invoke(object); + } finally { + if (!accessible) { + method.setAccessible(false); + } + } + } + } + + private Type mapPrimitiveClasses(Type type) { + if (type == Integer.TYPE) { + return Integer.class; + } + if (type == Long.TYPE) { + return Long.class; + } + if (type == Boolean.TYPE) { + return Boolean.class; + } + if (type == Double.TYPE) { + return Double.class; + } + if (type == Float.TYPE) { + return Float.class; + } + if (type == Short.TYPE) { + return Short.class; + } + if (type == Character.TYPE) { + return Character.class; + } + + return type; + } + + private boolean setField(Field field, Object createdObject, Object value) { + if (value != null) { + if (!isAcceptableType(field.getClass(), value) && value instanceof Adaptable) { + value = ((Adaptable) value).adaptTo(field.getClass()); + if (value == null) { + return false; + } + } + boolean accessible = field.isAccessible(); + try { + if (!accessible) { + field.setAccessible(true); + } + field.set(createdObject, value); + return true; + } catch (Exception e) { + log.error("unable to inject field", e); + return false; + } finally { + if (!accessible) { + field.setAccessible(false); + } + } + } else { + return false; + } + } + + private boolean setMethod(Method method, Map<Method, Object> methods, Object value) { + if (value != null) { + if (!isAcceptableType(method.getReturnType(), value) && value instanceof Adaptable) { + value = ((Adaptable) value).adaptTo(method.getReturnType()); + if (value == null) { + return false; + } + } + methods.put(method, value); + return true; + } else { + return false; + } + } + + private boolean isAcceptableType(Class<?> type, Object value) { + if (type.isInstance(value)) { + return true; + } + + if (type == Integer.TYPE) { + return Integer.class.isInstance(value); + } + if (type == Long.TYPE) { + return Long.class.isInstance(value); + } + if (type == Boolean.TYPE) { + return Boolean.class.isInstance(value); + } + if (type == Double.TYPE) { + return Double.class.isInstance(value); + } + if (type == Float.TYPE) { + return Float.class.isInstance(value); + } + if (type == Short.TYPE) { + return Short.class.isInstance(value); + } + if (type == Character.TYPE) { + return Character.class.isInstance(value); + } + + return false; + } + + @Activate + protected void activate(final ComponentContext ctx) { + this.queue = new ReferenceQueue<Object>(); + this.disposalCallbacks = new ConcurrentHashMap<java.lang.ref.Reference<Object>, DisposalCallbackRegistryImpl>(); + Hashtable<Object, Object> properties = new Hashtable<Object, Object>(); + properties.put("scheduler.concurrent", false); + properties.put("scheduler.period", Long.valueOf(30)); + + this.jobRegistration = ctx.getBundleContext().registerService(Runnable.class.getName(), this, + properties); + + this.listener = new ModelPackageBundleListener(ctx.getBundleContext(), this); + } + + @Deactivate + protected void deactivate() { + this.listener.unregisterAll(); + if (jobRegistration != null) { + jobRegistration.unregister(); + jobRegistration = null; + } + } + + protected void bindInjector(final Injector injector, final Map<String, Object> props) { + synchronized (injectors) { + injectors.put(ServiceUtil.getComparableForServiceRanking(props), injector); + sortedInjectors = injectors.values().toArray(new Injector[injectors.size()]); + } + } + + protected void unbindInjector(final Injector injector, final Map<String, Object> props) { + synchronized (injectors) { + injectors.remove(ServiceUtil.getComparableForServiceRanking(props)); + sortedInjectors = injectors.values().toArray(new Injector[injectors.size()]); + } + } + +} diff --git a/src/main/java/org/apache/sling/models/impl/ModelPackageBundleListener.java b/src/main/java/org/apache/sling/models/impl/ModelPackageBundleListener.java new file mode 100644 index 0000000..ba45ca0 --- /dev/null +++ b/src/main/java/org/apache/sling/models/impl/ModelPackageBundleListener.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.sling.api.adapter.AdapterFactory; +import org.apache.sling.commons.osgi.PropertiesUtil; +import org.apache.sling.models.annotations.Model; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.BundleTracker; +import org.osgi.util.tracker.BundleTrackerCustomizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ModelPackageBundleListener implements BundleTrackerCustomizer { + + private static final String HEADER = "Sling-Model-Packages"; + + private static final Logger log = LoggerFactory.getLogger(ModelPackageBundleListener.class); + + private final BundleContext bundleContext; + + private final BundleTracker bundleTracker; + + private final AdapterFactory factory; + + public ModelPackageBundleListener(BundleContext bundleContext, AdapterFactory factory) { + this.bundleContext = bundleContext; + this.factory = factory; + this.bundleTracker = new BundleTracker(bundleContext, Bundle.ACTIVE, this); + this.bundleTracker.open(); + } + + @Override + public Object addingBundle(Bundle bundle, BundleEvent event) { +List<ServiceRegistration> regs = new ArrayList<ServiceRegistration>(); + + Dictionary<?, ?> headers = bundle.getHeaders(); + String packageList = PropertiesUtil.toString(headers.get(HEADER), null); + if (packageList != null) { + + packageList = StringUtils.deleteWhitespace(packageList); + String[] packages = packageList.split(","); + for (String singlePackage : packages) { + @SuppressWarnings("unchecked") + Enumeration<URL> classUrls = bundle.findEntries("/" + singlePackage.replace('.', '/'), "*.class", + true); + while (classUrls.hasMoreElements()) { + URL url = classUrls.nextElement(); + String className = toClassName(url); + try { + Class<?> clazz = bundle.loadClass(className); + Model annotation = clazz.getAnnotation(Model.class); + if (annotation != null) { + Class<?>[] adaptables = annotation.adaptables(); + String[] classNames = toStringArray(adaptables); + Dictionary<String, Object> registrationProps = new Hashtable<String, Object>(); + registrationProps.put(AdapterFactory.ADAPTER_CLASSES, className); + registrationProps.put(AdapterFactory.ADAPTABLE_CLASSES, classNames); + ServiceRegistration reg = bundleContext.registerService(AdapterFactory.SERVICE_NAME, + factory, registrationProps); + regs.add(reg); + } + } catch (ClassNotFoundException e) { + log.warn("Unable to load class", e); + } + + } + } + } + return regs.toArray(new ServiceRegistration[0]); + } + + @Override + public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) { + } + + @Override + public void removedBundle(Bundle bundle, BundleEvent event, Object object) { + if (object instanceof ServiceRegistration[]) { + for (ServiceRegistration reg : (ServiceRegistration[]) object) { + reg.unregister(); + } + } + } + + public synchronized void unregisterAll() { + this.bundleTracker.close(); + } + + /** Convert class URL to class name */ + private String toClassName(URL url) { + final String f = url.getFile(); + final String cn = f.substring(1, f.length() - ".class".length()); + return cn.replace('/', '.'); + } + + private String[] toStringArray(Class<?>[] classes) { + String[] arr = new String[classes.length]; + for (int i = 0; i < classes.length; i++) { + arr[i] = classes[i].getName(); + } + return arr; + } + +} diff --git a/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java new file mode 100644 index 0000000..2a6fcc3 --- /dev/null +++ b/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl.injectors; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Type; + +import javax.servlet.ServletRequest; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.api.scripting.SlingBindings; +import org.apache.sling.models.spi.DisposalCallbackRegistry; +import org.apache.sling.models.spi.Injector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component +@Service +public class BindingsInjector implements Injector { + + private static final Logger log = LoggerFactory.getLogger(BindingsInjector.class); + + @Override + public String getName() { + return "script-bindings"; + } + + private static Object getValue(SlingBindings bindings, String name, Class<?> type) { + Object value = bindings.get(name); + if (type.isInstance(value)) { + return value; + } else { + return null; + } + } + + public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) { + SlingBindings bindings = getBindings(adaptable); + if (bindings == null) { + return null; + } + if (type instanceof Class<?>) { + return getValue(bindings, name, (Class<?>) type); + } else { + log.debug("BindingsInjector doesn't support non-class type {}", type); + return null; + } + + } + + private SlingBindings getBindings(Object adaptable) { + if (adaptable instanceof ServletRequest) { + ServletRequest request = (ServletRequest) adaptable; + return (SlingBindings) request.getAttribute(SlingBindings.class.getName()); + } else { + return null; + } + } + +} diff --git a/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java new file mode 100644 index 0000000..6afec71 --- /dev/null +++ b/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl.injectors; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Type; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.spi.DisposalCallbackRegistry; +import org.apache.sling.models.spi.Injector; + +@Component +@Service +public class ChildResourceInjector implements Injector { + + @Override + public String getName() { + return "child-resources"; + } + + @Override + public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) { + if (adaptable instanceof Resource) { + return ((Resource) adaptable).getChild(name); + } else { + return null; + } + } + +} diff --git a/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java new file mode 100644 index 0000000..f19c9ed --- /dev/null +++ b/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl.injectors; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Array; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import javax.servlet.ServletRequest; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.api.scripting.SlingBindings; +import org.apache.sling.api.scripting.SlingScriptHelper; +import org.apache.sling.models.annotations.Filter; +import org.apache.sling.models.spi.DisposalCallback; +import org.apache.sling.models.spi.DisposalCallbackRegistry; +import org.apache.sling.models.spi.Injector; +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component +@Service +public class OSGiServiceInjector implements Injector { + + private static final Logger log = LoggerFactory.getLogger(OSGiServiceInjector.class); + + private BundleContext bundleContext; + + @Override + public String getName() { + return "osgi-services"; + } + + @Activate + public void activate(ComponentContext ctx) { + this.bundleContext = ctx.getBundleContext(); + } + + public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) { + Filter filter = element.getAnnotation(Filter.class); + String filterString = null; + if (filter != null) { + filterString = filter.value(); + } + + return getValue(adaptable, type, filterString, callbackRegistry); + } + + private <T> Object getService(Object adaptable, Class<T> type, String filter, DisposalCallbackRegistry callbackRegistry) { + SlingScriptHelper helper = getScriptHelper(adaptable); + + if (helper != null) { + T[] services = helper.getServices(type, filter); + if (services == null || services.length == 0) { + return null; + } else { + return services[0]; + } + } else { + try { + ServiceReference[] refs = bundleContext.getServiceReferences(type.getName(), filter); + if (refs == null || refs.length == 0) { + return null; + } else { + callbackRegistry.addDisposalCallback(new Callback(refs, bundleContext)); + return bundleContext.getService(refs[0]); + } + } catch (InvalidSyntaxException e) { + log.error("invalid filter expression", e); + return null; + } + } + } + + private <T> Object[] getServices(Object adaptable, Class<T> type, String filter, DisposalCallbackRegistry callbackRegistry) { + SlingScriptHelper helper = getScriptHelper(adaptable); + + if (helper != null) { + T[] services = helper.getServices(type, filter); + return services; + } else { + try { + ServiceReference[] refs = bundleContext.getServiceReferences(type.getName(), filter); + if (refs == null || refs.length == 0) { + return null; + } else { + callbackRegistry.addDisposalCallback(new Callback(refs, bundleContext)); + List<Object> services = new ArrayList<Object>(); + for (ServiceReference ref : refs) { + Object service = bundleContext.getService(ref); + if (service != null) { + services.add(service); + } + } + return services.toArray(); + } + } catch (InvalidSyntaxException e) { + log.error("invalid filter expression", e); + return null; + } + } + } + + private SlingScriptHelper getScriptHelper(Object adaptable) { + if (adaptable instanceof ServletRequest) { + ServletRequest request = (ServletRequest) adaptable; + SlingBindings bindings = (SlingBindings) request.getAttribute(SlingBindings.class.getName()); + if (bindings != null) { + return bindings.getSling(); + } else { + return null; + } + } else { + return null; + } + } + + private Object getValue(Object adaptable, Type type, String filterString, DisposalCallbackRegistry callbackRegistry) { + if (type instanceof Class) { + Class<?> injectedClass = (Class<?>) type; + if (injectedClass.isArray()) { + Object[] services = getServices(adaptable, injectedClass.getComponentType(), filterString, callbackRegistry); + Object arr = Array.newInstance(injectedClass.getComponentType(), services.length); + for (int i = 0; i < services.length; i++) { + Array.set(arr, i, services[i]); + } + return arr; + } else { + return getService(adaptable, injectedClass, filterString, callbackRegistry); + } + } else if (type instanceof ParameterizedType) { + ParameterizedType ptype = (ParameterizedType) type; + if (ptype.getActualTypeArguments().length != 1) { + return null; + } + Class<?> collectionType = (Class<?>) ptype.getRawType(); + if (!(collectionType.equals(Collection.class) || collectionType.equals(List.class))) { + return null; + } + + Class<?> serviceType = (Class<?>) ptype.getActualTypeArguments()[0]; + Object[] services = getServices(adaptable, serviceType, filterString, callbackRegistry); + return Arrays.asList(services); + } else { + log.warn("Cannot handle type {}", type); + return null; + } + } + + private static class Callback implements DisposalCallback { + private final ServiceReference[] refs; + private final BundleContext context; + + public Callback(ServiceReference[] refs, BundleContext context) { + this.refs = refs; + this.context = context; + } + + @Override + public void onDisposed() { + if (refs != null) { + for (ServiceReference ref : refs) { + context.ungetService(ref); + } + } + } + } + +} diff --git a/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.java new file mode 100644 index 0000000..ccd7138 --- /dev/null +++ b/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.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.impl.injectors; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Type; + +import javax.servlet.ServletRequest; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.models.spi.DisposalCallbackRegistry; +import org.apache.sling.models.spi.Injector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component +@Service +public class RequestAttributeInjector implements Injector { + + private static final Logger log = LoggerFactory.getLogger(RequestAttributeInjector.class); + + @Override + public String getName() { + return "request-attributes"; + } + + @Override + public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) { + if (!(adaptable instanceof ServletRequest)) { + return null; + } else if (declaredType instanceof Class<?>) { + Class<?> clazz = (Class<?>) declaredType; + Object attribute = ((ServletRequest)adaptable).getAttribute(name); + if (clazz.isInstance(attribute)) { + return attribute; + } else { + return null; + } + } else { + log.debug("BindingsInjector doesn't support non-class type {}", declaredType); + return null; + } + } + +} diff --git a/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java new file mode 100644 index 0000000..f81fb35 --- /dev/null +++ b/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl.injectors; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Type; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.api.adapter.Adaptable; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.models.spi.DisposalCallbackRegistry; +import org.apache.sling.models.spi.Injector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component +@Service +public class ValueMapInjector implements Injector { + + private static final Logger log = LoggerFactory.getLogger(ValueMapInjector.class); + + @Override + public String getName() { + return "valuemap"; + } + + public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) { + ValueMap map = getMap(adaptable); + if (map == null) { + return null; + } else if (type instanceof Class<?>) { + return map.get(name, (Class<?>) type); + } else { + log.debug("ValueMapInjector doesn't support non-class types {}", type); + return null; + } + } + + private ValueMap getMap(Object adaptable) { + if (adaptable instanceof ValueMap) { + return (ValueMap) adaptable; + } else if (adaptable instanceof Adaptable) { + ValueMap map = ((Adaptable) adaptable).adaptTo(ValueMap.class); + return map; + } else { + return null; + } + } + +} diff --git a/src/test/java/org/apache/sling/models/impl/DefaultTest.java b/src/test/java/org/apache/sling/models/impl/DefaultTest.java new file mode 100644 index 0000000..a0ca9e7 --- /dev/null +++ b/src/test/java/org/apache/sling/models/impl/DefaultTest.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.api.wrappers.ValueMapDecorator; +import org.apache.sling.models.impl.injectors.ValueMapInjector; +import org.apache.sling.models.testmodels.classes.DefaultStringModel; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.service.component.ComponentContext; + +@RunWith(MockitoJUnitRunner.class) +public class DefaultTest { + + @Mock + private ComponentContext componentCtx; + + @Mock + private BundleContext bundleContext; + + private ModelAdapterFactory factory; + + @Before + public void setup() { + when(componentCtx.getBundleContext()).thenReturn(bundleContext); + factory = new ModelAdapterFactory(); + factory.activate(componentCtx); + factory.bindInjector(new ValueMapInjector(), + Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L)); + } + + @Test + public void testDefaultStringValue() { + ValueMap vm = new ValueMapDecorator(Collections.<String, Object>emptyMap()); + + Resource res = mock(Resource.class); + when(res.adaptTo(ValueMap.class)).thenReturn(vm); + + DefaultStringModel model = factory.getAdapter(res, DefaultStringModel.class); + assertNotNull(model); + assertEquals("firstDefault", model.getFirstProperty()); + assertEquals(2, model.getSecondProperty().length); + } +} diff --git a/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java b/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java new file mode 100644 index 0000000..087c075 --- /dev/null +++ b/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; + +import javax.inject.Inject; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.scripting.SlingBindings; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.Source; +import org.apache.sling.models.impl.injectors.BindingsInjector; +import org.apache.sling.models.impl.injectors.RequestAttributeInjector; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.runners.MockitoJUnitRunner; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.service.component.ComponentContext; + +@RunWith(MockitoJUnitRunner.class) +public class MultipleInjectorTest { + + @Spy + private BindingsInjector bindingsInjector; + + @Spy + private RequestAttributeInjector attributesInjector; + + @Mock + private SlingHttpServletRequest request; + + @Mock + private ComponentContext componentCtx; + + @Mock + private BundleContext bundleContext; + + private ModelAdapterFactory factory; + + private SlingBindings bindings; + + @Before + public void setup() { + when(componentCtx.getBundleContext()).thenReturn(bundleContext); + bindings = new SlingBindings(); + + factory = new ModelAdapterFactory(); + factory.activate(componentCtx); + factory.bindInjector(bindingsInjector, Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 2L)); + factory.bindInjector(attributesInjector, + Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 1L)); + + when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings); + } + + @Test + public void testInjectorOrder() { + String bindingsValue = "bindings value"; + bindings.put("firstAttribute", bindingsValue); + + String attributeValue = "attribute value"; + when(request.getAttribute("firstAttribute")).thenReturn(attributeValue); + + ForTwoInjectors obj = factory.getAdapter(request, ForTwoInjectors.class); + + assertNotNull(obj); + assertEquals(obj.firstAttribute, bindingsValue); + + verifyNoMoreInteractions(attributesInjector); + } + + @Test + public void testInjectorOrderWithSource() { + String bindingsValue = "bindings value"; + bindings.put("firstAttribute", bindingsValue); + + String attributeValue = "attribute value"; + when(request.getAttribute("firstAttribute")).thenReturn(attributeValue); + + ForTwoInjectorsWithSource obj = factory.getAdapter(request, ForTwoInjectorsWithSource.class); + + assertNotNull(obj); + assertEquals(obj.firstAttribute, attributeValue); + + verify(bindingsInjector).getName(); + verifyNoMoreInteractions(bindingsInjector); + } + + @Model(adaptables = SlingHttpServletRequest.class) + public static class ForTwoInjectors { + + @Inject + private String firstAttribute; + + } + + @Model(adaptables = SlingHttpServletRequest.class) + public static class ForTwoInjectorsWithSource { + + @Inject + @Source("request-attributes") + private String firstAttribute; + + } + +} diff --git a/src/test/java/org/apache/sling/models/impl/OSGiInjectionTest.java b/src/test/java/org/apache/sling/models/impl/OSGiInjectionTest.java new file mode 100644 index 0000000..a7989ee --- /dev/null +++ b/src/test/java/org/apache/sling/models/impl/OSGiInjectionTest.java @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; +import java.util.Dictionary; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.scripting.SlingBindings; +import org.apache.sling.api.scripting.SlingScriptHelper; +import org.apache.sling.models.impl.injectors.OSGiServiceInjector; +import org.apache.sling.models.testmodels.classes.ArrayOSGiModel; +import org.apache.sling.models.testmodels.classes.CollectionOSGiModel; +import org.apache.sling.models.testmodels.classes.ListOSGiModel; +import org.apache.sling.models.testmodels.classes.RequestOSGiModel; +import org.apache.sling.models.testmodels.classes.SetOSGiModel; +import org.apache.sling.models.testmodels.classes.SimpleOSGiModel; +import org.apache.sling.models.testmodels.interfaces.ServiceInterface; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleListener; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentContext; + +@RunWith(MockitoJUnitRunner.class) +public class OSGiInjectionTest { + private ModelAdapterFactory factory; + + @Mock + private ComponentContext componentCtx; + + @Mock + private BundleContext bundleContext; + + @Mock + private SlingScriptHelper helper; + + private SlingBindings bindings = new SlingBindings(); + + @Before + public void setup() { + when(componentCtx.getBundleContext()).thenReturn(bundleContext); + factory = new ModelAdapterFactory(); + factory.activate(componentCtx); + + OSGiServiceInjector injectorFactory = new OSGiServiceInjector(); + injectorFactory.activate(componentCtx); + factory.bindInjector(injectorFactory, Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L)); + + bindings.setSling(helper); + } + + @Test + public void testSimpleOSGiModel() throws Exception { + ServiceReference ref = mock(ServiceReference.class); + ServiceInterface service = mock(ServiceInterface.class); + when(bundleContext.getServiceReferences(ServiceInterface.class.getName(), null)).thenReturn( + new ServiceReference[] { ref }); + when(bundleContext.getService(ref)).thenReturn(service); + + Resource res = mock(Resource.class); + + SimpleOSGiModel model = factory.getAdapter(res, SimpleOSGiModel.class); + assertNotNull(model); + assertNotNull(model.getService()); + assertEquals(service, model.getService()); + + verifyNoMoreInteractions(res); + } + + @Test + public void testRequestOSGiModel() throws Exception { + ServiceInterface service = mock(ServiceInterface.class); + + SlingHttpServletRequest request = mock(SlingHttpServletRequest.class); + when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings); + + when(helper.getServices(ServiceInterface.class, null)).thenReturn(new ServiceInterface[] { service }); + + RequestOSGiModel model = factory.getAdapter(request, RequestOSGiModel.class); + assertNotNull(model); + assertNotNull(model.getService()); + assertEquals(service, model.getService()); + + verify(bundleContext).registerService(eq(Runnable.class.getName()), eq(factory), any(Dictionary.class)); + verify(bundleContext).addBundleListener(any(BundleListener.class)); + verify(bundleContext).getBundles(); + verifyNoMoreInteractions(bundleContext); + } + + @Test + public void testListOSGiModel() throws Exception { + ServiceReference ref1 = mock(ServiceReference.class); + ServiceInterface service1 = mock(ServiceInterface.class); + when(bundleContext.getService(ref1)).thenReturn(service1); + ServiceReference ref2 = mock(ServiceReference.class); + ServiceInterface service2 = mock(ServiceInterface.class); + when(bundleContext.getService(ref2)).thenReturn(service2); + + when(bundleContext.getServiceReferences(ServiceInterface.class.getName(), null)).thenReturn( + new ServiceReference[] { ref1, ref2 }); + + Resource res = mock(Resource.class); + + ListOSGiModel model = factory.getAdapter(res, ListOSGiModel.class); + assertNotNull(model); + assertNotNull(model.getServices()); + assertEquals(2, model.getServices().size()); + assertEquals(service1, model.getServices().get(0)); + assertEquals(service2, model.getServices().get(1)); + + verifyNoMoreInteractions(res); + } + + @Test + public void testArrayOSGiModel() throws Exception { + ServiceReference ref1 = mock(ServiceReference.class); + ServiceInterface service1 = mock(ServiceInterface.class); + when(bundleContext.getService(ref1)).thenReturn(service1); + ServiceReference ref2 = mock(ServiceReference.class); + ServiceInterface service2 = mock(ServiceInterface.class); + when(bundleContext.getService(ref2)).thenReturn(service2); + + when(bundleContext.getServiceReferences(ServiceInterface.class.getName(), null)).thenReturn( + new ServiceReference[] { ref1, ref2 }); + + Resource res = mock(Resource.class); + + ArrayOSGiModel model = factory.getAdapter(res, ArrayOSGiModel.class); + assertNotNull(model); + assertNotNull(model.getServices()); + assertEquals(2, model.getServices().length); + assertEquals(service1, model.getServices()[0]); + assertEquals(service2, model.getServices()[1]); + + verifyNoMoreInteractions(res); + } + + @Test + public void testCollectionOSGiModel() throws Exception { + ServiceReference ref1 = mock(ServiceReference.class); + ServiceInterface service1 = mock(ServiceInterface.class); + when(bundleContext.getService(ref1)).thenReturn(service1); + ServiceReference ref2 = mock(ServiceReference.class); + ServiceInterface service2 = mock(ServiceInterface.class); + when(bundleContext.getService(ref2)).thenReturn(service2); + + when(bundleContext.getServiceReferences(ServiceInterface.class.getName(), null)).thenReturn( + new ServiceReference[] { ref1, ref2 }); + + Resource res = mock(Resource.class); + + CollectionOSGiModel model = factory.getAdapter(res, CollectionOSGiModel.class); + assertNotNull(model); + assertNotNull(model.getServices()); + assertEquals(2, model.getServices().size()); + + assertTrue(model.getServices().contains(service1)); + assertTrue(model.getServices().contains(service2)); + + verifyNoMoreInteractions(res); + } + + @Test + public void testSetOSGiModel() throws Exception { + ServiceReference ref1 = mock(ServiceReference.class); + ServiceInterface service1 = mock(ServiceInterface.class); + when(bundleContext.getService(ref1)).thenReturn(service1); + ServiceReference ref2 = mock(ServiceReference.class); + ServiceInterface service2 = mock(ServiceInterface.class); + when(bundleContext.getService(ref2)).thenReturn(service2); + + when(bundleContext.getServiceReferences(ServiceInterface.class.getName(), null)).thenReturn( + new ServiceReference[] { ref1, ref2 }); + + Resource res = mock(Resource.class); + + SetOSGiModel model = factory.getAdapter(res, SetOSGiModel.class); + assertNull(model); + + verify(bundleContext).registerService(eq(Runnable.class.getName()), eq(factory), any(Dictionary.class)); + verify(bundleContext).addBundleListener(any(BundleListener.class)); + verify(bundleContext).getBundles(); + verifyNoMoreInteractions(res, bundleContext); + } +} diff --git a/src/test/java/org/apache/sling/models/impl/PostConstructTest.java b/src/test/java/org/apache/sling/models/impl/PostConstructTest.java new file mode 100644 index 0000000..7b59be4 --- /dev/null +++ b/src/test/java/org/apache/sling/models/impl/PostConstructTest.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.impl; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.testmodels.classes.SubClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentContext; + +@RunWith(MockitoJUnitRunner.class) +public class PostConstructTest { + + @Mock + private ComponentContext componentCtx; + + @Mock + private BundleContext bundleContext; + + @Before + public void setup() { + when(componentCtx.getBundleContext()).thenReturn(bundleContext); + } + + @Test + public void testClassOrder() { + Resource r = mock(Resource.class); + ModelAdapterFactory factory = new ModelAdapterFactory(); + factory.activate(componentCtx); + // no injectors are necessary + + SubClass sc = factory.getAdapter(r, SubClass.class); + assertTrue(sc.getPostConstructCalledTimestampInSub() > sc.getPostConstructCalledTimestampInSuper()); + assertTrue(sc.getPostConstructCalledTimestampInSuper() > 0); + } +} diff --git a/src/test/java/org/apache/sling/models/impl/RequestInjectionTest.java b/src/test/java/org/apache/sling/models/impl/RequestInjectionTest.java new file mode 100644 index 0000000..8a48ef0 --- /dev/null +++ b/src/test/java/org/apache/sling/models/impl/RequestInjectionTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.scripting.SlingBindings; +import org.apache.sling.api.scripting.SlingScriptHelper; +import org.apache.sling.models.impl.injectors.BindingsInjector; +import org.apache.sling.models.testmodels.classes.BindingsModel; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.service.component.ComponentContext; + +@RunWith(MockitoJUnitRunner.class) +public class RequestInjectionTest { + + @Mock + private ComponentContext componentCtx; + + @Mock + private BundleContext bundleContext; + + private ModelAdapterFactory factory; + + @Mock + private SlingHttpServletRequest request; + + @Mock + private SlingScriptHelper sling; + + @Before + public void setup() { + when(componentCtx.getBundleContext()).thenReturn(bundleContext); + + SlingBindings bindings = new SlingBindings(); + bindings.setSling(sling); + when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings); + + factory = new ModelAdapterFactory(); + factory.activate(componentCtx); + factory.bindInjector(new BindingsInjector(), + Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L)); + } + + @Test + public void testNamedInjection() { + BindingsModel model = factory.getAdapter(request, BindingsModel.class); + assertNotNull(model.getSling()); + assertEquals(sling, model.getSling()); + } + +} diff --git a/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java b/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java new file mode 100644 index 0000000..46e825a --- /dev/null +++ b/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang.RandomStringUtils; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.api.wrappers.ValueMapDecorator; +import org.apache.sling.models.impl.injectors.ChildResourceInjector; +import org.apache.sling.models.impl.injectors.ValueMapInjector; +import org.apache.sling.models.testmodels.classes.ChildModel; +import org.apache.sling.models.testmodels.classes.ResourceModelWithRequiredField; +import org.apache.sling.models.testmodels.classes.SimplePropertyModel; +import org.apache.sling.models.testmodels.interfaces.ChildResourceModel; +import org.apache.sling.models.testmodels.interfaces.ChildValueMapModel; +import org.apache.sling.models.testmodels.interfaces.ParentModel; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.service.component.ComponentContext; + +@RunWith(MockitoJUnitRunner.class) +public class ResourceModelClassesTest { + + @Mock + private ComponentContext componentCtx; + + @Mock + private BundleContext bundleContext; + + private ModelAdapterFactory factory; + + @Before + public void setup() { + when(componentCtx.getBundleContext()).thenReturn(bundleContext); + + factory = new ModelAdapterFactory(); + factory.activate(componentCtx); + factory.bindInjector(new ValueMapInjector(), + Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 1L)); + factory.bindInjector(new ChildResourceInjector(), + Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L)); + } + + @Test + public void testSimplePropertyModel() { + Map<String, Object> map = new HashMap<String, Object>(); + map.put("first", "first-value"); + map.put("third", "third-value"); + map.put("intProperty", new Integer(3)); + ValueMap vm = new ValueMapDecorator(map); + + Resource res = mock(Resource.class); + when(res.adaptTo(ValueMap.class)).thenReturn(vm); + + SimplePropertyModel model = factory.getAdapter(res, SimplePropertyModel.class); + assertNotNull(model); + assertEquals("first-value", model.getFirst()); + assertNull(model.getSecond()); + assertEquals("third-value", model.getThirdProperty()); + assertEquals(3, model.getIntProperty()); + assertTrue(model.isPostConstructCalled()); + } + + @Test + public void testRequiredPropertyModel() { + Map<String, Object> map = new HashMap<String, Object>(); + map.put("first", "first-value"); + map.put("third", "third-value"); + ValueMap vm = spy(new ValueMapDecorator(map)); + + Resource res = mock(Resource.class); + when(res.adaptTo(ValueMap.class)).thenReturn(vm); + + ResourceModelWithRequiredField model = factory.getAdapter(res, ResourceModelWithRequiredField.class); + assertNull(model); + + verify(vm).get("required", String.class); + } + + @Test + public void testChildResource() { + Resource child = mock(Resource.class); + + Resource res = mock(Resource.class); + when(res.getChild("firstChild")).thenReturn(child); + + ChildResourceModel model = factory.getAdapter(res, ChildResourceModel.class); + assertNotNull(model); + assertEquals(child, model.getFirstChild()); + } + + @Test + public void testChildValueMap() { + ValueMap map = ValueMapDecorator.EMPTY; + + Resource child = mock(Resource.class); + when(child.adaptTo(ValueMap.class)).thenReturn(map); + + Resource res = mock(Resource.class); + when(res.getChild("firstChild")).thenReturn(child); + + ChildValueMapModel model = factory.getAdapter(res, ChildValueMapModel.class); + assertNotNull(model); + assertEquals(map, model.getFirstChild()); + } + + @Test + public void testChildModel() { + Object value = RandomStringUtils.randomAlphabetic(10); + Map<String, Object> props = Collections.singletonMap("property", value); + ValueMap map = new ValueMapDecorator(props); + + final Resource child = mock(Resource.class); + when(child.adaptTo(ValueMap.class)).thenReturn(map); + when(child.adaptTo(ChildModel.class)).thenAnswer(new Answer<ChildModel>() { + + @Override + public ChildModel answer(InvocationOnMock invocation) throws Throwable { + return factory.getAdapter(child, ChildModel.class); + } + + }); + + Resource res = mock(Resource.class); + when(res.getChild("firstChild")).thenReturn(child); + + ParentModel model = factory.getAdapter(res, ParentModel.class); + assertNotNull(model); + + ChildModel childModel = model.getFirstChild(); + assertNotNull(childModel); + assertEquals(value, childModel.getProperty()); + } + +} diff --git a/src/test/java/org/apache/sling/models/impl/ResourceModelInterfacesTest.java b/src/test/java/org/apache/sling/models/impl/ResourceModelInterfacesTest.java new file mode 100644 index 0000000..6d1b0c8 --- /dev/null +++ b/src/test/java/org/apache/sling/models/impl/ResourceModelInterfacesTest.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl; + +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.api.wrappers.ValueMapDecorator; +import org.apache.sling.models.impl.injectors.ValueMapInjector; +import org.apache.sling.models.testmodels.classes.ResourceModelWithRequiredField; +import org.apache.sling.models.testmodels.interfaces.SimplePropertyModel; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.service.component.ComponentContext; + +@RunWith(MockitoJUnitRunner.class) +public class ResourceModelInterfacesTest { + + @Mock + private ComponentContext componentCtx; + + @Mock + private BundleContext bundleContext; + + private ModelAdapterFactory factory; + + @Before + public void setup() { + when(componentCtx.getBundleContext()).thenReturn(bundleContext); + factory = new ModelAdapterFactory(); + factory.activate(componentCtx); + factory.bindInjector(new ValueMapInjector(), + Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L)); + } + + @Test + public void testSimplePropertyModel() { + Map<String, Object> map = new HashMap<String, Object>(); + map.put("first", "first-value"); + map.put("third", "third-value"); + map.put("fourth", true); + ValueMap vm = new ValueMapDecorator(map); + + Resource res = mock(Resource.class); + when(res.adaptTo(ValueMap.class)).thenReturn(vm); + + SimplePropertyModel model = factory.getAdapter(res, SimplePropertyModel.class); + assertNotNull(model); + assertEquals("first-value", model.getFirst()); + assertNull(model.getSecond()); + assertEquals("third-value", model.getThirdProperty()); + assertTrue(model.isFourth()); + } + + @Test + public void testRequiredPropertyModel() { + Map<String, Object> map = new HashMap<String, Object>(); + map.put("first", "first-value"); + map.put("third", "third-value"); + ValueMap vm = spy(new ValueMapDecorator(map)); + + Resource res = mock(Resource.class); + when(res.adaptTo(ValueMap.class)).thenReturn(vm); + + ResourceModelWithRequiredField model = factory.getAdapter(res, ResourceModelWithRequiredField.class); + assertNull(model); + + verify(vm).get("required", String.class); + } + +} diff --git a/src/test/java/org/apache/sling/models/impl/ViaTest.java b/src/test/java/org/apache/sling/models/impl/ViaTest.java new file mode 100644 index 0000000..0485030 --- /dev/null +++ b/src/test/java/org/apache/sling/models/impl/ViaTest.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl; + +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; + +import java.util.Collections; + +import org.apache.commons.lang.RandomStringUtils; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.api.wrappers.ValueMapDecorator; +import org.apache.sling.models.impl.injectors.ValueMapInjector; +import org.apache.sling.models.testmodels.classes.ViaModel; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.service.component.ComponentContext; + +@RunWith(MockitoJUnitRunner.class) +public class ViaTest { + + @Mock + private Resource resource; + + @Mock + private SlingHttpServletRequest request; + + @Mock + private ComponentContext componentCtx; + + @Mock + private BundleContext bundleContext; + + private ModelAdapterFactory factory; + + @Before + public void setup() { + when(componentCtx.getBundleContext()).thenReturn(bundleContext); + when(request.getResource()).thenReturn(resource); + factory = new ModelAdapterFactory(); + factory.activate(componentCtx); + factory.bindInjector(new ValueMapInjector(), + Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L)); + } + + @Test + public void testProjectionToResource() { + String value = RandomStringUtils.randomAlphanumeric(10); + ValueMap map = new ValueMapDecorator(Collections.<String, Object> singletonMap("firstProperty", value)); + when(resource.adaptTo(ValueMap.class)).thenReturn(map); + + ViaModel model = factory.getAdapter(request, ViaModel.class); + assertNotNull(model); + assertEquals(value, model.getFirstProperty()); + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ArrayOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/ArrayOSGiModel.java new file mode 100644 index 0000000..3e31a97 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/ArrayOSGiModel.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.testmodels.classes; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.testmodels.interfaces.ServiceInterface; + +@Model(adaptables = Resource.class) +public class ArrayOSGiModel { + + @Inject + private ServiceInterface[] services; + + public ServiceInterface[] getServices() { + return services; + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/BindingsModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/BindingsModel.java new file mode 100644 index 0000000..342b9cf --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/BindingsModel.java @@ -0,0 +1,37 @@ +/* + * 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.testmodels.classes; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.scripting.SlingScriptHelper; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables=SlingHttpServletRequest.class) +public class BindingsModel { + + @Inject + @Named("sling") + private SlingScriptHelper sling; + + public SlingScriptHelper getSling() { + return sling; + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ChildModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/ChildModel.java new file mode 100644 index 0000000..071e37f --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/ChildModel.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.testmodels.classes; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = Resource.class) +public class ChildModel { + + @Inject + private String property; + + public String getProperty() { + return property; + } +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/CollectionOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/CollectionOSGiModel.java new file mode 100644 index 0000000..115b5e4 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/CollectionOSGiModel.java @@ -0,0 +1,37 @@ +/* + * 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.testmodels.classes; + +import java.util.Collection; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.testmodels.interfaces.ServiceInterface; + +@Model(adaptables = Resource.class) +public class CollectionOSGiModel { + + @Inject + private Collection<ServiceInterface> services; + + public Collection<ServiceInterface> getServices() { + return services; + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/DefaultStringModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/DefaultStringModel.java new file mode 100644 index 0000000..fecfcb0 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/DefaultStringModel.java @@ -0,0 +1,43 @@ +/* + * 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.testmodels.classes; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Default; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = Resource.class) +public class DefaultStringModel { + + @Inject + @Default(values = "firstDefault") + private String firstProperty; + + @Inject + @Default(values = { "firstDefault", "secondDefault" }) + private String[] secondProperty; + + public String getFirstProperty() { + return firstProperty; + } + + public String[] getSecondProperty() { + return secondProperty; + } +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ListOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/ListOSGiModel.java new file mode 100644 index 0000000..33b2ed4 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/ListOSGiModel.java @@ -0,0 +1,37 @@ +/* + * 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.testmodels.classes; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.testmodels.interfaces.ServiceInterface; + +@Model(adaptables = Resource.class) +public class ListOSGiModel { + + @Inject + private List<ServiceInterface> services; + + public List<ServiceInterface> getServices() { + return services; + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/RequestOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/RequestOSGiModel.java new file mode 100644 index 0000000..253e9ef --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/RequestOSGiModel.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.testmodels.classes; + +import javax.inject.Inject; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.testmodels.interfaces.ServiceInterface; + +@Model(adaptables = SlingHttpServletRequest.class) +public class RequestOSGiModel { + + @Inject + private ServiceInterface service; + + public ServiceInterface getService() { + return service; + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ResourceModelWithRequiredField.java b/src/test/java/org/apache/sling/models/testmodels/classes/ResourceModelWithRequiredField.java new file mode 100644 index 0000000..8ebc5f6 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/ResourceModelWithRequiredField.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.testmodels.classes; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = Resource.class) +public class ResourceModelWithRequiredField { + + @Inject + private String required; + + public String getRequired() { + return required; + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SetOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/SetOSGiModel.java new file mode 100644 index 0000000..9e185c6 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/SetOSGiModel.java @@ -0,0 +1,37 @@ +/* + * 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.testmodels.classes; + +import java.util.Set; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.testmodels.interfaces.ServiceInterface; + +@Model(adaptables = Resource.class) +public class SetOSGiModel { + + @Inject + private Set<ServiceInterface> services; + + public Set<ServiceInterface> getServices() { + return services; + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SimpleOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/SimpleOSGiModel.java new file mode 100644 index 0000000..1fc3d62 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/SimpleOSGiModel.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.testmodels.classes; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.testmodels.interfaces.ServiceInterface; + +@Model(adaptables = Resource.class) +public class SimpleOSGiModel { + + @Inject + private ServiceInterface service; + + public ServiceInterface getService() { + return service; + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SimplePropertyModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/SimplePropertyModel.java new file mode 100644 index 0000000..ddd3b2f --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/SimplePropertyModel.java @@ -0,0 +1,71 @@ +/* + * 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.testmodels.classes; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import javax.inject.Named; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.Optional; + +@Model(adaptables = Resource.class) +public class SimplePropertyModel { + + @Inject + private String first; + + @Inject + @Optional + private String second; + + @Inject + @Named("third") + private String thirdProperty; + + @Inject + private int intProperty; + + private boolean postConstructCalled; + + public int getIntProperty() { + return intProperty; + } + + public String getFirst() { + return first; + } + + public String getSecond() { + return second; + } + + public String getThirdProperty() { + return thirdProperty; + } + + @PostConstruct + protected void postConstruct() { + postConstructCalled = true; + } + + public boolean isPostConstructCalled() { + return postConstructCalled; + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SubClass.java b/src/test/java/org/apache/sling/models/testmodels/classes/SubClass.java new file mode 100644 index 0000000..997cf74 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/SubClass.java @@ -0,0 +1,43 @@ +/* + * 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.testmodels.classes; + +import javax.annotation.PostConstruct; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables=Resource.class) +public class SubClass extends SuperClass { + + private long postConstructCalledTimestampInSub; + + public long getPostConstructCalledTimestampInSub() { + return postConstructCalledTimestampInSub; + } + + @PostConstruct + protected void pc2() { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + postConstructCalledTimestampInSub = System.currentTimeMillis(); + } +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SuperClass.java b/src/test/java/org/apache/sling/models/testmodels/classes/SuperClass.java new file mode 100644 index 0000000..a7047ce --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/SuperClass.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.testmodels.classes; + +import javax.annotation.PostConstruct; + +public class SuperClass { + + private long postConstructCalledTimestampInSuper; + + public long getPostConstructCalledTimestampInSuper() { + return postConstructCalledTimestampInSuper; + } + + @PostConstruct + protected void pc() { + postConstructCalledTimestampInSuper = System.currentTimeMillis(); + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ViaModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/ViaModel.java new file mode 100644 index 0000000..5e5054e --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/ViaModel.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.testmodels.classes; + +import javax.inject.Inject; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.Via; + +@Model(adaptables = SlingHttpServletRequest.class) +public class ViaModel { + + @Inject + @Via("resource") + private String firstProperty; + + public String getFirstProperty() { + return firstProperty; + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/interfaces/ChildResourceModel.java b/src/test/java/org/apache/sling/models/testmodels/interfaces/ChildResourceModel.java new file mode 100644 index 0000000..b2ce65c --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/interfaces/ChildResourceModel.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.testmodels.interfaces; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = Resource.class) +public interface ChildResourceModel { + + @Inject + public Resource getFirstChild(); + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/interfaces/ChildValueMapModel.java b/src/test/java/org/apache/sling/models/testmodels/interfaces/ChildValueMapModel.java new file mode 100644 index 0000000..e7021d7 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/interfaces/ChildValueMapModel.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.testmodels.interfaces; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = Resource.class) +public interface ChildValueMapModel { + + @Inject + public ValueMap getFirstChild(); + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/interfaces/ParentModel.java b/src/test/java/org/apache/sling/models/testmodels/interfaces/ParentModel.java new file mode 100644 index 0000000..e0ec29d --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/interfaces/ParentModel.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.testmodels.interfaces; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.testmodels.classes.ChildModel; + +@Model(adaptables = Resource.class) +public interface ParentModel { + + @Inject + public ChildModel getFirstChild(); +} diff --git a/src/test/java/org/apache/sling/models/testmodels/interfaces/ResourceModelWithRequiredField.java b/src/test/java/org/apache/sling/models/testmodels/interfaces/ResourceModelWithRequiredField.java new file mode 100644 index 0000000..dd68f78 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/interfaces/ResourceModelWithRequiredField.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.testmodels.interfaces; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = Resource.class) +public interface ResourceModelWithRequiredField { + + @Inject + public String getRequired(); + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/interfaces/ServiceInterface.java b/src/test/java/org/apache/sling/models/testmodels/interfaces/ServiceInterface.java new file mode 100644 index 0000000..73c9b41 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/interfaces/ServiceInterface.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.testmodels.interfaces; + +public interface ServiceInterface { + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/interfaces/SimplePropertyModel.java b/src/test/java/org/apache/sling/models/testmodels/interfaces/SimplePropertyModel.java new file mode 100644 index 0000000..cdff785 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/interfaces/SimplePropertyModel.java @@ -0,0 +1,43 @@ +/* + * 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.testmodels.interfaces; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.Optional; + +@Model(adaptables = Resource.class) +public interface SimplePropertyModel { + + @Inject + public String getFirst(); + + @Inject + @Optional + public String getSecond(); + + @Inject + @Named("third") + public String getThirdProperty(); + + @Inject + public boolean isFourth(); + +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
