001/* 002 * Copyright 2002-2014 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.mail.javamail; 018 019import java.io.File; 020import java.io.IOException; 021import java.io.InputStream; 022import javax.activation.FileTypeMap; 023import javax.activation.MimetypesFileTypeMap; 024 025import org.springframework.beans.factory.InitializingBean; 026import org.springframework.core.io.ClassPathResource; 027import org.springframework.core.io.Resource; 028 029/** 030 * Spring-configurable {@code FileTypeMap} implementation that will read 031 * MIME type to file extension mappings from a standard JavaMail MIME type 032 * mapping file, using a standard {@code MimetypesFileTypeMap} underneath. 033 * 034 * <p>The mapping file should be in the following format, as specified by the 035 * Java Activation Framework: 036 * 037 * <pre class="code"> 038 * # map text/html to .htm and .html files 039 * text/html html htm HTML HTM</pre> 040 * 041 * Lines starting with {@code #} are treated as comments and are ignored. All 042 * other lines are treated as mappings. Each mapping line should contain the MIME 043 * type as the first entry and then each file extension to map to that MIME type 044 * as subsequent entries. Each entry is separated by spaces or tabs. 045 * 046 * <p>By default, the mappings in the {@code mime.types} file located in the 047 * same package as this class are used, which cover many common file extensions 048 * (in contrast to the out-of-the-box mappings in {@code activation.jar}). 049 * This can be overridden using the {@code mappingLocation} property. 050 * 051 * <p>Additional mappings can be added via the {@code mappings} bean property, 052 * as lines that follow the {@code mime.types} file format. 053 * 054 * @author Rob Harrop 055 * @author Juergen Hoeller 056 * @since 1.2 057 * @see #setMappingLocation 058 * @see #setMappings 059 * @see javax.activation.MimetypesFileTypeMap 060 */ 061public class ConfigurableMimeFileTypeMap extends FileTypeMap implements InitializingBean { 062 063 /** 064 * The {@code Resource} to load the mapping file from. 065 */ 066 private Resource mappingLocation = new ClassPathResource("mime.types", getClass()); 067 068 /** 069 * Used to configure additional mappings. 070 */ 071 private String[] mappings; 072 073 /** 074 * The delegate FileTypeMap, compiled from the mappings in the mapping file 075 * and the entries in the {@code mappings} property. 076 */ 077 private FileTypeMap fileTypeMap; 078 079 080 /** 081 * Specify the {@code Resource} from which mappings are loaded. 082 * <p>Needs to follow the {@code mime.types} file format, as specified 083 * by the Java Activation Framework, containing lines such as:<br> 084 * {@code text/html html htm HTML HTM} 085 */ 086 public void setMappingLocation(Resource mappingLocation) { 087 this.mappingLocation = mappingLocation; 088 } 089 090 /** 091 * Specify additional MIME type mappings as lines that follow the 092 * {@code mime.types} file format, as specified by the 093 * Java Activation Framework, for example:<br> 094 * {@code text/html html htm HTML HTM} 095 */ 096 public void setMappings(String... mappings) { 097 this.mappings = mappings; 098 } 099 100 101 /** 102 * Creates the final merged mapping set. 103 */ 104 @Override 105 public void afterPropertiesSet() { 106 getFileTypeMap(); 107 } 108 109 /** 110 * Return the delegate FileTypeMap, compiled from the mappings in the mapping file 111 * and the entries in the {@code mappings} property. 112 * @see #setMappingLocation 113 * @see #setMappings 114 * @see #createFileTypeMap 115 */ 116 protected final FileTypeMap getFileTypeMap() { 117 if (this.fileTypeMap == null) { 118 try { 119 this.fileTypeMap = createFileTypeMap(this.mappingLocation, this.mappings); 120 } 121 catch (IOException ex) { 122 throw new IllegalStateException( 123 "Could not load specified MIME type mapping file: " + this.mappingLocation, ex); 124 } 125 } 126 return this.fileTypeMap; 127 } 128 129 /** 130 * Compile a {@link FileTypeMap} from the mappings in the given mapping file 131 * and the given mapping entries. 132 * <p>The default implementation creates an Activation Framework {@link MimetypesFileTypeMap}, 133 * passing in an InputStream from the mapping resource (if any) and registering 134 * the mapping lines programmatically. 135 * @param mappingLocation a {@code mime.types} mapping resource (can be {@code null}) 136 * @param mappings MIME type mapping lines (can be {@code null}) 137 * @return the compiled FileTypeMap 138 * @throws IOException if resource access failed 139 * @see javax.activation.MimetypesFileTypeMap#MimetypesFileTypeMap(java.io.InputStream) 140 * @see javax.activation.MimetypesFileTypeMap#addMimeTypes(String) 141 */ 142 protected FileTypeMap createFileTypeMap(Resource mappingLocation, String[] mappings) throws IOException { 143 MimetypesFileTypeMap fileTypeMap = null; 144 if (mappingLocation != null) { 145 InputStream is = mappingLocation.getInputStream(); 146 try { 147 fileTypeMap = new MimetypesFileTypeMap(is); 148 } 149 finally { 150 is.close(); 151 } 152 } 153 else { 154 fileTypeMap = new MimetypesFileTypeMap(); 155 } 156 if (mappings != null) { 157 for (String mapping : mappings) { 158 fileTypeMap.addMimeTypes(mapping); 159 } 160 } 161 return fileTypeMap; 162 } 163 164 165 /** 166 * Delegates to the underlying FileTypeMap. 167 * @see #getFileTypeMap() 168 */ 169 @Override 170 public String getContentType(File file) { 171 return getFileTypeMap().getContentType(file); 172 } 173 174 /** 175 * Delegates to the underlying FileTypeMap. 176 * @see #getFileTypeMap() 177 */ 178 @Override 179 public String getContentType(String fileName) { 180 return getFileTypeMap().getContentType(fileName); 181 } 182 183}