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.impl.config.jndi;
17  
18  import java.io.IOException;
19  import java.io.ObjectInputStream;
20  import java.util.HashMap;
21  import java.util.Map;
22  
23  import javax.annotation.concurrent.ThreadSafe;
24  import javax.naming.Context;
25  import javax.naming.InitialContext;
26  import javax.naming.NameClassPair;
27  import javax.naming.NamingEnumeration;
28  import javax.naming.NamingException;
29  
30  import de.smartics.properties.api.config.domain.Property;
31  import de.smartics.properties.api.config.domain.PropertyCollection;
32  import de.smartics.properties.api.config.domain.PropertyProvider;
33  import de.smartics.properties.api.config.domain.PropertyStoreAccessor;
34  import de.smartics.properties.api.config.domain.SerializableConfigurationPropertiesManagement;
35  import de.smartics.properties.api.config.domain.key.ConfigurationKey;
36  import de.smartics.properties.api.core.app.JndiContext;
37  import de.smartics.properties.api.core.domain.PropertyDescriptorRegistry;
38  import de.smartics.properties.api.core.security.PropertyValueSecurity;
39  import de.smartics.properties.spi.config.support.AbstractConfigurationPropertiesManagement;
40  import de.smartics.properties.spi.config.support.NativePropertyCollection;
41  import de.smartics.properties.spi.config.support.SerializableConfigurationPropertiesManagementSpi;
42  import de.smartics.util.lang.NullArgumentException;
43  
44  /**
45   * Implementation based on JNDI.
46   */
47  @ThreadSafe
48  public final class JndiConfigurationProperties extends
49      AbstractConfigurationPropertiesManagement implements
50      SerializableConfigurationPropertiesManagement,
51      SerializableConfigurationPropertiesManagementSpi
52  { // NOPMD
53    // ********************************* Fields *********************************
54  
55    // --- constants ------------------------------------------------------------
56  
57    /**
58     * The class version identifier.
59     */
60    private static final long serialVersionUID = 1L;
61  
62    // --- members --------------------------------------------------------------
63  
64    /**
65     * The reference to the configuration within the JNDI context.
66     */
67    private transient JndiContext context;
68  
69    /**
70     * The helper to access properties in a property store directly.
71     *
72     * @serial
73     */
74    private final PropertyStoreAccessor propertyStoreAccessor =
75        new JndiPropertyStoreAccessor();
76  
77    // ****************************** Initializer *******************************
78  
79    // ****************************** Constructors ******************************
80  
81    /**
82     * Default constructor.
83     *
84     * @param key the key that identifies the configuration.
85     * @param registry the registry to resolve property descriptors.
86     * @param decrypter the helper to decrypt secured property values.
87     * @throws NullArgumentException if {@code key}, {@code registry} or
88     *           {@code decrypter} is <code>null</code>.
89     * @throws NamingException if the JNDI context cannot be created.
90     * @throws IllegalArgumentException on any problem accessing the JNDI context.
91     */
92    public JndiConfigurationProperties(final ConfigurationKey<?> key,
93        final PropertyDescriptorRegistry registry,
94        final PropertyValueSecurity decrypter) throws NullArgumentException,
95      NamingException, IllegalArgumentException
96    {
97      super(key, registry, decrypter);
98  
99      this.context = initContext(key);
100   }
101 
102   // ****************************** Inner Classes *****************************
103 
104   /**
105    * Provides direct access to the properties in the property store.
106    */
107   private final class JndiPropertyStoreAccessor implements
108       PropertyStoreAccessor
109   {
110     /**
111      * The class version identifier.
112      * <p>
113      * The value of this constant is {@value}.
114      * </p>
115      */
116     private static final long serialVersionUID = 1L;
117 
118     @Override
119     public Property getPropertyFromStore(final String name)
120       throws JndiPropertyStoreException
121     {
122       try
123       {
124         final String value = context.lookup(name);
125         final Property property =
126             new Property(context.getSourceId(), name, value);
127         return property;
128       }
129       catch (final NamingException e)
130       {
131         throw new JndiPropertyStoreException(new JndiPropertyStoreMessageBean(
132             JndiPropertyStoreCode.CANNOT_GET_PROPERTY, e, getKey(), name));
133       }
134     }
135 
136     @Override
137     public Property setPropertyToStore(final String name, final String value)
138       throws JndiPropertyStoreException
139     {
140       try
141       {
142         final Property oldProperty = getPropertyFromStore(name);
143         context.set(name, value);
144         return oldProperty;
145       }
146       catch (final NamingException e)
147       {
148         throw new JndiPropertyStoreException(new JndiPropertyStoreMessageBean(
149             JndiPropertyStoreCode.CANNOT_SET_PROPERTY, e, getKey(), name));
150       }
151     }
152 
153     @Override
154     public Property deletePropertyInStore(final String name)
155       throws JndiPropertyStoreException
156     {
157       try
158       {
159         final Property oldProperty = getPropertyFromStore(name);
160         context.set(name, null);
161         return oldProperty;
162       }
163       catch (final NamingException e)
164       {
165         throw new JndiPropertyStoreException(new JndiPropertyStoreMessageBean(
166             JndiPropertyStoreCode.CANNOT_DELETE_PROPERTY, e, getKey(), name));
167       }
168     }
169 
170     @Override
171     public PropertyCollection getPropertyCollectionFromStore()
172       throws JndiPropertyStoreException
173     {
174       try
175       {
176         final Map<String, Property> properties =
177             new HashMap<String, Property>();
178         final NamingEnumeration<NameClassPair> list = context.list();
179         while (list.hasMore())
180         {
181           final NameClassPair pair = list.next();
182           final String name = pair.getName();
183           final Property property = getPropertyFromStore(name);
184           properties.put(name, property);
185         }
186 
187         return new NativePropertyCollection(properties);
188       }
189       catch (final NamingException e)
190       {
191         throw new JndiPropertyStoreException(
192             JndiPropertyStoreMessageBean.collection(e, getKey()));
193       }
194     }
195   }
196 
197   // ********************************* Methods ********************************
198 
199   // --- init -----------------------------------------------------------------
200 
201   private static JndiContext initContext(final ConfigurationKey<?> key)
202     throws NamingException, IllegalArgumentException
203   {
204     final Context jndi = new InitialContext();
205     final JndiContext context = JndiContext.config(jndi, key.toString());
206     return context;
207   }
208 
209   // --- get&set --------------------------------------------------------------
210 
211   @Override
212   public PropertyStoreAccessor getPropertyStoreAccessor()
213   {
214     return propertyStoreAccessor;
215   }
216 
217   // --- business -------------------------------------------------------------
218 
219   @Override
220   protected void setPropertiesToStore(final PropertyProvider provider)
221     throws JndiPropertyStoreException
222   {
223     String name = "<unknown>";
224     try
225     {
226       for (final Property property : provider.getProperties())
227       {
228         name = property.getName();
229         final String value = property.getValue();
230         context.set(name, value);
231       }
232     }
233     catch (final NamingException e)
234     {
235       throw new JndiPropertyStoreException(new JndiPropertyStoreMessageBean(
236           JndiPropertyStoreCode.CANNOT_SET_PROPERTY, e, getKey(), name));
237     }
238   }
239 
240   /**
241    * {@inheritDoc}
242    *
243    * @impl since JNDI values are currently not persisted.
244    * @todo check JNDI configuration for persistence.
245    */
246   @Override
247   protected void addDefinitionsToStore(final PropertyProvider properties)
248     throws JndiPropertyStoreException
249   {
250     setPropertiesToStore(properties);
251   }
252 
253   @Override
254   public SerializableConfigurationPropertiesManagement toSerializable()
255   {
256     return this;
257   }
258 
259   // --- object basics --------------------------------------------------------
260 
261   /**
262    * Reads the object from the given stream.
263    *
264    * @param in the stream to read from.
265    * @throws IOException on read problems or if the JNDI context cannot be
266    *           established.
267    * @throws ClassNotFoundException if a class cannot be found.
268    */
269   private void readObject(final ObjectInputStream in) throws IOException,
270     ClassNotFoundException
271   {
272     in.defaultReadObject();
273 
274     try
275     {
276       this.context = initContext(getKey());
277     }
278     catch (final NamingException e)
279     {
280       throw new IOException("Cannot reestablish the JNDI context.", e);
281     }
282     catch (final IllegalArgumentException e)
283     {
284       throw new IOException("Cannot reestablish the JNDI context.", e);
285     }
286   }
287 }