View Javadoc
1   /**
2    * This file Copyright (c) 2003-2018 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.util.ObservationUtil;
37  import info.magnolia.context.MgnlContext;
38  import info.magnolia.jcr.util.NodeTypes;
39  import info.magnolia.jcr.util.NodeUtil;
40  import info.magnolia.jcr.util.PropertyUtil;
41  import info.magnolia.repository.RepositoryConstants;
42  
43  import java.util.Hashtable;
44  import java.util.Iterator;
45  import java.util.Map;
46  
47  import javax.jcr.Node;
48  import javax.jcr.PathNotFoundException;
49  import javax.jcr.RepositoryException;
50  import javax.jcr.Session;
51  import javax.jcr.observation.EventIterator;
52  import javax.jcr.observation.EventListener;
53  
54  import org.apache.commons.lang3.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   * @deprecated since 5.4.6. This class will be replaced (see MAGNOLIA-6576).
62   */
63  @Deprecated
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/";
68      public static final String DEFAULT_ICON = ICONS_PATH + "general.png";
69      private static final String NODEPATH = "/server/MIMEMapping";
70      public static final String DEFAULT_ICON_STYLE = "icon-file";
71  
72      private static Map<String, MIMEMappingItem> cachedContent = new Hashtable<String, MIMEMappingItem>();
73      public static final String DEFAULT_CHAR_ENCODING = "UTF-8";
74      public static final String DEFAULT_EXTENSION = "html";
75  
76      /**
77       * Used to keep the configuration in memory.
78       */
79      protected static class MIMEMappingItem {
80  
81          protected String ext;
82  
83          protected String mime;
84  
85          protected String icon;
86  
87          protected String iconStyle;
88      }
89  
90  
91      /**
92       * Utility class, don't instantiate.
93       */
94      private MIMEMapping() {
95          // unused
96      }
97  
98      /**
99       * Reads all configured mime mapping (config/server/MIMEMapping).
100      */
101     public static void init() {
102         log.info("Initializing MIMEMapping from {}", NODEPATH);
103         load();
104         registerEventListener();
105     }
106 
107     /**
108      * Reads all configured mime mapping (config/server/MIMEMapping).
109      */
110     public static void load() {
111         MIMEMapping.cachedContent.clear();
112         try {
113             final Session session = MgnlContext.getSystemContext().getJCRSession(RepositoryConstants.CONFIG);
114 
115             final Iterable<Node> mimeList = NodeUtil.getNodes(session.getNode(NODEPATH), NodeTypes.ContentNode.NAME);
116             MIMEMapping.cacheContent(mimeList.iterator());
117             log.debug("MIMEMapping loaded from {}", NODEPATH);
118         } catch (PathNotFoundException e) {
119             log.warn("No MIMEMapping info configured at {}", NODEPATH);
120         } catch (RepositoryException e) {
121             log.error("Failed to load MIMEMapping: {}", e.getMessage(), e);
122         }
123     }
124 
125     public static void reload() {
126         log.info("Reloading MIMEMapping from {}", NODEPATH);
127         MIMEMapping.load();
128     }
129 
130     /**
131      * Register an event listener: reload cache configuration when something changes.
132      */
133     private static void registerEventListener() {
134         log.info("Registering event listener for MIMEMapping");
135 
136         ObservationUtil.registerChangeListener(RepositoryConstants.CONFIG, NODEPATH, new EventListener() {
137             @Override
138             public void onEvent(EventIterator iterator) {
139                 // reload everything
140                 reload();
141             }
142         });
143     }
144 
145     /**
146      * Cache all MIME types configured.
147      */
148     private static void cacheContent(Iterator<Node> iterator) {
149         while (iterator.hasNext()) {
150             Node c = iterator.next();
151             try {
152                 MIMEMappingItem item = new MIMEMappingItem();
153                 item.ext = PropertyUtil.getString(c, "extension", c.getName());
154                 item.mime = PropertyUtil.getString(c, "mime-type");
155                 item.icon = PropertyUtil.getString(c, "icon");
156                 item.iconStyle = PropertyUtil.getString(c, "iconStyle");
157 
158                 MIMEMapping.cachedContent.put(item.ext, item);
159             } catch (Exception e) {
160                 log.error("Failed to cache MIMEMapping");
161             }
162         }
163     }
164 
165     /**
166      * Get MIME type String.
167      *
168      * @param key extension for which MIME type is requested
169      * @return MIME type
170      */
171     public static String getMIMEType(String key) {
172         if (StringUtils.isEmpty(key)) {
173             return StringUtils.EMPTY;
174         }
175         // check that the cached content contains the key first to avoid NPE when accessing 'mime'
176         String loweredKey = key.toLowerCase();
177         if (MIMEMapping.cachedContent.containsKey(loweredKey)) {
178             return MIMEMapping.cachedContent.get(loweredKey).mime;
179         }
180 
181         // this is expected by the caller
182         return null;
183 
184     }
185 
186     /**
187      * Returns the mime-type associated with this extension, or the server's default.
188      */
189     public static String getMIMETypeOrDefault(String extension) {
190         String mimeType = getMIMEType(extension);
191 
192         if (StringUtils.isNotEmpty(mimeType)) {
193             return mimeType;
194         }
195 
196         if (StringUtils.isNotEmpty(extension)) {
197             log.info("Cannot find MIME type for extension \"{}\"", extension);
198         }
199 
200         String defaultExtension = ServerConfiguration.getInstance().getDefaultExtension();
201         if (StringUtils.isBlank(defaultExtension)) {
202             defaultExtension = DEFAULT_EXTENSION;
203         }
204         return getMIMEType(defaultExtension);
205     }
206 
207     public static String getContentEncoding(String contentType) {
208         if (contentType != null) {
209             int index = contentType.lastIndexOf(";");
210             if (index > -1) {
211                 String encoding = contentType.substring(index + 1).toLowerCase().trim();
212                 encoding = encoding.replaceAll("charset=", StringUtils.EMPTY);
213                 return encoding;
214             }
215         }
216         return StringUtils.EMPTY;
217     }
218 
219     public static String getContentEncodingOrDefault(String contentType) {
220         final String characterEncoding = getContentEncoding(contentType);
221 
222         return (StringUtils.isEmpty(characterEncoding)) ? DEFAULT_CHAR_ENCODING : characterEncoding;
223     }
224 
225     /**
226      * Returns the icon used for rendering this type.
227      *
228      * @return the icon name
229      */
230     public static String getMIMETypeIcon(String extension) {
231         MIMEMappingItem item = MIMEMapping.cachedContent.get(extension.toLowerCase());
232         if (item != null) {
233             return StringUtils.defaultIfEmpty(item.icon, DEFAULT_ICON);
234         }
235 
236         return DEFAULT_ICON;
237 
238     }
239 
240     /**
241      * Returns the icon css style name.
242      *
243      * @return icon css style name.
244      */
245     public static String getMIMETypeIconStyle(String mimeType) {
246         String genericMimeType = StringUtils.substringBefore(mimeType, "/");
247         MIMEMappingItem mimeMappingItem = null;
248         for (MIMEMappingItem item : cachedContent.values()) {
249             if (item.mime != null && item.mime.equals(mimeType) && StringUtils.isNotBlank(item.iconStyle)) {
250                 mimeMappingItem = item;
251                 break;
252             }
253             if (item.mime != null && item.mime.equals(genericMimeType) && StringUtils.isNotBlank(item.iconStyle)) {
254                 mimeMappingItem = item;
255             }
256         }
257         if (mimeMappingItem != null && StringUtils.isNotBlank(mimeMappingItem.iconStyle)) {
258             return mimeMappingItem.iconStyle;
259         }
260         return DEFAULT_ICON_STYLE;
261     }
262 }