Clover Coverage Report
Coverage timestamp: Sun Mar 23 2008 08:24:39 GMT
123   536   31   9.46
48   201   0.35   6.5
13     3.31  
2    
 
 
  MultiRepository       Line # 107 41 10 87.3% 0.87323946
  MultiThreadSearch       Line # 336 82 22 91.2% 0.91150445
 
  (1)
 
1    /*
2    * Copyright (c) 2000-2005, University of Salford
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions are met:
7    *
8    * Redistributions of source code must retain the above copyright notice, this
9    * list of conditions and the following disclaimer.
10    *
11    * Redistributions in binary form must reproduce the above copyright notice,
12    * this list of conditions and the following disclaimer in the documentation
13    * and/or other materials provided with the distribution.
14    *
15    * Neither the name of the University of Salford nor the names of its
16    * contributors may be used to endorse or promote products derived from this
17    * software without specific prior written permission.
18    *
19    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20    * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22    * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29    * POSSIBILITY OF SUCH DAMAGE.
30    */
31    /*
32    * Copyright (c) 2006, University of Kent
33    * All rights reserved.
34    *
35    * Redistribution and use in source and binary forms, with or without
36    * modification, are permitted provided that the following conditions are met:
37    *
38    * Redistributions of source code must retain the above copyright notice, this
39    * list of conditions and the following disclaimer.
40    *
41    * Redistributions in binary form must reproduce the above copyright notice,
42    * this list of conditions and the following disclaimer in the documentation
43    * and/or other materials provided with the distribution.
44    *
45    * 1. Neither the name of the University of Kent nor the names of its
46    * contributors may be used to endorse or promote products derived from this
47    * software without specific prior written permission.
48    *
49    * 2. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
50    * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
51    * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
52    * PURPOSE ARE DISCLAIMED.
53    *
54    * 3. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
55    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
58    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
59    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
60    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
61    * POSSIBILITY OF SUCH DAMAGE.
62    *
63    * 4. YOU AGREE THAT THE EXCLUSIONS IN PARAGRAPHS 2 AND 3 ABOVE ARE REASONABLE
64    * IN THE CIRCUMSTANCES. IN PARTICULAR, YOU ACKNOWLEDGE (1) THAT THIS
65    * SOFTWARE HAS BEEN MADE AVAILABLE TO YOU FREE OF CHARGE, (2) THAT THIS
66    * SOFTWARE IS NOT "PRODUCT" QUALITY, BUT HAS BEEN PRODUCED BY A RESEARCH
67    * GROUP WHO DESIRE TO MAKE THIS SOFTWARE FREELY AVAILABLE TO PEOPLE WHO WISH
68    * TO USE IT, AND (3) THAT BECAUSE THIS SOFTWARE IS NOT OF "PRODUCT" QUALITY
69    * IT IS INEVITABLE THAT THERE WILL BE BUGS AND ERRORS, AND POSSIBLY MORE
70    * SERIOUS FAULTS, IN THIS SOFTWARE.
71    *
72    * 5. This license is governed, except to the extent that local laws
73    * necessarily apply, by the laws of England and Wales.
74    */
75   
76    package issrg.utils.repository;
77   
78    import javax.naming.directory.DirContext;
79    import javax.naming.CompositeName;
80    import javax.naming.directory.Attribute;
81    import javax.naming.directory.Attributes;
82    import javax.naming.directory.BasicAttribute;
83    import java.util.Vector;
84    import java.security.Principal;
85   
86    import org.apache.log4j.*;// added for logging
87    /**
88    * This class is the implementation of the Attribute Repository for
89    * multithreaded access to a cluster of repositories.
90    * It can be built out of an array of Repositories. Each of these repositories
91    * constitutes a root for searches.
92    *
93    * <p>The object can be used for retrieving similar information from multiple
94    * directories simultaneously. For example, it is useful when retrieving X.509
95    * Attribute Certificates for PMI entities that possess ACs issued by different
96    * issuers (therefore, stored in different repositories available to them).
97    *
98    * <p>The object creates multiple threads (one per repository) when attributes
99    * are requested and performs the searches simultaneously. This improves
100    * efficiency, since most of the time the repositories
101    * are waiting for a reply. The object waits till all of the contexts return
102    * anything or report an error, so the result is always complete.
103    *
104    * @author A Otenko
105    * @version 0.2
106    */
 
