View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2014 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.handler.url.integrator.impl;
21  
22  import java.util.Collection;
23  
24  import javax.annotation.PostConstruct;
25  
26  import org.apache.commons.lang3.StringUtils;
27  import org.apache.sling.api.SlingHttpServletRequest;
28  import org.apache.sling.api.resource.Resource;
29  import org.apache.sling.api.resource.ValueMap;
30  import org.apache.sling.models.annotations.Model;
31  import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
32  import org.apache.sling.models.annotations.injectorspecific.Self;
33  import org.apache.sling.models.annotations.injectorspecific.SlingObject;
34  import org.jetbrains.annotations.NotNull;
35  import org.jetbrains.annotations.Nullable;
36  
37  import com.day.cq.wcm.api.Page;
38  
39  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
40  import io.wcm.handler.url.integrator.IntegratorHandler;
41  import io.wcm.handler.url.integrator.IntegratorMode;
42  import io.wcm.handler.url.integrator.IntegratorNameConstants;
43  import io.wcm.handler.url.integrator.IntegratorProtocol;
44  import io.wcm.handler.url.spi.UrlHandlerConfig;
45  import io.wcm.sling.commons.request.RequestPath;
46  import io.wcm.sling.models.annotations.AemObject;
47  
48  /**
49   * Default implementation of a {@link IntegratorHandler}
50   */
51  @Model(adaptables = {
52      SlingHttpServletRequest.class, Resource.class
53  }, adapters = IntegratorHandler.class)
54  public final class IntegratorHandlerImpl implements IntegratorHandler {
55  
56    @Self
57    private UrlHandlerConfig urlHandlerConfig;
58  
59    // optional injections (only available if called inside a request)
60    @SlingObject(injectionStrategy = InjectionStrategy.OPTIONAL)
61    private SlingHttpServletRequest request;
62    @AemObject(injectionStrategy = InjectionStrategy.OPTIONAL)
63    private Page currentPage;
64  
65    private boolean integratorTemplateMode;
66    private boolean integratorTemplateSecureMode;
67  
68    @PostConstruct
69    private void postConstruct() {
70      detectIntegratorTemplateModes();
71    }
72  
73    /**
74     * Detect integrator template modes - check selectors in current url.
75     */
76    private void detectIntegratorTemplateModes() {
77      // integrator mode cannot be active if no modes defined
78      if (urlHandlerConfig.getIntegratorModes().isEmpty()) {
79        return;
80      }
81      if (request != null && RequestPath.hasSelector(request, SELECTOR_INTEGRATORTEMPLATE_SECURE)) {
82        integratorTemplateSecureMode = true;
83      }
84      else if (request != null && RequestPath.hasSelector(request, SELECTOR_INTEGRATORTEMPLATE)) {
85        integratorTemplateMode = true;
86      }
87    }
88  
89    /**
90     * Checks if current request is in integrator template oder integrator template secure mode.
91     * @return true if in integrator template or integrator template secure mode
92     */
93    @Override
94    public boolean isIntegratorTemplateMode() {
95      return integratorTemplateMode || integratorTemplateSecureMode;
96    }
97  
98    /**
99     * Checks if current request is in integrator template secure mode.
100    * @return true if in integrator template secure mode
101    */
102   @Override
103   public boolean isIntegratorTemplateSecureMode() {
104     return integratorTemplateSecureMode;
105   }
106 
107   /**
108    * Returns selector for integrator template mode.
109    * In HTTPS mode the secure selector is returned, otherwise the default selector.
110    * HTTPS mode is active if the current page is an integrator page and has simple mode-HTTPs activated, or
111    * the secure integrator mode selector is included in the current request.
112    * @return Integrator template selector
113    */
114   @Override
115   public @NotNull String getIntegratorTemplateSelector() {
116     if (currentPage != null && urlHandlerConfig.isIntegrator(currentPage)) {
117       if (isResourceUrlSecure(currentPage)) {
118         return SELECTOR_INTEGRATORTEMPLATE_SECURE;
119       }
120       else {
121         return SELECTOR_INTEGRATORTEMPLATE;
122       }
123     }
124     if (integratorTemplateSecureMode) {
125       return SELECTOR_INTEGRATORTEMPLATE_SECURE;
126     }
127     else {
128       return SELECTOR_INTEGRATORTEMPLATE;
129     }
130   }
131 
132   /**
133    * Get integrator mode configured for the current page.
134    * @return Integrator mode (simple or extended)
135    */
136   @Override
137   public @NotNull IntegratorMode getIntegratorMode() {
138     return getIntegratorMode(currentPage);
139   }
140 
141   @Override
142   public @NotNull IntegratorMode getIntegratorMode(@Nullable Page page) {
143     ValueMap props = getPagePropertiesNullSafe(page);
144     return getIntegratorMode(props);
145   }
146 
147 
148   /**
149    * Read integrator mode from content container. Defaults to first integrator mode defined.
150    * @param properties Content container
151    * @return Integrator mode
152    */
153   @SuppressWarnings({ "null", "java:S2637" })
154   @SuppressFBWarnings("NP_NONNULL_RETURN_VIOLATION")
155   private @NotNull IntegratorMode getIntegratorMode(ValueMap properties) {
156     IntegratorMode mode = null;
157     Collection<IntegratorMode> integratorModes = urlHandlerConfig.getIntegratorModes();
158     String modeString = properties.get(IntegratorNameConstants.PN_INTEGRATOR_MODE, String.class);
159     if (StringUtils.isNotEmpty(modeString)) {
160       for (IntegratorMode candidate : integratorModes) {
161         if (StringUtils.equals(modeString, candidate.getId())) {
162           mode = candidate;
163           break;
164         }
165       }
166     }
167     // fallback to first mode defined in configuration
168     if (mode == null && !integratorModes.isEmpty()) {
169       mode = integratorModes.iterator().next();
170     }
171     return mode;
172   }
173 
174   /**
175    * Read integrator protocol from content container. Default to AUTO.
176    * @param properties Content container
177    * @return Integrator protocol
178    */
179   @SuppressWarnings("null")
180   private IntegratorProtocol getIntegratorProtocol(ValueMap properties) {
181     IntegratorProtocol protocol = IntegratorProtocol.AUTO;
182     try {
183       String protocolString = properties.get(IntegratorNameConstants.PN_INTEGRATOR_PROTOCOL, String.class);
184       if (StringUtils.isNotEmpty(protocolString)) {
185         protocol = IntegratorProtocol.valueOf(protocolString.toUpperCase());
186       }
187     }
188     catch (IllegalArgumentException ex) {
189       // ignore
190     }
191     return protocol;
192   }
193 
194   /**
195    * Checks whether resource URLs should be rendered in secure mode or not.
196    * @return true if resource URLs should be rendered in secure mode
197    */
198   private boolean isResourceUrlSecure(Page page) {
199     ValueMap props = getPagePropertiesNullSafe(page);
200     IntegratorMode mode = getIntegratorMode(props);
201     if (mode.isDetectProtocol()) {
202       IntegratorProtocol integratorProtocol = getIntegratorProtocol(props);
203       if (integratorProtocol == IntegratorProtocol.HTTPS) {
204         return true;
205       }
206       else if (integratorProtocol == IntegratorProtocol.AUTO) {
207         return RequestPath.hasSelector(request, IntegratorHandler.SELECTOR_INTEGRATORTEMPLATE_SECURE);
208       }
209 
210     }
211     return false;
212   }
213 
214   /**
215    * Get valuemap/papge properties of current page.
216    * @param page Page
217    * @return Value map of current page or empty map if current page is null.
218    */
219   private static ValueMap getPagePropertiesNullSafe(Page page) {
220     if (page != null) {
221       return page.getProperties();
222     }
223     else {
224       return ValueMap.EMPTY;
225     }
226   }
227 
228 }