/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.wicket.request.resource; import java.io.IOException; import java.io.InputStream; import javax.servlet.http.HttpServletResponse; import org.apache.wicket.Application; import org.apache.wicket.util.lang.Bytes; import org.apache.wicket.util.lang.Checks; import org.apache.wicket.util.resource.IResourceStream; import org.apache.wicket.util.resource.IResourceStreamWriter; import org.apache.wicket.util.resource.ResourceStreamNotFoundException; import org.apache.wicket.util.time.Duration; import org.apache.wicket.util.time.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A {@link AbstractResource resource} that loads its data from {@link IResourceStream} */ public class ResourceStreamResource extends AbstractResource { private static final long serialVersionUID = 1L; private static final Logger logger = LoggerFactory.getLogger(ResourceStreamResource.class); private final IResourceStream stream; private String fileName; private ContentDisposition contentDisposition = ContentDisposition.INLINE; private String textEncoding; private Duration cacheDuration; /** * Constructor. */ public ResourceStreamResource() { this(null); } /** * Constructor. * * @param stream * the resource stream to read from */ public ResourceStreamResource(IResourceStream stream) { this.stream = stream; } /** * @param fileName * @return this object, for chaining */ public ResourceStreamResource setFileName(String fileName) { this.fileName = fileName; return this; } /** * @param contentDisposition * @return this object, for chaining */ public ResourceStreamResource setContentDisposition(ContentDisposition contentDisposition) { this.contentDisposition = contentDisposition; return this; } /** * @param textEncoding * @return this object, for chaining */ public ResourceStreamResource setTextEncoding(String textEncoding) { this.textEncoding = textEncoding; return this; } /** * @return the duration for which the resource will be cached by the browser */ public Duration getCacheDuration() { return cacheDuration; } /** * @param cacheDuration * the duration for which the resource will be cached by the browser * @return this object, for chaining */ public ResourceStreamResource setCacheDuration(Duration cacheDuration) { this.cacheDuration = cacheDuration; return this; } /** * Lazy or dynamic initialization of the wrapped IResourceStream(Writer) * * @param attributes * The request attributes * @return the underlying IResourceStream. May be {@code null}. */ protected IResourceStream getResourceStream(Attributes attributes) { return stream; } private IResourceStream internalGetResourceStream(Attributes attributes) { final IResourceStream resourceStream = getResourceStream(attributes); Checks.notNull(resourceStream, "%s#getResourceStream(attributes) should not return null!", getClass().getName()); return resourceStream; } @Override protected ResourceResponse newResourceResponse(Attributes attributes) { final IResourceStream resourceStream = internalGetResourceStream(attributes); ResourceResponse data = new ResourceResponse(); Time lastModifiedTime = resourceStream.lastModifiedTime(); if (lastModifiedTime != null) { data.setLastModified(lastModifiedTime); } if (cacheDuration != null) { data.setCacheDuration(cacheDuration); } // performance check; don't bother to do anything if the resource is still cached by client if (data.dataNeedsToBeWritten(attributes)) { InputStream inputStream = null; if (resourceStream instanceof IResourceStreamWriter == false) { try { inputStream = resourceStream.getInputStream(); } catch (ResourceStreamNotFoundException e) { data.setError(HttpServletResponse.SC_NOT_FOUND); close(resourceStream); } } data.setContentDisposition(contentDisposition); Bytes length = resourceStream.length(); if (length != null) { data.setContentLength(length.bytes()); } data.setFileName(fileName); String contentType = resourceStream.getContentType(); if (contentType == null && fileName != null && Application.exists()) { contentType = Application.get().getMimeType(fileName); } data.setContentType(contentType); data.setTextEncoding(textEncoding); if (resourceStream instanceof IResourceStreamWriter) { data.setWriteCallback(new WriteCallback() { @Override public void writeData(Attributes attributes) throws IOException { ((IResourceStreamWriter)resourceStream).write(attributes.getResponse().getOutputStream()); close(resourceStream); } }); } else { final InputStream s = inputStream; data.setWriteCallback(new WriteCallback() { @Override public void writeData(Attributes attributes) throws IOException { try { writeStream(attributes, s); } finally { close(resourceStream); } } }); } } return data; } private void close(IResourceStream stream) { try { stream.close(); } catch (IOException e) { logger.error("Couldn't close ResourceStream", e); } } }