View Javadoc
1   /**
2    * This file Copyright (c) 2003-2014 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.cms.beans.config;
35  
36  import info.magnolia.cms.core.Content;
37  import info.magnolia.cms.core.HierarchyManager;
38  import info.magnolia.cms.core.ItemType;
39  import info.magnolia.cms.util.NodeDataUtil;
40  import info.magnolia.cms.util.ObservationUtil;
41  import info.magnolia.context.MgnlContext;
42  import info.magnolia.repository.RepositoryConstants;
43  
44  import java.util.Collection;
45  import java.util.Hashtable;
46  import java.util.Iterator;
47  import java.util.Map;
48  
49  import javax.jcr.PathNotFoundException;
50  import javax.jcr.RepositoryException;
51  import javax.jcr.observation.EventIterator;
52  import javax.jcr.observation.EventListener;
53  
54  import org.apache.commons.lang.StringUtils;
55  import org.slf4j.Logger;
56  import org.slf4j.LoggerFactory;
57  
58  /**
59   * Manages mappings of file extensions with their MIME types and icon.
60   *
61   * @author Sameer Charles
62   * @version 1.1
63   */
64  public class MIMEMapping {
65      private static final Logger log = LoggerFactory.getLogger(MIMEMapping.class);
66  
67      public static final String ICONS_PATH = "/.resources/file-icons/"; //$NON-NLS-1$
68      public static final String DEFAULT_ICON = ICONS_PATH + "general.png";
69      private static final String NODEPATH = "/server/MIMEMapping"; //$NON-NLS-1$
70  
71      private static Map<String, MIMEMappingItem> cachedContent = new Hashtable<String, MIMEMappingItem>();
72      private static final String DEFAULT_CHAR_ENCODING = "UTF-8";
73  
74      /**
75       * Used to keep the configuration in memory.
76       */
77      protected static class MIMEMappingItem {
78  
79          protected String ext;
80  
81          protected String mime;
82  
83          protected String icon;
84      }
85  
86  
87      /**
88       * Utility class, don't instantiate.
89       */
90      private MIMEMapping() {
91          // unused
92      }
93  
94      /**
95       * Reads all configured mime mapping (config/server/MIMEMapping).
96       */
97      public static void init() {
98          log.info("Initializing MIMEMapping from {}", NODEPATH);
99          load();
100         registerEventListener();
101     }
102 
103     /**
104      * Reads all configured mime mapping (config/server/MIMEMapping).
105      */
106     public static void load() {
107         MIMEMapping.cachedContent.clear();
108         try {
109             final HierarchyManager hm = MgnlContext.getSystemContext().getHierarchyManager(RepositoryConstants.CONFIG);
110 
111             Collection<Content> mimeList = hm.getContent(NODEPATH).getChildren(ItemType.CONTENTNODE);
112             MIMEMapping.cacheContent(mimeList);
113             log.debug("MIMEMapping loaded from {}", NODEPATH); //$NON-NLS-1$
114         } catch (PathNotFoundException e) {
115             log.warn("No MIMEMapping info configured at {}", NODEPATH); //$NON-NLS-1$
116         }catch (RepositoryException e) {
117             log.error("Failed to load MIMEMapping: " + e.getMessage(), e);
118         }
119     }
120 
121     public static void reload() {
122         log.info("Reloading MIMEMapping from {}", NODEPATH);
123         MIMEMapping.load();
124     }
125 
126     /**
127      * Register an event listener: reload cache configuration when something changes.
128      */
129     private static void registerEventListener() {
130         log.info("Registering event listener for MIMEMapping"); //$NON-NLS-1$
131 
132         ObservationUtil.registerChangeListener(RepositoryConstants.CONFIG, NODEPATH, new EventListener() {
133             @Override
134             public void onEvent(EventIterator iterator) {
135                 // reload everything
136                 reload();
137             }
138         });
139     }
140 
141     /**
142      * Cache all MIME types configured.
143      */
144     private static void cacheContent(Collection<Content> mimeList) {
145         Iterator<Content> iterator = mimeList.iterator();
146         while (iterator.hasNext()) {
147             Content c = iterator.next();
148             try {
149                 MIMEMappingItem item = new MIMEMappingItem();
150                 item.ext = NodeDataUtil.getString(c, "extension", c.getName());//$NON-NLS-1$
151                 item.mime = c.getNodeData("mime-type").getString();//$NON-NLS-1$
152                 item.icon = NodeDataUtil.getString(c, "icon");
153 
154                 MIMEMapping.cachedContent.put(item.ext, item);
155             }
156             catch (Exception e) {
157                 log.error("Failed to cache MIMEMapping"); //$NON-NLS-1$
158             }
159         }
160     }
161 
162     /**
163      * Get MIME type String.
164      * @param key extension for which MIME type is requested
165      * @return MIME type
166      */
167     public static String getMIMEType(String key) {
168         if (StringUtils.isEmpty(key)) {
169             return StringUtils.EMPTY;
170         }
171         // check that the cached content contains the key first to avoid NPE when accessing 'mime'
172         String loweredKey = key.toLowerCase();
173         if (MIMEMapping.cachedContent.containsKey(loweredKey)) {
174             return MIMEMapping.cachedContent.get(loweredKey).mime;
175         }
176 
177         // this is expected by the caller
178         return null;
179 
180     }
181 
182     /**
183      * Returns the mime-type associated with this extension, or the server's default.
184      */
185     public static String getMIMETypeOrDefault(String extension) {
186         String mimeType = getMIMEType(extension);
187 
188         if (StringUtils.isNotEmpty(mimeType)) {
189             return mimeType;
190         }
191 
192         if (StringUtils.isNotEmpty(extension)) {
193             log.info("Cannot find MIME type for extension \"{}\"", extension);
194         }
195 
196         String defaultExtension = ServerConfiguration.getInstance().getDefaultExtension();
197         if(StringUtils.isBlank(defaultExtension)){
198             defaultExtension = "html";
199         }
200         return getMIMEType(defaultExtension);
201     }
202 
203     public static String getContentEncoding(String contentType) {
204         if (contentType != null) {
205             int index = contentType.lastIndexOf(";"); //$NON-NLS-1$
206             if (index > -1) {
207                 String encoding = contentType.substring(index + 1).toLowerCase().trim();
208                 encoding = encoding.replaceAll("charset=", StringUtils.EMPTY); //$NON-NLS-1$
209                 return encoding;
210             }
211         }
212         return StringUtils.EMPTY;
213     }
214 
215     public static String getContentEncodingOrDefault(String contentType) {
216         final String characterEncoding = getContentEncoding(contentType);
217 
218         return (StringUtils.isEmpty(characterEncoding)) ? DEFAULT_CHAR_ENCODING : characterEncoding;
219     }
220 
221     /**
222      * Returns the icon used for rendering this type.
223      * @return the icon name
224      */
225     public static String getMIMETypeIcon(String extension) {
226         MIMEMappingItem item = MIMEMapping.cachedContent.get(extension.toLowerCase());
227         if (item != null) {
228             return StringUtils.defaultIfEmpty(item.icon, DEFAULT_ICON);
229         }
230 
231         return DEFAULT_ICON;
232 
233     }
234 }