1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.directory.server.config;
22
23
24 import java.lang.reflect.Array;
25 import java.lang.reflect.Constructor;
26 import java.lang.reflect.Field;
27 import java.lang.reflect.InvocationTargetException;
28 import java.lang.reflect.Method;
29 import java.lang.reflect.ParameterizedType;
30 import java.lang.reflect.Type;
31 import java.util.ArrayList;
32 import java.util.Collection;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Set;
36
37 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
38 import org.apache.directory.api.ldap.model.cursor.Cursor;
39 import org.apache.directory.api.ldap.model.entry.Attribute;
40 import org.apache.directory.api.ldap.model.entry.Entry;
41 import org.apache.directory.api.ldap.model.entry.Value;
42 import org.apache.directory.api.ldap.model.exception.LdapException;
43 import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
44 import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
45 import org.apache.directory.api.ldap.model.filter.EqualityNode;
46 import org.apache.directory.api.ldap.model.message.AliasDerefMode;
47 import org.apache.directory.api.ldap.model.message.SearchScope;
48 import org.apache.directory.api.ldap.model.name.Dn;
49 import org.apache.directory.api.ldap.model.name.Rdn;
50 import org.apache.directory.api.ldap.model.schema.AttributeType;
51 import org.apache.directory.api.ldap.model.schema.ObjectClass;
52 import org.apache.directory.api.ldap.model.schema.SchemaManager;
53 import org.apache.directory.api.util.Strings;
54 import org.apache.directory.server.config.beans.AdsBaseBean;
55 import org.apache.directory.server.config.beans.ConfigBean;
56 import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
57 import org.apache.directory.server.core.api.partition.PartitionTxn;
58 import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
59 import org.apache.directory.server.i18n.I18n;
60 import org.apache.directory.server.xdbm.IndexEntry;
61 import org.apache.directory.server.xdbm.search.PartitionSearchResult;
62 import org.apache.directory.server.xdbm.search.SearchEngine;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66
67
68
69
70
71
72
73 public class ConfigPartitionReader
74 {
75
76 private static final Logger LOG = LoggerFactory.getLogger( ConfigPartitionReader.class );
77
78
79 private AbstractBTreePartition configPartition;
80
81
82 private SearchEngine se;
83
84
85 private SchemaManager schemaManager;
86
87
88 private static final String ADS_PREFIX = "ads-";
89
90
91 private static final String ADS_SUFFIX = "Bean";
92
93
94
95
96
97
98
99
100 public ConfigPartitionReader( AbstractBTreePartition configPartition )
101 {
102 if ( configPartition == null )
103 {
104 throw new IllegalArgumentException( I18n.err( I18n.ERR_503 ) );
105 }
106
107 if ( !configPartition.isInitialized() )
108 {
109 throw new IllegalStateException( I18n.err( I18n.ERR_504 ) );
110 }
111
112 this.configPartition = configPartition;
113 se = configPartition.getSearchEngine();
114 this.schemaManager = configPartition.getSchemaManager();
115 }
116
117
118
119
120
121
122 private ObjectClass findObjectClass( Attribute objectClass ) throws Exception
123 {
124 Set<ObjectClass> candidates = new HashSet<>();
125
126
127 for ( Value ocValue : objectClass )
128 {
129 String ocName = ocValue.getString();
130 String ocOid = schemaManager.getObjectClassRegistry().getOidByName( ocName );
131 ObjectClass oc = schemaManager.getObjectClassRegistry().get( ocOid );
132
133 if ( oc.isStructural() )
134 {
135 candidates.add( oc );
136 }
137 }
138
139
140 for ( Value ocValue : objectClass )
141 {
142 String ocName = ocValue.getString();
143 String ocOid = schemaManager.getObjectClassRegistry().getOidByName( ocName );
144 ObjectClass oc = schemaManager.getObjectClassRegistry().get( ocOid );
145
146 for ( ObjectClass superior : oc.getSuperiors() )
147 {
148 if ( oc.isStructural() && candidates.contains( superior ) )
149 {
150 candidates.remove( superior );
151 }
152 }
153 }
154
155
156 ObjectClass result = candidates.toArray( new ObjectClass[]
157 {} )[0];
158
159 LOG.debug( "The top level object class is {}", result.getName() );
160 return result;
161 }
162
163
164
165
166
167
168
169
170
171 private AdsBaseBean createBean( ObjectClass objectClass ) throws ConfigurationException
172 {
173
174 String objectClassName = objectClass.getName();
175
176
177
178 String beanName = this.getClass().getPackage().getName() + ".beans."
179 + Character.toUpperCase( objectClassName.charAt( ADS_PREFIX.length() ) )
180 + objectClassName.substring( ADS_PREFIX.length() + 1 ) + ADS_SUFFIX;
181
182 try
183 {
184 Class<?> clazz = Class.forName( beanName );
185 Constructor<?> constructor = clazz.getConstructor();
186 AdsBaseBean./../../../org/apache/directory/server/config/beans/AdsBaseBean.html#AdsBaseBean">AdsBaseBean bean = ( AdsBaseBean ) constructor.newInstance();
187
188 LOG.debug( "Bean {} created for ObjectClass {}", beanName, objectClassName );
189
190 return bean;
191 }
192 catch ( ClassNotFoundException cnfe )
193 {
194 String message = "Cannot find a Bean class for the ObjectClass name " + objectClassName;
195 LOG.error( message );
196 throw new ConfigurationException( message );
197 }
198 catch ( SecurityException e )
199 {
200 String message = "Cannot access to the class " + beanName;
201 LOG.error( message );
202 throw new ConfigurationException( message );
203 }
204 catch ( NoSuchMethodException nsme )
205 {
206 String message = "Cannot find a constructor for the class " + beanName;
207 LOG.error( message );
208 throw new ConfigurationException( message );
209 }
210 catch ( InvocationTargetException ite )
211 {
212 String message = "Cannot invoke the class " + beanName + ", " + ite.getMessage();
213 LOG.error( message );
214 throw new ConfigurationException( message );
215 }
216 catch ( IllegalAccessException iae )
217 {
218 String message = "Cannot access to the constructor for class " + beanName;
219 LOG.error( message );
220 throw new ConfigurationException( message );
221 }
222 catch ( InstantiationException ie )
223 {
224 String message = "Cannot instantiate the class " + beanName + ", " + ie.getMessage();
225 LOG.error( message );
226 throw new ConfigurationException( message );
227 }
228 }
229
230
231
232
233
234 private void readSingleValueField( AdsBaseBean bean, Field beanField, Attribute fieldAttr )
235 throws ConfigurationException
236 {
237 if ( fieldAttr == null )
238 {
239 return;
240 }
241
242
243 Value value = fieldAttr.get();
244 String valueStr = "";
245
246 if ( value != null )
247 {
248 valueStr = value.getString();
249 }
250
251 Class<?> type = beanField.getType();
252
253
254 try
255 {
256 if ( type == String.class )
257 {
258 beanField.set( bean, valueStr );
259 }
260 else if ( type == byte[].class )
261 {
262 if ( value != null )
263 {
264 beanField.set( bean, value.getBytes() );
265 }
266 else
267 {
268 beanField.set( bean, Strings.EMPTY_BYTES );
269 }
270 }
271 else if ( type == int.class )
272 {
273 beanField.setInt( bean, Integer.parseInt( valueStr ) );
274 }
275 else if ( type == long.class )
276 {
277 beanField.setLong( bean, Long.parseLong( valueStr ) );
278 }
279 else if ( type == boolean.class )
280 {
281 beanField.setBoolean( bean, Boolean.parseBoolean( valueStr ) );
282 }
283 else if ( type == Dn.class )
284 {
285 try
286 {
287 Dn dn = new Dn( valueStr );
288 beanField.set( bean, dn );
289 }
290 catch ( LdapInvalidDnException lide )
291 {
292 String message = "The Dn '" + valueStr + "' for attribute " + fieldAttr.getId()
293 + " is not a valid Dn";
294 LOG.error( message );
295 throw new ConfigurationException( message );
296 }
297 }
298 }
299 catch ( IllegalArgumentException | IllegalAccessException e )
300 {
301 String message = "Cannot store '" + valueStr + "' into attribute " + fieldAttr.getId();
302 LOG.error( message );
303 throw new ConfigurationException( message );
304 }
305 }
306
307
308
309
310
311 private void readMultiValuedField( AdsBaseBean bean, Field field, Attribute attribute )
312 throws ConfigurationException
313 {
314 if ( attribute == null )
315 {
316 return;
317 }
318
319 Class<?> type = field.getType();
320
321 String fieldName = field.getName();
322 String addMethodName = "add" + Character.toUpperCase( fieldName.charAt( 0 ) ) + fieldName.substring( 1 );
323
324
325 for ( Value value : attribute )
326 {
327 String valueStr = value.getString();
328
329 try
330 {
331 if ( type == String.class )
332 {
333 field.set( bean, valueStr );
334 }
335 else if ( type == int.class )
336 {
337 field.setInt( bean, Integer.parseInt( valueStr ) );
338 }
339 else if ( type == long.class )
340 {
341 field.setLong( bean, Long.parseLong( valueStr ) );
342 }
343 else if ( type == boolean.class )
344 {
345 field.setBoolean( bean, Boolean.parseBoolean( valueStr ) );
346 }
347 else if ( type == Dn.class )
348 {
349 try
350 {
351 Dn dn = new Dn( valueStr );
352 field.set( bean, dn );
353 }
354 catch ( LdapInvalidDnException lide )
355 {
356 String message = "The Dn '" + valueStr + "' for attribute " + attribute.getId()
357 + " is not a valid Dn";
358 LOG.error( message );
359 throw new ConfigurationException( message );
360 }
361 }
362 else if ( ( type == Set.class ) || ( type == List.class ) )
363 {
364 Type genericFieldType = field.getGenericType();
365 Class<?> fieldArgClass = null;
366
367 if ( genericFieldType instanceof ParameterizedType )
368 {
369 ParameterizedType parameterizedType = ( ParameterizedType ) genericFieldType;
370 Type[] fieldArgTypes = parameterizedType.getActualTypeArguments();
371
372 for ( Type fieldArgType : fieldArgTypes )
373 {
374 fieldArgClass = ( Class<?> ) fieldArgType;
375 }
376 }
377
378 Method method = bean.getClass().getMethod( addMethodName,
379 Array.newInstance( fieldArgClass, 0 ).getClass() );
380
381 method.invoke( bean, new Object[] { new String[] { valueStr } } );
382 }
383 }
384 catch ( IllegalArgumentException | IllegalAccessException e )
385 {
386 String message = "Cannot store '" + valueStr + "' into attribute " + attribute.getId();
387 LOG.error( message );
388 throw new ConfigurationException( message );
389 }
390 catch ( SecurityException e )
391 {
392 String message = "Cannot access to the class " + bean.getClass().getName();
393 LOG.error( message );
394 throw new ConfigurationException( message );
395 }
396 catch ( NoSuchMethodException nsme )
397 {
398 String message = "Cannot find a method " + addMethodName + " in the class " + bean.getClass().getName();
399 LOG.error( message );
400 throw new ConfigurationException( message );
401 }
402 catch ( InvocationTargetException ite )
403 {
404 String message = "Cannot invoke the class " + bean.getClass().getName() + ", " + ite.getMessage();
405 LOG.error( message );
406 throw new ConfigurationException( message );
407 }
408 catch ( NegativeArraySizeException nase )
409 {
410
411 }
412 }
413 }
414
415
416 private void readFieldValue( AdsBaseBean bean, Field field, Entry entry, String attributeTypeName, boolean mandatory )
417 throws ConfigurationException
418 {
419
420 Attribute attribute = entry.get( attributeTypeName );
421
422 if ( attribute != null )
423 {
424 if ( attribute.size() > 0 )
425 {
426 if ( !isMultiple( field.getType() ) )
427 {
428 readSingleValueField( bean, field, attribute );
429 }
430 else
431 {
432 readMultiValuedField( bean, field, attribute );
433 }
434 }
435 else if ( attribute.size() == 0 )
436 {
437
438 readSingleValueField( bean, field, attribute );
439 }
440 else if ( mandatory )
441 {
442
443 String message = "No value was configured for entry with DN '"
444 + entry.getDn() + "' and attribute type '" + attributeTypeName + "'.";
445 LOG.error( message );
446 throw new ConfigurationException( message );
447 }
448 }
449 else
450 {
451 if ( mandatory )
452 {
453
454 String message = "No value was configured for entry with DN '"
455 + entry.getDn() + "' and attribute type '" + attributeTypeName + "'.";
456 LOG.error( message );
457 throw new ConfigurationException( message );
458 }
459 }
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473 public List<AdsBaseBean> read( Dn baseDn, String name, SearchScope scope, boolean mandatory )
474 throws ConfigurationException
475 {
476 LOG.debug( "Reading from '{}', objectClass '{}'", baseDn, name );
477
478
479
480 AttributeType ocAt = schemaManager.getAttributeType( SchemaConstants.OBJECT_CLASS_AT );
481 EqualityNode<String> filter = null;
482
483 try
484 {
485 filter = new EqualityNode<>( ocAt, new Value( ocAt, name ) );
486 }
487 catch ( LdapInvalidAttributeValueException liave )
488 {
489 throw new ConfigurationException( liave.getMessage() );
490 }
491
492 Cursor<IndexEntry<String, String>> cursor = null;
493
494
495 List<AdsBaseBean> beansList = new ArrayList<>();
496
497 try
498 {
499
500
501 try ( PartitionTxn partitionTxn = configPartition.beginReadTransaction() )
502 {
503 SearchOperationContext/context/SearchOperationContext.html#SearchOperationContext">SearchOperationContext searchContext = new SearchOperationContext( null );
504 searchContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
505 searchContext.setDn( baseDn );
506 searchContext.setFilter( filter );
507 searchContext.setScope( scope );
508 searchContext.setPartition( configPartition );
509 searchContext.setTransaction( partitionTxn );
510 PartitionSearchResult searchResult = se.computeResult( partitionTxn, schemaManager, searchContext );
511
512 cursor = searchResult.getResultSet();
513
514
515 if ( !cursor.next() )
516 {
517 if ( mandatory )
518 {
519 cursor.close();
520
521
522 String message = "No instance was configured under the DN '"
523 + baseDn + "' for the objectClass '" + name + "'.";
524 LOG.error( message );
525 throw new ConfigurationException( message );
526 }
527 else
528 {
529 return null;
530 }
531 }
532
533
534 do
535 {
536 IndexEntry<String, String> forwardEntry = cursor.get();
537
538
539 Entry entry = configPartition.fetch( partitionTxn, forwardEntry.getId() );
540 LOG.debug( "Entry read : {}", entry );
541
542 AdsBaseBean bean = readConfig( entry );
543
544 beansList.add( bean );
545 }
546 while ( cursor.next() );
547 }
548 }
549 catch ( ConfigurationException ce )
550 {
551 throw ce;
552 }
553 catch ( Exception e )
554 {
555 String message = "An error occured while reading the configuration DN '"
556 + baseDn + "' for the objectClass '" + name + "':\n" + e.getMessage();
557 LOG.error( message );
558 throw new ConfigurationException( message, e );
559 }
560 finally
561 {
562 if ( cursor != null )
563 {
564 try
565 {
566 cursor.close();
567 }
568 catch ( Exception e )
569 {
570
571
572 throw new ConfigurationException( e.getMessage(), e.getCause() );
573 }
574 }
575 }
576
577 return beansList;
578 }
579
580
581
582
583
584
585
586
587
588 public AdsBaseBean readConfig( Entry entry ) throws Exception
589 {
590
591
592 ObjectClass objectClass = findObjectClass( entry.get( SchemaConstants.OBJECT_CLASS_AT ) );
593
594
595 AdsBaseBean bean = createBean( objectClass );
596
597
598 bean.setDn( entry.getDn() );
599
600
601 Class<?> beanClass = bean.getClass();
602
603
604
605 boolean adsBaseBeanClassFound = false;
606
607
608 while ( !adsBaseBeanClassFound )
609 {
610
611 if ( beanClass == AdsBaseBean.class )
612 {
613 adsBaseBeanClassFound = true;
614 }
615
616
617 Field[] fields = beanClass.getDeclaredFields();
618 for ( Field field : fields )
619 {
620
621 field.setAccessible( true );
622
623
624 Class<?> fieldClass = field.getType();
625
626
627 ConfigurationElement configurationElement = field.getAnnotation( ConfigurationElement.class );
628 if ( configurationElement != null )
629 {
630
631 String fieldAttributeType = configurationElement.attributeType();
632 String fieldObjectClass = configurationElement.objectClass();
633 String container = configurationElement.container();
634 boolean isOptional = configurationElement.isOptional();
635
636
637 if ( ( fieldAttributeType != null ) && ( !"".equals( fieldAttributeType ) ) )
638 {
639 readFieldValue( bean, field, entry, fieldAttributeType, !isOptional );
640 }
641
642 else if ( ( fieldObjectClass != null ) && ( !"".equals( fieldObjectClass ) ) )
643 {
644
645 if ( isMultiple( fieldClass ) && ( container != null )
646 && ( !"".equals( container ) ) )
647 {
648
649 Dn newBase = entry.getDn().add( "ou=" + container );
650
651
652 Collection<AdsBaseBean> fieldValues = read( newBase, fieldObjectClass,
653 SearchScope.ONELEVEL, !isOptional );
654
655
656 if ( ( fieldValues != null ) && !fieldValues.isEmpty() )
657 {
658 field.set( bean, fieldValues );
659 }
660 }
661
662 else
663 {
664
665 List<AdsBaseBean> fieldValues = read( entry.getDn(), fieldObjectClass,
666 SearchScope.ONELEVEL, !isOptional );
667
668
669 if ( ( fieldValues != null ) && !fieldValues.isEmpty() )
670 {
671 field.set( bean, fieldValues.get( 0 ) );
672 }
673 }
674 }
675 }
676 }
677
678
679 beanClass = beanClass.getSuperclass();
680 }
681
682 return bean;
683 }
684
685
686
687
688
689
690
691
692
693
694
695 private boolean isMultiple( Class<?> clazz )
696 {
697 return Collection.class.isAssignableFrom( clazz );
698 }
699
700
701
702
703
704
705
706
707
708
709 public ConfigBean readConfig() throws LdapException
710 {
711
712 return readConfig( new Dn( new Rdn( SchemaConstants.OU_AT, "config" ) ) );
713 }
714
715
716
717
718
719
720
721
722
723 public ConfigBean readConfig( String baseDn ) throws LdapException
724 {
725
726 return readConfig( new Dn( baseDn ) );
727 }
728
729
730
731
732
733
734
735
736
737 public ConfigBean readConfig( Dn baseDn ) throws ConfigurationException
738 {
739
740 return readConfig( baseDn, ConfigSchemaConstants.ADS_DIRECTORY_SERVICE_OC.getValue() );
741 }
742
743
744
745
746
747
748
749
750
751
752 public ConfigBean readConfig( String baseDn, String objectClass ) throws LdapException
753 {
754 return readConfig( new Dn( baseDn ), objectClass );
755 }
756
757
758
759
760
761
762
763
764
765
766 public ConfigBean readConfig( Dn baseDn, String objectClass ) throws ConfigurationException
767 {
768 LOG.debug( "Reading configuration for the {} element, from {} ", objectClass, baseDn );
769 ConfigBeang/beans/ConfigBean.html#ConfigBean">ConfigBean configBean = new ConfigBean();
770
771 if ( baseDn == null )
772 {
773 baseDn = configPartition.getSuffixDn();
774 }
775
776 List<AdsBaseBean> beans = read( baseDn, objectClass, SearchScope.ONELEVEL, true );
777
778 if ( LOG.isDebugEnabled() )
779 {
780 if ( ( beans == null ) || beans.isEmpty() )
781 {
782 LOG.debug( "No {} element to read", objectClass );
783 }
784 else
785 {
786 LOG.debug( beans.get( 0 ).toString() );
787 }
788 }
789
790 configBean.setDirectoryServiceBeans( beans );
791
792 return configBean;
793 }
794 }