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.resources;
017
018import java.io.ByteArrayOutputStream;
019import java.io.IOException;
020import java.io.OutputStream;
021import java.io.Serializable;
022import java.net.URISyntaxException;
023import java.nio.charset.StandardCharsets;
024import java.util.Map;
025
026import org.apache.avalon.framework.parameters.Parameters;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.cocoon.ProcessingException;
030import org.apache.cocoon.environment.ObjectModelHelper;
031import org.apache.cocoon.environment.Request;
032import org.apache.excalibur.source.Source;
033
034import org.ametys.core.DevMode;
035import org.ametys.core.DevMode.DEVMODE;
036import org.ametys.plugins.core.ui.resources.css.CSSFileHelper;
037import org.ametys.plugins.core.ui.resources.css.JSASSResourceURIExtensionPoint;
038
039/**
040 * Resource handler for CSS files
041 */
042public class CssResourceHandler extends ExpiresResourceHandler
043{
044    private ProxiedContextPathProvider _proxiedContextPathProvider;
045    private JSASSResourceURIExtensionPoint _jsassResourceURIExtensionPoint;
046
047    @Override
048    public void service(ServiceManager manager) throws ServiceException
049    {
050        super.service(manager);
051        
052        _proxiedContextPathProvider = (ProxiedContextPathProvider) manager.lookup(ProxiedContextPathProvider.ROLE);
053        _jsassResourceURIExtensionPoint = (JSASSResourceURIExtensionPoint) manager.lookup(JSASSResourceURIExtensionPoint.ROLE);
054    }
055    
056    @Override
057    public Source setup(String location, Map objectModel, Parameters par, Map<String, Object> additionalParameters) throws IOException, ProcessingException
058    {
059        additionalParameters.put("location", location);
060        return super.setup(location, objectModel, par, additionalParameters);
061    }
062    
063    @Override
064    public Serializable getKey(Source source, Map objectModel, Parameters parameters, Map<String, Object> additionalParameters)
065    {
066        // files are cached by source and by context path
067        return super.getKey(source, objectModel, parameters, additionalParameters) + "*" + _proxiedContextPathProvider.getContextPath();
068    }
069    
070    @Override
071    public void generateResource(Source source, OutputStream out, Map objectModel, Parameters parameters, Map<String, Object> additionalParameters) throws IOException, ProcessingException
072    {
073        Request request = ObjectModelHelper.getRequest(objectModel);
074        if (DEVMODE.SUPER_DEVELOPPMENT.equals(DevMode.getDeveloperMode(request)))
075        {
076            // Files are not aggregated, thus don't need to be modified
077            super.generateResource(source, out, objectModel, parameters, additionalParameters);
078            return;
079        }
080        
081        // Replace relative URIs with absolute values, required before aggregating files served through a new location
082        ByteArrayOutputStream baos = new ByteArrayOutputStream();
083        super.generateResource(source, baos, objectModel, parameters, additionalParameters);
084        String content = baos.toString(StandardCharsets.UTF_8.toString());
085        
086        String location = (String) additionalParameters.get("location");
087        try
088        {
089            String locationPath = _jsassResourceURIExtensionPoint.resolve(location);
090            content = CSSFileHelper.replaceRelativeUri(content, locationPath, _jsassResourceURIExtensionPoint, _proxiedContextPathProvider.getContextPath(), _proxiedContextPathProvider.getContextPath());
091        }
092        catch (URISyntaxException e)
093        {
094            throw new ProcessingException("Unable to replace relative URIs inside of css file '" + location + "'", e);
095        }
096        
097        out.write(content.getBytes(StandardCharsets.UTF_8));
098    }
099}