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.collections.JsonMap.*; 020import static org.apache.juneau.http.HttpHeaders.*; 021import static org.apache.juneau.http.HttpResources.*; 022import static org.apache.juneau.internal.FileUtils.*; 023 024import java.io.*; 025import java.util.*; 026 027import org.apache.http.*; 028import org.apache.juneau.common.utils.*; 029import org.apache.juneau.cp.*; 030import org.apache.juneau.http.resource.*; 031import org.apache.juneau.http.response.*; 032import org.apache.juneau.internal.*; 033import org.apache.juneau.rest.*; 034 035import jakarta.activation.*; 036 037/** 038 * API for retrieving localized static files from either the classpath or file system. 039 * 040 * <p> 041 * Provides the same functionality as {@link BasicFileFinder} but adds support for returning files as {@link HttpResource} 042 * objects with arbitrary headers. 043 * 044 * <h5 class='section'>See Also:</h5><ul> 045 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/StaticFiles">Static files</a> 046 * </ul> 047 */ 048public class BasicStaticFiles implements StaticFiles { 049 050 private final Header[] headers; 051 private final MimetypesFileTypeMap mimeTypes; 052 private final int hashCode; 053 private final FileFinder fileFinder; 054 055 /** 056 * Creates a new builder for this object. 057 * 058 * @param beanStore The bean store to use for creating beans. 059 * @return A new builder for this object. 060 */ 061 public static StaticFiles.Builder create(BeanStore beanStore) { 062 return new StaticFiles.Builder(beanStore); 063 } 064 065 /** 066 * Constructor. 067 * 068 * @param beanStore The bean store containing injectable beans for this logger. 069 */ 070 public BasicStaticFiles(BeanStore beanStore) { 071 this(StaticFiles 072 .create(beanStore) 073 .type(BasicStaticFiles.class) 074 .dir("static") 075 .dir("htdocs") 076 .cp(beanStore.getBean(ResourceSupplier.class).get().getResourceClass(), "htdocs", true) 077 .cp(beanStore.getBean(ResourceSupplier.class).get().getResourceClass(), "/htdocs", true) 078 .caching(1_000_000) 079 .exclude("(?i).*\\.(class|properties)") 080 .headers(cacheControl("max-age=86400, public")) 081 ); 082 } 083 084 /** 085 * Constructor. 086 * 087 * @param builder The builder object. 088 */ 089 public BasicStaticFiles(StaticFiles.Builder builder) { 090 this.headers = builder.headers.toArray(new Header[builder.headers.size()]); 091 this.mimeTypes = builder.mimeTypes; 092 this.hashCode = HashCode.of(hashCode(), headers); 093 this.fileFinder = builder.fileFinder.build(); 094 } 095 096 /** 097 * Constructor. 098 * 099 * <p> 100 * Can be used when subclassing and overriding the {@link #resolve(String, Locale)} method. 101 */ 102 protected BasicStaticFiles() { 103 this.headers = new Header[0]; 104 this.mimeTypes = null; 105 this.hashCode = HashCode.of(hashCode(), headers); 106 this.fileFinder = null; 107 } 108 109 /** 110 * Resolve the specified path. 111 * 112 * <p> 113 * Subclasses can override this method to provide specialized handling. 114 * 115 * @param path The path to resolve to a static file. 116 * @param locale Optional locale. 117 * @return The resource, or <jk>null</jk> if not found. 118 */ 119 @Override /* StaticFiles */ 120 public Optional<HttpResource> resolve(String path, Locale locale) { 121 try { 122 Optional<InputStream> is = getStream(path, locale); 123 if (! is.isPresent()) 124 return Utils.opte(); 125 return Utils.opt(streamResource(is.get()) 126 .setHeaders(contentType(mimeTypes == null ? null : mimeTypes.getContentType(getFileName(path)))) 127 .addHeaders(headers)); 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 && Utils.eq(this, (BasicStaticFiles)o, (x,y)->Utils.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}