1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 module hunt.shiro.config.ReflectionBuilder; 20 21 // import hunt.commons.beanutils.BeanUtilsBean; 22 // import hunt.commons.beanutils.SuppressPropertiesBeanIntrospector; 23 // import hunt.shiro.codec.Base64; 24 // import hunt.shiro.codec.Hex; 25 // import hunt.shiro.config.event.BeanEvent; 26 // import hunt.shiro.config.event.ConfiguredBeanEvent; 27 // import hunt.shiro.config.event.DestroyedBeanEvent; 28 // import hunt.shiro.config.event.InitializedBeanEvent; 29 // import hunt.shiro.config.event.InstantiatedBeanEvent; 30 import hunt.shiro.event.EventBus; 31 import hunt.shiro.event.EventBusAware; 32 import hunt.shiro.event.Subscribe; 33 import hunt.shiro.event.support.DefaultEventBus; 34 import hunt.shiro.Exceptions; 35 // import hunt.shiro.util.Assert; 36 import hunt.shiro.util.ByteSource; 37 // import hunt.shiro.util.ClassUtils; 38 // import hunt.shiro.util.Factory; 39 import hunt.shiro.util.LifecycleUtils; 40 import hunt.shiro.util.Common; 41 // import hunt.shiro.util.StringUtils; 42 // import org.slf4j.Logger; 43 // import org.slf4j.LoggerFactory; 44 45 // import java.beans.PropertyDescriptor; 46 // import java.util.ArrayList; 47 // import java.util.Arrays; 48 // import java.util.Collection; 49 // import java.util.Collections; 50 // import java.util.LinkedHashMap; 51 // import java.util.LinkedHashSet; 52 // import java.util.List; 53 // import java.util.Map; 54 // import java.util.Set; 55 56 import hunt.collection; 57 import hunt.Exceptions; 58 import hunt.logging.Logger; 59 import hunt.String; 60 61 import std.traits; 62 import std.string; 63 64 65 /** 66 * Object builder that uses reflection and Apache Commons BeanUtils to build objects given a 67 * map of "property values". Typically these come from the Shiro INI configuration and are used 68 * to construct or modify the SecurityManager, its dependencies, and web-based security filters. 69 * <p/> 70 * Recognizes {@link Factory} implementations and will call 71 * {@link hunt.shiro.util.Factory#getInstance() getInstance} to satisfy any reference to this bean. 72 * 73 * @since 0.9 74 */ 75 class ReflectionBuilder { 76 77 //TODO - complete JavaDoc 78 79 private enum string OBJECT_REFERENCE_BEGIN_TOKEN = "$"; 80 private enum string ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN = "\\$"; 81 private enum string GLOBAL_PROPERTY_PREFIX = "shiro"; 82 private enum char MAP_KEY_VALUE_DELIMITER = ':'; 83 private enum string HEX_BEGIN_TOKEN = "0x"; 84 private enum string NULL_VALUE_TOKEN = "null"; 85 private enum string EMPTY_STRING_VALUE_TOKEN = "\"\""; 86 private enum char STRING_VALUE_DELIMETER = '"'; 87 private enum char MAP_PROPERTY_BEGIN_TOKEN = '['; 88 private enum char MAP_PROPERTY_END_TOKEN = ']'; 89 90 private enum string EVENT_BUS_NAME = "eventBus"; 91 92 private Map!(string, Object) objects; 93 94 /** 95 * Interpolation allows for ${key} substitution of values. 96 * @since 1.4 97 */ 98 // private Interpolator interpolator; 99 100 /** 101 * @since 1.3 102 */ 103 private EventBus eventBus; 104 /** 105 * Keeps track of event subscribers that were automatically registered by this ReflectionBuilder during 106 * object construction. This is used in case a new EventBus is discovered during object graph 107 * construction: upon discovery of the new EventBus, the existing subscribers will be unregistered from the 108 * old EventBus and then re-registered with the new EventBus. 109 * 110 * @since 1.3 111 */ 112 private Map!(string, Object) registeredEventSubscribers; 113 114 /** 115 * @since 1.4 116 */ 117 // private final BeanUtilsBean beanUtilsBean; 118 119 this() { 120 this(null); 121 } 122 123 this(Map!(string, Object) defaults) { 124 125 // SHIRO-619 126 // beanUtilsBean = new BeanUtilsBean(); 127 // beanUtilsBean.getPropertyUtils().addBeanIntrospector(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS); 128 129 // this.interpolator = createInterpolator(); 130 131 this.objects = createDefaultObjectMap(); 132 this.registeredEventSubscribers = new LinkedHashMap!(string, Object)(); 133 apply(defaults); 134 } 135 136 //@since 1.3 137 private Map!(string, Object) createDefaultObjectMap() { 138 Map!(string, Object) map = new LinkedHashMap!(string, Object)(); 139 map.put(EVENT_BUS_NAME, new DefaultEventBus()); 140 return map; 141 } 142 143 private void apply(Map!(string, Object) objects) { 144 if(!isEmpty(objects)) { 145 this.objects.putAll(objects); 146 } 147 EventBus found = findEventBus(this.objects); 148 assert(found !is null, "An " ~ fullyQualifiedName!EventBus ~ 149 " instance must be present in the object defaults"); 150 enableEvents(found); 151 } 152 153 Map!(string, Object) getObjects() { 154 return objects; 155 } 156 157 /** 158 * @param objects 159 */ 160 void setObjects(Map!(string, Object) objects) { 161 this.objects.clear(); 162 this.objects.putAll(createDefaultObjectMap()); 163 apply(objects); 164 } 165 166 //@since 1.3 167 private void enableEvents(EventBus eventBus) { 168 assert(eventBus, "EventBus argument cannot be null."); 169 //clean up old auto-registered subscribers: 170 foreach(Object subscriber ; this.registeredEventSubscribers.values()) { 171 this.eventBus.unregister(subscriber); 172 } 173 this.registeredEventSubscribers.clear(); 174 175 this.eventBus = eventBus; 176 177 foreach(string key, Object value; this.objects) { 178 enableEventsIfNecessary(value, key); 179 } 180 } 181 182 //@since 1.3 183 private void enableEventsIfNecessary(Object bean, string name) { 184 bool applied = applyEventBusIfNecessary(bean); 185 if (!applied) { 186 //if the event bus is applied, and the bean wishes to be a subscriber as well (not just a publisher), 187 // we assume that the implementation registers itself with the event bus, i.e. eventBus.register(this); 188 189 //if the event bus isn't applied, only then do we need to check to see if the bean is an event subscriber, 190 // and if so, register it on the event bus automatically since it has no ability to do so itself: 191 if (isEventSubscriber(bean, name)) { 192 //found an event subscriber, so register them with the EventBus: 193 this.eventBus.register(bean); 194 this.registeredEventSubscribers.put(name, bean); 195 } 196 } 197 } 198 199 //@since 1.3 200 private bool isEventSubscriber(Object bean, string name) { 201 // List annotatedMethods = ClassUtils.getAnnotatedMethods(bean.getClass(), Subscribe.class); 202 // return !isEmpty(annotatedMethods); 203 implementationMissing(false); 204 return false; 205 } 206 207 //@since 1.3 208 protected EventBus findEventBus(Map!(string, Object) objects) { 209 210 if (isEmpty(objects)) { 211 return null; 212 } 213 214 //prefer a named object first: 215 EventBus value = cast(EventBus)objects.get(EVENT_BUS_NAME); 216 if (value !is null) { 217 return value; 218 } 219 220 //couldn't find a named 'eventBus' EventBus object. Try to find the first typed value we can: 221 foreach(Object v ; objects.values()) { 222 value = cast(EventBus)v; 223 if (value !is null) { 224 return value; 225 } 226 } 227 228 return null; 229 } 230 231 private bool applyEventBusIfNecessary(Object value) { 232 EventBusAware eba = cast(EventBusAware)value; 233 if (eba !is null) { 234 eba.setEventBus(this.eventBus); 235 return true; 236 } 237 return false; 238 } 239 240 Object getBean(string id) { 241 return objects.get(id); 242 } 243 244 T getBean(T)(string id) { 245 // if (requiredType is null) { 246 // throw new NullPointerException("requiredType argument cannot be null."); 247 // } 248 Object bean = objects.get(id); 249 if (bean is null) { 250 return null; 251 } 252 T v = cast(T) bean; 253 assert(v !is null, 254 "Bean with id [" ~ id ~ "] is not of the required type [" ~ typeid(T).toString() ~ "]."); 255 return cast(T) v; 256 } 257 258 private string parseBeanId(string lhs) { 259 assert(lhs); 260 if (lhs.indexOf('.') < 0) { 261 return lhs; 262 } 263 string classSuffix = ".class"; 264 int index = cast(int)lhs.indexOf(classSuffix); 265 if (index >= 0) { 266 return lhs[0 .. index]; 267 } 268 return null; 269 } 270 271 Map!(string, Object) buildObjects(Map!(string, string) kvPairs) { 272 273 if (kvPairs !is null && !kvPairs.isEmpty()) { 274 275 // BeanConfigurationProcessor processor = new BeanConfigurationProcessor(); 276 277 // for (Map.Entry<string, string> entry : kvPairs.entrySet()) { 278 // string lhs = entry.getKey(); 279 // string rhs = interpolator.interpolate(entry.getValue()); 280 281 // string beanId = parseBeanId(lhs); 282 // if (beanId !is null) { //a beanId could be parsed, so the line is a bean instance definition 283 // processor.add(new InstantiationStatement(beanId, rhs)); 284 // } else { //the line must be a property configuration 285 // processor.add(new AssignmentStatement(lhs, rhs)); 286 // } 287 // } 288 289 // processor.execute(); 290 implementationMissing(false); 291 } 292 293 //SHIRO-413: init method must be called for constructed objects that are Initializable 294 LifecycleUtils.init(objects.values()); 295 296 return objects; 297 } 298 299 void destroy() { 300 Map!(string, Object) immutableObjects = objects; 301 302 //destroy objects in the opposite order they were initialized: 303 List!(MapEntry!(string, Object)) entries = new ArrayList!(MapEntry!(string, Object))(); 304 foreach(MapEntry!(string, Object) entry; objects) { 305 entries.add(entry); 306 } 307 // TODO: Tasks pending completion -@zxp at 5/13/2019, 6:24:35 PM 308 // 309 // Collections.reverse(entries); 310 311 foreach(MapEntry!(string, Object) v; entries) { 312 string id = v.getKey(); 313 Object bean = v.getValue(); 314 Object busObject = cast(Object)this.eventBus; 315 //don't destroy the eventbus until the end - we need it to still be 'alive' while publishing destroy events: 316 if (bean == busObject) { //memory equality check (not .equals) on purpose 317 LifecycleUtils.destroy(bean); 318 implementationMissing(false); 319 // BeanEvent event = new DestroyedBeanEvent(id, bean, immutableObjects); 320 // eventBus.publish(event); 321 this.eventBus.unregister(bean); //bean is now destroyed - it should not receive any other events 322 } 323 } 324 //only now destroy the event bus: 325 LifecycleUtils.destroy(cast(Object)this.eventBus); 326 } 327 328 protected void createNewInstance(Map!(string, Object) objects, string name, string value) { 329 330 Object currentInstance = objects.get(name); 331 if (currentInstance !is null) { 332 infof("An instance with name '%s' already exists. " ~ 333 "Redefining this object as a new instance of type %s", name, value); 334 } 335 336 Object instance;//name with no property, assume right hand side of equals sign is the class name: 337 try { 338 instance = Object.factory(value); // ClassUtils.newInstance(value); 339 Nameable n = cast(Nameable)instance; 340 if (n !is null) { 341 n.setName(name); 342 } 343 } catch (Exception e) { 344 string msg = "Unable to instantiate class [" ~ value ~ "] for object named '" ~ name ~ "'. " ~ 345 "Please ensure you've specified the fully qualified class name correctly."; 346 throw new ConfigurationException(msg, e); 347 } 348 objects.put(name, instance); 349 } 350 351 protected void applyProperty(string key, string value, Map!(string, Object) objects) { 352 353 int index = cast(int)key.indexOf('.'); 354 355 if (index >= 0) { 356 string name = key[0 .. index]; 357 string property = key[index + 1 .. $]; 358 359 if (icmp(GLOBAL_PROPERTY_PREFIX, name) == 0) { 360 applyGlobalProperty(objects, property, value); 361 } else { 362 applySingleProperty(objects, name, property, value); 363 } 364 365 } else { 366 throw new IllegalArgumentException("All property keys must contain a '.' character. " ~ 367 "(e.g. myBean.property = value) These should already be separated out by buildObjects()."); 368 } 369 } 370 371 protected void applyGlobalProperty(Map!(string, Object) objects, string property, string value) { 372 implementationMissing(false); 373 foreach (Object instance ; objects.byValue) { 374 try { 375 // PropertyDescriptor pd = beanUtilsBean.getPropertyUtils().getPropertyDescriptor(instance, property); 376 // if (pd !is null) { 377 // applyProperty(instance, property, value); 378 // } 379 } catch (Exception e) { 380 string msg = "Error retrieving property descriptor for instance " ~ 381 "of type [" ~ typeid(instance).name ~ "] " ~ 382 "while setting property [" ~ property ~ "]"; 383 throw new ConfigurationException(msg, e); 384 } 385 } 386 } 387 388 protected void applySingleProperty(Map!(string, Object) objects, string name, string property, string value) { 389 Object instance = objects.get(name); 390 // if (property.equals("class")) { 391 // throw new IllegalArgumentException("Property keys should not contain 'class' properties since these " ~ 392 // "should already be separated out by buildObjects()."); 393 394 // } else if (instance is null) { 395 // string msg = "Configuration error. Specified object [" ~ name ~ "] with property [" ~ 396 // property ~ "] without first defining that object's class. Please first " ~ 397 // "specify the class property first, e.g. myObject = fully_qualified_class_name " ~ 398 // "and then define additional properties."; 399 // throw new IllegalArgumentException(msg); 400 401 // } else { 402 // applyProperty(instance, property, value); 403 // } 404 implementationMissing(false); 405 } 406 407 protected bool isReference(string value) { 408 return value !is null && value.startsWith(OBJECT_REFERENCE_BEGIN_TOKEN); 409 } 410 411 protected string getId(string referenceToken) { 412 return referenceToken[0 .. OBJECT_REFERENCE_BEGIN_TOKEN.length]; 413 } 414 415 protected Object getReferencedObject(string id) { 416 Object o = objects !is null && !objects.isEmpty() ? objects.get(id) : null; 417 if (o is null) { 418 string msg = "The object with id [" ~ id ~ "] has not yet been defined and therefore cannot be " ~ 419 "referenced. Please ensure objects are defined in the order in which they should be " ~ 420 "created and made available for future reference."; 421 throw new UnresolveableReferenceException(msg); 422 } 423 return o; 424 } 425 426 protected string unescapeIfNecessary(string value) { 427 if (value !is null && value.startsWith(ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN)) { 428 return value[0 .. ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN.length - 1]; 429 } 430 return value; 431 } 432 433 protected Object resolveReference(string reference) { 434 string id = getId(reference); 435 tracef("Encountered object reference '%s'. Looking up object with id '%s'", reference, id); 436 Object referencedObject = getReferencedObject(id); 437 // if (referencedObject instanceof Factory) { 438 // return ((Factory) referencedObject).getInstance(); 439 // } 440 // return referencedObject; 441 implementationMissing(false); 442 return null; 443 } 444 445 protected bool isTypedProperty(Object object, string propertyName, TypeInfo_Class clazz) { 446 if (clazz is null) { 447 throw new NullPointerException("type (class) argument cannot be null."); 448 } 449 450 implementationMissing(false); 451 return false; 452 // try { 453 // PropertyDescriptor descriptor = beanUtilsBean.getPropertyUtils().getPropertyDescriptor(object, propertyName); 454 // if (descriptor is null) { 455 // string msg = "Property '" ~ propertyName ~ "' does not exist for object of " ~ 456 // "type " ~ object.getClass().getName() ~ "."; 457 // throw new ConfigurationException(msg); 458 // } 459 // TypeInfo_Class propertyClazz = descriptor.getPropertyType(); 460 // return clazz.isAssignableFrom(propertyClazz); 461 // } catch (ConfigurationException ce) { 462 // //let it propagate: 463 // throw ce; 464 // } catch (Exception e) { 465 // string msg = "Unable to determine if property [" ~ propertyName ~ "] represents a " ~ clazz.getName(); 466 // throw new ConfigurationException(msg, e); 467 // } 468 } 469 470 // protected Set<?> toSet(string sValue) { 471 // string[] tokens = StringUtils.split(sValue); 472 // if (tokens is null || tokens.length <= 0) { 473 // return null; 474 // } 475 476 // //SHIRO-423: check to see if the value is a referenced Set already, and if so, return it immediately: 477 // if (tokens.length == 1 && isReference(tokens[0])) { 478 // Object reference = resolveReference(tokens[0]); 479 // if (reference instanceof Set) { 480 // return (Set)reference; 481 // } 482 // } 483 484 // Set<string> setTokens = new LinkedHashSet<string>(Arrays.asList(tokens)); 485 486 // //now convert into correct values and/or references: 487 // Set<Object> values = new LinkedHashSet<Object>(setTokens.size()); 488 // for (string token : setTokens) { 489 // Object value = resolveValue(token); 490 // values.add(value); 491 // } 492 // return values; 493 // } 494 495 // protected Map<?, ?> toMap(string sValue) { 496 // string[] tokens = StringUtils.split(sValue, StringUtils.DEFAULT_DELIMITER_CHAR, 497 // StringUtils.DEFAULT_QUOTE_CHAR, StringUtils.DEFAULT_QUOTE_CHAR, true, true); 498 // if (tokens is null || tokens.length <= 0) { 499 // return null; 500 // } 501 502 // //SHIRO-423: check to see if the value is a referenced Map already, and if so, return it immediately: 503 // if (tokens.length == 1 && isReference(tokens[0])) { 504 // Object reference = resolveReference(tokens[0]); 505 // if (reference instanceof Map) { 506 // return (Map)reference; 507 // } 508 // } 509 510 // Map<string, string> mapTokens = new LinkedHashMap<string, string>(tokens.length); 511 // for (string token : tokens) { 512 // string[] kvPair = StringUtils.split(token, MAP_KEY_VALUE_DELIMITER); 513 // if (kvPair is null || kvPair.length != 2) { 514 // string msg = "Map property value [" ~ sValue ~ "] contained key-value pair token [" ~ 515 // token ~ "] that does not properly split to a single key and pair. This must be the " ~ 516 // "case for all map entries."; 517 // throw new ConfigurationException(msg); 518 // } 519 // mapTokens.put(kvPair[0], kvPair[1]); 520 // } 521 522 // //now convert into correct values and/or references: 523 // Map<Object, Object> map = new LinkedHashMap<Object, Object>(mapTokens.size()); 524 // for (Map.Entry<string, string> entry : mapTokens.entrySet()) { 525 // Object key = resolveValue(entry.getKey()); 526 // Object value = resolveValue(entry.getValue()); 527 // map.put(key, value); 528 // } 529 // return map; 530 // } 531 532 // // @since 1.2.2 533 // protected Collection<?> toCollection(string sValue) { 534 535 // string[] tokens = StringUtils.split(sValue); 536 // if (tokens is null || tokens.length <= 0) { 537 // return null; 538 // } 539 540 // //SHIRO-423: check to see if the value is a referenced Collection already, and if so, return it immediately: 541 // if (tokens.length == 1 && isReference(tokens[0])) { 542 // Object reference = resolveReference(tokens[0]); 543 // if (reference instanceof Collection) { 544 // return (Collection)reference; 545 // } 546 // } 547 548 // //now convert into correct values and/or references: 549 // List<Object> values = new ArrayList<Object>(tokens.length); 550 // for (string token : tokens) { 551 // Object value = resolveValue(token); 552 // values.add(value); 553 // } 554 // return values; 555 // } 556 557 // protected List<?> toList(string sValue) { 558 // string[] tokens = StringUtils.split(sValue); 559 // if (tokens is null || tokens.length <= 0) { 560 // return null; 561 // } 562 563 // //SHIRO-423: check to see if the value is a referenced List already, and if so, return it immediately: 564 // if (tokens.length == 1 && isReference(tokens[0])) { 565 // Object reference = resolveReference(tokens[0]); 566 // if (reference instanceof List) { 567 // return (List)reference; 568 // } 569 // } 570 571 // //now convert into correct values and/or references: 572 // List<Object> values = new ArrayList<Object>(tokens.length); 573 // for (string token : tokens) { 574 // Object value = resolveValue(token); 575 // values.add(value); 576 // } 577 // return values; 578 // } 579 580 // protected byte[] toBytes(string sValue) { 581 // if (sValue is null) { 582 // return null; 583 // } 584 // byte[] bytes; 585 // if (sValue.startsWith(HEX_BEGIN_TOKEN)) { 586 // string hex = sValue.substring(HEX_BEGIN_TOKEN.length()); 587 // bytes = Hex.decode(hex); 588 // } else { 589 // //assume base64 encoded: 590 // bytes = Base64.decode(sValue); 591 // } 592 // return bytes; 593 // } 594 595 protected Object resolveValue(string stringValue) { 596 Object value; 597 if (isReference(stringValue)) { 598 value = resolveReference(stringValue); 599 } else { 600 value = new String(unescapeIfNecessary(stringValue)); 601 } 602 return value; 603 } 604 605 protected string checkForNullOrEmptyLiteral(string stringValue) { 606 if (stringValue is null) { 607 return null; 608 } 609 //check if the value is the actual literal string 'null' (expected to be wrapped in quotes): 610 if (stringValue == ("\"null\"")) { 611 return NULL_VALUE_TOKEN; 612 } 613 //or the actual literal string of two quotes '""' (expected to be wrapped in quotes): 614 else if (stringValue == ("\"\"\"\"")) { 615 return EMPTY_STRING_VALUE_TOKEN; 616 } else { 617 return stringValue; 618 } 619 } 620 621 protected void applyProperty(Object object, string propertyPath, Object value) { 622 implementationMissing(false); 623 624 // int mapBegin = propertyPath.indexOf(MAP_PROPERTY_BEGIN_TOKEN); 625 // int mapEnd = -1; 626 // string mapPropertyPath = null; 627 // string keyString = null; 628 629 // string remaining = null; 630 631 // if (mapBegin >= 0) { 632 // //a map is being referenced in the overall property path. Find just the map's path: 633 // mapPropertyPath = propertyPath.substring(0, mapBegin); 634 // //find the end of the map reference: 635 // mapEnd = propertyPath.indexOf(MAP_PROPERTY_END_TOKEN, mapBegin); 636 // //find the token in between the [ and the ] (the map/array key or index): 637 // keyString = propertyPath.substring(mapBegin+1, mapEnd); 638 639 // //find out if there is more path reference to follow. If not, we're at a terminal of the OGNL expression 640 // if (propertyPath.length() > (mapEnd+1)) { 641 // remaining = propertyPath.substring(mapEnd+1); 642 // if (remaining.startsWith(".")) { 643 // remaining = StringUtils.clean(remaining.substring(1)); 644 // } 645 // } 646 // } 647 648 // if (remaining is null) { 649 // //we've terminated the OGNL expression. Check to see if we're assigning a property or a map entry: 650 // if (keyString is null) { 651 // //not a map or array value assignment - assign the property directly: 652 // setProperty(object, propertyPath, value); 653 // } else { 654 // //we're assigning a map or array entry. Check to see which we should call: 655 // if (isTypedProperty(object, mapPropertyPath, Map.class)) { 656 // Map map = (Map)getProperty(object, mapPropertyPath); 657 // Object mapKey = resolveValue(keyString); 658 // //noinspection unchecked 659 // map.put(mapKey, value); 660 // } else { 661 // //must be an array property. Convert the key string to an index: 662 // int index = Integer.valueOf(keyString); 663 // setIndexedProperty(object, mapPropertyPath, index, value); 664 // } 665 // } 666 // } else { 667 // //property is being referenced as part of a nested path. Find the referenced map/array entry and 668 // //recursively call this method with the remaining property path 669 // Object referencedValue = null; 670 // if (isTypedProperty(object, mapPropertyPath, Map.class)) { 671 // Map map = (Map)getProperty(object, mapPropertyPath); 672 // Object mapKey = resolveValue(keyString); 673 // referencedValue = map.get(mapKey); 674 // } else { 675 // //must be an array property: 676 // int index = Integer.valueOf(keyString); 677 // referencedValue = getIndexedProperty(object, mapPropertyPath, index); 678 // } 679 680 // if (referencedValue is null) { 681 // throw new ConfigurationException("Referenced map/array value '" ~ mapPropertyPath ~ "[" ~ 682 // keyString ~ "]' does not exist."); 683 // } 684 685 // applyProperty(referencedValue, remaining, value); 686 // } 687 } 688 689 private void setProperty(Object object, string propertyPath, Object value) { 690 // try { 691 // if (log.isTraceEnabled()) { 692 // log.trace("Applying property [{}] value [{}] on object of type [{}]", 693 // new Object[]{propertyPath, value, object.getClass().getName()}); 694 // } 695 // beanUtilsBean.setProperty(object, propertyPath, value); 696 // } catch (Exception e) { 697 // string msg = "Unable to set property '" ~ propertyPath ~ "' with value [" ~ value ~ "] on object " ~ 698 // "of type " ~ (object !is null ? object.getClass().getName() : null) ~ ". If " ~ 699 // "'" ~ value ~ "' is a reference to another (previously defined) object, prefix it with " ~ 700 // "'" ~ OBJECT_REFERENCE_BEGIN_TOKEN ~ "' to indicate that the referenced " ~ 701 // "object should be used as the actual value. " ~ 702 // "For example, " ~ OBJECT_REFERENCE_BEGIN_TOKEN + value; 703 // throw new ConfigurationException(msg, e); 704 // } 705 706 implementationMissing(false); 707 } 708 709 private Object getProperty(Object object, string propertyPath) { 710 implementationMissing(false); 711 return null; 712 // try { 713 // return beanUtilsBean.getPropertyUtils().getProperty(object, propertyPath); 714 // } catch (Exception e) { 715 // throw new ConfigurationException("Unable to access property '" ~ propertyPath ~ "'", e); 716 // } 717 } 718 719 private void setIndexedProperty(Object object, string propertyPath, int index, Object value) { 720 implementationMissing(false); 721 // try { 722 // beanUtilsBean.getPropertyUtils().setIndexedProperty(object, propertyPath, index, value); 723 // } catch (Exception e) { 724 // throw new ConfigurationException("Unable to set array property '" ~ propertyPath ~ "'", e); 725 // } 726 } 727 728 private Object getIndexedProperty(Object object, string propertyPath, int index) { 729 730 implementationMissing(false); 731 return null; 732 // try { 733 // return beanUtilsBean.getPropertyUtils().getIndexedProperty(object, propertyPath, index); 734 // } catch (Exception e) { 735 // throw new ConfigurationException("Unable to acquire array property '" ~ propertyPath ~ "'", e); 736 // } 737 } 738 739 protected bool isIndexedPropertyAssignment(string propertyPath) { 740 return propertyPath.endsWith("" ~ MAP_PROPERTY_END_TOKEN); 741 } 742 743 protected void applyProperty(Object object, string propertyName, string stringValue) { 744 745 // Object value; 746 747 // if (NULL_VALUE_TOKEN.equals(stringValue)) { 748 // value = null; 749 // } else if (EMPTY_STRING_VALUE_TOKEN.equals(stringValue)) { 750 // value = StringUtils.EMPTY_STRING; 751 // } else if (isIndexedPropertyAssignment(propertyName)) { 752 // string checked = checkForNullOrEmptyLiteral(stringValue); 753 // value = resolveValue(checked); 754 // } else if (isTypedProperty(object, propertyName, Set.class)) { 755 // value = toSet(stringValue); 756 // } else if (isTypedProperty(object, propertyName, Map.class)) { 757 // value = toMap(stringValue); 758 // } else if (isTypedProperty(object, propertyName, List.class)) { 759 // value = toList(stringValue); 760 // } else if (isTypedProperty(object, propertyName, Collection.class)) { 761 // value = toCollection(stringValue); 762 // } else if (isTypedProperty(object, propertyName, byte[].class)) { 763 // value = toBytes(stringValue); 764 // } else if (isTypedProperty(object, propertyName, ByteSource.class)) { 765 // byte[] bytes = toBytes(stringValue); 766 // value = ByteSource.Util.bytes(bytes); 767 // } else { 768 // string checked = checkForNullOrEmptyLiteral(stringValue); 769 // value = resolveValue(checked); 770 // } 771 772 // applyProperty(object, propertyName, value); 773 774 implementationMissing(false); 775 } 776 777 // private Interpolator createInterpolator() { 778 779 // if (ClassUtils.isAvailable("hunt.commons.configuration2.interpol.ConfigurationInterpolator")) { 780 // return new CommonsInterpolator(); 781 // } 782 783 // return new DefaultInterpolator(); 784 // } 785 786 /** 787 * Sets the {@link Interpolator} used when evaluating the right side of the expressions. 788 * @since 1.4 789 */ 790 // void setInterpolator(Interpolator interpolator) { 791 // this.interpolator = interpolator; 792 // } 793 794 // private class BeanConfigurationProcessor { 795 796 // private final List<Statement> statements = new ArrayList<Statement>(); 797 // private final List<BeanConfiguration> beanConfigurations = new ArrayList<BeanConfiguration>(); 798 799 // void add(Statement statement) { 800 801 // statements.add(statement); //we execute bean configuration statements in the order they are declared. 802 803 // if (statement instanceof InstantiationStatement) { 804 // InstantiationStatement is = (InstantiationStatement)statement; 805 // beanConfigurations.add(new BeanConfiguration(is)); 806 // } else { 807 // AssignmentStatement as = (AssignmentStatement)statement; 808 // //statements always apply to the most recently defined bean configuration with the same name, so we 809 // //have to traverse the configuration list starting at the end (most recent elements are appended): 810 // bool addedToConfig = false; 811 // string beanName = as.getRootBeanName(); 812 // for( int i = beanConfigurations.size()-1; i >= 0; i--) { 813 // BeanConfiguration mostRecent = beanConfigurations.get(i); 814 // string mostRecentBeanName = mostRecent.getBeanName(); 815 // if (beanName.equals(mostRecentBeanName)) { 816 // mostRecent.add(as); 817 // addedToConfig = true; 818 // break; 819 // } 820 // } 821 822 // if (!addedToConfig) { 823 // // the AssignmentStatement must be for an existing bean that does not yet have a corresponding 824 // // configuration object (this would happen if the bean is in the default objects map). Because 825 // // BeanConfiguration instances don't exist for default (already instantiated) beans, 826 // // we simulate a creation of one to satisfy this processors implementation: 827 // beanConfigurations.add(new BeanConfiguration(as)); 828 // } 829 // } 830 // } 831 832 // void execute() { 833 834 // for( Statement statement : statements) { 835 836 // statement.execute(); 837 838 // BeanConfiguration bd = statement.getBeanConfiguration(); 839 840 // if (bd.isExecuted()) { //bean is fully configured, no more statements to execute for it: 841 842 // //bean configured overrides the 'eventBus' bean - replace the existing eventBus with the one configured: 843 // if (bd.getBeanName().equals(EVENT_BUS_NAME)) { 844 // EventBus eventBus = (EventBus)bd.getBean(); 845 // enableEvents(eventBus); 846 // } 847 848 // //ignore global 'shiro.' shortcut mechanism: 849 // if (!bd.isGlobalConfig()) { 850 // BeanEvent event = new ConfiguredBeanEvent(bd.getBeanName(), bd.getBean(), 851 // Collections.unmodifiableMap(objects)); 852 // eventBus.publish(event); 853 // } 854 855 // //initialize the bean if necessary: 856 // LifecycleUtils.init(bd.getBean()); 857 858 // //ignore global 'shiro.' shortcut mechanism: 859 // if (!bd.isGlobalConfig()) { 860 // BeanEvent event = new InitializedBeanEvent(bd.getBeanName(), bd.getBean(), 861 // Collections.unmodifiableMap(objects)); 862 // eventBus.publish(event); 863 // } 864 // } 865 // } 866 // } 867 // } 868 869 // private class BeanConfiguration { 870 871 // private final InstantiationStatement instantiationStatement; 872 // private final List<AssignmentStatement> assignments = new ArrayList<AssignmentStatement>(); 873 // private final string beanName; 874 // private Object bean; 875 876 // private BeanConfiguration(InstantiationStatement statement) { 877 // statement.setBeanConfiguration(this); 878 // this.instantiationStatement = statement; 879 // this.beanName = statement.lhs; 880 // } 881 882 // private BeanConfiguration(AssignmentStatement as) { 883 // this.instantiationStatement = null; 884 // this.beanName = as.getRootBeanName(); 885 // add(as); 886 // } 887 888 // string getBeanName() { 889 // return this.beanName; 890 // } 891 892 // bool isGlobalConfig() { //BeanConfiguration instance representing the global 'shiro.' properties 893 // // (we should remove this concept). 894 // return GLOBAL_PROPERTY_PREFIX.equals(getBeanName()); 895 // } 896 897 // void add(AssignmentStatement as) { 898 // as.setBeanConfiguration(this); 899 // assignments.add(as); 900 // } 901 902 // /** 903 // * When this configuration is parsed sufficiently to create (or find) an actual bean instance, that instance 904 // * will be associated with its configuration by setting it via this method. 905 // * 906 // * @param bean the bean instantiated (or found) that corresponds to this BeanConfiguration instance. 907 // */ 908 // void setBean(Object bean) { 909 // this.bean = bean; 910 // } 911 912 // Object getBean() { 913 // return this.bean; 914 // } 915 916 // /** 917 // * Returns true if all configuration statements have been executed. 918 // * @return true if all configuration statements have been executed. 919 // */ 920 // bool isExecuted() { 921 // if (instantiationStatement !is null && !instantiationStatement.isExecuted()) { 922 // return false; 923 // } 924 // for (AssignmentStatement as : assignments) { 925 // if (!as.isExecuted()) { 926 // return false; 927 // } 928 // } 929 // return true; 930 // } 931 // } 932 933 // private abstract class Statement { 934 935 // protected final string lhs; 936 // protected final string rhs; 937 // protected Object bean; 938 // private Object result; 939 // private bool executed; 940 // private BeanConfiguration beanConfiguration; 941 942 // private Statement(string lhs, string rhs) { 943 // this.lhs = lhs; 944 // this.rhs = rhs; 945 // this.executed = false; 946 // } 947 948 // void setBeanConfiguration(BeanConfiguration bd) { 949 // this.beanConfiguration = bd; 950 // } 951 952 // BeanConfiguration getBeanConfiguration() { 953 // return this.beanConfiguration; 954 // } 955 956 // Object execute() { 957 // if (!isExecuted()) { 958 // this.result = doExecute(); 959 // this.executed = true; 960 // } 961 // if (!getBeanConfiguration().isGlobalConfig()) { 962 // assert(this.bean, "Implementation must set the root bean for which it executed."); 963 // } 964 // return this.result; 965 // } 966 967 // Object getBean() { 968 // return this.bean; 969 // } 970 971 // protected void setBean(Object bean) { 972 // this.bean = bean; 973 // if (this.beanConfiguration.getBean() is null) { 974 // this.beanConfiguration.setBean(bean); 975 // } 976 // } 977 978 // Object getResult() { 979 // return result; 980 // } 981 982 // protected abstract Object doExecute(); 983 984 // bool isExecuted() { 985 // return executed; 986 // } 987 // } 988 989 // private class InstantiationStatement : Statement { 990 991 // private InstantiationStatement(string lhs, string rhs) { 992 // super(lhs, rhs); 993 // } 994 995 // override 996 // protected Object doExecute() { 997 // string beanName = this.lhs; 998 // createNewInstance(objects, beanName, this.rhs); 999 // Object instantiated = objects.get(beanName); 1000 // setBean(instantiated); 1001 1002 // //also ensure the instantiated bean has access to the event bus or is subscribed to events if necessary: 1003 // //Note: because events are being enabled on this bean here (before the instantiated event below is 1004 // //triggered), beans can react to their own instantiation events. 1005 // enableEventsIfNecessary(instantiated, beanName); 1006 1007 // BeanEvent event = new InstantiatedBeanEvent(beanName, instantiated, Collections.unmodifiableMap(objects)); 1008 // eventBus.publish(event); 1009 1010 // return instantiated; 1011 // } 1012 // } 1013 1014 // private class AssignmentStatement extends Statement { 1015 1016 // private final string rootBeanName; 1017 1018 // private AssignmentStatement(string lhs, string rhs) { 1019 // super(lhs, rhs); 1020 // int index = lhs.indexOf('.'); 1021 // this.rootBeanName = lhs.substring(0, index); 1022 // } 1023 1024 // override 1025 // protected Object doExecute() { 1026 // applyProperty(lhs, rhs, objects); 1027 // Object bean = objects.get(this.rootBeanName); 1028 // setBean(bean); 1029 // return null; 1030 // } 1031 1032 // string getRootBeanName() { 1033 // return this.rootBeanName; 1034 // } 1035 // } 1036 1037 ////////////////////////// 1038 // From CollectionUtils // 1039 ////////////////////////// 1040 // CollectionUtils cannot be removed from shiro-core until 2.0 as it has a dependency on PrincipalCollection 1041 1042 private static bool isEmpty(K, V)(Map!(K, V) m) { 1043 return m is null || m.isEmpty(); 1044 } 1045 1046 private static bool isEmpty(E)(Collection!E c) { 1047 return c is null || c.isEmpty(); 1048 } 1049 1050 }