Java tutorial
/* * Copyright 2012 david gonzalez. * * 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. */ package com.activecq.api; import com.activecq.api.plugins.wrappers.ComponentPluginsWrapper; import com.day.cq.commons.inherit.HierarchyNodeInheritanceValueMap; import com.day.cq.search.QueryBuilder; import com.day.cq.wcm.api.Page; import com.day.cq.wcm.api.PageManager; import com.day.cq.wcm.api.components.Component; import com.day.cq.wcm.api.components.ComponentContext; import com.day.cq.wcm.api.components.EditContext; import com.day.cq.wcm.api.designer.Design; import com.day.cq.wcm.api.designer.Designer; import com.day.cq.wcm.api.designer.Style; import com.day.cq.wcm.commons.WCMUtils; import javax.jcr.Node; import javax.jcr.RepositoryException; import org.apache.commons.lang.StringUtils; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.resource.*; import org.apache.sling.api.scripting.SlingBindings; import org.apache.sling.api.scripting.SlingScriptHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <p> * This SlingHttpServletRequest must target a Resource whose resourceType * resolves to a CQ Component. * </p><p> * The ActiveComponent class is abstract as the intent is each Component * implementation will subclass it. The methods in this class are protected, * encouraging moving component domain logic into the subclass, instead of * keeping it in the JSP. * </p><p> * If provided methods need to be exposed to the JSP, the subclass must override * them as public methods, and return the super.method(..) call. * </p><p> * ActiveComponent is not thread safe and (at this time) is only meant to be * instantiated in a JSP. * </p><p> * The method signatures for subclasses are encouraged to follow the get/set * bean pattern for heightened compatability with JSTL. * </p> * * @author david */ public abstract class ActiveComponent { @SuppressWarnings("unused") private static final Logger log = LoggerFactory.getLogger(ActiveComponent.class); /* * Universal Fields */ private SlingScriptHelper slingScriptHelper; private SlingHttpServletRequest request; private SlingHttpServletResponse response; private Component component; private Node node; private Resource resource; private ComponentContext componentContext; private EditContext editContext; /* * Request Fields */ private Page requestPage; private Design requestDesign; private Style requestStyle; private ValueMap requestPageProperties; private ValueMap requestDesignProperties; /* * Resource Fields */ private ValueMap resourceProperties; private Page resourcePage; private Design resourceDesign; private Style resourceStyle; private ValueMap resourcePageProperties; private ValueMap resourceDesignProperties; /* * Utility Fields */ private Designer designer; private ResourceResolver resourceResolver; private QueryBuilder queryBuilder; private PageManager pageManager; /** * <p>Wrapper that contains standard ActiveComponent Plug-ins for supporting * Diff, XSS protection, I18n, and WCMMode switching. * </p><p> * The plug-ins act as a convenient methods of access for common, and often * overlooked functionality. * </p> */ public final ComponentPluginsWrapper Plugins; /** * <p> * ActiveComponent constructor which takes a SlingHttpServletRequest object. * </p><p> * This SlingHttpServletRequest must target a Resource whose resourceType * resolves to a CQ Component. * </p> * * * @param request * @throws RepositoryException * @throws LoginException */ protected ActiveComponent(SlingHttpServletRequest request) throws RepositoryException, LoginException { // HTTP Request this.request = request; if (this.request == null) { throw new IllegalArgumentException("Sling HTTP Request must NOT be null."); } // Get SlingScriptHelper from SlingBindings included as an attribute on the Sling HTTP Request SlingBindings slingBindings = (SlingBindings) this.request.getAttribute(SlingBindings.class.getName()); if (slingBindings == null) { throw new IllegalArgumentException( "SlingHttpServletRequest most have contain a SlingBindings object at key: " + SlingBindings.class.getName()); } // Sling Script Helper this.slingScriptHelper = slingBindings.getSling(); if (this.slingScriptHelper == null) { throw new IllegalArgumentException("SlingScriptHelper must NOT be null."); } // HTTP Response this.response = this.slingScriptHelper.getResponse(); if (this.response == null) { throw new IllegalArgumentException("SlingScriptHelper's Sling HTTP Response must NOT be null."); } // Resource of component (Regardless of which page/resource it lives under) this.resource = this.request.getResource(); if (this.resource == null) { throw new IllegalArgumentException("Resource must NOT be null."); } // CQ Component object this.component = WCMUtils.getComponent(this.resource); if (this.component == null) { throw new IllegalArgumentException("Resource must have a resourceType of a CQ Component."); } // JCR Node this.node = WCMUtils.getNode(this.resource); // Initialize Plugins this.Plugins = new ComponentPluginsWrapper(this.request); } /** * ************************************************************************* * Request & Response ************************************************************************* */ /** * Get the Sling Request that initiated the request for this component * * @return the Sling Request that initiated the request for this component */ protected SlingHttpServletRequest getRequest() { return this.request; } /** * Get the Sling Response that will be used to transport this component * rendition to the client * * @return the Sling Response that will be used to transport this component * rendition to the client */ protected SlingHttpServletResponse getResponse() { return this.response; } /** * ************************************************************************* * Getters for Component Resource * * These will deal directly with the Resource node instance that represents * a particular instance of a Component and does care which Page the * component actually resides on. ************************************************************************* */ /** * <p> * Getter for Resource object that represents the Component Instance * </p><p> * http://dev.day.com/docs/en/cq/current/javadoc/org/apache/sling/api/resource/Resource.html * </p> * @return Resource representing the component */ protected Resource getResource() { return this.resource; } /** * <p> * Getter for the resource's Component object. * </p><p> * This object represents the Component implementation under /apps or /libs. * </p><p> * http://dev.day.com/docs/en/cq/current/javadoc/com/day/cq/wcm/api/components/ComponentContext.html * </p> * @return CQ Component representing the component */ protected Component getComponent() { return this.component; } /** * Check this component has a valid node * * @return true is Node exists */ protected boolean hasNode() { return this.node != null; } /** * <p> * Getter for Resource's JCR Node. JCR Node should be used only for modify * the Node. Sling APIs should be used to read from the resources. * </p><p> * http://www.day.com/maven/jsr170/javadocs/jcr-1.0/javax/jcr/Node.html * </p> * @return Node representing the component */ protected Node getNode() { return this.node; } /** * <p> * Get for the Component's ComponentContext for the Request * </p><p> * The Component Context's context is that of the Request, not necessarily * the page that contains the component. * </p><p> * http://dev.day.com/docs/en/cq/current/javadoc/com/day/cq/wcm/api/components/ComponentContext.html * </p> * @return the Component Content of the Request */ protected ComponentContext getComponentContext() { if (this.componentContext == null) { this.componentContext = WCMUtils.getComponentContext(this.getRequest()); } return this.componentContext; } /** * <p> * Get the Component's Edit Context * </p><p> * http://dev.day.com/docs/en/cq/current/javadoc/com/day/cq/wcm/api/components/EditContext.html * </p> * * @return the CQ Component's Edit Context */ protected EditContext getEditContext() { if (this.editContext == null) { if (this.getComponentContext() != null) { this.editContext = this.getComponentContext().getEditContext(); } } return this.editContext; } /** * ************************************************************************* * Lazy-loading Getters for Request-oriented Attributes ************************************************************************* */ /** * <p> * Getter for the Request Resource's Page object. * <p></p> * This represents the page resource that was requested, and sling includes * the component (regardless of where the include's path points). * <p></p> * http://dev.day.com/docs/en/cq/current/javadoc/com/day/cq/wcm/api/Page.html * <p> * * @return the Request's CQ Page object */ protected Page getRequestPage() { if (this.requestPage == null) { if (this.getComponentContext() != null) { this.requestPage = this.getComponentContext().getPage(); if (this.requestPage == null) { this.requestPage = this.getResourcePage(); } } } return this.requestPage; } /** * <p> * Convenience method. * </p><p> * Handles the "normal" case of using the Request Page's Page object. * </p> * * @return the Request's CQ Page object */ protected Page getPage() { return this.getRequestPage(); } /** * <p> * Getter for the Requested Page's Design * </p><p> * http://dev.day.com/docs/en/cq/current/javadoc/index.html?com/day/cq/wcm/api/designer/class-use/Design.html *</p> * * @return the Requested Page's Design */ protected Design getRequestDesign() { if (this.requestDesign == null) { if (this.getDesigner() != null) { if (this.getRequestPage() != null) { this.requestDesign = this.getDesigner().getDesign(this.getRequestPage()); } } } return this.requestDesign; } /** * <p> * Convenience method. * </p><p> * Handles the "normal" case of using the Request Page's Design * </p> * * @returnThe Requested Page's Design */ protected Design getDesign() { return this.getRequestDesign(); } /** * Gets the Page Properties for the Page associated with Request * * @return the Page Properties as a HierarchyNodeInheritanceValueMap */ protected ValueMap getRequestPageProperties() { if (this.requestPageProperties == null) { if (this.getRequestPage() != null) { this.requestPageProperties = new HierarchyNodeInheritanceValueMap( this.getRequestPage().getContentResource()); } } return this.requestPageProperties; } /** * Alias for getPageProperties() * * @return the Page Properties as a HierarchyNodeInheritanceValueMap */ protected ValueMap getPageProperties() { return this.getRequestPageProperties(); } /** * Gets the Page Properties for the Page associated with the Resource * * @return the Page Properties as a HierarchyNodeInheritanceValueMap */ protected ValueMap getResourcePageProperties() { if (this.resourcePageProperties == null) { if (this.getResourcePage() != null) { this.resourcePageProperties = new HierarchyNodeInheritanceValueMap( this.getResourcePage().getContentResource()); } } return this.resourcePageProperties; } /** * Getter for the Component's Page's Style * * @return Style associated with the Request */ protected Style getRequestStyle() { if (this.requestStyle == null) { if (this.getRequestDesign() != null && this.getComponentContext() != null) { this.requestStyle = this.getRequestDesign().getStyle(this.getComponentContext().getCell()); } } return this.requestStyle; } /** * <p> * Convenience method. * </p><p> * Alias for getRequestStyle() * </p> * @return Style associated with the Request */ protected Style getStyle() { return this.getRequestStyle(); } /** * Get a ValueMap of the properties for the content Resource's Design * * @return Style ValueMap */ protected ValueMap getRequestDesignProperties() { if (this.requestDesignProperties == null) { this.requestDesignProperties = this.getRequestStyle(); } return this.requestDesignProperties; } /** * <p> * Convenience method. * </p><p>* * Get a ValueMap of the properties for the content Resource's Design * </p> * * @return Style ValueMap */ protected ValueMap getDesignProperties() { return this.getRequestDesignProperties(); } /** * ************************************************************************* * Lazy-loading Getters for Resource-oriented Data ************************************************************************* */ /** * <p> * Getter for the Component instance's Page object. * <p></p> * This represents the page resource who is the parent of the component * instance. This may not be the same as the getRequetsPage() Page. * <p></p> * http://dev.day.com/docs/en/cq/current/javadoc/com/day/cq/wcm/api/Page.html * <p> * * @return CQ Page the resource lives under */ protected Page getResourcePage() { if (this.resourcePage == null) { if (this.getPageManager() != null) { this.resourcePage = this.getPageManager().getContainingPage(this.getResource()); } } return this.resourcePage; } /** * <p> * Getter for the Component Page's Design * <p></p> * This represents the Design for the page who is the parent of the resource. * This may not be the same as the getRequetsPage() Page. * <p></p> * http://dev.day.com/docs/en/cq/current/javadoc/index.html?com/day/cq/wcm/api/designer/class-use/Design.html * <p> * * @return Design for the */ protected Design getResourceDesign() { if (this.resourceDesign == null) { if (this.getDesigner() != null) { if (this.getRequestPage() != null) { this.resourceDesign = this.getDesigner().getDesign(this.getResourcePage()); } } } return this.resourceDesign; } /** * Get the style associated with the Resource's Page * * @return */ protected Style getResourceStyle() { if (this.resourceStyle == null) { if (this.getResourceDesign() != null && this.getComponentContext() != null) { this.resourceStyle = this.getResourceDesign().getStyle(this.getComponentContext().getCell()); } } return this.resourceStyle; } /** * Get a ValueMap of properties for the content Resource * * @return */ protected ValueMap getProperties() { if (this.resourceProperties == null) { this.resourceProperties = ResourceUtil.getValueMap(this.getResource()); } return this.resourceProperties; } /** * Get the Design properties associated with the Resource's style (rather * than the Requests') * * @return */ protected ValueMap getResourceDesignProperties() { if (this.resourceDesignProperties == null) { this.resourceDesignProperties = this.getResourceStyle(); } return this.resourceDesignProperties; } /** * ************************************************************************* * Lazy-loading Getters for Universal Helper and Utility objects ************************************************************************* */ /** * Getter for a CQ Designer for the Sling Request's user * * @return */ protected Designer getDesigner() { if (this.designer == null) { this.designer = this.getResourceResolver().adaptTo(Designer.class); } return this.designer; } /** * Getter for a CQ PageManager for the Sling Request's user * * @return */ protected PageManager getPageManager() { if (this.pageManager == null) { if (this.getResourceResolver() != null) { this.pageManager = this.getResourceResolver().adaptTo(PageManager.class); } } return this.pageManager; } /** * Getter for a CQ Resource Resolver for the Sling Request's user * * @return */ protected ResourceResolver getResourceResolver() { if (this.resourceResolver == null) { this.resourceResolver = this.getRequest().getResourceResolver(); } return this.resourceResolver; } /** * Getter for a CQ Query Builder for the Sling Request's user * * @return */ protected QueryBuilder getQueryBuilder() { if (this.queryBuilder == null) { this.queryBuilder = this.getResourceResolver().adaptTo(QueryBuilder.class); } return this.queryBuilder; } /** * ************************************************************************* * OSGi Service Exposure Methods ************************************************************************* */ /** * Exposes ability to get services from within the ActiveComponent class, * which itself is not a Sling Service * * @param <ServiceType> * @param type * @return the requested service or null. */ protected <ServiceType> ServiceType getService(Class<ServiceType> type) { if (this.slingScriptHelper != null) { return slingScriptHelper.getService(type); } else { throw new IllegalStateException("SlingScriptHelper is NULL"); } } /** * ************************************************************************* * Property Getter Methods ************************************************************************* */ // Resource (Dialog) Properties /** * * @param <T> Data type of klass * @param key Property key * @param klass Expected return data type * @return The resource property */ public <T> T getProperty(String key, Class<T> klass) { if (String.class.equals(klass)) { final String current = (String) getPropertyGeneric(this.getResource(), key, klass); return (T) this.Plugins.Diff.getDiff(this.getResource(), key, current); } else { return getPropertyGeneric(this.getResource(), key, klass); } } /** * * @param <T> Data type of defaultValue * @param key Property key * @param defaultValue * @return The resource property */ public <T> T getProperty(String key, T defaultValue) { if (defaultValue instanceof String) { final String current = (String) getPropertyGeneric(this.getResource(), key, defaultValue); return (T) this.Plugins.Diff.getDiff(this.getResource(), key, current); } else { return getPropertyGeneric(this.getResource(), key, defaultValue); } } // Design Dialog Properties /** * Getter for Design associated with the Request * * @param <T> Data type of klass * @param key Property key * @param klass Expected return data type * @return The resource property */ public <T> T getDesignProperty(String key, Class<T> klass) { return getPropertyGeneric(this.getRequestDesignProperties(), key, klass); } /** * Getter for Design associated with the Request * * @param <T> Data type * @param key Property key * @param defaultValue Default value if node property is blank * @return The resource property */ public <T> T getDesignProperty(String key, T defaultValue) { return getPropertyGeneric(this.getRequestDesignProperties(), key, defaultValue); } // Any Resource /** * Getter for resource property * * @param <T> Data type of klass * @param resource The resource to get the value from * @param key Property key * @param klass Expected return data type * @return The resource property */ public <T> T getProperty(Resource resource, String key, Class<T> klass) { if (resource == null) { return null; } return getPropertyGeneric(resource, key, klass); } /** * * @param <T> Data type of klass * @param resource The resource to get the value from * @param key Property key * @param defaultValue Default value if node property is blank * @return The resource property */ public <T> T getProperty(Resource resource, String key, T defaultValue) { if (resource == null) { return null; } return getPropertyGeneric(resource, key, defaultValue); } /** * * @param <T> Data type of klass * @param resource The resource to get the value from * @param key Property key * @param klass Expected return data type * @return The resource property */ private <T> T getPropertyGeneric(Resource resource, String key, Class<T> klass) { if (resource == null) { return null; } final ValueMap valueMap = resource.adaptTo(ValueMap.class); return this.getPropertyGeneric(valueMap, key, klass); } @SuppressWarnings("unchecked") private <T> T getPropertyGeneric(ValueMap valueMap, String key, Class<T> klass) { if (valueMap == null || klass == null) { return null; } if (!valueMap.containsKey(key)) { return null; } boolean isString = String.class.equals(klass); if (isString) { // Strip leading and trailing whitespace String strValue = StringUtils.strip((String) valueMap.get(key, klass)); return (T) strValue; } else { return valueMap.get(key, klass); } } private <T> T getPropertyGeneric(Resource resource, String key, T defaultValue) { if (resource == null) { return null; } final ValueMap valueMap = resource.adaptTo(ValueMap.class); return this.getPropertyGeneric(valueMap, key, defaultValue); } @SuppressWarnings("unchecked") private <T> T getPropertyGeneric(ValueMap valueMap, String key, T defaultValue) { if (valueMap == null) { return defaultValue; } if (!valueMap.containsKey(key)) { return defaultValue; } boolean isString = false; if (defaultValue != null) { isString = String.class.equals(defaultValue.getClass()); } if (isString) { // Strip leading and trailing whitespace String strValue = StringUtils.strip((String) valueMap.get(key, defaultValue)); return (T) strValue; } else { return valueMap.get(key, defaultValue); } } }