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.repository.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 public static final MimeTypeDetectors DEFAULT_DETECTORS = new MimeTypeDetectors(); 045 046 /** 047 * Class loader factory instance that always returns the {@link Thread#getContextClassLoader() current thread's context class 048 * loader}, or if <code>null</code> the class loader for this class. 049 */ 050 protected static final ClassLoaderFactory DEFAULT_CLASSLOADER_FACTORY = new StandardClassLoaderFactory( 051 MimeTypeDetectors.class.getClassLoader()); 052 053 private final ComponentLibrary<MimeTypeDetector, MimeTypeDetectorConfig> library; 054 private final AtomicReference<Logger> logger; 055 056 public MimeTypeDetectors() { 057 logger = new AtomicReference<Logger>(Logger.getLogger(getClass())); 058 library = new ComponentLibrary<MimeTypeDetector, MimeTypeDetectorConfig>(true); 059 library.setClassLoaderFactory(DEFAULT_CLASSLOADER_FACTORY); 060 } 061 062 /** 063 * Adds the configuration for a MIME-type detector <em>before</em> any previously added configurations, or updates any 064 * existing one that represents the {@link MimeTypeDetectorConfig#equals(Object) same configuration} 065 * 066 * @param config the new configuration; must not be <code>null</code>. 067 * @return <code>true</code> if the detector was added, or <code>false</code> if there already was an existing detector 068 * configuration. 069 * @see #removeDetector(MimeTypeDetectorConfig) 070 */ 071 public boolean addDetector( MimeTypeDetectorConfig config ) { 072 return library.add(config); 073 } 074 075 /** 076 * Gets the class loader factory that should be used to load MIME-type detectors. By default, this service uses a factory that 077 * will return either the {@link Thread#getContextClassLoader() current thread's context class loader}, or if 078 * <code>null</code> the class loader for this class. 079 * 080 * @return the class loader factory; never <code>null</code> 081 * @see #setClassLoaderFactory(ClassLoaderFactory) 082 */ 083 public ClassLoaderFactory getClassLoaderFactory() { 084 return library.getClassLoaderFactory(); 085 } 086 087 /** 088 * Gets the logger for this system 089 * 090 * @return the logger 091 */ 092 public Logger getLogger() { 093 return logger.get(); 094 } 095 096 /** 097 * Returns the first non-null result of iterating over the {@link #addDetector(MimeTypeDetectorConfig) registered} MIME-type 098 * detectors in the reverse order in which they were registered to determine the MIME-type of a data source, using its 099 * supplied content and/or its supplied name, depending upon the implementation. If the MIME-type cannot be determined by any 100 * registered detector, "text/plain" or "application/octet-stream" will be returned, the former only if it is determined the 101 * stream contains no nulls. 102 * 103 * @param name The name of the data source; may be <code>null</code>. 104 * @param content The content of the data source; may be <code>null</code>. 105 * @return The MIME-type of the data source; never <code>null</code>. 106 * @throws IOException If an error occurs reading the supplied content. 107 */ 108 public String mimeTypeOf( String name, 109 InputStream content ) throws IOException { 110 if (content != null && content.markSupported()) { 111 content.mark(Integer.MAX_VALUE); 112 } 113 // Check if registered detectors can determine MIME-type 114 for (MimeTypeDetector detector : library.getInstances()) { 115 String mimeType = detector.mimeTypeOf(name, content); 116 if (mimeType != null) return mimeType; 117 } 118 // If not, try to analyze stream to determine if it represents text or binary content 119 if (content != null && content.markSupported()) { 120 try { 121 content.reset(); 122 for (int chr = content.read(); chr >= 0; chr = content.read()) { 123 if (chr == 0) return "application/octet-stream"; 124 } 125 } catch (IOException meansTooManyBytesRead) { 126 return "application/octet-stream"; 127 } 128 } 129 return "text/plain"; 130 } 131 132 /** 133 * Removes the configuration for a MIME-type detector. 134 * 135 * @param config the configuration to be removed; must not be <code>null</code>. 136 * @return <code>true</code> if the configuration was removed, or <code>false</code> if there was no existing configuration 137 * @see #addDetector(MimeTypeDetectorConfig) 138 */ 139 public boolean removeDetector( MimeTypeDetectorConfig config ) { 140 return library.remove(config); 141 } 142 143 /** 144 * Sets the Maven Repository that should be used to load the MIME-type detectors. By default, this service uses a factory that 145 * will return either the {@link Thread#getContextClassLoader() current thread's context class loader}, or if 146 * <code>null</code> the class loader for this class. 147 * 148 * @param classLoaderFactory the class loader factory, or <code>null</code> if the default class loader factory should be 149 * used. 150 * @see #getClassLoaderFactory() 151 */ 152 public void setClassLoaderFactory( ClassLoaderFactory classLoaderFactory ) { 153 library.setClassLoaderFactory(classLoaderFactory != null ? classLoaderFactory : DEFAULT_CLASSLOADER_FACTORY); 154 } 155 156 /** 157 * Sets the logger for this system. 158 * 159 * @param logger the logger, or <code>null</code> if the standard logging should be used 160 */ 161 public void setLogger( Logger logger ) { 162 this.logger.set(logger != null ? logger : Logger.getLogger(getClass())); 163 } 164 }