001 /* 002 * JBoss DNA (http://www.jboss.org/dna) 003 * See the COPYRIGHT.txt file distributed with this work for information 004 * regarding copyright ownership. Some portions may be licensed 005 * to Red Hat, Inc. under one or more contributor license agreements. 006 * See the AUTHORS.txt file in the distribution for a full listing of 007 * individual contributors. 008 * 009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA 010 * is licensed to you under the terms of the GNU Lesser General Public License as 011 * published by the Free Software Foundation; either version 2.1 of 012 * the License, or (at your option) any later version. 013 * 014 * JBoss DNA is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 * Lesser General Public License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this software; if not, write to the Free 021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 023 */ 024 package org.jboss.dna.graph.mimetype; 025 026 import java.io.IOException; 027 import java.io.InputStream; 028 import java.util.concurrent.atomic.AtomicReference; 029 import net.jcip.annotations.ThreadSafe; 030 import org.jboss.dna.common.component.ClassLoaderFactory; 031 import org.jboss.dna.common.component.ComponentLibrary; 032 import org.jboss.dna.common.component.StandardClassLoaderFactory; 033 import org.jboss.dna.common.util.Logger; 034 import org.jboss.dna.graph.mimetype.MimeTypeDetector; 035 036 /** 037 * Facility for managing {@link MimeTypeDetectorConfig}s. 038 * 039 * @author jverhaeg 040 */ 041 @ThreadSafe 042 public final class MimeTypeDetectors implements MimeTypeDetector { 043 044 /** 045 * Class loader factory instance that always returns the {@link Thread#getContextClassLoader() current thread's context class 046 * loader}, or if <code>null</code> the class loader for this class. 047 */ 048 protected static final ClassLoaderFactory DEFAULT_CLASSLOADER_FACTORY = new StandardClassLoaderFactory( 049 MimeTypeDetectors.class.getClassLoader()); 050 051 private final ComponentLibrary<MimeTypeDetector, MimeTypeDetectorConfig> library; 052 private final AtomicReference<Logger> logger; 053 054 public MimeTypeDetectors() { 055 logger = new AtomicReference<Logger>(Logger.getLogger(getClass())); 056 library = new ComponentLibrary<MimeTypeDetector, MimeTypeDetectorConfig>(true); 057 library.setClassLoaderFactory(DEFAULT_CLASSLOADER_FACTORY); 058 } 059 060 /** 061 * Adds the configuration for a MIME-type detector <em>before</em> any previously added configurations, or updates any 062 * existing one that represents the {@link MimeTypeDetectorConfig#equals(Object) same configuration} 063 * 064 * @param config the new configuration; must not be <code>null</code>. 065 * @return <code>true</code> if the detector was added, or <code>false</code> if there already was an existing detector 066 * configuration. 067 * @see #removeDetector(MimeTypeDetectorConfig) 068 */ 069 public boolean addDetector( MimeTypeDetectorConfig config ) { 070 return library.add(config); 071 } 072 073 /** 074 * Gets the class loader factory that should be used to load MIME-type detectors. By default, this service uses a factory that 075 * will return either the {@link Thread#getContextClassLoader() current thread's context class loader}, or if 076 * <code>null</code> the class loader for this class. 077 * 078 * @return the class loader factory; never <code>null</code> 079 * @see #setClassLoaderFactory(ClassLoaderFactory) 080 */ 081 public ClassLoaderFactory getClassLoaderFactory() { 082 return library.getClassLoaderFactory(); 083 } 084 085 /** 086 * Gets the logger for this system 087 * 088 * @return the logger 089 */ 090 public Logger getLogger() { 091 return logger.get(); 092 } 093 094 /** 095 * Returns the first non-null result of iterating over the {@link #addDetector(MimeTypeDetectorConfig) registered} MIME-type 096 * detectors in the reverse order in which they were registered to determine the MIME-type of a data source, using its 097 * supplied content and/or its supplied name, depending upon the implementation. If the MIME-type cannot be determined by any 098 * registered detector, "text/plain" or "application/octet-stream" will be returned, the former only if it is determined the 099 * stream contains no nulls. 100 * 101 * @param name The name of the data source; may be <code>null</code>. 102 * @param content The content of the data source; may be <code>null</code>. 103 * @return The MIME-type of the data source; never <code>null</code>. 104 * @throws IOException If an error occurs reading the supplied content. 105 */ 106 public String mimeTypeOf( String name, 107 InputStream content ) throws IOException { 108 if (content != null && content.markSupported()) { 109 content.mark(Integer.MAX_VALUE); 110 } 111 // Check if registered detectors can determine MIME-type 112 for (MimeTypeDetector detector : library.getInstances()) { 113 String mimeType = detector.mimeTypeOf(name, content); 114 if (mimeType != null) return mimeType; 115 } 116 // If not, try to analyze stream to determine if it represents text or binary content 117 if (content != null && content.markSupported()) { 118 try { 119 content.reset(); 120 for (int chr = content.read(); chr >= 0; chr = content.read()) { 121 if (chr == 0) return "application/octet-stream"; 122 } 123 } catch (IOException meansTooManyBytesRead) { 124 return "application/octet-stream"; 125 } 126 } 127 return "text/plain"; 128 } 129 130 /** 131 * Removes the configuration for a MIME-type detector. 132 * 133 * @param config the configuration to be removed; must not be <code>null</code>. 134 * @return <code>true</code> if the configuration was removed, or <code>false</code> if there was no existing configuration 135 * @see #addDetector(MimeTypeDetectorConfig) 136 */ 137 public boolean removeDetector( MimeTypeDetectorConfig config ) { 138 return library.remove(config); 139 } 140 141 /** 142 * Sets the Maven Repository that should be used to load the MIME-type detectors. By default, this service uses a factory that 143 * will return either the {@link Thread#getContextClassLoader() current thread's context class loader}, or if 144 * <code>null</code> the class loader for this class. 145 * 146 * @param classLoaderFactory the class loader factory, or <code>null</code> if the default class loader factory should be 147 * used. 148 * @see #getClassLoaderFactory() 149 */ 150 public void setClassLoaderFactory( ClassLoaderFactory classLoaderFactory ) { 151 library.setClassLoaderFactory(classLoaderFactory != null ? classLoaderFactory : DEFAULT_CLASSLOADER_FACTORY); 152 } 153 154 /** 155 * Sets the logger for this system. 156 * 157 * @param logger the logger, or <code>null</code> if the standard logging should be used 158 */ 159 public void setLogger( Logger logger ) { 160 this.logger.set(logger != null ? logger : Logger.getLogger(getClass())); 161 } 162 }