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