001/* 002 * Copyright 2018 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.core.minimize.css; 017 018import java.io.ByteArrayOutputStream; 019import java.io.IOException; 020import java.io.InputStream; 021import java.io.OutputStream; 022import java.io.Serializable; 023import java.net.URISyntaxException; 024import java.nio.charset.StandardCharsets; 025import java.util.Map; 026 027import org.apache.avalon.framework.parameters.Parameters; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.cocoon.ProcessingException; 031import org.apache.cocoon.environment.ObjectModelHelper; 032import org.apache.commons.io.IOUtils; 033import org.apache.commons.lang3.StringUtils; 034import org.apache.excalibur.source.Source; 035import org.apache.excalibur.source.SourceException; 036 037import org.ametys.core.DevMode; 038import org.ametys.core.DevMode.DEVMODE; 039import org.ametys.core.resources.DefaultResourceHandler; 040import org.ametys.plugins.core.ui.resources.css.JSASSResourceURIExtensionPoint; 041 042/** 043 * Resource handler for minimized css 044 */ 045public class MinimizedCSSResourceHandler extends DefaultResourceHandler 046{ 047 /** JS minimize manager */ 048 protected MinimizeCSSManager _cssMinimizeManager; 049 private JSASSResourceURIExtensionPoint _jsassResourceURIExtensionPoint; 050 051 private String _minimizedUri; 052 private String _originalUri; 053 054 @Override 055 public void service(ServiceManager manager) throws ServiceException 056 { 057 super.service(manager); 058 059 _cssMinimizeManager = (MinimizeCSSManager) manager.lookup(MinimizeCSSManager.ROLE); 060 _jsassResourceURIExtensionPoint = (JSASSResourceURIExtensionPoint) manager.lookup(JSASSResourceURIExtensionPoint.ROLE); 061 } 062 063 @Override 064 public Source setup(String location, Map objectModel, Parameters par, boolean readForDownload) throws IOException, ProcessingException 065 { 066 // The .min.css location 067 _minimizedUri = location; 068 069 Source source = null; 070 try 071 { 072 source = _resolver.resolveURI(location); 073 } 074 catch (SourceException e) 075 { 076 // Nothing 077 } 078 079 if (source == null || !source.exists()) 080 { 081 // If the file does not exists at the original location, but it is a .min.css file, retrieve the .css file instead to process it 082 _resolver.release(source); 083 084 String sitemapURI = "/" + StringUtils.removeEnd(ObjectModelHelper.getRequest(objectModel).getSitemapURI(), ".min.css") + ".css"; 085 086 source = super.setup("cocoon:" + sitemapURI, objectModel, par, readForDownload); 087 088 // The .css location 089 _originalUri = sitemapURI; 090 } 091 092 _source = source; 093 return _source; 094 } 095 096 @Override 097 public Serializable getKey() 098 { 099 Serializable key = super.getKey(); 100 return key + "*" + _minimizedUri; 101 } 102 103 @Override 104 public void generate(OutputStream out) throws IOException, ProcessingException 105 { 106 if (_originalUri == null) 107 { 108 _cssMinimizeManager.validateAndOutputMinimizedFile(_source, out, _minimizedUri); 109 return; 110 } 111 112 // Retrieve the .css file and minimize it to serve a .min.css generated on the fly 113 try (InputStream is = _source.getInputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream()) 114 { 115 IOUtils.copy(is, bos); 116 String code = bos.toString("UTF-8"); 117 118 // when lazy loading the source map, wait for the .map to be directly called to generate it 119 boolean lazyLoadSourceMap = DEVMODE.PRODUCTION.equals(DevMode.getDeveloperMode(ObjectModelHelper.getRequest(_objectModel))); 120 121 String resolvedOriginalUri = null; 122 try 123 { 124 resolvedOriginalUri = _jsassResourceURIExtensionPoint.resolve(_originalUri); 125 } 126 catch (URISyntaxException e) 127 { 128 getLogger().error("Unable to resolve URI location because URI syntax '" + _originalUri + "' is not supported", e); 129 } 130 131 String result; 132 if (resolvedOriginalUri == null || lazyLoadSourceMap) 133 { 134 result = _cssMinimizeManager.minimizeCss(code, resolvedOriginalUri); 135 } 136 else 137 { 138 result = _cssMinimizeManager.minimizeCss(code, _originalUri, resolvedOriginalUri + ".map", _source.getLastModified()); 139 } 140 141 out.write(result.getBytes(StandardCharsets.UTF_8)); 142 } 143 } 144}