| /* |
| * 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.jcr.js.nodetypes.downloaddefaultbinary; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.PrintWriter; |
| import java.util.Arrays; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import javax.jcr.Node; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.Value; |
| import javax.jcr.nodetype.NodeType; |
| import javax.jcr.nodetype.NodeTypeManager; |
| import javax.jcr.nodetype.PropertyDefinition; |
| import javax.servlet.Servlet; |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.felix.scr.annotations.Component; |
| import org.apache.felix.scr.annotations.Properties; |
| import org.apache.felix.scr.annotations.Property; |
| import org.apache.felix.scr.annotations.Service; |
| import org.apache.sling.api.SlingHttpServletRequest; |
| import org.apache.sling.api.SlingHttpServletResponse; |
| import org.apache.sling.api.servlets.SlingSafeMethodsServlet; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Provides a download for binary default values. |
| * |
| * The fully qualified URL to specify a default value looks like this: |
| * <code>/ns:ntName/binPropDef/binary/true/true/true/true/version/1.default_binary_value.bin</code> |
| * The fully qualified format is: <code>/node type/property definition |
| * name/required property type name/is autoCreated/is mandatory/is |
| * protected/is multiple/on parent version action name/index of the |
| * default value.default_binary_value.bin</code> |
| * |
| * In case you know which elements identify a property definition unambiguously |
| * you can shorten the URL. E.g. if you are sure the property definition |
| * 'binPropDef' does not exist twice within the node type 'ns:ntName' you can |
| * use the URL <code>/ns:ntName/binPropDef/1.default_binary_value.bin</code> to |
| * download the second binary default value from that property definition. |
| * |
| * If you want to download the first binary default value you can shorten the |
| * URL even more by skipping the index in the URL like this: |
| * <code>/ns:ntName/binPropDef/default_binary_value.bin</code> |
| * |
| * The type name, the boolean Strings and the parent version action name are |
| * case insensitive. |
| * |
| * This long identification is needed as a property definition name with its |
| * type may not be unique within a node type. This is not only the case for |
| * residual property definitions. The JCR does not specify that there can be |
| * only one combination of property definition name / required property type |
| * name. Thats the reason why it is qualified like this. |
| * |
| */ |
| |
| @Component |
| @Service(Servlet.class) |
| @Properties({ @Property(name = "service.description", value = "Download Servlet for binary properties"), |
| @Property(name = "service.vendor", value = "Sandro Boehme"), |
| @Property(name = "sling.servlet.selectors", value = "default_binary_value"), |
| @Property(name = "sling.servlet.extensions", value = "bin"), |
| @Property(name = "sling.servlet.resourceTypes", value = "sling/servlet/default") |
| |
| }) |
| public class DownloadDefaultBinaryValueServlet extends SlingSafeMethodsServlet { |
| |
| private static final long serialVersionUID = -1L; |
| |
| /** default log */ |
| private final Logger log = LoggerFactory.getLogger(DownloadDefaultBinaryValueServlet.class); |
| |
| @SuppressWarnings("deprecation") |
| @Override |
| protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException { |
| response.setContentType("application/octet-stream; charset=UTF-8"); |
| String requestURI = request.getRequestURI(); |
| String[] idFields = requestURI.substring(1).split("/"); |
| try { |
| NodeTypeManager nodeTypeManager = request.getResourceResolver().getResource("/").adaptTo(Node.class).getSession() |
| .getWorkspace().getNodeTypeManager(); |
| NodeType nodeType = nodeTypeManager.getNodeType(idFields[0]); |
| PropertyDefinition[] propertyDefinitions = nodeType.getPropertyDefinitions(); |
| List<PropertyDefinition> propertyDefinitionList = Arrays.asList(propertyDefinitions); |
| if (propertyDefinitionList != null) { |
| |
| // Every matcher represents a path element in the URL and is initialized with its value. |
| // It will try to match the value of a path element with the corresponding property |
| // element of all property definitions from the node type in findMatchingPropertyDef(). |
| PropertyMatcher[] propertyMatcher = new PropertyMatcher[] { new PropertyNameMatcher(idFields, 1), |
| new RequiredPropertyTypeMatcher(idFields, 2), new AutoCreatedMatcher(idFields, 3), |
| new MandatoryMatcher(idFields, 4), new ProtectedMatcher(idFields, 5), new MultipleMatcher(idFields, 6), |
| new OnParentVersionMatcher(idFields, 7) }; |
| |
| PropertyDefinition propDef = findMatchingPropertyDef(propertyDefinitionList, new LinkedList<PropertyMatcher>( |
| Arrays.asList(propertyMatcher))); |
| if (propDef != null) { |
| Value[] defaultValues = propDef.getDefaultValues(); |
| if (defaultValues != null && defaultValues.length > 0) { |
| int startIndex = requestURI.lastIndexOf('/') + 1; |
| int endIndex = requestURI.indexOf("default_binary_value.bin") - 1; |
| int defaultValueIndex = 0; |
| if (endIndex - startIndex == 1) { |
| String indexString = requestURI.substring(startIndex, endIndex); |
| defaultValueIndex = Integer.parseInt(indexString); |
| } |
| try { |
| if (defaultValueIndex < defaultValues.length) { |
| Value defaultValue = defaultValues[defaultValueIndex]; |
| InputStream stream = defaultValue.getStream(); |
| BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream)); |
| PrintWriter writer = response.getWriter(); |
| String line = null; |
| while ((line = bufferedReader.readLine()) != null) { |
| writer.write(line); |
| } |
| writer.flush(); |
| writer.close(); |
| response.setStatus(HttpServletResponse.SC_OK); |
| } else { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND); |
| } |
| } catch (NumberFormatException nfe) { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND); |
| } |
| } else { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND); |
| } |
| } else { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND); |
| } |
| } |
| } catch (RepositoryException e) { |
| log.error("Could not return the binary file.", e); |
| throw new ServletException(e); |
| } |
| } |
| |
| /** |
| * This method pulls the first matcher out of the list and iterates over the list of specified property definitions to find matches. |
| * Lets say this is a PropertyNameMatcher that has been initialized with the property name from the URL. Than it will match for every |
| * property definition who's name is equal to the one specified in the URL. The matched property definitions and the rest of the matchers |
| * will be provided for the next recursive call of the method to work through the other path elements until all matchers are processed or |
| * until only one property definition matches. In the first case null is returned and in the second case the identified property definition |
| * is returned. |
| * @param propertyDefinitions The list of property definitions. |
| * @param propertyMatcherList The list of matcher in the order of appearance of their type in the URL. A matcher checks if the |
| * content of a path element it was initialized with matches its corresponding value in the property definition. |
| * @return Returns the property definition that is identified by the URL or null if no property definition matches the values specified in the URL. |
| */ |
| private PropertyDefinition findMatchingPropertyDef(List<PropertyDefinition> propertyDefinitions, |
| List<PropertyMatcher> propertyMatcherList) { |
| if (propertyMatcherList.size() > 0) { |
| // retrieve the matcher to be used for this iteration |
| PropertyMatcher propertyMatcher = propertyMatcherList.get(0); |
| // remove the matcher to make the next matcher available for the |
| // next iteration |
| propertyMatcherList.remove(0); |
| List<PropertyDefinition> matchedPropDefs = new LinkedList<PropertyDefinition>(); |
| // try to match all property definitions with the top matcher |
| for (PropertyDefinition propertyDefinition : propertyDefinitions) { |
| if (propertyMatcher.match(propertyDefinition)) { |
| matchedPropDefs.add(propertyDefinition); |
| } |
| } |
| if (matchedPropDefs.size() == 1) { |
| return matchedPropDefs.get(0); |
| } else if (matchedPropDefs.size() > 1) { |
| return findMatchingPropertyDef(matchedPropDefs, propertyMatcherList); |
| } |
| } |
| return null; |
| } |
| } |