001// Licensed under the Apache License, Version 2.0 (the "License");
002// you may not use this file except in compliance with the License.
003// You may obtain a copy of the License at
004//
005// http://www.apache.org/licenses/LICENSE-2.0
006//
007// Unless required by applicable law or agreed to in writing, software
008// distributed under the License is distributed on an "AS IS" BASIS,
009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
010// See the License for the specific language governing permissions and
011// limitations under the License.
012
013package org.apache.tapestry5.internal.services.assets;
014
015import org.apache.tapestry5.http.ContentType;
016import org.apache.tapestry5.http.services.Response;
017import org.apache.tapestry5.services.assets.AssetChecksumGenerator;
018import org.apache.tapestry5.services.assets.CompressionStatus;
019import org.apache.tapestry5.services.assets.ResponseCustomizer;
020import org.apache.tapestry5.services.assets.StreamableResource;
021
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.OutputStream;
025import java.util.Objects;
026
027public class StreamableResourceImpl implements StreamableResource
028{
029    protected final String description;
030
031    private final ContentType contentType;
032
033    protected final CompressionStatus compression;
034
035    protected final long lastModified;
036
037    protected final BytestreamCache bytestreamCache;
038
039    protected final AssetChecksumGenerator assetChecksumGenerator;
040
041    protected final ResponseCustomizer responseCustomizer;
042
043    public StreamableResourceImpl(String description, ContentType contentType, CompressionStatus compression, long lastModified, BytestreamCache bytestreamCache, AssetChecksumGenerator assetChecksumGenerator, ResponseCustomizer responseCustomizer)
044    {
045        this.lastModified = lastModified;
046        this.description = description;
047        this.bytestreamCache = bytestreamCache;
048        this.contentType = contentType;
049        this.compression = compression;
050        this.assetChecksumGenerator = assetChecksumGenerator;
051        this.responseCustomizer = responseCustomizer;
052    }
053
054    public String getDescription()
055    {
056        return description;
057    }
058
059    public CompressionStatus getCompression()
060    {
061        return compression;
062    }
063
064    public ContentType getContentType()
065    {
066        return contentType;
067    }
068
069    public int getSize()
070    {
071        return bytestreamCache.size();
072    }
073
074    public long getLastModified()
075    {
076        return lastModified;
077    }
078
079    public void streamTo(OutputStream os) throws IOException
080    {
081        bytestreamCache.writeTo(os);
082    }
083
084    public InputStream openStream() throws IOException
085    {
086        return bytestreamCache.openStream();
087    }
088
089    @Override
090    public String toString()
091    {
092        return String.format("StreamableResource<%s %s %s lastModified: %tc size: %d>", contentType, description, compression.name(),
093                lastModified, getSize());
094    }
095
096    public String getChecksum() throws IOException
097    {
098        // Currently, we rely on AssetChecksumGenerator to manage a cache, but that may be better done
099        // here (but must be threads-afe).
100        return assetChecksumGenerator.generateChecksum(this);
101    }
102
103    @Override
104    public StreamableResource addResponseCustomizer(final ResponseCustomizer customizer)
105    {
106        final ResponseCustomizer oldCustomizer = responseCustomizer;
107
108        if (oldCustomizer == null)
109        {
110            return withNewResourceCustomizer(customizer);
111        }
112
113        return withNewResourceCustomizer(new ResponseCustomizer()
114        {
115            @Override
116            public void customizeResponse(StreamableResource resource, Response response) throws IOException
117            {
118                oldCustomizer.customizeResponse(resource, response);
119                customizer.customizeResponse(resource, response);
120            }
121        });
122    }
123
124    @Override
125    public ResponseCustomizer getResponseCustomizer()
126    {
127        return responseCustomizer;
128    }
129
130    @Override
131    public StreamableResource withContentType(ContentType newContentType)
132    {
133        return new StreamableResourceImpl(description, newContentType, compression, lastModified, bytestreamCache, assetChecksumGenerator, responseCustomizer);
134    }
135
136    private StreamableResourceImpl withNewResourceCustomizer(ResponseCustomizer customizer)
137    {
138        return new StreamableResourceImpl(description, contentType, compression, lastModified, bytestreamCache, assetChecksumGenerator, customizer);
139    }
140
141    @Override
142    public int hashCode() 
143    {
144        return Objects.hash(bytestreamCache.size(), compression, contentType, description, lastModified);
145    }
146
147    @Override
148    public boolean equals(Object obj) 
149    {
150        if (this == obj) 
151        {
152            return true;
153        }
154        if (!(obj instanceof StreamableResourceImpl)) 
155        {
156            return false;
157        }
158        StreamableResourceImpl other = (StreamableResourceImpl) obj;
159        return Objects.equals(bytestreamCache.size(), other.bytestreamCache.size()) && compression == other.compression && Objects.equals(contentType, other.contentType)
160                && Objects.equals(description, other.description) && lastModified == other.lastModified;
161    }
162    
163    
164}