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.js;
017
018import java.io.ByteArrayOutputStream;
019import java.io.IOException;
020import java.io.OutputStream;
021import java.io.Serializable;
022import java.nio.charset.StandardCharsets;
023import java.util.Map;
024
025import org.apache.avalon.framework.parameters.Parameters;
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.cocoon.ProcessingException;
029import org.apache.cocoon.environment.ObjectModelHelper;
030import org.apache.excalibur.source.Source;
031import org.apache.excalibur.source.SourceException;
032
033import org.ametys.core.DevMode;
034import org.ametys.core.DevMode.DEVMODE;
035import org.ametys.core.resources.JSResourceHandler;
036
037/**
038 * Resource handler for minimized javascript file
039 */
040public class MinimizedJSResourceHandler extends JSResourceHandler
041{
042    /** JS minimize manager */
043    protected MinimizeJSManager _jSMinimizeManager;
044
045    private String _realFileUri;
046
047    @Override
048    public void service(ServiceManager manager) throws ServiceException
049    {
050        super.service(manager);
051        _jSMinimizeManager = (MinimizeJSManager) manager.lookup(MinimizeJSManager.ROLE);
052    }
053    
054    @Override
055    public Source setup(String location, Map objectModel, Parameters par, boolean readForDownload) throws IOException, ProcessingException
056    {
057        _requestedLocation = location;
058        _objectModel = objectModel;
059        
060        // Cached
061        _setExpiresToNoCache = false;
062
063        Source source = null;
064        try 
065        {
066            source = _resolver.resolveURI(location);
067        } 
068        catch (SourceException e) 
069        {
070            // Nothing
071        }
072        
073        if (source == null || !source.exists())
074        {
075            // If the file does not exists at the original location, but it is a .min.js file, retrieve the .js file instead to process it
076            _resolver.release(source);
077            
078            String realLocation = location.substring(0, location.length() - ".min.js".length()) + ".js";
079            source = super.setup(realLocation, objectModel, par, readForDownload);
080            
081            // The .js location
082            _realFileUri = realLocation;
083        }
084        
085        _source = source;
086        return _source;
087    }
088    
089    @Override
090    public Serializable getKey()
091    {
092        Serializable key = super.getKey();
093        return key + "*" + _requestedLocation;
094    }
095    
096    @Override
097    public void generate(OutputStream out) throws IOException, ProcessingException
098    {
099        if (_realFileUri == null)
100        {
101            _jSMinimizeManager.validateAndOutputMinimizedFile(_source, out, _requestedLocation);
102            return;
103        }
104        
105        // Retrieve the .js file and minimize it to serve a .min.js generated on the fly
106        try (ByteArrayOutputStream bos = new ByteArrayOutputStream())
107        {
108            super.generate(bos);
109            
110            String code = bos.toString("UTF-8");
111            String locale = getLocale();
112            
113            // when lazy loading the source map, wait for the .map to be directly called to generate it
114            boolean lazyLoadSourceMap = DEVMODE.PRODUCTION.equals(DevMode.getDeveloperMode(ObjectModelHelper.getRequest(_objectModel)));
115            String result;
116            if (lazyLoadSourceMap)
117            {
118                result = _jSMinimizeManager.minimizeJS(code, _realFileUri);
119            }
120            else
121            {
122                result = _jSMinimizeManager.minimizeJS(code, _realFileUri, _realFileUri + ".map" + (locale != null ? "#" + locale : ""), _source.getLastModified());
123            }
124
125            out.write(result.getBytes(StandardCharsets.UTF_8));
126        }
127    }
128}