blob: b82b17342c3291f9e9db619958f3d9bba478c0ea [file] [log] [blame]
Pierre De Ropa0204f52010-03-06 22:23:57 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.felix.dm.impl.metatype;
20
21import java.io.IOException;
22import java.io.InputStream;
23import java.net.URL;
24import java.util.ArrayList;
25import java.util.Dictionary;
26import java.util.Enumeration;
27import java.util.HashMap;
28import java.util.List;
29import java.util.Locale;
30import java.util.Map;
31import java.util.Properties;
32import java.util.StringTokenizer;
33import java.util.TreeSet;
34
35import org.apache.felix.dm.dependencies.PropertyMetaData;
36import org.apache.felix.dm.impl.Logger;
37import org.apache.felix.dm.impl.dependencies.ConfigurationDependencyImpl;
38import org.osgi.framework.Constants;
39import org.osgi.service.cm.ConfigurationException;
40import org.osgi.service.cm.ManagedService;
41import org.osgi.service.log.LogService;
42import org.osgi.service.metatype.MetaTypeProvider;
43import org.osgi.service.metatype.ObjectClassDefinition;
44
45/**
46 * When a ConfigurationDepdendency is configured with properties metadata, we provide
47 * a specific ManagedService which also implements the MetaTypeProvider interface. This interface
48 * allows the MetaTypeService to retrieve our properties metadata, which will then be handled by webconsole.
49 */
50public class MetaTypeProviderImpl implements MetaTypeProvider, ManagedService
51{
52 private ConfigurationDependencyImpl m_configDependency;
53 private List m_propertiesMetaData = new ArrayList();
54 private String m_description;
55 private String m_heading;
56 private String m_localization;
57 private Map m_localesProperties = new HashMap();
58 private Logger m_logger;
59
60 public MetaTypeProviderImpl(ConfigurationDependencyImpl configurationDependency)
61 {
62 m_configDependency = configurationDependency;
63 m_logger = configurationDependency.getLogger();
64 // Set the default localization file base name (see core specification, in section Localization on page 68).
65 // By default, this file can be stored in OSGI-INF/l10n/bundle.properties (and corresponding localized version
66 // in OSGI-INF/l10n/bundle_en_GB_welsh.properties, OSGI-INF/l10n/bundle_en_GB.properties, etc ...
67 // This default localization property file name can be overriden using the PropertyMetaData.setLocalization method.
68 m_localization = (String) m_configDependency.getBundleContext().getBundle().getHeaders().get(
69 Constants.BUNDLE_LOCALIZATION);
70 if (m_localization == null)
71 {
72 m_localization = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
73 }
74 }
75
76 /**
77 * Registers the metatype information of a given configuration property
78 * @param property
79 */
80 public void add(PropertyMetaData property)
81 {
82 m_propertiesMetaData.add(property);
83 }
84
85 /**
86 * A human readable description of the PID this annotation is associated with. Example: "Configuration for the PrinterService bundle".
87 * @return A human readable description of the PID this annotation is associated with (may be localized)
88 */
89 public void setDescription(String description)
90 {
91 m_description = description;
92 }
93
94 /**
95 * The label used to display the tab name (or section) where the properties are displayed. Example: "Printer Service".
96 * @return The label used to display the tab name where the properties are displayed (may be localized)
97 */
98 public void setName(String heading)
99 {
100 m_heading = heading;
101 }
102
103 /**
104 * Points to the basename of the Properties file that can localize the Meta Type informations.
105 * By default, (e.g. <code>setLocalization("person")</code> would match person_du_NL.properties in the root bundle directory.
106 * The default localization base name for the properties is OSGI-INF/l10n/bundle, but can
107 * be overridden by the manifest Bundle-Localization header (see core specification, in section Localization on page 68).
108 */
109 public void setLocalization(String path)
110 {
111 if (path.endsWith(".properties"))
112 {
113 throw new IllegalArgumentException(
114 "path must point to the base name of the propertie file, "
115 + "excluding local suffixes. For example: "
116 + "foo/bar/person is valid and matches the property file \"foo/bar/person_bundle_en_GB_welsh.properties\"");
117 }
118 m_localization = path.startsWith("/") ? path.substring(1) : path;
119 }
120
121 // --------------- MetaTypeProvider interface -------------------------------------------------
122
123 /**
124 * Returns all the Locales our bundle is containing. For instance, if our bundle contains the following localization files:
125 * OSGI-INF/l10n/bundle_en_GB_welsh.properties and OSGI-INF/l10n/bundle_en_GB.properties, then this method will return
126 * "en_GB", "en_GB_welsh" ...
127 * @return the list of Locale supported by our bundle.
128 */
129 public String[] getLocales()
130 {
131 int lastSlash = m_localization.lastIndexOf("/");
132 String path = (lastSlash == -1) ? "/" : ("/" + m_localization.substring(0, lastSlash - 1));
133 String base = (lastSlash == -1) ? m_localization : m_localization.substring(lastSlash + 1);
134 Enumeration e = m_configDependency.getBundleContext().getBundle().findEntries(path,
135 base + "*.properties", false);
Pierre De Ropd3c2aab2010-03-07 09:52:12 +0000136 if (e == null) {
137 return null;
138 }
139
Pierre De Ropa0204f52010-03-06 22:23:57 +0000140 TreeSet set = new TreeSet();
141 while (e.hasMoreElements())
142 {
143 // We have found a locale property file in the form of "path/file[_language[_ country[_variation]].properties"
144 // And now, we have to get the "language[_country[_variation]]" part ...
145 URL url = (URL) e.nextElement();
146 String name = url.getPath();
147 name = name.substring(name.lastIndexOf("/") + 1);
148 int underscore = name.indexOf("_");
149 if (underscore != -1)
150 {
151 name = name.substring(underscore + 1, name.length() - ".properties".length());
152 }
153 if (name.length() > 0)
154 {
155 set.add(name);
156 }
157 }
158
159 String[] locales = (String[]) set.toArray(new String[set.size()]);
160 return locales.length == 0 ? null : locales;
161 }
162
163 /**
164 * Returns the ObjectClassDefinition for a given Pid/Locale.
165 */
166 public ObjectClassDefinition getObjectClassDefinition(String id, String locale)
167 {
168 try
169 {
170 // Check if the id matches our PID
171 if (!id.equals(m_configDependency.getName()))
172 {
173 m_configDependency.getLogger().log(LogService.LOG_ERROR,
174 "id " + id + " does not match pid " + m_configDependency.getName());
175 return null;
176 }
177
178 Properties localeProperties = getLocaleProperties(locale);
179 return new ObjectClassDefinitionImpl(m_configDependency.getName(), m_heading,
180 m_description, m_propertiesMetaData, new Resource(localeProperties));
181 }
182
183 catch (Throwable t)
184 {
185 m_configDependency.getLogger().log(
186 Logger.LOG_ERROR,
187 "Unexpected exception while geting ObjectClassDefinition for " + id + " (locale="
188 + locale + ")", t);
189 return null;
190 }
191 }
192
193 /**
194 * We also implements the ManagedService and we just delegates the configuration handling to
195 * our associated ConfigurationDependency.
196 */
197 public void updated(Dictionary properties) throws ConfigurationException
198 {
199 m_configDependency.updated(properties);
200 }
201
202 /**
203 * Gets the properties for a given Locale.
204 * @param locale
205 * @return
206 * @throws IOException
207 */
208 private synchronized Properties getLocaleProperties(String locale) throws IOException
209 {
210 locale = locale == null ? Locale.getDefault().toString() : locale;
211 Properties properties = (Properties) m_localesProperties.get(locale);
212 if (properties == null)
213 {
214 properties = new Properties();
215 URL url = m_configDependency.getBundleContext().getBundle().getEntry(
216 m_localization + ".properties");
217 if (url != null)
218 {
219 loadLocale(properties, url);
220 }
221
222 String path = m_localization;
223 StringTokenizer tok = new StringTokenizer(locale, "_");
224 while (tok.hasMoreTokens())
225 {
226 path += "_" + tok.nextToken();
227 url = m_configDependency.getBundleContext().getBundle().getEntry(path + ".properties");
228 if (url != null)
229 {
230 properties = new Properties(properties);
231 loadLocale(properties, url);
232 }
233 }
234
235 m_localesProperties.put(locale, properties);
236 }
237
238 return properties;
239 }
240
241 /**
242 * Loads a Locale Properties file.
243 * @param properties
244 * @param url
245 * @throws IOException
246 */
247 private void loadLocale(Properties properties, URL url) throws IOException
248 {
249 InputStream in = null;
250 try
251 {
252 in = url.openStream();
253 properties.load(in);
254 }
255 finally
256 {
257 if (in != null)
258 {
259 try
260 {
261 in.close();
262 }
263 catch (IOException ignored)
264 {
265 }
266 }
267 }
268 }
269}