|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2004-2020, Oracle and/or its affiliates. |
| 2 | + * Copyright (c) 2004-2021, Oracle and/or its affiliates. |
3 | 3 | *
|
4 | 4 | * Licensed under the 2-clause BSD license.
|
5 | 5 | *
|
|
83 | 83 | import java.util.logging.Logger;
|
84 | 84 | import java.util.regex.Matcher;
|
85 | 85 | import java.util.regex.Pattern;
|
| 86 | +import java.util.stream.Collectors; |
86 | 87 |
|
87 | 88 | import static com.oracle.labs.mlrg.olcut.config.PropertySheet.StoredFieldType;
|
88 | 89 |
|
@@ -1474,6 +1475,66 @@ public <T extends Configurable> List<T> lookupAll(Class<T> c, ComponentListener<
|
1474 | 1475 | return lookupAll(c);
|
1475 | 1476 | }
|
1476 | 1477 |
|
| 1478 | + /** |
| 1479 | + * Looks for a single instance of a specified Configurable in the configuration. If there is |
| 1480 | + * no such instance, this method returns null. If there is more than one instance, an |
| 1481 | + * exception is thrown. lookupSingleton can optionally include any class that may be |
| 1482 | + * assignable (a subclass or implementation) to the provided type. Note that this |
| 1483 | + * could potentially allow a user to load their own class. If the security of the |
| 1484 | + * class you're looking up is important, declare it final to prevent code insertion. |
| 1485 | + * |
| 1486 | + * @param c The Class of component to lookup. |
| 1487 | + * @param allowAssignable allow types that are assignable to the given class to match |
| 1488 | + * @param <T> The type of the component. |
| 1489 | + * @return the one instance of the desired class this configuration manager knows about or null if |
| 1490 | + * no such instance is present |
| 1491 | + * @throws PropertyException if there is more than one instance |
| 1492 | + */ |
| 1493 | + @SuppressWarnings("unchecked") // Casts to T are implicitly checked as we use Class<T> to find the names. |
| 1494 | + public <T extends Configurable> T lookupSingleton(Class<T> c, boolean allowAssignable) throws PropertyException { |
| 1495 | + |
| 1496 | + List<String> instanceNames = new ArrayList<>(); |
| 1497 | + for(Map.Entry<String, ConfigurationData> e : configurationDataMap.entrySet()) { |
| 1498 | + ConfigurationData rpd = e.getValue(); |
| 1499 | + try { |
| 1500 | + Class pclass = Class.forName(rpd.getClassName()); |
| 1501 | + if (!rpd.isImportable() && |
| 1502 | + ((allowAssignable && c.isAssignableFrom(pclass)) || |
| 1503 | + (!allowAssignable && rpd.getClassName().equals(c.getName())))) { |
| 1504 | + instanceNames.add(e.getKey()); |
| 1505 | + } |
| 1506 | + } catch(ClassNotFoundException ex) { |
| 1507 | + logger.warning(String.format("No class %s found in ConfigurationManager", |
| 1508 | + rpd.getClassName())); |
| 1509 | + } |
| 1510 | + } |
| 1511 | + |
| 1512 | + // |
| 1513 | + // Check that we got only one instance and that it is not an interface. |
| 1514 | + if (instanceNames.isEmpty()) { |
| 1515 | + return null; |
| 1516 | + } |
| 1517 | + if (instanceNames.size() > 1) { |
| 1518 | + String names = instanceNames.stream().collect(Collectors.joining(", ")); |
| 1519 | + throw new PropertyException("", "Multiple instances of " + c.getName() + " found in configuration: " + names); |
| 1520 | + } |
| 1521 | + |
| 1522 | + String matchedName = instanceNames.get(0); |
| 1523 | + ConfigurationData cd = configurationDataMap.get(matchedName); |
| 1524 | + try { |
| 1525 | + Class matchedClass = Class.forName(cd.getClassName()); |
| 1526 | + if (!matchedClass.isInterface()) { |
| 1527 | + return (T)lookup(matchedName); |
| 1528 | + } else { |
| 1529 | + throw new PropertyException("matchedName", "Cannot instantiate component with type " |
| 1530 | + + matchedClass + " since it is an interface"); |
| 1531 | + } |
| 1532 | + } catch (ClassNotFoundException e) { |
| 1533 | + throw new PropertyException(e,matchedName,"Class not found for component " + matchedName); |
| 1534 | + } |
| 1535 | + } |
| 1536 | + |
| 1537 | + |
1477 | 1538 | /**
|
1478 | 1539 | * Gets a list of all of the component names of the components that have
|
1479 | 1540 | * a given type. This will not instantiate the components.
|
|
0 commit comments