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}