View Javadoc

1   // 
2   //Copyright (c) 2003, Caltha - Gajda, Krzewski, Mach, Potempski Sp.J. 
3   //All rights reserved. 
4   //   
5   //Redistribution and use in source and binary forms, with or without modification,  
6   //are permitted provided that the following conditions are met: 
7   //   
8   //* Redistributions of source code must retain the above copyright notice,  
9   //this list of conditions and the following disclaimer. 
10  //* Redistributions in binary form must reproduce the above copyright notice,  
11  //this list of conditions and the following disclaimer in the documentation  
12  //and/or other materials provided with the distribution. 
13  //* Neither the name of the Caltha - Gajda, Krzewski, Mach, Potempski Sp.J.  
14  //nor the names of its contributors may be used to endorse or promote products  
15  //derived from this software without specific prior written permission. 
16  // 
17  //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  
18  //AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  
19  //WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
20  //IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,  
21  //INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,  
22  //BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
23  //OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
24  //WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  
25  //ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  
26  //POSSIBILITY OF SUCH DAMAGE. 
27  //
28  
29  package org.objectledge.naming.db;
30  
31  import java.sql.Connection;
32  import java.sql.ResultSet;
33  import java.sql.SQLException;
34  import java.sql.Statement;
35  import java.util.ArrayList;
36  import java.util.HashMap;
37  import java.util.Hashtable;
38  import java.util.Iterator;
39  import java.util.List;
40  import java.util.Map;
41  
42  import javax.naming.CompositeName;
43  import javax.naming.Context;
44  import javax.naming.Name;
45  import javax.naming.NamingEnumeration;
46  import javax.naming.NamingException;
47  import javax.naming.directory.Attribute;
48  import javax.naming.directory.Attributes;
49  import javax.naming.directory.BasicAttribute;
50  import javax.naming.directory.BasicAttributes;
51  import javax.naming.directory.DirContext;
52  import javax.naming.directory.ModificationItem;
53  import javax.naming.directory.SearchControls;
54  import javax.naming.directory.SearchResult;
55  
56  import org.objectledge.database.DatabaseUtils;
57  import org.objectledge.database.persistence.Persistence;
58  import org.objectledge.database.persistence.PersistenceException;
59  
60  /**
61   * Database based implementation of java.naming.directory.DirContext interface.
62   * 
63   * @author <a href="mailto:pablo@caltha.pl">Pawel Potempski</a>
64   */
65  public class DatabaseDirContext extends DatabaseContext implements DirContext
66  {
67      /***
68       * The directory context constructor.
69       * 
70       * @param env the environment. 
71       */
72      public DatabaseDirContext(Hashtable env)
73      {
74          super(env);
75      }
76  
77      /***
78       * The context constructor.
79       * 
80       * @param env the environment.
81       * @param context the persistent context delegate.
82       * @param persistence the persistence.
83       * @throws NamingException if operation failed.
84       */
85      protected DatabaseDirContext(Hashtable env, PersistentContext context, Persistence persistence)
86          throws NamingException
87      {
88          super(env, context, persistence);
89      }
90  
91      /***
92       * {@inheritDoc}
93       */
94      public Object lookup(Name name) throws NamingException
95      {
96          if (name.isEmpty())
97          {
98              return new DatabaseDirContext(env, context, persistence);
99          }
100         String dn = getDN(name);
101         List list = lookupContext(dn);
102         if(list.size() == 0)
103         {
104             throw new NamingException("faled to retrieve context '"+dn+"' from database");
105         }
106         if(list.size() > 1)
107         {
108             throw new NamingException("ambigious context '"+dn+"' in database");
109         }
110         return new DatabaseDirContext(env, (PersistentContext)list.get(0), persistence);        
111     }
112 
113     /***
114      * {@inheritDoc}
115      */
116     public Attributes getAttributes(Name name) throws NamingException
117     {
118         return getAttributes(name, null);
119     }
120 
121     /***
122      * {@inheritDoc}
123      */
124     public Attributes getAttributes(String name) throws NamingException
125     {
126         return getAttributes(new CompositeName(name));
127     }
128 
129     /***
130      * {@inheritDoc}
131      */
132     public Attributes getAttributes(Name name, String[] attrIds) throws NamingException
133     {
134         DatabaseDirContext ctx = (DatabaseDirContext)lookup(name);
135         long contextId = ctx.getDelegate().getContextId();
136         Attributes allAttributes = getAllAttributes(contextId);
137         return filterAttributes(allAttributes, attrIds);
138     }
139 
140     /***
141      * {@inheritDoc}
142      */
143     public Attributes getAttributes(String name, String[] attrIds) throws NamingException
144     {
145         return getAttributes(new CompositeName(name), attrIds);
146     }
147 
148     /***
149      * {@inheritDoc}
150      */
151     public void modifyAttributes(Name name, int modOp, Attributes attrs) throws NamingException
152     {
153         if (attrs == null || attrs.size() == 0)
154         {
155             return;
156         }
157         NamingEnumeration enumerator = attrs.getAll();
158         ModificationItem[] items = new ModificationItem[attrs.size()];
159         int i = 0;
160         while(enumerator.hasMoreElements())
161         {
162             items[i++] = new ModificationItem(modOp, (Attribute)enumerator.next());
163         }
164         modifyAttributes(name, items);        
165     }
166 
167     /***
168      * {@inheritDoc}
169      */
170     public void modifyAttributes(String name, int modOp, Attributes attrs)
171         throws NamingException
172     {
173         modifyAttributes(new CompositeName(name), modOp, attrs);
174     }
175 
176     /***
177      * {@inheritDoc}
178      */
179     public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException
180     {
181         List failures = new ArrayList();
182         DatabaseDirContext ctx = (DatabaseDirContext)lookup(name);
183         boolean transactionControler;
184         try
185         {
186             transactionControler = persistence.getDatabase().beginTransaction();
187         }
188         catch(SQLException e)
189         {
190             throw new RuntimeException("Failed to begin transaction",e);
191         }
192         for (int i=0; i<mods.length; i++)
193         {      
194             Attribute attribute = mods[i].getAttribute();
195             NamingEnumeration values = attribute.getAll();
196             switch (mods[i].getModificationOp())
197             {
198                 case ADD_ATTRIBUTE:
199                     try
200                     {
201                         while (values.hasMore())
202                         {
203                             String value = (String)values.next();
204                             PersistentAttribute pAttribute = new PersistentAttribute(
205                                 ctx.getDelegate().getContextId(),
206                                 attribute.getID(), value);
207                             persistence.save(pAttribute);
208                         }    
209                     }   
210                     catch(PersistenceException e)
211                     {
212                         failures.add(mods[i]);
213                     }
214                     break;
215                 case REPLACE_ATTRIBUTE:
216                     try
217                     {
218                         StringBuilder sb = new StringBuilder();
219                         sb.append("context_id = ");
220                         sb.append(ctx.getDelegate().getContextId());
221                         sb.append(" and name = '");
222                         sb.append(attribute.getID());
223                         sb.append("'");
224                         persistence.delete(sb.toString(), PersistentAttribute.FACTORY);
225                         while (values.hasMore())
226                         {
227                             String value = (String)values.next();
228                             PersistentAttribute pAttribute = new PersistentAttribute(
229                                 ctx.getDelegate().getContextId(),
230                                 attribute.getID(), value);
231                             persistence.save(pAttribute);
232                         }    
233                     }   
234                     catch(PersistenceException e)
235                     {
236                         failures.add(mods[i]);
237                     }
238                     break;
239                 case REMOVE_ATTRIBUTE:
240                     try
241                     {
242                         if(values.hasMore())
243                         {
244                             while(values.hasMore())
245                             {
246                                 String value = (String)values.next();
247                                 StringBuilder sb = new StringBuilder();
248                                 sb.append("context_id = ");
249                                 sb.append(ctx.getDelegate().getContextId());
250                                 sb.append(" and name = '");
251                                 sb.append(attribute.getID());
252                                 sb.append("' and value = '");
253                                 sb.append(value);
254                                 sb.append("'");                            
255                                 persistence.delete(sb.toString(), PersistentAttribute.FACTORY);
256                             }
257                         }
258                         else
259                         {
260                             StringBuilder sb = new StringBuilder();
261                             sb.append("context_id = ");
262                             sb.append(ctx.getDelegate().getContextId());
263                             sb.append(" and name = '");
264                             sb.append(attribute.getID());
265                             sb.append("'");                            
266                             persistence.delete(sb.toString(), PersistentAttribute.FACTORY);
267                         }
268                     }    
269                     catch(PersistenceException e)
270                     {
271                         failures.add(mods[i]);
272                     }
273                 
274                     break;
275                 default:
276                     break;
277             }
278         }
279         if( failures.size() > 0)
280         {
281             try
282             {
283                 persistence.getDatabase().rollbackTransaction(transactionControler);
284             }
285             catch(SQLException e)
286             {
287                 throw new RuntimeException("Failed to begin transaction",e);
288             }
289         }
290         else
291         {
292             try
293             {
294                 persistence.getDatabase().commitTransaction(transactionControler);
295             }
296             catch(SQLException e)
297             {
298                 throw new RuntimeException("Failed to begin transaction",e);
299             }                    
300         }
301     }
302 
303     /***
304      * {@inheritDoc}
305      */
306     public void modifyAttributes(String name, ModificationItem[] mods) 
307         throws NamingException
308     {
309         modifyAttributes(new CompositeName(name), mods);
310     }
311 
312     /***
313      * {@inheritDoc}
314      */
315     public void bind(Name name, Object obj, Attributes attrs) throws NamingException
316     {
317         throw new UnsupportedOperationException();
318     }
319 
320     /***
321      * {@inheritDoc}
322      */
323     public void bind(String name, Object obj, Attributes attrs) throws NamingException
324     {
325         throw new UnsupportedOperationException();
326     }
327 
328     /***
329      * {@inheritDoc}
330      */
331     public void rebind(Name name, Object obj, Attributes attrs) throws NamingException
332     {
333         throw new UnsupportedOperationException();
334     }
335 
336     /***
337      * {@inheritDoc}
338      */
339     public void rebind(String name, Object obj, Attributes attrs) throws NamingException
340     {
341         throw new UnsupportedOperationException();
342     }
343 
344     /***
345      * {@inheritDoc}
346      */
347     public void destroySubcontext(Name name) throws NamingException
348     {
349         DatabaseContext ctx = (DatabaseContext)lookup(name);
350         long contextId = ctx.getDelegate().getContextId();
351         deleteAllAttributes(contextId);
352         super.destroySubcontext(name);        
353     }
354 
355     /***
356      * {@inheritDoc}
357      */
358     public DirContext createSubcontext(Name name, Attributes attrs) throws NamingException
359     {
360         PersistentContext delegate = createContextDelegate(name);
361         DatabaseDirContext subContext = new DatabaseDirContext(env, delegate, persistence);  
362         if (attrs != null && attrs.size() > 0)
363         {
364              subContext.modifyAttributes("", DirContext.ADD_ATTRIBUTE, attrs);
365         }
366         return subContext;
367     }
368 
369     /***
370      * {@inheritDoc}
371      */
372     public DirContext createSubcontext(String name, Attributes attrs) throws NamingException
373     {
374         return createSubcontext(new CompositeName(name),attrs);
375     }
376     
377     /***
378      * {@inheritDoc}
379      */
380     public Context createSubcontext(String name) throws NamingException
381     {
382         return createSubcontext(name,null);
383     }
384         
385     /***
386      * {@inheritDoc}
387      */
388     public Context createSubcontext(Name name) throws NamingException
389     {
390         return createSubcontext(name, null);
391     }
392         
393 
394     /***
395      * {@inheritDoc}
396      */
397     public DirContext getSchema(Name name) throws NamingException
398     {
399         throw new UnsupportedOperationException();
400     }
401 
402     /***
403      * {@inheritDoc}
404      */
405     public DirContext getSchema(String name) throws NamingException
406     {
407         return getSchema(new CompositeName(name));
408     }
409 
410     /***
411      * {@inheritDoc}
412      */
413     public DirContext getSchemaClassDefinition(Name name) throws NamingException
414     {
415         throw new UnsupportedOperationException();
416     }
417 
418     /***
419      * {@inheritDoc}
420      */
421     public DirContext getSchemaClassDefinition(String name) throws NamingException
422     {
423         return getSchemaClassDefinition(new CompositeName(name));
424     }
425 
426     /***
427      * {@inheritDoc}
428      */
429     public NamingEnumeration search(Name name, Attributes matchingAttributes, 
430                                      String[] attributesToReturn)
431         throws NamingException
432     {
433         List searchResult = new ArrayList();
434         DatabaseDirContext ctx = (DatabaseDirContext)lookup(name);
435         long contextId = ctx.getDelegate().getContextId();
436         Map attributeMap = getChildrenAttributes(contextId);
437         Iterator it = attributeMap.keySet().iterator();
438         while(it.hasNext())
439         {
440             String dn = (String)it.next();        
441             Attributes attributes = (Attributes)attributeMap.get(dn);
442             NamingEnumeration enumeration = matchingAttributes.getAll();
443             if(attributes == null && enumeration.hasMore())
444             {
445                 break;
446             }
447             boolean found = true;
448             outer: while(enumeration.hasMore())
449             {
450                 Attribute searchAttr = (Attribute)enumeration.next();
451                 Attribute attr = (Attribute)attributes.get(searchAttr.getID());
452                 if(attr == null)
453                 {
454                     found = false;
455                     break;
456                 }
457                 NamingEnumeration values = searchAttr.getAll();
458                 while (values.hasMore())
459                 {
460                     String value = (String)values.next();
461                     if(attr.contains(value))
462                     {
463                         break outer;
464                     }
465                 }
466                 found = false;
467             }
468             if(found)
469             {
470                 SearchResult result = new SearchResult(dn.substring(0, getNameInNamespace()
471                     .length() + 1), null, filterAttributes(attributes, attributesToReturn));
472                 result.setNameInNamespace(dn);
473                 searchResult.add(result);
474             }
475         }
476         return new DefaultEnumeration(searchResult);
477     }
478 
479     /***
480      * {@inheritDoc}
481      */
482     public NamingEnumeration search(String name, Attributes matchingAttributes, 
483                                      String[] attributesToReturn)
484         throws NamingException
485     {
486         return search(new CompositeName(name), matchingAttributes, null);
487     }
488 
489     /***
490      * {@inheritDoc}
491      */
492     public NamingEnumeration search(Name name, Attributes matchingAttributes)
493         throws NamingException
494     {
495         return search(name, matchingAttributes, null);
496     }
497 
498     /***
499      * {@inheritDoc}
500      */
501     public NamingEnumeration search(String name, Attributes matchingAttributes)
502         throws NamingException
503     {
504         return search(new CompositeName(name), matchingAttributes);
505     }
506 
507     /***
508      * {@inheritDoc}
509      */
510     public NamingEnumeration search(Name name, String filter, SearchControls cons)
511         throws NamingException
512     {
513         throw new UnsupportedOperationException();
514     }
515 
516     /***
517      * {@inheritDoc}
518      */
519     public NamingEnumeration search(String name, String filter, SearchControls cons)
520         throws NamingException
521     {
522         return search(new CompositeName(name), filter, cons);
523     }
524 
525     /***
526      * {@inheritDoc}
527      */
528     public NamingEnumeration search(Name name, String filterExpr, 
529                                      Object[] filterArgs, SearchControls cons) 
530         throws NamingException
531     {
532         throw new UnsupportedOperationException();
533     }
534 
535     /***
536      * {@inheritDoc}
537      */
538     public NamingEnumeration search(String name, String filterExpr,
539                                      Object[] filterArgs, SearchControls cons)
540         throws NamingException
541     {
542         throw new UnsupportedOperationException();
543     }
544 
545     /***
546      * Get all attributes that belongs to specified context.
547      * 
548      * @param contextId the context id.
549      * @return the attributes.
550      * @throws NamingException thrown if operation fails.
551      */
552     private Attributes getAllAttributes(long contextId)
553         throws NamingException
554     {
555         Attributes attrs = new BasicAttributes();
556         try
557         {
558             List list = persistence.load("context_id = "+contextId, PersistentAttribute.FACTORY);
559             for(int i = 0; i < list.size(); i++)
560             {
561                 PersistentAttribute attribute = (PersistentAttribute)list.get(i);
562                 Attribute attr = attrs.get(attribute.getName());
563                 if (attr != null)
564                 {
565                     attr.add(attribute.getValue());
566                 }
567                 else
568                 {
569                     attr = new BasicAttribute(attribute.getName());
570                     attr.add(attribute.getValue());
571                     attrs.put(attr);
572                 }                
573             }
574             return attrs;
575         }
576         catch(PersistenceException e)
577         {
578             throw new DatabaseNamingException("failed to retrieve context from database",e);
579         }
580     }
581 
582     /***
583      * Delete all attributes that belongs to specified context.
584      * 
585      * @param contextId the context id.
586      * @throws NamingException thrown if operation fails.
587      */
588     private void deleteAllAttributes(long contextId)
589         throws NamingException
590     {
591         Attributes attrs = new BasicAttributes();
592         try
593         {
594             List list = persistence.load("context_id = "+contextId, PersistentAttribute.FACTORY);
595             for(int i = 0; i < list.size(); i++)
596             {
597                 PersistentAttribute attribute = (PersistentAttribute)list.get(i);
598                 persistence.delete(attribute);
599             }
600         }
601         catch(PersistenceException e)
602         {
603             throw new DatabaseNamingException("failed to retrieve context from database",e);
604         }
605     }
606 
607     /***
608      * Filter attributes with attribute list.
609      * 
610      * @param attributes the attributes to be filter.
611      * @param attrIds the filter.
612      * @return the filtered attributes.
613      */
614     private Attributes filterAttributes(Attributes attributes, String[] attrIds)
615     {
616         if(attrIds == null)
617         {
618             return attributes;
619         }
620         Attributes target = new BasicAttributes();
621         for(int i = 0; i < attrIds.length; i++)
622         {
623             Attribute attr = attributes.get(attrIds[i]);
624             if(attr != null)
625             {
626                 target.put(attr);   
627             }
628         }
629         return target;
630     }
631     
632     /***
633      * Get all attributes from child contexts of specified context. 
634      * 
635      * @param parentId the parent context id.
636      * @return the map with attributes grouped by contexts.
637      * @throws NamingException thrown if operation fails.
638      */
639     private Map getChildrenAttributes(long parentId)
640         throws NamingException
641     {
642         Map map = new HashMap();
643         Connection conn = null;
644         try
645         {
646             conn = persistence.getDatabase().getConnection();
647             Statement statement = conn.createStatement();
648             ResultSet rs = statement.executeQuery(
649                 "SELECT dn, name, value FROM ledge_naming_context, ledge_naming_attribute " +
650                 "WHERE ledge_naming_context.context_id = ledge_naming_attribute.context_id" +
651                 " and ledge_naming_context.parent=" + parentId);            
652             while(rs.next())
653             {
654                 String dn = rs.getString("dn");
655                 String name = rs.getString("name");
656                 String value = rs.getString("value");
657                 Attributes attributes = (Attributes)map.get(dn);
658                 if(attributes == null)
659                 {
660                     attributes = new BasicAttributes();
661                     map.put(dn,attributes);
662                 }
663                 Attribute attribute = attributes.get(name);
664                 if (attribute != null)
665                 {
666                     attribute.add(value);
667                 }
668                 else
669                 {
670                     attribute = new BasicAttribute(name);
671                     attribute.add(value);
672                     attributes.put(attribute);
673                 }
674             }
675         }
676         catch (Exception e)
677         {
678             throw new DatabaseNamingException("Failed to execute query", e);
679         }
680         finally
681         {
682             DatabaseUtils.close(conn);
683         }
684         return map;        
685     }
686 }