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.plugins.repository.workspace;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.util.Map;
021
022import org.apache.avalon.framework.parameters.ParameterException;
023import org.apache.avalon.framework.parameters.Parameters;
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.avalon.framework.service.Serviceable;
027import org.apache.cocoon.ProcessingException;
028import org.apache.cocoon.environment.ObjectModelHelper;
029import org.apache.cocoon.environment.Response;
030import org.apache.cocoon.environment.SourceResolver;
031import org.apache.cocoon.reading.AbstractReader;
032import org.apache.cocoon.reading.Reader;
033import org.apache.commons.io.IOUtils;
034import org.apache.commons.lang.StringUtils;
035import org.apache.excalibur.source.SourceUtil;
036import org.xml.sax.SAXException;
037
038import org.ametys.plugins.repository.AmetysObject;
039import org.ametys.plugins.repository.AmetysObjectResolver;
040import org.ametys.plugins.repository.metadata.BinaryMetadata;
041import org.ametys.plugins.repository.metadata.CompositeMetadata;
042import org.ametys.plugins.repository.metadata.File;
043import org.ametys.plugins.repository.metadata.Folder;
044import org.ametys.plugins.repository.metadata.ModifiableMetadataAwareAmetysObject;
045import org.ametys.plugins.repository.metadata.RichText;
046
047/**
048 * {@link Reader} for exposing a binary property from a JCR repository.<br>
049 * Mime type can be forced by using parameter <code>mime-type</code>.
050 */
051public class BinaryMetadataReader extends AbstractReader implements Serviceable
052{
053    private AmetysObjectResolver _resolver;
054    private String _mimeType;
055    private String _mdName;
056    private String _compositePath;
057    private String _richtextPath;
058    private String _mdType;
059    private String _aoId;
060    
061    public void service(ServiceManager manager) throws ServiceException
062    {
063        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
064    }
065    
066    @Override
067    public void setup(SourceResolver res, Map objModel, String src, Parameters params) throws ProcessingException, SAXException, IOException
068    {
069        super.setup(res, objModel, src, params);
070        
071        try
072        {
073            _mimeType = params.getParameter("mime-type");
074            _mdName = params.getParameter("name");
075            _compositePath = params.getParameter("compositePath");
076            _richtextPath = params.getParameter("richtextPath");
077            _mdType = params.getParameter("type");
078            _aoId = params.getParameter("id");
079        }
080        catch (ParameterException e)
081        {
082            throw new ProcessingException(e);
083        }
084    }
085    
086    @Override
087    public String getMimeType()
088    {
089        if (!StringUtils.isEmpty(_mimeType))
090        {
091            return _mimeType;
092        }
093        
094        return _getMimeType();
095    }
096
097    @SuppressWarnings("resource")
098    public void generate() throws IOException, SAXException, ProcessingException
099    {
100        try
101        {
102            String filename = null;
103            long length = 0;
104            
105            AmetysObject ao = _getAmetysObject();
106            
107            if (getLogger().isInfoEnabled())
108            {
109                getLogger().info("Trying to read a binary for object of id: '" + _aoId + "'");
110            }
111            
112            CompositeMetadata holder = _getCompositeMetadata (ao);
113            InputStream is = null;
114            
115            if (_mdType.equals("RICHTEXT-CONTENT"))
116            {            
117                String[] rtTokens = _richtextPath.split("/");
118                RichText rt = holder.getRichText(rtTokens[0]);
119                
120                length = rt.getLength();
121                filename = rtTokens[0];
122                is = rt.getInputStream();
123            }
124            else if (_mdType.equals("RICHTEXT"))
125            {
126                RichText rt = holder.getRichText(_mdName);
127                
128                length = rt.getLength();
129                filename = _mdName;
130                is = rt.getInputStream();
131            }
132            else if (_mdType.equals("BINARY"))
133            {
134                BinaryMetadata binaryMetadata = holder.getBinaryMetadata(_mdName);
135                filename = binaryMetadata.getFilename();
136                is = binaryMetadata.getInputStream();
137                length = binaryMetadata.getLength();
138            }
139            else if (_mdType.equals("FILE-CONTENT"))
140            {
141                //TODO this case is not tested
142                String[] rtTokens = _richtextPath.split("/");
143                RichText rt = holder.getRichText(rtTokens[0]);
144                length = rt.getLength();
145                
146                Folder folder = rt.getAdditionalDataFolder();            
147                for (int i = 2; i < rtTokens.length - 1; i++)
148                {
149                    folder = folder.getFolder(rtTokens[i]);
150                }
151                
152                File file = folder.getFile(rtTokens[rtTokens.length - 1]);
153                filename = file.getName();
154                is = file.getResource().getInputStream();
155            }
156
157
158            Response response = ObjectModelHelper.getResponse(objectModel);
159            
160            response.setHeader("Content-Length", Long.toString(length));
161            response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
162            
163            SourceUtil.copy(is, out);
164        }
165        catch (Exception e)
166        {
167            throw new ProcessingException("Unable to download binary property of path " + _mdName, e);
168        }
169        finally
170        {
171            IOUtils.closeQuietly(out);
172        }
173    }
174    
175    private CompositeMetadata _getCompositeMetadata (AmetysObject ao)
176    {
177        CompositeMetadata holder = ((ModifiableMetadataAwareAmetysObject) ao).getMetadataHolder();
178        
179        // deep search if inside composite metadata
180        if (!_compositePath.isEmpty())
181        {
182            String[] tokens = _compositePath.split("/"); 
183            for (String token : tokens)
184            {
185                holder = holder.getCompositeMetadata(token);
186            }
187        }  
188        
189        return holder;
190    }
191    
192    private String _getMimeType ()
193    {
194        AmetysObject ao = _getAmetysObject();
195        CompositeMetadata holder = _getCompositeMetadata (ao);
196        
197        if (_mdType.equals("RICHTEXT-CONTENT"))
198        {            
199            String[] rtTokens = _richtextPath.split("/");
200            RichText rt = holder.getRichText(rtTokens[0]);
201            return rt.getMimeType();
202        }
203        else if (_mdType.equals("RICHTEXT"))
204        {
205            return holder.getRichText(_mdName).getMimeType();
206        }
207        else if (_mdType.equals("BINARY"))
208        {
209            return holder.getBinaryMetadata(_mdName).getMimeType();
210        }
211        else if (_mdType.equals("FILE-CONTENT"))
212        {
213            String[] rtTokens = _richtextPath.split("/");
214            RichText rt = holder.getRichText(rtTokens[0]);
215            
216            Folder folder = rt.getAdditionalDataFolder();            
217            for (int i = 2; i < rtTokens.length - 1; i++)
218            {
219                folder = folder.getFolder(rtTokens[i]);
220            }
221            
222            File file = folder.getFile(rtTokens[rtTokens.length - 1]);
223            return file.getResource().getMimeType();
224        }
225        
226        return "application/unknown";
227    }
228    
229    private AmetysObject _getAmetysObject ()
230    {
231        if (_aoId.equals("/"))
232        {
233            return _resolver.resolveByPath("/");
234        }
235        else
236        {
237            return _resolver.resolveById(_aoId);
238        }
239    }
240}