diff --git a/slice-core/src/main/java/com/cognifide/slice/core/internal/adapter/AdapterFactoryManager.java b/slice-core/src/main/java/com/cognifide/slice/core/internal/adapter/AdapterFactoryManager.java index 817d5660..5bb3df7d 100644 --- a/slice-core/src/main/java/com/cognifide/slice/core/internal/adapter/AdapterFactoryManager.java +++ b/slice-core/src/main/java/com/cognifide/slice/core/internal/adapter/AdapterFactoryManager.java @@ -31,6 +31,7 @@ import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Service; +import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.adapter.AdapterFactory; import org.apache.sling.api.resource.Resource; import org.osgi.framework.Bundle; @@ -145,7 +146,7 @@ private ServiceRegistration createAdapterFactory(Collection> classes, S SliceAdapterFactory factory = new SliceAdapterFactory(name); Dictionary properties = new Hashtable(); - properties.put(AdapterFactory.ADAPTABLE_CLASSES, new String[] { Resource.class.getName() }); + properties.put(AdapterFactory.ADAPTABLE_CLASSES, new String[] { Resource.class.getName(), SlingHttpServletRequest.class.getName() }); properties.put(AdapterFactory.ADAPTER_CLASSES, adapterClassNames); return bundleContext.registerService(AdapterFactory.class.getName(), factory, properties); } diff --git a/slice-core/src/main/java/com/cognifide/slice/core/internal/adapter/SliceAdapterFactory.java b/slice-core/src/main/java/com/cognifide/slice/core/internal/adapter/SliceAdapterFactory.java index b4783fee..bc212471 100644 --- a/slice-core/src/main/java/com/cognifide/slice/core/internal/adapter/SliceAdapterFactory.java +++ b/slice-core/src/main/java/com/cognifide/slice/core/internal/adapter/SliceAdapterFactory.java @@ -20,6 +20,7 @@ package com.cognifide.slice.core.internal.adapter; +import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.adapter.AdapterFactory; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; @@ -47,10 +48,16 @@ public SliceAdapterFactory(String injectorName) { @Override public AdapterType getAdapter(Object adaptable, Class type) { - if (!(adaptable instanceof Resource)) { + if (!((adaptable instanceof Resource) || (adaptable instanceof SlingHttpServletRequest))) { return null; } - Resource resource = (Resource) adaptable; + + Resource resource; + if(adaptable instanceof Resource) { + resource = (Resource) adaptable; + } else { + resource = ((SlingHttpServletRequest) adaptable).getResource(); + } InjectorWithContext injector = getInjector(resource); if (injector != null) { @@ -67,7 +74,7 @@ public AdapterType getAdapter(Object adaptable, Class } } - private InjectorWithContext getInjector(Resource resource) { + private InjectorWithContext getInjector(Resource resource) { ResourceResolver resourceResolver = resource.getResourceResolver(); InjectorsRepository repository = resourceResolver.adaptTo(InjectorsRepository.class); if (repository == null) { diff --git a/slice-mapper-api/src/main/java/com/cognifide/slice/mapper/annotation/RequestAttribute.java b/slice-mapper-api/src/main/java/com/cognifide/slice/mapper/annotation/RequestAttribute.java new file mode 100644 index 00000000..bd0110c0 --- /dev/null +++ b/slice-mapper-api/src/main/java/com/cognifide/slice/mapper/annotation/RequestAttribute.java @@ -0,0 +1,56 @@ +/*- + * #%L + * Slice - Mapper API + * %% + * Copyright (C) 2012 Cognifide Limited + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.cognifide.slice.mapper.annotation; + +import java.lang.annotation.*; + +/** + * Indicates that a given field should be mapped from a request attribute . The + * name of the request attribute is indicated by the name of the field or value of {@link RequestAttribute} (if + * specified). + * Example: + * + *
+ * {@literal @}SliceResource
+ * public class ExampleModel {
+ *
+ *   {@literal @}RequestAttribute
+ *   private String myAttribute;
+ *
+ *   {@literal @}RequestAttribute("second-attribute")
+ *   private Boolean secondAttribute;
+ * }
+ * 
+ * + * @author roy.teeuwen + * + */ +@Documented +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface RequestAttribute { + + /** + * Custom request attribute name. If empty, property name is read from field's name. + * + * @return value + */ + String value() default ""; +} diff --git a/slice-mapper-api/src/main/java/com/cognifide/slice/mapper/annotation/RequestParameter.java b/slice-mapper-api/src/main/java/com/cognifide/slice/mapper/annotation/RequestParameter.java new file mode 100644 index 00000000..202d5512 --- /dev/null +++ b/slice-mapper-api/src/main/java/com/cognifide/slice/mapper/annotation/RequestParameter.java @@ -0,0 +1,56 @@ +/*- + * #%L + * Slice - Mapper API + * %% + * Copyright (C) 2012 Cognifide Limited + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.cognifide.slice.mapper.annotation; + +import java.lang.annotation.*; + +/** + * Indicates that a given field should be mapped from a request parameter . The + * name of the request parameter is indicated by the name of the field or value of {@link RequestParameter} (if + * specified). Only a type of string is allowed + * Example: + * + *
+ * {@literal @}SliceResource
+ * public class ExampleModel {
+ *
+ *   {@literal @}RequestParameter
+ *   private String myParameter;
+ *
+ *   {@literal @}RequestParameter("second-parameter")
+ *   private String secondParameter;
+ * }
+ * 
+ * + * @author roy.teeuwen + * + */ +@Documented +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface RequestParameter { + + /** + * Custom request parameter name. If empty, property name is read from field's name. + * + * @return value + */ + String value() default ""; +} diff --git a/slice-mapper-api/src/main/java/com/cognifide/slice/mapper/annotation/package-info.java b/slice-mapper-api/src/main/java/com/cognifide/slice/mapper/annotation/package-info.java index 786bb96b..40e27dec 100644 --- a/slice-mapper-api/src/main/java/com/cognifide/slice/mapper/annotation/package-info.java +++ b/slice-mapper-api/src/main/java/com/cognifide/slice/mapper/annotation/package-info.java @@ -17,7 +17,7 @@ * limitations under the License. * #L% */ -@Version("4.3.0") +@Version("4.4.0") package com.cognifide.slice.mapper.annotation; import aQute.bnd.annotation.Version; diff --git a/slice-mapper/src/main/java/com/cognifide/slice/mapper/MapperBuilder.java b/slice-mapper/src/main/java/com/cognifide/slice/mapper/MapperBuilder.java index 503da0b2..be69272a 100644 --- a/slice-mapper/src/main/java/com/cognifide/slice/mapper/MapperBuilder.java +++ b/slice-mapper/src/main/java/com/cognifide/slice/mapper/MapperBuilder.java @@ -30,13 +30,7 @@ import com.cognifide.slice.mapper.api.processor.PriorityFieldProcessor; import com.cognifide.slice.mapper.impl.CustomProcessorsCollector; import com.cognifide.slice.mapper.impl.postprocessor.EscapeValuePostProcessor; -import com.cognifide.slice.mapper.impl.processor.BooleanFieldProcessor; -import com.cognifide.slice.mapper.impl.processor.ChildrenFieldProcessor; -import com.cognifide.slice.mapper.impl.processor.DefaultFieldProcessor; -import com.cognifide.slice.mapper.impl.processor.EnumFieldProcessor; -import com.cognifide.slice.mapper.impl.processor.ListFieldProcessor; -import com.cognifide.slice.mapper.impl.processor.SliceReferenceFieldProcessor; -import com.cognifide.slice.mapper.impl.processor.SliceResourceFieldProcessor; +import com.cognifide.slice.mapper.impl.processor.*; import com.google.inject.Inject; import com.google.inject.Module; @@ -65,6 +59,12 @@ public final class MapperBuilder { @Inject private ChildrenFieldProcessor childrenFieldProcessor; + @Inject + private RequestAttributeProcessor requestAttributeProcessor; + + @Inject + private RequestParameterProcessor requestParameterProcessor; + @Inject private CustomProcessorsCollector customProcessorsCollector; @@ -120,6 +120,8 @@ public MapperBuilder addSliceProcessors() { processors.add(sliceReferenceFieldProcessor); // @SliceReference processors.add(sliceResourceFieldProcessor); // @SliceResource processors.add(childrenFieldProcessor); // child models @Children + processors.add(requestAttributeProcessor); // @RequestAttribute + processors.add(requestParameterProcessor); // @RequestParameter processors.add(new ListFieldProcessor()); // Subclasses of Collection and arrays processors.add(new BooleanFieldProcessor()); // booleans processors.add(new EnumFieldProcessor()); // enums diff --git a/slice-mapper/src/main/java/com/cognifide/slice/mapper/impl/processor/RequestAttributeProcessor.java b/slice-mapper/src/main/java/com/cognifide/slice/mapper/impl/processor/RequestAttributeProcessor.java new file mode 100644 index 00000000..00e5c2ec --- /dev/null +++ b/slice-mapper/src/main/java/com/cognifide/slice/mapper/impl/processor/RequestAttributeProcessor.java @@ -0,0 +1,60 @@ +/*- + * #%L + * Slice - Mapper + * %% + * Copyright (C) 2012 Cognifide Limited + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.cognifide.slice.mapper.impl.processor; + +import com.cognifide.slice.api.qualifier.Nullable; +import com.cognifide.slice.mapper.annotation.RequestAttribute; +import com.cognifide.slice.mapper.api.processor.FieldProcessor; +import com.google.inject.Inject; +import org.apache.commons.lang.StringUtils; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; + +import javax.servlet.ServletRequest; +import java.lang.reflect.Field; + +public class RequestAttributeProcessor implements FieldProcessor { + + @Inject + @Nullable + private ServletRequest servletRequest; + + @Override + public boolean accepts(final Resource resource, final Field field) { + return field.isAnnotationPresent(RequestAttribute.class); + } + + @Override + public Object mapResourceToField(Resource resource, ValueMap valueMap, Field field, String propertyName) { + if (servletRequest != null) { + String attributeName = getAttributeName(field); + return servletRequest.getAttribute(attributeName); + } + return null; + } + + private String getAttributeName(Field field) { + final RequestAttribute annotation = field.getAnnotation(RequestAttribute.class); + if ((annotation != null) && StringUtils.isNotBlank(annotation.value())) { + return annotation.value(); + } + return field.getName(); + } +} diff --git a/slice-mapper/src/main/java/com/cognifide/slice/mapper/impl/processor/RequestParameterProcessor.java b/slice-mapper/src/main/java/com/cognifide/slice/mapper/impl/processor/RequestParameterProcessor.java new file mode 100644 index 00000000..7b2e57ba --- /dev/null +++ b/slice-mapper/src/main/java/com/cognifide/slice/mapper/impl/processor/RequestParameterProcessor.java @@ -0,0 +1,72 @@ +/*- + * #%L + * Slice - Mapper + * %% + * Copyright (C) 2012 Cognifide Limited + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.cognifide.slice.mapper.impl.processor; + +import com.cognifide.slice.api.qualifier.Nullable; +import com.cognifide.slice.mapper.annotation.RequestParameter; +import com.cognifide.slice.mapper.api.processor.FieldProcessor; +import com.google.inject.Inject; +import org.apache.commons.lang.StringUtils; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; + +import javax.servlet.ServletRequest; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collection; + +public class RequestParameterProcessor implements FieldProcessor { + + @Inject + @Nullable + private ServletRequest servletRequest; + + @Override + public boolean accepts(final Resource resource, final Field field) { + Class fieldType = field.getType(); + return (Collection.class.isAssignableFrom(fieldType) || fieldType.equals(String.class)) && field.isAnnotationPresent(RequestParameter.class); + } + + @Override + public Object mapResourceToField(Resource resource, ValueMap valueMap, Field field, String propertyName) { + if (servletRequest == null) { + return null; + } + String parameterName = getParameterName(field); + Class fieldType = field.getType(); + if (Collection.class.isAssignableFrom(fieldType)) { + String[] parameters = servletRequest.getParameterValues(parameterName); + if (parameters != null) { + return Arrays.asList(parameters); + } + } else { + return servletRequest.getParameter(parameterName); + } + return null; + } + + private String getParameterName(Field field) { + final RequestParameter annotation = field.getAnnotation(RequestParameter.class); + if ((annotation != null) && StringUtils.isNotBlank(annotation.value())) { + return annotation.value(); + } + return field.getName(); + } +} diff --git a/slice-mapper/src/main/java/com/cognifide/slice/mapper/strategy/impl/AnnotatedFieldMapperStrategy.java b/slice-mapper/src/main/java/com/cognifide/slice/mapper/strategy/impl/AnnotatedFieldMapperStrategy.java index 54777e34..a4c26352 100644 --- a/slice-mapper/src/main/java/com/cognifide/slice/mapper/strategy/impl/AnnotatedFieldMapperStrategy.java +++ b/slice-mapper/src/main/java/com/cognifide/slice/mapper/strategy/impl/AnnotatedFieldMapperStrategy.java @@ -23,18 +23,22 @@ import java.lang.reflect.Field; import com.cognifide.slice.mapper.annotation.JcrProperty; +import com.cognifide.slice.mapper.annotation.RequestAttribute; +import com.cognifide.slice.mapper.annotation.RequestParameter; import com.cognifide.slice.mapper.strategy.MapperStrategy; /** - * AnnotatedFieldMapperStrategy defines a strategy where only fields annotated by {@link JcrProperty} are - * mapped. - * + * AnnotatedFieldMapperStrategy defines a strategy where only fields annotated by + * {@link JcrProperty}, {@link RequestAttribute} or {@link RequestParameter} are mapped. + * */ public class AnnotatedFieldMapperStrategy implements MapperStrategy { @Override public boolean shouldFieldBeMapped(Field field) { - return field.isAnnotationPresent(JcrProperty.class); + return (field.isAnnotationPresent(JcrProperty.class) + || field.isAnnotationPresent(RequestAttribute.class) + || field.isAnnotationPresent(RequestParameter.class)); } }