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 super(); 101 this.headers = new Header[0]; 102 this.mimeTypes = null; 103 this.hashCode = HashCode.of(hashCode(), headers); 104 this.fileFinder = null; 105 } 106 107 /** 108 * Resolve the specified path. 109 * 110 * <p> 111 * Subclasses can override this method to provide specialized handling. 112 * 113 * @param path The path to resolve to a static file. 114 * @param locale Optional locale. 115 * @return The resource, or <jk>null</jk> if not found. 116 */ 117 @Override /* StaticFiles */ 118 public Optional<HttpResource> resolve(String path, Locale locale) { 119 try { 120 Optional<InputStream> is = getStream(path, locale); 121 if (! is.isPresent()) 122 return empty(); 123 return optional( 124 streamResource(is.get()) 125 .setHeaders(contentType(mimeTypes == null ? null : mimeTypes.getContentType(getFileName(path)))) 126 .addHeaders(headers) 127 ); 128 } catch (IOException e) { 129 throw new InternalServerError(e); 130 } 131 } 132 133 @Override 134 public int hashCode() { 135 return hashCode; 136 } 137 138 @Override /* Object */ 139 public boolean equals(Object o) { 140 return super.equals(o) && o instanceof BasicStaticFiles && eq(this, (BasicStaticFiles)o, (x,y)->eq(x.headers, y.headers)); 141 } 142 143 @Override /* FileFinder */ 144 public Optional<InputStream> getStream(String name, Locale locale) throws IOException { 145 return fileFinder.getStream(name, locale); 146 } 147 148 @Override /* FileFinder */ 149 public Optional<String> getString(String name, Locale locale) throws IOException { 150 return fileFinder.getString(name, locale); 151 } 152 153 @Override /* Object */ 154 public String toString() { 155 return filteredMap() 156 .append("headers", headers) 157 .asReadableString(); 158 } 159}