View Javadoc

1   /*
2    * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.properties.api.core.app;
17  
18  import javax.naming.Context;
19  import javax.naming.NameAlreadyBoundException;
20  import javax.naming.NameClassPair;
21  import javax.naming.NameNotFoundException;
22  import javax.naming.NamingEnumeration;
23  import javax.naming.NamingException;
24  
25  import org.apache.commons.lang.ObjectUtils;
26  import org.apache.commons.lang.StringUtils;
27  
28  /**
29   * Defines constants for this library.
30   */
31  public final class JndiContext
32  {
33    // ********************************* Fields *********************************
34  
35    // --- constants ------------------------------------------------------------
36  
37    /**
38     * The path within the JNDI context that provides information for
39     * smartics-properties.
40     */
41    private static final String JNDI_CONTEXT_PATH = "/smartics-properties";
42  
43    /**
44     * The path within the JNDI context to access boot configuration properties.
45     */
46    public static final String JNDI_CONTEXT_PATH_BOOT = JNDI_CONTEXT_PATH
47                                                        + "/boot";
48  
49    /**
50     * The path within the JNDI context to access boot configuration properties.
51     */
52    private static final String JNDI_CONTEXT_PATH_CONFIG = JNDI_CONTEXT_PATH
53                                                           + "/config";
54  
55    // --- members --------------------------------------------------------------
56  
57    /**
58     * The context to lookup the values.
59     */
60    private final Context context;
61  
62    /**
63     * A path within the context to set the root of the configuration. May be
64     * <code>null</code> in which case the context is itself the root. The subpath
65     * is required to start with a slash ( <code>/</code>), but may be
66     * <code>null</code>. If it contains only whitespaces, the subpath is assumed
67     * to be <code>null</code>.
68     */
69    private final String path;
70  
71    // ****************************** Initializer *******************************
72  
73    // ****************************** Constructors ******************************
74  
75    /**
76     * Default constructor.
77     *
78     * @param context the context to lookup the values.
79     * @param subpath a path within the context to set the root of the
80     *          configuration. The subpath is required to start with a slash (
81     *          <code>/</code>), but may be <code>null</code>. If it contains only
82     *          whitespaces, the subpath is assumed to be <code>null</code>.
83     * @param configKey the configuration key which to be the last element of the
84     *          subpath. The key is normalized so that all slashes are replaced by
85     *          pipes.
86     * @throws IllegalArgumentException if the JNDI context cannot be initialized.
87     */
88    private JndiContext(final Context context, final String subpath,
89        final String configKey) throws IllegalArgumentException
90    {
91      this.context = context;
92      this.path = calcPath(subpath, configKey);
93  
94      initialize();
95    }
96  
97    // ****************************** Inner Classes *****************************
98  
99    // ********************************* Methods ********************************
100 
101   // --- init -----------------------------------------------------------------
102 
103   private static String calcPath(final String path, final String configKey)
104   {
105     return path + normalizeKey(configKey);
106   }
107 
108   private static String normalizeKey(final String configKey)
109   {
110     if (StringUtils.isBlank(configKey))
111     {
112       return "";
113     }
114 
115     return '/' + configKey.replace('/', '|');
116   }
117 
118   // --- factory --------------------------------------------------------------
119 
120   /**
121    * Provides access to the boot properties within a JNDI context.
122    *
123    * @param context the root context.
124    * @return the JNDI context for boot properties.
125    * @throws IllegalArgumentException if the JNDI context cannot be initialized.
126    */
127   public static JndiContext boot(final Context context)
128     throws IllegalArgumentException
129   {
130     return new JndiContext(context, JNDI_CONTEXT_PATH_BOOT, null);
131   }
132 
133   /**
134    * Provides access to the custom properties within a JNDI context.
135    *
136    * @param context the custom context.
137    * @param configKey the key to the configuration. Will be used as a path
138    *          within the context to set the root of the configuration.
139    * @return the JNDI context for custom properties.
140    * @throws IllegalArgumentException if the JNDI context cannot be initialized.
141    */
142   public static JndiContext config(final Context context, final String configKey)
143     throws IllegalArgumentException
144   {
145     return new JndiContext(context, JNDI_CONTEXT_PATH_CONFIG, configKey);
146   }
147 
148   // --- get&set --------------------------------------------------------------
149 
150   // --- business -------------------------------------------------------------
151 
152   /**
153    * Initialized the context.
154    *
155    * @throws NamingException on any problem encountered.
156    */
157   private void initialize() throws IllegalArgumentException
158   {
159     final String path = this.path.substring(1);
160     try
161     {
162       context.createSubcontext(path);
163     }
164     catch (final NameAlreadyBoundException e)
165     {
166       // OK, the context has already been established.
167     }
168     catch (final NamingException e)
169     {
170       throw new IllegalArgumentException("Cannot create subcontext for '"
171                                          + path + "'.", e);
172     }
173   }
174 
175   /**
176    * Runs a lookup for the value with the given {@code key} in the JNDI context.
177    *
178    * @param key the key to the value.
179    * @return the value to the key in the context.
180    * @throws NamingException on any problem accessing the value.
181    */
182   public Object lookupObject(final String key) throws NamingException
183   {
184     try
185     {
186       final String jndiKey = createJndiKey(key);
187       final Object value = context.lookup(jndiKey);
188       return value;
189     }
190     catch (final NameNotFoundException e)
191     {
192       return null;
193     }
194 
195   }
196 
197   private String createJndiKey(final String key)
198   {
199     final String normalized = key.replace('.', '/');
200     return path + '/' + normalized;
201   }
202 
203   /**
204    * Runs a lookup for the value with the given {@code key} in the JNDI context
205    * and returns the value as a {@link String}.
206    *
207    * @param key the key to the value.
208    * @return the value to the key in the context.
209    * @throws NamingException on any problem accessing the value.
210    */
211   public String lookup(final String key) throws NamingException
212   {
213     try
214     {
215       final String value = ObjectUtils.toString(lookupObject(key), null);
216       return value;
217     }
218     catch (final NameNotFoundException e)
219     {
220       return null;
221     }
222   }
223 
224   /**
225    * Runs a lookup for the value with the given {@code key} in the JNDI context.
226    *
227    * @param key the key to the value.
228    * @return the value to the key in the context.
229    * @throws NamingException on any problem accessing the value.
230    */
231   public Boolean lookupBoolean(final String key) throws NamingException
232   {
233     final Object value = lookupObject(key);
234 
235     if (value instanceof Boolean)
236     {
237       return (Boolean) value;
238     }
239 
240     final Boolean booleanValue =
241         Boolean.valueOf(ObjectUtils.toString(value, null));
242     return booleanValue;
243   }
244 
245   /**
246    * Sets the value for the given {@code key}.
247    *
248    * @param key the key to the value.
249    * @param value the value to the key in the context.
250    * @throws NamingException on any problem accessing the value.
251    */
252   public void set(final String key, final Object value) throws NamingException
253   {
254     final String jndiKey = createJndiKey(key);
255     if (value != null)
256     {
257       context.rebind(jndiKey, value);
258     }
259     else
260     {
261       try
262       {
263         if (context.lookup(jndiKey) != null)
264         {
265           context.unbind(jndiKey);
266         }
267       }
268       catch (final NameNotFoundException e)
269       {
270         // OK, not existing.
271       }
272     }
273   }
274 
275   /**
276    * Returns the list of stored values in the context.
277    *
278    * @return the list of stored values in the context.
279    * @throws NamingException on any problem accessing the naming context.
280    */
281   public NamingEnumeration<NameClassPair> list() throws NamingException
282   {
283     return context.list(path);
284   }
285 
286   /**
287    * Returns the name of the JNDI namespace.
288    *
289    * @return the name of the JNDI namespace.
290    */
291   public String getSourceId()
292   {
293     return "java:" + path;
294   }
295 
296   // --- object basics --------------------------------------------------------
297 
298 }