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