001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.portlet;
016    
017    import com.liferay.portal.kernel.util.AggregateResourceBundle;
018    import com.liferay.portal.kernel.util.ArrayUtil;
019    import com.liferay.portal.kernel.util.CharPool;
020    import com.liferay.portal.kernel.util.LocaleUtil;
021    import com.liferay.portal.kernel.util.ResourceBundleUtil;
022    import com.liferay.portal.kernel.util.StringPool;
023    import com.liferay.portal.model.Portlet;
024    import com.liferay.registry.Filter;
025    import com.liferay.registry.Registry;
026    import com.liferay.registry.RegistryUtil;
027    import com.liferay.registry.ServiceReference;
028    import com.liferay.registry.ServiceRegistration;
029    import com.liferay.registry.ServiceTracker;
030    import com.liferay.registry.ServiceTrackerCustomizer;
031    import com.liferay.registry.collections.StringServiceRegistrationMap;
032    import com.liferay.registry.collections.StringServiceRegistrationMapImpl;
033    
034    import java.io.Closeable;
035    
036    import java.util.Collections;
037    import java.util.Enumeration;
038    import java.util.HashSet;
039    import java.util.Iterator;
040    import java.util.List;
041    import java.util.Map.Entry;
042    import java.util.ResourceBundle;
043    import java.util.Set;
044    import java.util.concurrent.ConcurrentHashMap;
045    import java.util.concurrent.ConcurrentMap;
046    import java.util.concurrent.CopyOnWriteArrayList;
047    
048    /**
049     * @author Raymond Aug??
050     * @author Tomas Polesovsky
051     */
052    public class ResourceBundleTracker implements Closeable {
053    
054            public ResourceBundleTracker(ClassLoader classLoader, Portlet portlet) {
055                    _classLoader = classLoader;
056                    _portlet = portlet;
057    
058                    Registry registry = RegistryUtil.getRegistry();
059    
060                    Filter filter = registry.getFilter(
061                            "(&(javax.portlet.name=" + portlet.getPortletId() +
062                                    ")(language.id=*)(objectClass=" +
063                                            ResourceBundle.class.getName() + "))");
064    
065                    _serviceTracker = registry.trackServices(
066                            filter, new ResourceBundleServiceTrackerCustomizer());
067    
068                    _serviceTracker.open();
069            }
070    
071            public void clear() {
072                    Set<Entry<String, ServiceRegistration<ResourceBundle>>> set =
073                            _serviceRegistrations.entrySet();
074    
075                    Iterator<Entry<String, ServiceRegistration<ResourceBundle>>> iterator =
076                            set.iterator();
077    
078                    while (iterator.hasNext()) {
079                            Entry<String, ServiceRegistration<ResourceBundle>> entry =
080                                    iterator.next();
081    
082                            ServiceRegistration<ResourceBundle> serviceRegistration =
083                                    entry.getValue();
084    
085                            serviceRegistration.unregister();
086    
087                            iterator.remove();
088                    }
089            }
090    
091            @Override
092            public void close() {
093                    clear();
094    
095                    _serviceTracker.close();
096            }
097    
098            public ResourceBundle getResourceBundle(String languageId) {
099                    if (languageId == null) {
100                            languageId = StringPool.BLANK;
101                    }
102    
103                    ResourceBundleWrapper resourceBundleWrapper =
104                            _resourceBundleWrappers.get(languageId);
105    
106                    if (resourceBundleWrapper == null) {
107                            resourceBundleWrapper = new ResourceBundleWrapper(languageId, null);
108    
109                            ResourceBundleWrapper previousResourceBundleWrapper =
110                                    _resourceBundleWrappers.putIfAbsent(
111                                            languageId, resourceBundleWrapper);
112    
113                            if (previousResourceBundleWrapper != null) {
114                                    resourceBundleWrapper = previousResourceBundleWrapper;
115                            }
116                    }
117    
118                    return resourceBundleWrapper;
119            }
120    
121            private final ClassLoader _classLoader;
122            private final Portlet _portlet;
123            private final ConcurrentMap<String, ResourceBundleWrapper>
124                    _resourceBundleWrappers = new ConcurrentHashMap<>();
125            private final StringServiceRegistrationMap<ResourceBundle>
126                    _serviceRegistrations = new StringServiceRegistrationMapImpl<>();
127            private final ServiceTracker<ResourceBundle, ResourceBundle>
128                    _serviceTracker;
129    
130            private class ResourceBundleServiceTrackerCustomizer
131                    implements ServiceTrackerCustomizer<ResourceBundle, ResourceBundle> {
132    
133                    @Override
134                    public ResourceBundle addingService(
135                            ServiceReference<ResourceBundle> serviceReference) {
136    
137                            Registry registry = RegistryUtil.getRegistry();
138    
139                            ResourceBundle resourceBundle = registry.getService(
140                                    serviceReference);
141    
142                            String languageId = (String)serviceReference.getProperty(
143                                    "language.id");
144    
145                            ResourceBundleWrapper resourceBundleWrapper =
146                                    _resourceBundleWrappers.get(languageId);
147    
148                            if (resourceBundleWrapper == null) {
149                                    resourceBundleWrapper = new ResourceBundleWrapper(
150                                            languageId, resourceBundle);
151    
152                                    ResourceBundleWrapper previousResourceBundleWrapper =
153                                            _resourceBundleWrappers.putIfAbsent(
154                                                    languageId, resourceBundleWrapper);
155    
156                                    if (previousResourceBundleWrapper == null) {
157                                            return resourceBundle;
158                                    }
159                                    else {
160                                            resourceBundleWrapper = previousResourceBundleWrapper;
161                                    }
162                            }
163    
164                            resourceBundleWrapper._addResourceBundle(resourceBundle);
165    
166                            return resourceBundle;
167                    }
168    
169                    @Override
170                    public void modifiedService(
171                            ServiceReference<ResourceBundle> serviceReference,
172                            ResourceBundle resourceBundle) {
173    
174                            removedService(serviceReference, resourceBundle);
175    
176                            addingService(serviceReference);
177                    }
178    
179                    @Override
180                    public void removedService(
181                            ServiceReference<ResourceBundle> serviceReference,
182                            ResourceBundle resourceBundle) {
183    
184                            Registry registry = RegistryUtil.getRegistry();
185    
186                            registry.ungetService(serviceReference);
187    
188                            String languageId = (String)serviceReference.getProperty(
189                                    "language.id");
190    
191                            ResourceBundleWrapper resourceBundleWrapper =
192                                    _resourceBundleWrappers.get(languageId);
193    
194                            if (resourceBundleWrapper != null) {
195                                    resourceBundleWrapper._removeResourceBundle(resourceBundle);
196                            }
197                    }
198    
199            }
200    
201            private class ResourceBundleWrapper extends ResourceBundle {
202    
203                    @Override
204                    public Enumeration<String> getKeys() {
205                            ResourceBundle resourceBundle = _resourceBundle;
206    
207                            if (resourceBundle == null) {
208                                    return Collections.emptyEnumeration();
209                            }
210    
211                            return resourceBundle.getKeys();
212                    }
213    
214                    @Override
215                    protected Object handleGetObject(String key) {
216                            ResourceBundle resourceBundle = _resourceBundle;
217    
218                            if ((resourceBundle != null) && resourceBundle.containsKey(key)) {
219                                    return resourceBundle.getObject(key);
220                            }
221    
222                            return null;
223                    }
224    
225                    @Override
226                    protected Set<String> handleKeySet() {
227                            Set<String> keySet = _keySet;
228    
229                            if (keySet == null) {
230                                    ResourceBundle resourceBundle = _resourceBundle;
231    
232                                    if (resourceBundle == null) {
233                                            keySet = Collections.emptySet();
234                                    }
235                                    else {
236                                            keySet = new HashSet<>();
237    
238                                            Enumeration<String> enumeration = resourceBundle.getKeys();
239    
240                                            while (enumeration.hasMoreElements()) {
241                                                    String key = enumeration.nextElement();
242    
243                                                    if (resourceBundle.containsKey(key)) {
244                                                            keySet.add(key);
245                                                    }
246                                            }
247                                    }
248    
249                                    _keySet = keySet;
250                            }
251    
252                            return keySet;
253                    }
254    
255                    private ResourceBundleWrapper(
256                            String languageId, ResourceBundle resourceBundle) {
257    
258                            _resourceBundle = resourceBundle;
259    
260                            if (resourceBundle != null) {
261                                    _resourceBundles.add(resourceBundle);
262                            }
263    
264                            if (languageId.isEmpty()) {
265                                    setParent(
266                                            ResourceBundleUtil.getBundle(
267                                                    _portlet.getResourceBundle(),
268                                                    LocaleUtil.fromLanguageId(languageId), _classLoader));
269                            }
270                            else {
271                                    String parentLanguageId = StringPool.BLANK;
272    
273                                    int index = languageId.lastIndexOf(CharPool.UNDERLINE);
274    
275                                    if (index > 0) {
276                                            parentLanguageId = languageId.substring(0, index);
277                                    }
278    
279                                    setParent(getResourceBundle(parentLanguageId));
280                            }
281                    }
282    
283                    private void _addResourceBundle(ResourceBundle resourceBundle) {
284                            _resourceBundles.add(resourceBundle);
285    
286                            _update();
287                    }
288    
289                    private void _removeResourceBundle(ResourceBundle resourceBundle) {
290                            if (_resourceBundles.remove(resourceBundle)) {
291                                    _update();
292                            }
293                    }
294    
295                    private void _update() {
296                            ResourceBundle[] resourceBundles = _resourceBundles.toArray(
297                                    new ResourceBundle[_resourceBundles.size()]);
298    
299                            if (resourceBundles.length == 0) {
300                                    _resourceBundle = null;
301                            }
302                            else if (resourceBundles.length == 1) {
303                                    _resourceBundle = resourceBundles[0];
304                            }
305                            else {
306                                    ArrayUtil.reverse(resourceBundles);
307    
308                                    _resourceBundle = new AggregateResourceBundle(resourceBundles);
309                            }
310    
311                            _keySet = null;
312                    }
313    
314                    private volatile Set<String> _keySet;
315                    private volatile ResourceBundle _resourceBundle;
316                    private final List<ResourceBundle> _resourceBundles =
317                            new CopyOnWriteArrayList<>();
318    
319            }
320    
321    }