/* * #%L * ACS AEM Tools Bundle * %% * Copyright (C) 2016 Adobe * %% * 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.adobe.acs.tools.clientlib_optimizer.impl; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.ServletException; import com.adobe.granite.ui.clientlibs.ClientLibrary; import com.adobe.granite.ui.clientlibs.HtmlLibraryManager; import com.adobe.granite.ui.clientlibs.LibraryType; import org.apache.commons.lang.StringUtils; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.sling.SlingServlet; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.request.RequestParameter; import org.apache.sling.api.servlets.SlingSafeMethodsServlet; import org.apache.sling.commons.json.JSONArray; import org.apache.sling.commons.json.JSONException; import org.apache.sling.commons.json.JSONObject; @SlingServlet( label = "ACS AEM Tools - ClientLibrary Optimizer Servlet", description = "...", methods = { "GET" }, resourceTypes = { "acs-tools/components/clientlibs-optimizer" }, selectors = { "optimize" }, extensions = { "json" } ) public class ClientLibOptimizerServlet extends SlingSafeMethodsServlet {; private static final String PARAM_LIBRARY_TYPE_CSS = "css"; private static final String PARAM_LIBRARY_TYPE_JS = "js"; private static final String PARAM_CATEGORIES = "categories"; @Reference private HtmlLibraryManager htmlLibraryManager; @Override protected final void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException { final Map<LibraryType, Boolean> types = new HashMap<LibraryType, Boolean>(); types.put(LibraryType.JS, this.hasLibraryTypeParam(request, PARAM_LIBRARY_TYPE_JS)); types.put(LibraryType.CSS, this.hasLibraryTypeParam(request, PARAM_LIBRARY_TYPE_CSS)); final List<String> categories = this.getCategories(this.getCategoriesParam(request), types); try { this.writeJsonResponse(categories, response); } catch (JSONException e) { throw new ServletException("Error constructing valid JSON response."); } } private Set<String> getCategoriesParam(final SlingHttpServletRequest request) { final LinkedHashSet<String> categories = new LinkedHashSet<String>(); final RequestParameter requestParameter = request.getRequestParameter(PARAM_CATEGORIES); if (requestParameter != null) { final String[] segments = StringUtils.split(requestParameter.getString(), ","); for (final String segment : segments) { if (StringUtils.isNotBlank(segment)) { categories.add(StringUtils.stripToEmpty(segment)); } } } return categories; } private boolean hasLibraryTypeParam(final SlingHttpServletRequest request, final String paramLibraryType) { final RequestParameter requestParameter = request.getRequestParameter(paramLibraryType); if (requestParameter != null) { return Boolean.parseBoolean(requestParameter.getString()); } return false; } private void writeJsonResponse(final List<String> categories, final SlingHttpServletResponse response) throws JSONException, IOException { final JSONObject jsonObject = new JSONObject(); jsonObject.put("categories", new JSONArray(categories)); response.getWriter().print(jsonObject.toString()); } private List<String> getSortedDependentCategories(Set<String> originalCategories, LibraryType type, List<String> existingCategories) { final Collection<ClientLibrary> libraries = htmlLibraryManager.getLibraries( originalCategories.toArray(new String[0]), null, // always request all types (to also consider transitive embeds/dependencies of the required type) true, false); return getSortedDependentCategories(libraries, originalCategories, type, existingCategories); } static List<String> getSortedDependentCategories(Collection<ClientLibrary> libraries, Set<String> requestedCategories, LibraryType type, List<String> existingCategories) { // sort libraries by path name List<ClientLibrary> sortedLibraries = new ArrayList<ClientLibrary>(libraries); Collections.sort(sortedLibraries, new ClientLibraryPathComparator()); for (ClientLibrary library : libraries) { int index = existingCategories.isEmpty() ? 0 : existingCategories.size(); ClientLibraryDependency dependency = new ClientLibraryDependency(null, library, requestedCategories, false, type); // don't give out all categories but only the requested ones and all dependent ones! existingCategories = dependency.buildDependencyTree(existingCategories, index); } return existingCategories; } // see https://github.com/Adobe-Consulting-Services/acs-aem-tools/pull/47 for a discussion around that // https://github.com/Adobe-Consulting-Services/acs-aem-tools/issues/12 private List<String> getCategories(Set<String> originalCategories, Map<LibraryType, Boolean> types) { List<String> categories = new ArrayList<String>(); if (types.get(LibraryType.JS)) { categories = getSortedDependentCategories(originalCategories, LibraryType.JS, categories); } if (types.get(LibraryType.CSS)) { categories = getSortedDependentCategories(originalCategories, LibraryType.CSS, categories); } return categories; } }