001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.rest.staticfile; 014 015import static org.apache.juneau.collections.JsonMap.*; 016import static org.apache.juneau.http.HttpHeaders.*; 017import static org.apache.juneau.http.HttpResources.*; 018import static org.apache.juneau.internal.CollectionUtils.*; 019import static org.apache.juneau.internal.FileUtils.*; 020import static org.apache.juneau.internal.ObjectUtils.*; 021 022import java.io.*; 023import java.util.*; 024 025import javax.activation.*; 026 027import org.apache.http.*; 028import org.apache.juneau.cp.*; 029import org.apache.juneau.http.resource.*; 030import org.apache.juneau.http.response.*; 031import org.apache.juneau.internal.*; 032import org.apache.juneau.rest.*; 033 034/** 035 * API for retrieving localized static files from either the classpath or file system. 036 * 037 * <p> 038 * Provides the same functionality as {@link BasicFileFinder} but adds support for returning files as {@link HttpResource} 039 * objects with arbitrary headers. 040 * 041 * <h5 class='section'>See Also:</h5><ul> 042 * <li class='link'><a class="doclink" href="../../../../../index.html#jrs.StaticFiles">Static files</a> 043 * </ul> 044 */ 045public class BasicStaticFiles implements StaticFiles { 046 047 private final Header[] headers; 048 private final MimetypesFileTypeMap mimeTypes; 049 private final int hashCode; 050 private final FileFinder fileFinder; 051 052 /** 053 * Creates a new builder for this object. 054 * 055 * @param beanStore The bean store to use for creating beans. 056 * @return A new builder for this object. 057 */ 058 public static StaticFiles.Builder create(BeanStore beanStore) { 059 return new StaticFiles.Builder(beanStore); 060 } 061 062 /** 063 * Constructor. 064 * 065 * @param beanStore The bean store containing injectable beans for this logger. 066 */ 067 public BasicStaticFiles(BeanStore beanStore) { 068 this(StaticFiles 069 .create(beanStore) 070 .type(BasicStaticFiles.class) 071 .dir("static") 072 .dir("htdocs") 073 .cp(beanStore.getBean(ResourceSupplier.class).get().getResourceClass(), "htdocs", true) 074 .cp(beanStore.getBean(ResourceSupplier.class).get().getResourceClass(), "/htdocs", true) 075 .caching(1_000_000) 076 .exclude("(?i).*\\.(class|properties)") 077 .headers(cacheControl("max-age=86400, public")) 078 ); 079 } 080 081 /** 082 * Constructor. 083 * 084 * @param builder The builder object. 085 */ 086 public BasicStaticFiles(StaticFiles.Builder builder) { 087 this.headers = builder.headers.toArray(new Header[builder.headers.size()]); 088 this.mimeTypes = builder.mimeTypes; 089 this.hashCode = HashCode.of(hashCode(), headers); 090 this.fileFinder = builder.fileFinder.build(); 091 } 092 093 /** 094 * Constructor. 095 * 096 * <p> 097 * Can be used when subclassing and overriding the {@link #resolve(String, Locale)} method. 098 */ 099 protected BasicStaticFiles() { 100 this.headers = new Header[0]; 101 this.mimeTypes = null; 102 this.hashCode = HashCode.of(hashCode(), headers); 103 this.fileFinder = null; 104 } 105 106 /** 107 * Resolve the specified path. 108 * 109 * <p> 110 * Subclasses can override this method to provide specialized handling. 111 * 112 * @param path The path to resolve to a static file. 113 * @param locale Optional locale. 114 * @return The resource, or <jk>null</jk> if not found. 115 */ 116 @Override /* StaticFiles */ 117 public Optional<HttpResource> resolve(String path, Locale locale) { 118 try { 119 Optional<InputStream> is = getStream(path, locale); 120 if (! is.isPresent()) 121 return empty(); 122 return optional( 123 streamResource(is.get()) 124 .setHeaders(contentType(mimeTypes == null ? null : mimeTypes.getContentType(getFileName(path)))) 125 .addHeaders(headers) 126 ); 127 } catch (IOException e) { 128 throw new InternalServerError(e); 129 } 130 } 131 132 @Override 133 public int hashCode() { 134 return hashCode; 135 } 136 137 @Override /* Object */ 138 public boolean equals(Object o) { 139 return super.equals(o) && o instanceof BasicStaticFiles && eq(this, (BasicStaticFiles)o, (x,y)->eq(x.headers, y.headers)); 140 } 141 142 @Override /* FileFinder */ 143 public Optional<InputStream> getStream(String name, Locale locale) throws IOException { 144 return fileFinder.getStream(name, locale); 145 } 146 147 @Override /* FileFinder */ 148 public Optional<String> getString(String name, Locale locale) throws IOException { 149 return fileFinder.getString(name, locale); 150 } 151 152 @Override /* Object */ 153 public String toString() { 154 return filteredMap() 155 .append("headers", headers) 156 .asReadableString(); 157 } 158}