    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}