107    public class MultiRepository extends DefaultRepository {
108    private AttributeRepository [] initialRepositories;
109    private MultiThreadSearch mts = new MultiThreadSearch();
110    private static Logger logger = Logger.getLogger(MultiRepository.class);
111   
 
112  0 toggle protected MultiRepository(){}
113   
114    /**
115    * This constructor builds the MultiRepository with a number of roots.
116    *
117    * @param repositories An array of AttributeRepositories to be used as the
118    * search roots
119    */
 
120  713 toggle public MultiRepository(AttributeRepository [] repositories) {
121  713 AttributeRepository [] r=new AttributeRepository[repositories.length];
122  713 System.arraycopy(repositories, 0, r, 0, r.length);
123   
124   
125    // now prune the null pointers
126   
127  713 int j=0; // the first null entry in the array - the number of non-null repositories
128  1448 for (int i=0; i<r.length; i++){
129  735 if (r[j]==null && r[i]!=null){
130  0 r[j]=r[i]; // move non-null repositories to the front
131  0 r[i]=null;
132    }
133   
134  735 if (r[j]!=null) j++;
135    }
136    // now j is the number of non-null repositories, and they all are at the beginning of the array
137   
138  713 initialRepositories=new AttributeRepository[j];
139  713 System.arraycopy(r, 0, initialRepositories, 0, initialRepositories.length);
140    }
141   
142    /**
143    * This creates a MultiRepository with a single root repository
144    *
145    * @param repository A AttributeRepository
146    */
 
147  682 toggle public MultiRepository(AttributeRepository repository) {
148  682 this(new AttributeRepository[]{repository});
149    }
150   
151    /**
152    * This method searches for the given attributes in the repositories provided
153    * at construction time.
154    *
155    * @param DN - the Principal naming the entry in the repositories
156    * @param AttributeNames - the names of the attributes to be retrieved; if
157    * null, all available attributes will be returned
158    *
159    * @return the requested Attributes
160    */
 
161  3221 toggle public Attributes getAttributes(java.security.Principal DN, String [] AttributeNames) throws RepositoryException{
162    // construct a synchronous list of entry names for each repository -
163    // use the same entry name for all repositories
164  3221 java.security.Principal dns[] = new java.security.Principal[initialRepositories.length];
165  6479 for (int i=0; i<dns.length; i++){
166  3258 dns[i]=DN;
167    }
168  3221 return getAttributes(dns, initialRepositories, AttributeNames);
169    }
170   
171    /**
172    * This method will collect attributes from all the entries identified by
173    * the TokenLocator. Each TokenLocator in the chain points to a location in
174    * a repository; if the repository in it is null, all the repositories
175    * provided at construction time
176    * MultiRepository will be searched; otherwise only the repository stated in
177    * the TokenLocator
178    * will be searched.
179    *
180    * @param locator - the locator of the entries with the attributes
181    * @param AttributeNames - the names of the attributes
182    *
183    * @return Attributes that are located in all the locations pointed to by the
184    * TokenLocator
185    */
 
186  3233 toggle public Attributes getAttributes(TokenLocator locator, String [] AttributeNames) throws RepositoryException {
187  3233 Vector dns = new Vector();
188  3233 Vector reps = new Vector();
189  6466 while(locator!=null){
190  3233 dns.add(locator.getLocator());
191  3233 AttributeRepository r = locator.getRepository();
192  3233 reps.add(r==null? this: r); // use this MultiRepository, if no specific repository was specified
193  3233 locator=locator.getAlternativeLocator();
194    }
195  3233 return getAttributes((Principal [])dns.toArray(new Principal[0]),
196    (AttributeRepository [])reps.toArray(new AttributeRepository [0]),
197    AttributeNames);
198    }
199   
200    /**
201    * This is method searches for a single attribute. This is a shortcut for
202    * getAttributes(locator, new String[]{AttributeName}).get(AttributeName);
203    *
204    * @param locator - the TokenLocator identifying multiple locations of the
205    * attribute
206    * @param AttributeName - the name of the attribute to retrieve
207    *
208    * @return the requested Attribute
209    */
 
210  3233 toggle public Attribute getAttribute(TokenLocator locator, String AttributeName) throws RepositoryException{
211  3233 String [] AttributeNames= new String[]{AttributeName};
212  3233 Attributes attrs = getAttributes(locator, AttributeNames);
213   
214  3233 return (attrs==null)?null:(attrs.get(AttributeName));
215    }
216   
217    /**
218    * This is the root method called by any other getAttributes that gets the
219    * set of named attributes from the entries with the DNs.
220    * It searches all provided repositories simultaneously. If the DN and named
221    * attribute
222    * exist in more than one of the named contexts, then multiple attribute
223    * values will be returned.
224    *
225    * <p>The list of entry names DN is synchronised with the list of
226    * repositories so that each entry name is used only for the corresponding
227    * repository, but the whole list of attributes is requested from each
228    * repository. When other getAttributes methods use this method, they simply
229    * construct such synchronous lists of entry names and repositories. These
230    * methods do
231    * not update the status or diagnosis set by this method, and they propagate
232    * the exceptions thrown by this method.
233    *
234    * <p>Effectively, this method constructs a new MultiThreadSearch and invokes
235    * a getAttributes method on it. Because this may cause recursive invocation
236    * of this method from different threads, this method is NOT synchronized.
237    * For example,
238    * a search for attributes in two chained TokenLocators with no repository
239    * specified constructs one MultiThreadSearch, which spawns two threads, each
240    * attempting to invoke getAttributes
241    * method on this very MultiRepository object; this should be allowed (no
242    * mutex on the method), and in its turn creates two other MultiThreadSearches,
243    * each spawning one thread for each initial repository passed to
244    * the MultiRepository at construction time. The side effect of this is that
245    * during the call to getAttributes the status and diagnosis values are
246    * undetermined, since the calls to multiple MultiThreadSearch will
247    * temporarily set
248    * the status and diagnosis of this MultiRepository, until the recursion
249    * unwinds back, and the MultiThreadSearch at the root of this recursion will
250    * set the ultimate diagnosis and status of invocation of getAttributes
251    * method. Note also that if the getAttributes method on the corresponding
252    * repositories is synchronized, this will only slow down the multithreaded
253    * search, but will not block the process (unless by some bad design they
254    * use the same MultiRepository that invoked them).
255    *
256    * <p>After calling the method the repository will be set into one of the
257    * states: FAILURE_STATUS, SUCCESS_STATUS or PARTIAL_SUCCESS_STATUS. Failure
258    * means there were no roots that succeeded. Success means that all of the
259    * roots succeeded (the entries were found and some or no attributes were
260    * retrieved). Partial success means that some of the roots failed, but some
261    * have succeeded, which may be in case some of the roots do not contain
262    * the required entry. The caller must find out himself what the cause is, and
263    * decide if the results are sufficiently successful.
264    *
265    * @param DN The distinguished names of the entry from which the attributes
266    * are requested
267    * @param repositories The repositories to be searched; must have the same number
268    * of elements, as DN; each DN must have a non-null repository corresponding
269    * @param AttributeNames The array of LDAP names for the attributes; can be
270    * null, if all available attributes and their values are to be retrieved
271    *
272    * @return the requested attributes; the Repository status reflects the status
273    * of retrieval, the diagnosis contains exceptions the underlying objects
274    * threw, if they failed
275    *
276    * @throws RepositoryException, if all of the repositories failed, in which case the
277    * embedded exception will be the Throwable returned by <code>getDiagnosis</code>
278    * method; FAILURE_STATUS will also be set
279    *
280    * @see MultiThreadSearch#getAttributes
281    */
 
282  6454 toggle protected Attributes getAttributes(java.security.Principal[] DN, AttributeRepository [] repositories, String [] AttributeNames) throws RepositoryException{
283  6454 MultiThreadSearch ms = new MultiThreadSearch();
284  6454 try{
285  6454 logger.debug("get attributes based on ");
286  12945 for (int i=0; i<DN.length; i++) logger.debug("dn = "+DN[i].getName());
287  12945 for (int i=0; i<repositories.length; i++) logger.debug("repository = "+repositories[i].getClass().getName());
288  12908 for (int i=0; i<AttributeNames.length; i++) logger.debug("attribute name = "+AttributeNames[i]);
289  6454 return ms.getAttributes(DN, repositories, AttributeNames);
290    }finally{
291  6454 mts=ms;
292    }
293    }
294   
295    /**
296    * This method returns the status of the repository. It is set when returning
297    * attributes.
298    *
299    * @return the integer value corresponding to the status
300    *
301    * @see getAttributes(java.security.Principal,String[])
302    */
 
303  0 toggle public int getStatus(){
304  0 return mts.status;
305    }
306   
307    /**
308    * This method returns the Throwable, representing the error, or null, if no
309    * error has been encountered (only if the repository is in SUCCESS_STATUS).
310    * The Throwable contains an error message and the stack trace of the error.
311    *
312    * @return Throwable object, representing the error, or null, if no error occured
313    */
 
314  3215 toggle public Throwable getDiagnosis(){
315  3215 return mts.diagnosis;
316    }
317   
318    /**
319    * This method returns the array of repositories used by the MultiRepository
320    * by default (when TokenLocators do not refer to a specific repository).
321    *
322    * @return the array of AttributeRespositories used by MultiRepository by
323    * default; is never null, does not contain null entries, but may be empty
324    * (zero length)
325    */
 
326  0 toggle public AttributeRepository [] getRepositories() {
327  0 return initialRepositories;
328    }
329    }
330   
331    /**
332    * This is a utility class that does the actual search in multiple repositories
333    * using multiple threads. The logic of invocation must guarantee that it is
334    * used from a single Thread.
335    */
 
