001/*
002 *  Copyright 2010 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.cms.content;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.io.Serializable;
021import java.net.URLDecoder;
022import java.util.Arrays;
023import java.util.Collection;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.avalon.framework.parameters.ParameterException;
029import org.apache.avalon.framework.parameters.Parameters;
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.avalon.framework.service.Serviceable;
033import org.apache.cocoon.ProcessingException;
034import org.apache.cocoon.caching.CacheableProcessingComponent;
035import org.apache.cocoon.environment.ObjectModelHelper;
036import org.apache.cocoon.environment.Request;
037import org.apache.cocoon.environment.Response;
038import org.apache.cocoon.environment.SourceResolver;
039import org.apache.cocoon.reading.ServiceableReader;
040import org.apache.commons.io.IOUtils;
041import org.apache.commons.lang.StringUtils;
042import org.apache.excalibur.source.SourceValidity;
043import org.xml.sax.SAXException;
044
045import org.ametys.cms.repository.Content;
046import org.ametys.core.util.ImageHelper;
047import org.ametys.core.util.cocoon.AbstractResourceReader;
048import org.ametys.core.util.cocoon.InvalidSourceValidity;
049import org.ametys.plugins.repository.AmetysObjectResolver;
050import org.ametys.plugins.repository.metadata.BinaryMetadata;
051import org.ametys.plugins.repository.metadata.CompositeMetadata;
052import org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType;
053import org.ametys.plugins.repository.metadata.MetadataAwareAmetysObject;
054import org.ametys.plugins.repository.version.VersionableAmetysObject;
055
056/**
057 * Reader for binary or file metadata.
058 */
059public class FileReader extends AbstractResourceReader implements Serviceable, CacheableProcessingComponent
060{
061    /** The ametys object resolver. */
062    protected AmetysObjectResolver _resolver;
063    
064    private MetadataAwareAmetysObject _ametysObject;
065    private BinaryMetadata _binary;
066    
067    private String _path;
068    
069    @Override
070    public void service(ServiceManager serviceManager) throws ServiceException
071    {
072        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
073    }
074    
075    @Override
076    public void doSetup(SourceResolver res, Map objModel, String src, Parameters par) throws ProcessingException, IOException
077    {
078        _ametysObject = getAmetysObject();
079        
080        CompositeMetadata metadata = _ametysObject.getMetadataHolder();
081        
082        try
083        {
084            _path = parameters.getParameter("path");
085        }
086        catch (ParameterException e)
087        {
088            throw new ProcessingException("The path parameter is mandatory for reading binary metadata.", e);
089        }
090        
091        List<String> pathElements = Arrays.asList(_path.split("/"));
092        
093        Iterator<String> it = pathElements.iterator();
094        
095        while (it.hasNext())
096        {
097            String pathElement = it.next();
098            
099            if (it.hasNext())
100            {
101                // not the last segment : it is a composite
102                metadata = metadata.getCompositeMetadata(URLDecoder.decode(pathElement, "UTF-8"));
103            }
104            else
105            {
106                String metadataName = URLDecoder.decode(pathElement, "UTF-8");
107                if (metadata.getType(metadataName) != MetadataType.BINARY)
108                {
109                    throw new UnsupportedOperationException("Only binary metadata are allowed");
110                }
111                
112                _binary = metadata.getBinaryMetadata(metadataName);
113            }
114        }
115    }
116
117    /**
118     * Get the ametys object.
119     * @return the ametys object.
120     * @throws ProcessingException if the information provided was insufficient to resolve an object.
121     */
122    protected MetadataAwareAmetysObject getAmetysObject() throws ProcessingException
123    {
124        MetadataAwareAmetysObject object = null;
125        
126        Request request = ObjectModelHelper.getRequest(objectModel);
127        String objectPath = parameters.getParameter("objectPath", request.getParameter("objectPath"));
128        String objectId = parameters.getParameter("objectId", request.getParameter("objectId"));
129        // Legacy parameter name.
130        if (StringUtils.isBlank(objectId))
131        {
132            objectId = parameters.getParameter("contentId", request.getParameter("contentId"));
133        }
134        
135        if (StringUtils.isNotBlank(objectId))
136        {
137            object = _resolver.resolveById(objectId);
138        }
139        else if (StringUtils.isNotBlank(objectPath))
140        {
141            objectPath = objectPath.replaceAll("%3A", ":");
142            object = _resolver.resolveByPath(objectPath);
143        }
144        else
145        {
146            object = (Content) request.getAttribute(Content.class.getName());
147        }
148        
149        if (object == null)
150        {
151            throw new ProcessingException("The object ID parameter is mandatory for reading binary metadata.");
152        }
153        
154        String revision = parameters.getParameter("contentVersion", null);
155        if (StringUtils.isNotEmpty(revision) && object instanceof VersionableAmetysObject)
156        {
157            ((VersionableAmetysObject) object).switchToRevision(revision);
158        }
159        
160        return object;
161    }
162    
163    @Override
164    protected InputStream getInputStream()
165    {
166        return _binary.getInputStream();
167    }
168    
169    @Override
170    protected String getFilename()
171    {
172        return _binary.getFilename();
173    }
174    
175    @Override
176    protected String getEncodedFilename()
177    {
178        return null;
179    }
180    
181    @Override
182    protected long getLength()
183    {
184        return _binary.getLength();
185    }
186
187    @Override
188    public Serializable getKey()
189    {
190        return _path + getKeySuffix();
191    }
192
193    @Override
194    public SourceValidity getValidity()
195    {
196        return new InvalidSourceValidity();
197    }
198    
199    @Override
200    public long getLastModified()
201    {
202        if (_binary != null)
203        {
204            return _binary.getLastModified().getTime();
205        }
206        
207        return super.getLastModified();
208    }
209    
210    @Override
211    public String getMimeType()
212    {
213        if (_binary != null)
214        {
215            return _binary.getMimeType();
216        }
217
218        return super.getMimeType();
219    }
220
221    @Override
222    public void recycle()
223    {
224        super.recycle();
225        
226        _binary = null;
227        _ametysObject = null;
228    }
229}