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