336    class MultiThreadSearch implements Runnable {
337    protected int status; // status of the Repository
338    protected Throwable diagnosis; // the ultimate status: the stack frame in it, the error message to deliver, etc
339    private static Logger logger = Logger.getLogger(MultiThreadSearch.class);
340    private AttributeRepository [] reps;
341    private Throwable [] statuses; // the latest Exceptions thrown by run() methods; null for OK
342    private Attributes [] results; // the latest results returned by run() methods
343   
344    /**
345    * Variables used by run() method
346    */
347    private java.security.Principal searchDN[]; // this is the DN that the run() method uses
348    private String [] attrIDs; // this is the IDs of attributes the run() method uses
349    private int launched=0; // counter of launched threads
350    private int [] finished={0}; // counter of terminated threads
351    // it is an array, because I want it to be secure when calling getAttributes twice in a row for any unobvious reason - I can do wait() on arrays
 
352  7167 toggle protected MultiThreadSearch(){}
353   
354    /**
355    * This method sets up a number of threads, each retrieving the whole set of
356    * attributes from one repository using the corresponding entry name. The
357    * list of entry names must be synchronous with the list of repositories.
358    *
359    * <p>As the result of calling this method, the diagnosis and status will be
360    * set.
361    *
362    * @param DN - the entry names; one for each repository
363    * @param repositories - the repositories; one for each entry in the DN array
364    * @param AttributeNames - the names of the attributes to be retrieved from
365    * all repositories; if null, all available attributes will be retrieved
366    *
367    * @return the requested Attributes
368    */
 
369  6454 toggle protected Attributes getAttributes(java.security.Principal[] DN, AttributeRepository [] repositories, String [] AttributeNames) throws RepositoryException{
370  6454 status = AttributeRepository.SUCCESS_STATUS;
371  6454 diagnosis = null; // nothing has been thrown so far
372  6454 launched=0;
373  6454 finished=new int[]{0}; // thus, stray threads will use their own copy
374  6454 searchDN=DN;
375  6454 reps=repositories;
376  6454 attrIDs = AttributeNames;
377  6454 statuses = new Throwable[reps.length];
378  6454 results = new Attributes[reps.length];
379   
380  6454 try{
381  6454 try{
382  12945 while (launched<reps.length){ // launched is updated by run() method, called by Thread().start()
383  6491 synchronized(this){
384  6491 new Thread(this).start(); // searches in different LDAPs will be mainly independent, and lots of waiting; so let them be different threads
385  6491 this.wait(); // wait till the run() passes by updating the launched counter
386    }
387    }
388  6454 synchronized(this.finished){
389  6454 if (finished[0]<reps.length){ // yet not everyone has finished.
390  6305 this.finished.wait(); // so hold me, until it sleeps.. ;-)
391    // until it wakes, actually :-)))))
392    }
393    }
394   
395    // now we are alright; collect the wrecks :-)
396    // or whatever the world has brought to us
397   
398    // we are synchronized, and noone else is supposed to replace statuses and results array values
399  6454 Exception pe = null;
400   
401  12945 for (int i=0; i<statuses.length; i++){
402  6491 if (statuses[i]!=null){
403    // something has happened in thread i while accessing reps[i]
404   
405    // at this point we should verify how crucial the error was
406  3 pe = new issrg.utils.ExceptionPairException(pe, statuses[i]); // now we can output the thing
407    }
408  6491 if (statuses[i]==null || results[i]!=null) status = AttributeRepository.PARTIAL_SUCCESS_STATUS;
409    }
410   
411  6454 if (pe!=null && status==AttributeRepository.SUCCESS_STATUS){
412    // this means that the branch status=PARTIAL_SUCCESS_STATUS has never been
413    // executed; meaning that all of the statuses were not null; meaning
414    // it is a complete failure.
415  0 throw new RepositoryException(null, pe);
416    }
417   
418  6454 if (pe==null){
419    // this means that all the iterations of the loop went through
420    // status=PARTIAL_SUCCESS_STATUS branch, so in fact it is a complete success
421  6451 status=AttributeRepository.SUCCESS_STATUS;
422    }
423   
424  6454 diagnosis = pe;
425   
426    // here it is safe to collect results
427    // do not amend the status variable
428   
429  6454 Attributes result = new javax.naming.directory.BasicAttributes();
430   
431  12945 for (int i=0; i<results.length; i++){
432  6491 Attributes a = results[i];
433  6491 if (a==null) continue;
434   
435  11862 for(javax.naming.NamingEnumeration ne = a.getIDs(); ne.hasMoreElements();){
436  5734 String id = (String)ne.nextElement();
437  5734 javax.naming.directory.Attribute attr = a.get(id), collection_attr=result.get(id);
438  5734 if (attr!=null){
439  5734 if (collection_attr==null){
440  5730 result.put(attr);
441    }else{
442  8 for(javax.naming.NamingEnumeration ne1 = attr.getAll(); ne1.hasMoreElements(); ){
443  4 collection_attr.add(ne1.nextElement()); // do I check for the return value?..
444    }
445   
446    // since we have updated the object by reference, this should be sufficient
447    // unless they return us a clone...
448    }
449    }
450    }
451    }
452   
453    // now result contains all Attributes and all values for each of them
454   
455  6454 return result;
456    }catch (InterruptedException ie){
457    // this and the following catches are put here for the reason
458    // we might want to change the error code or the exception thrown
459    // (e.g. throw a descendant of the PbaException, not the PbaException itself)
460  0 throw new RepositoryException("The thread was interrupted while waiting", ie);
461    }catch (javax.naming.NamingException ne){
462  0 throw new RepositoryException("Naming violation", ne);
463    }catch(Throwable th){
464  0 throw new RepositoryException("Unknown error", th);
465    }
466    }catch(RepositoryException pe){ // really, this try{}catch block is needed purely to set this status
467    // note, that nothing else is thrown withing that block(!)
468    // so the method is error-free; i mean, all the reports can be
469    // reported to the caller in a valid manner
470  0 status = AttributeRepository.FAILURE_STATUS;
471  0 diagnosis=pe;
472  0 throw pe;
473    }
474   
475    }
476   
477    /**
478    * This method is needed for efficient Attribute retrieval. You should not
479    * call it directly, as this is part of Runnable interface.
480    */
 
481  6491 toggle public void run(){
482  6491 int idx;
483    // lets have a local copy of everything, so if the getAttribtues screws up,
484    // we won't screw up a consequent call.
485  6491 AttributeRepository rep;
486  6491 int [] fin;
487  6491 int when_notify;
488  6491 Attributes [] ress;
489  6491 Throwable [] stats;
490   
491  6491 java.security.Principal searchDN;
492  6491 String [] attrIDs = this.attrIDs;
493   
494   
495  6491 synchronized(this){
496  6491 when_notify = reps.length;
497  6491 idx=launched;
498  6491 ress=results;
499  6491 stats=statuses;
500  6491 searchDN = this.searchDN[idx];
501  6491 fin = finished; // save the pointer, so even if anything happens to the caller thread, we won't spoil the subsequent calls to the run()
502  6491 results[idx]=null;
503  6491 rep=reps[idx];
504   
505    // unlock the waiter - let us launch search in another repository
506  6491 launched++;
507  6491 this.notifyAll();
508    }
509   
510    // now i believe all entries about the idx index are locked for my sole use
511   
512  6491 Attributes result=null;
513  6491 Throwable status=null;
514  6491 try{
515    // do some real retrieval here :-)
516   
517    // calling the implementation's method - it will go look for the attributes
518  6491 logger.debug("start to get "+searchDN.getName()+" from "+rep.getClass().getName());
519  6491 result = rep.getAttributes(searchDN, attrIDs);
520  6491 logger.debug("successful");
521  6491 status = rep.getDiagnosis(); // this is not thread-safe; If multiple threads use the same repository it may happen some diagnosis is lost, but the advantage is that multiple threads may invoke search on the same repository
522    }catch(Throwable th){
523  0 status=th;
524    }finally{
525  6491 ress[idx]=result;
526  6491 stats[idx]=status;
527   
528  6491 synchronized(fin){
529  6491 fin[0]++;
530  6491 if (when_notify<=fin[0]){ // release the wait of the getAttributes()
531  6454 fin.notifyAll();
532    }
533    }
534    }
535    }
536    }