    001    // Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // You may obtain a copy of the License at
    006    //
    007    // http://www.apache.org/licenses/LICENSE-2.0
    008    //
    009    // Unless required by applicable law or agreed to in writing, software
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.ioc.internal.util;
    016
    017    import org.apache.tapestry5.func.F;
    018    import org.apache.tapestry5.func.Mapper;
    019    import org.apache.tapestry5.func.Predicate;
    020    import org.apache.tapestry5.internal.plastic.PlasticInternalUtils;
    021    import org.apache.tapestry5.ioc.*;
    022    import org.apache.tapestry5.ioc.annotations.*;
    023    import org.apache.tapestry5.ioc.def.*;
    024    import org.apache.tapestry5.ioc.internal.NullAnnotationProvider;
    025    import org.apache.tapestry5.ioc.services.Coercion;
    026    import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
    027    import org.apache.tapestry5.plastic.MethodAdvice;
    028    import org.apache.tapestry5.plastic.MethodInvocation;
    029    import org.apache.tapestry5.plastic.PlasticUtils;
    030    import org.slf4j.Logger;
    031
    032    import javax.annotation.PostConstruct;
    033    import javax.inject.Named;
    034    import java.io.Closeable;
    035    import java.io.IOException;
    036    import java.lang.annotation.Annotation;
    037    import java.lang.annotation.Retention;
    038    import java.lang.annotation.RetentionPolicy;
    039    import java.lang.reflect.*;
    040    import java.net.URL;
    041    import java.util.*;
    042    import java.util.concurrent.atomic.AtomicLong;
    043    import java.util.regex.Matcher;
    044    import java.util.regex.Pattern;
    045
    046    /**
    047     * Utilities used within various internal implementations of the tapestry-ioc module.
    048     */
    049    @SuppressWarnings("all")
    050    public class InternalUtils
    051    {
    052        /**
    053         * @since 5.2.2
    054         */
    055        public static final boolean SERVICE_CLASS_RELOADING_ENABLED = Boolean.parseBoolean(System.getProperty(
    056                IOCConstants.SERVICE_CLASS_RELOADING_ENABLED, "true"));
    057
    058        /**
    059         * Leading punctuation on member names that is stripped off to form a property name or new member name.
    060         */
    061        private static final String NAME_PREFIX = "_$";
    062
    063        /**
    064         * Pattern used to eliminate leading and trailing underscores and dollar signs.
    065         */
    066        private static final Pattern NAME_PATTERN = Pattern.compile("^[_|$]*([\\p{javaJavaIdentifierPart}]+?)[_|$]*$",
    067                Pattern.CASE_INSENSITIVE);
    068
    069        /**
    070         * @since 5.3
    071         */
    072        public static AnnotationProvider NULL_ANNOTATION_PROVIDER = new NullAnnotationProvider();
    073
    074        /**
    075         * Converts a method to a user presentable string using a {@link PlasticProxyFactory} to obtain a {@link Location}
    076         * (where possible). {@link #asString(Method)} is used under the covers, to present a detailed, but not excessive,
    077         * description of the class, method and parameters.
    078         *
    079         * @param method       method to convert to a string
    080         * @param proxyFactory used to obtain the {@link Location}
    081         * @return the method formatted for presentation to the user
    082         */
    083        public static String asString(Method method, PlasticProxyFactory proxyFactory)
    084        {
    085            Location location = proxyFactory.getMethodLocation(method);
    086
    087            return location != null ? location.toString() : asString(method);
    088        }
    089
    090        /**
    091         * Converts a method to a user presentable string consisting of the containing class name, the method name, and the
    092         * short form of the parameter list (the class name of each parameter type, shorn of the package name portion).
    093         *
    094         * @param method
    095         * @return short string representation
    096         */
    097        public static String asString(Method method)
    098        {
    099            StringBuilder buffer = new StringBuilder();
    100
    101            buffer.append(method.getDeclaringClass().getName());
    102            buffer.append(".");
    103            buffer.append(method.getName());
    104            buffer.append("(");
    105
    106            for (int i = 0; i < method.getParameterTypes().length; i++)
    107            {
    108                if (i > 0)
    109                    buffer.append(", ");
    110
    111                String name = method.getParameterTypes()[i].getSimpleName();
    112
    113                buffer.append(name);
    114            }
    115
    116            return buffer.append(")").toString();
    117        }
    118
    119        /**
    120         * Returns the size of an object array, or null if the array is empty.
    121         */
    122
    123        public static int size(Object[] array)
    124        {
    125            return array == null ? 0 : array.length;
    126        }
    127
    128        public static int size(Collection collection)
    129        {
    130            return collection == null ? 0 : collection.size();
    131        }
    132
    133        /**
    134         * Strips leading "_" and "$" and trailing "_" from the name.
    135         */
    136        public static String stripMemberName(String memberName)
    137        {
    138            assert InternalUtils.isNonBlank(memberName);
    139            Matcher matcher = NAME_PATTERN.matcher(memberName);
    140
    141            if (!matcher.matches())
    142                throw new IllegalArgumentException(String.format("Input '%s' is not a valid Java identifier.", memberName));
    143
    144            return matcher.group(1);
    145        }
    146
    147        /**
    148         * Converts an enumeration (of Strings) into a sorted list of Strings.
    149         */
    150        public static List<String> toList(Enumeration e)
    151        {
    152            List<String> result = CollectionFactory.newList();
    153
    154            while (e.hasMoreElements())
    155            {
    156                String name = (String) e.nextElement();
    157
    158                result.add(name);
    159            }
    160
    161            Collections.sort(result);
    162
    163            return result;
    164        }
    165
    166        /**
    167         * Finds a specific annotation type within an array of annotations.
    168         *
    169         * @param <T>
    170         * @param annotations     to search
    171         * @param annotationClass to match
    172         * @return the annotation instance, if found, or null otherwise
    173         */
    174        public static <T extends Annotation> T findAnnotation(Annotation[] annotations, Class<T> annotationClass)
    175        {
    176            for (Annotation a : annotations)
    177            {
    178                if (annotationClass.isInstance(a))
    179                    return annotationClass.cast(a);
    180            }
    181
    182            return null;
    183        }
    184
    185        private static ObjectCreator<Object> asObjectCreator(final Object fixedValue)
    186        {
    187            return new ObjectCreator<Object>()
    188            {
    189                public Object createObject()
    190                {
    191                    return fixedValue;
    192                }
    193            };
    194        }
    195
    196        private static ObjectCreator calculateInjection(final Class injectionType, Type genericType, final Annotation[] annotations,
    197                                                        final ObjectLocator locator, InjectionResources resources)
    198        {
    199            final AnnotationProvider provider = new AnnotationProvider()
    200            {
    201                public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
    202                {
    203                    return findAnnotation(annotations, annotationClass);
    204                }
    205            };
    206
    207            // At some point, it would be nice to eliminate InjectService, and rely
    208            // entirely on service interface type and point-of-injection markers.
    209
    210            InjectService is = provider.getAnnotation(InjectService.class);
    211
    212            if (is != null)
    213            {
    214                String serviceId = is.value();
    215
    216                return asObjectCreator(locator.getService(serviceId, injectionType));
    217            }
    218
    219            Named named = provider.getAnnotation(Named.class);
    220
    221            if (named != null)
    222            {
    223                return asObjectCreator(locator.getService(named.value(), injectionType));
    224            }
    225
    226            // In the absence of @InjectService, try some autowiring. First, does the
    227            // parameter type match one of the resources (the parameter defaults)?
    228
    229            if (provider.getAnnotation(Inject.class) == null)
    230            {
    231                Object result = resources.findResource(injectionType, genericType);
    232
    233                if (result != null)
    234                {
    235                    return asObjectCreator(result);
    236                }
    237            }
    238
    239            // TAP5-1765: For @Autobuild, special case where we always compute a fresh value
    240            // for the injection on every use.  Elsewhere, we compute once when generating the
    241            // construction plan and just use the singleton value repeatedly.
    242
    243            if (provider.getAnnotation(Autobuild.class) != null)
    244            {
    245                return new ObjectCreator()
    246                {
    247                    public Object createObject()
    248                    {
    249                        return locator.getObject(injectionType, provider);
    250                    }
    251                };
    252            }
    253
    254            // Otherwise, make use of the MasterObjectProvider service to resolve this type (plus
    255            // any other information gleaned from additional annotation) into the correct object.
    256
    257            return asObjectCreator(locator.getObject(injectionType, provider));
    258        }
    259
    260        public static ObjectCreator[] calculateParametersForMethod(Method method, ObjectLocator locator,
    261                                                                   InjectionResources resources, OperationTracker tracker)
    262        {
    263
    264            return calculateParameters(locator, resources, method.getParameterTypes(), method.getGenericParameterTypes(),
    265                    method.getParameterAnnotations(), tracker);
    266        }
    267
    268        public static ObjectCreator[] calculateParameters(final ObjectLocator locator, final InjectionResources resources,
    269                                                          Class[] parameterTypes, final Type[] genericTypes, Annotation[][] parameterAnnotations,
    270                                                          OperationTracker tracker)
    271        {
    272            int parameterCount = parameterTypes.length;
    273
    274            ObjectCreator[] parameters = new ObjectCreator[parameterCount];
    275
    276            for (int i = 0; i < parameterCount; i++)
    277            {
    278                final Class type = parameterTypes[i];
    279                final Type genericType = genericTypes[i];
    280                final Annotation[] annotations = parameterAnnotations[i];
    281
    282                String description = String.format("Determining injection value for parameter #%d (%s)", i + 1,
    283                        PlasticUtils.toTypeName(type));
    284
    285                final Invokable<ObjectCreator> operation = new Invokable<ObjectCreator>()
    286                {
    287                    public ObjectCreator invoke()
    288                    {
    289                        return calculateInjection(type, genericType, annotations, locator, resources);
    290                    }
    291                };
    292
    293                parameters[i] = tracker.invoke(description, operation);
    294            }
    295
    296            return parameters;
    297        }
    298
    299        /**
    300         * Injects into the fields (of all visibilities) when the {@link org.apache.tapestry5.ioc.annotations.Inject} or
    301         * {@link org.apache.tapestry5.ioc.annotations.InjectService} annotations are present.
    302         *
    303         * @param object    to be initialized
    304         * @param locator   used to resolve external dependencies
    305         * @param resources provides injection resources for fields
    306         * @param tracker   track operations
    307         */
    308        public static void injectIntoFields(final Object object, final ObjectLocator locator,
    309                                            final InjectionResources resources, OperationTracker tracker)
    310        {
    311            Class clazz = object.getClass();
    312
    313            while (clazz != Object.class)
    314            {
    315                Field[] fields = clazz.getDeclaredFields();
    316
    317                for (final Field f : fields)
    318                {
    319                    // Ignore all static and final fields.
    320
    321                    int fieldModifiers = f.getModifiers();
    322
    323                    if (Modifier.isStatic(fieldModifiers) || Modifier.isFinal(fieldModifiers))
    324                        continue;
    325
    326                    final AnnotationProvider ap = new AnnotationProvider()
    327                    {
    328                        public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
    329                        {
    330                            return f.getAnnotation(annotationClass);
    331                        }
    332                    };
    333
    334                    String description = String.format("Calculating possible injection value for field %s.%s (%s)",
    335                            clazz.getName(), f.getName(),
    336                            PlasticUtils.toTypeName(f.getType()));
    337
    338                    tracker.run(description, new Runnable()
    339                    {
    340                        public void run()
    341                        {
    342                            final Class<?> fieldType = f.getType();
    343
    344                            InjectService is = ap.getAnnotation(InjectService.class);
    345                            if (is != null)
    346                            {
    347                                inject(object, f, locator.getService(is.value(), fieldType));
    348                                return;
    349                            }
    350
    351                            if (ap.getAnnotation(Inject.class) != null || ap.getAnnotation(InjectResource.class) != null)
    352                            {
    353                                Object value = resources.findResource(fieldType, f.getGenericType());
    354
    355                                if (value != null)
    356                                {
    357                                    inject(object, f, value);
    358                                    return;
    359                                }
    360
    361                                inject(object, f, locator.getObject(fieldType, ap));
    362                                return;
    363                            }
    364
    365                            if (ap.getAnnotation(javax.inject.Inject.class) != null)
    366                            {
    367                                Named named = ap.getAnnotation(Named.class);
    368
    369                                if (named == null)
    370                                {
    371                                    inject(object, f, locator.getObject(fieldType, ap));
    372                                } else
    373                                {
    374                                    inject(object, f, locator.getService(named.value(), fieldType));
    375                                }
    376
    377                                return;
    378                            }
    379
    380                            // Ignore fields that do not have the necessary annotation.
    381
    382                        }
    383                    });
    384                }
    385
    386                clazz = clazz.getSuperclass();
    387            }
    388        }
    389
    390        private synchronized static void inject(Object target, Field field, Object value)
    391        {
    392            try
    393            {
    394                if (!field.isAccessible())
    395                    field.setAccessible(true);
    396
    397                field.set(target, value);
    398
    399                // Is there a need to setAccessible back to false?
    400            } catch (Exception ex)
    401            {
    402                throw new RuntimeException(String.format("Unable to set field '%s' of %s to %s: %s", field.getName(),
    403                        target, value, toMessage(ex)));
    404            }
    405        }
    406
    407        /**
    408         * Joins together some number of elements to form a comma separated list.
    409         */
    410        public static String join(List elements)
    411        {
    412            return join(elements, ", ");
    413        }
    414
    415        /**
    416         * Joins together some number of elements. If a value in the list is the empty string, it is replaced with the
    417         * string "(blank)".
    418         *
    419         * @param elements  objects to be joined together
    420         * @param separator used between elements when joining
    421         */
    422        public static String join(List elements, String separator)
    423        {
    424            switch (elements.size())
    425            {
    426                case 0:
    427                    return "";
    428
    429                case 1:
    430                    return elements.get(0).toString();
    431
    432                default:
    433
    434                    StringBuilder buffer = new StringBuilder();
    435                    boolean first = true;
    436
    437                    for (Object o : elements)
    438                    {
    439                        if (!first)
    440                            buffer.append(separator);
    441
    442                        String string = String.valueOf(o);
    443
    444                        if (string.equals(""))
    445                            string = "(blank)";
    446
    447                        buffer.append(string);
    448
    449                        first = false;
    450                    }
    451
    452                    return buffer.toString();
    453            }
    454        }
    455
    456        /**
    457         * Creates a sorted copy of the provided elements, then turns that into a comma separated list.
    458         *
    459         * @return the elements converted to strings, sorted, joined with comma ... or "(none)" if the elements are null or
    460         *         empty
    461         */
    462        public static String joinSorted(Collection elements)
    463        {
    464            if (elements == null || elements.isEmpty())
    465                return "(none)";
    466
    467            List<String> list = CollectionFactory.newList();
    468
    469            for (Object o : elements)
    470                list.add(String.valueOf(o));
    471
    472            Collections.sort(list);
    473
    474            return join(list);
    475        }
    476
    477        /**
    478         * Returns true if the input is null, or is a zero length string (excluding leading/trailing whitespace).
    479         */
    480
    481        public static boolean isBlank(String input)
    482        {
    483            return input == null || input.length() == 0 || input.trim().length() == 0;
    484        }
    485
    486        /**
    487         * Returns true if the input is an empty collection.
    488         */
    489
    490        public static boolean isEmptyCollection(Object input)
    491        {
    492            if (input instanceof Collection)
    493            {
    494                return ((Collection) input).isEmpty();
    495            }
    496
    497            return false;
    498        }
    499
    500        public static boolean isNonBlank(String input)
    501        {
    502            return !isBlank(input);
    503        }
    504
    505        /**
    506         * Capitalizes a string, converting the first character to uppercase.
    507         */
    508        public static String capitalize(String input)
    509        {
    510            if (input.length() == 0)
    511                return input;
    512
    513            return input.substring(0, 1).toUpperCase() + input.substring(1);
    514        }
    515
    516        /**
    517         * Sniffs the object to see if it is a {@link Location} or {@link Locatable}. Returns null if null or not
    518         * convertable to a location.
    519         */
    520
    521        public static Location locationOf(Object location)
    522        {
    523            if (location == null)
    524                return null;
    525
    526            if (location instanceof Location)
    527                return (Location) location;
    528
    529            if (location instanceof Locatable)
    530                return ((Locatable) location).getLocation();
    531
    532            return null;
    533        }
    534
    535        /**
    536         * Extracts the string keys from a map and returns them in sorted order. The keys are converted to strings.
    537         *
    538         * @param map the map to extract keys from (may be null)
    539         * @return the sorted keys, or the empty set if map is null
    540         */
    541
    542        public static List<String> sortedKeys(Map map)
    543        {
    544            if (map == null)
    545                return Collections.emptyList();
    546
    547            List<String> keys = CollectionFactory.newList();
    548
    549            for (Object o : map.keySet())
    550                keys.add(String.valueOf(o));
    551
    552            Collections.sort(keys);
    553
    554            return keys;
    555        }
    556
    557        public static <K, V> Set<K> keys(Map<K, V> map)
    558        {
    559            if (map == null)
    560                return Collections.emptySet();
    561
    562            return map.keySet();
    563        }
    564
    565        /**
    566         * Gets a value from a map (which may be null).
    567         *
    568         * @param <K>
    569         * @param <V>
    570         * @param map the map to extract from (may be null)
    571         * @param key
    572         * @return the value from the map, or null if the map is null
    573         */
    574
    575        public static <K, V> V get(Map<K, V> map, K key)
    576        {
    577            if (map == null)
    578                return null;
    579
    580            return map.get(key);
    581        }
    582
    583        /**
    584         * Returns true if the method provided is a static method.
    585         */
    586        public static boolean isStatic(Method method)
    587        {
    588            return Modifier.isStatic(method.getModifiers());
    589        }
    590
    591        public static <T> Iterator<T> reverseIterator(final List<T> list)
    592        {
    593            final ListIterator<T> normal = list.listIterator(list.size());
    594
    595            return new Iterator<T>()
    596            {
    597                public boolean hasNext()
    598                {
    599                    return normal.hasPrevious();
    600                }
    601
    602                public T next()
    603                {
    604                    // TODO Auto-generated method stub
    605                    return normal.previous();
    606                }
    607
    608                public void remove()
    609                {
    610                    throw new UnsupportedOperationException();
    611                }
    612            };
    613        }
    614
    615        /**
    616         * Return true if the input string contains the marker for symbols that must be expanded.
    617         */
    618        public static boolean containsSymbols(String input)
    619        {
    620            return input.contains("${");
    621        }
    622
    623        /**
    624         * Searches the string for the final period ('.') character and returns everything after that. The input string is
    625         * generally a fully qualified class name, though tapestry-core also uses this method for the occasional property
    626         * expression (which is also dot separated). Returns the input string unchanged if it does not contain a period
    627         * character.
    628         */
    629        public static String lastTerm(String input)
    630        {
    631            assert InternalUtils.isNonBlank(input);
    632            int dotx = input.lastIndexOf('.');
    633
    634            if (dotx < 0)
    635                return input;
    636
    637            return input.substring(dotx + 1);
    638        }
    639
    640        /**
    641         * Searches a class for the "best" constructor, the public constructor with the most parameters. Returns null if
    642         * there are no public constructors. If there is more than one constructor with the maximum number of parameters, it
    643         * is not determined which will be returned (don't build a class like that!). In addition, if a constructor is
    644         * annotated with {@link org.apache.tapestry5.ioc.annotations.Inject}, it will be used (no check for multiple such
    645         * constructors is made, only at most a single constructor should have the annotation).
    646         *
    647         * @param clazz to search for a constructor for
    648         * @return the constructor to be used to instantiate the class, or null if no appropriate constructor was found
    649         */
    650        public static Constructor findAutobuildConstructor(Class clazz)
    651        {
    652            Constructor[] constructors = clazz.getConstructors();
    653
    654            switch (constructors.length)
    655            {
    656                case 1:
    657
    658                    return constructors[0];
    659
    660                case 0:
    661
    662                    return null;
    663
    664                default:
    665                    break;
    666            }
    667
    668            for (Constructor c : constructors)
    669            {
    670                if (c.getAnnotation(Inject.class) != null)
    671                    return c;
    672            }
    673
    674            Constructor standardConstructor = findConstructorByAnnotation(constructors, Inject.class);
    675            Constructor javaxConstructor = findConstructorByAnnotation(constructors, javax.inject.Inject.class);
    676
    677            if (standardConstructor != null && javaxConstructor != null)
    678                throw new IllegalArgumentException(
    679                        String.format(
    680                                "Too many autobuilt constructors found. Please use either '@%s' or '@%s' annotation to mark a constructor for autobuilding.",
    681                                Inject.class.getName(), javax.inject.Inject.class.getName()));
    682
    683            if (standardConstructor != null)
    684                return standardConstructor;
    685
    686            if (javaxConstructor != null)
    687                return javaxConstructor;
    688
    689            // Choose a constructor with the most parameters.
    690
    691            Comparator<Constructor> comparator = new Comparator<Constructor>()
    692            {
    693                public int compare(Constructor o1, Constructor o2)
    694                {
    695                    return o2.getParameterTypes().length - o1.getParameterTypes().length;
    696                }
    697            };
    698
    699            Arrays.sort(constructors, comparator);
    700
    701            return constructors[0];
    702        }
    703
    704        private static <T extends Annotation> Constructor findConstructorByAnnotation(Constructor[] constructors,
    705                                                                                      Class<T> annotationClass)
    706        {
    707            for (Constructor c : constructors)
    708            {
    709                if (c.getAnnotation(annotationClass) != null)
    710                    return c;
    711            }
    712
    713            return null;
    714        }
    715
    716        /**
    717         * Adds a value to a specially organized map where the values are lists of objects. This somewhat simulates a map
    718         * that allows multiple values for the same key.
    719         *
    720         * @param map   to store value into
    721         * @param key   for which a value is added
    722         * @param value to add
    723         * @param <K>   the type of key
    724         * @param <V>   the type of the list
    725         */
    726        public static <K, V> void addToMapList(Map<K, List<V>> map, K key, V value)
    727        {
    728            List<V> list = map.get(key);
    729
    730            if (list == null)
    731            {
    732                list = CollectionFactory.newList();
    733                map.put(key, list);
    734            }
    735
    736            list.add(value);
    737        }
    738
    739        /**
    740         * Validates that the marker annotation class had a retention policy of runtime.
    741         *
    742         * @param markerClass the marker annotation class
    743         */
    744        public static void validateMarkerAnnotation(Class markerClass)
    745        {
    746            Retention policy = (Retention) markerClass.getAnnotation(Retention.class);
    747
    748            if (policy != null && policy.value() == RetentionPolicy.RUNTIME)
    749                return;
    750
    751            throw new IllegalArgumentException(UtilMessages.badMarkerAnnotation(markerClass));
    752        }
    753
    754        public static void validateMarkerAnnotations(Class[] markerClasses)
    755        {
    756            for (Class markerClass : markerClasses)
    757                validateMarkerAnnotation(markerClass);
    758        }
    759
    760        public static void close(Closeable stream)
    761        {
    762            if (stream != null)
    763                try
    764                {
    765                    stream.close();
    766                } catch (IOException ex)
    767                {
    768                    // Ignore.
    769                }
    770        }
    771
    772        /**
    773         * Extracts the message from an exception. If the exception's message is null, returns the exceptions class name.
    774         *
    775         * @param exception to extract message from
    776         * @return message or class name
    777         */
    778        public static String toMessage(Throwable exception)
    779        {
    780            String message = exception.getMessage();
    781
    782            if (message != null)
    783                return message;
    784
    785            return exception.getClass().getName();
    786        }
    787
    788        public static void validateConstructorForAutobuild(Constructor constructor)
    789        {
    790            Class clazz = constructor.getDeclaringClass();
    791
    792            if (!Modifier.isPublic(clazz.getModifiers()))
    793                throw new IllegalArgumentException(String.format(
    794                        "Class %s is not a public class and may not be autobuilt.", clazz.getName()));
    795
    796            if (!Modifier.isPublic(constructor.getModifiers()))
    797                throw new IllegalArgumentException(
    798                        String.format(
    799                                "Constructor %s is not public and may not be used for autobuilding an instance of the class. "
    800                                        + "You should make the constructor public, or mark an alternate public constructor with the @Inject annotation.",
    801                                constructor));
    802        }
    803
    804        /**
    805         * @since 5.3
    806         */
    807        public static final Mapper<Class, AnnotationProvider> CLASS_TO_AP_MAPPER = new Mapper<Class, AnnotationProvider>()
    808        {
    809            public AnnotationProvider map(final Class element)
    810            {
    811                return toAnnotationProvider(element);
    812            }
    813
    814        };
    815
    816        /**
    817         * @since 5.3
    818         */
    819        public static AnnotationProvider toAnnotationProvider(final Class element)
    820        {
    821            return new AnnotationProvider()
    822            {
    823                public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
    824                {
    825                    return annotationClass.cast(element.getAnnotation(annotationClass));
    826                }
    827            };
    828        }
    829
    830        ;
    831
    832        /**
    833         * @since 5.3
    834         */
    835        public static final Mapper<Method, AnnotationProvider> METHOD_TO_AP_MAPPER = new Mapper<Method, AnnotationProvider>()
    836        {
    837            public AnnotationProvider map(final Method element)
    838            {
    839                return toAnnotationProvider(element);
    840            }
    841        };
    842
    843        public static final Method findMethod(Class containingClass, String methodName, Class... parameterTypes)
    844        {
    845            if (containingClass == null)
    846                return null;
    847
    848            try
    849            {
    850                return containingClass.getMethod(methodName, parameterTypes);
    851            } catch (SecurityException ex)
    852            {
    853                throw new RuntimeException(ex);
    854            } catch (NoSuchMethodException ex)
    855            {
    856                return null;
    857            }
    858        }
    859
    860        /**
    861         * @since 5.3
    862         */
    863        public static ServiceDef3 toServiceDef3(ServiceDef sd)
    864        {
    865            if (sd instanceof ServiceDef3)
    866                return (ServiceDef3) sd;
    867
    868            final ServiceDef2 sd2 = toServiceDef2(sd);
    869
    870            return new ServiceDef3()
    871            {
    872                // ServiceDef3 methods:
    873
    874                public AnnotationProvider getClassAnnotationProvider()
    875                {
    876                    return toAnnotationProvider(getServiceInterface());
    877                }
    878
    879                public AnnotationProvider getMethodAnnotationProvider(final String methodName, final Class... argumentTypes)
    880                {
    881                    return toAnnotationProvider(findMethod(getServiceInterface(), methodName, argumentTypes));
    882                }
    883
    884                // ServiceDef2 methods:
    885
    886                public boolean isPreventDecoration()
    887                {
    888                    return sd2.isPreventDecoration();
    889                }
    890
    891                public ObjectCreator createServiceCreator(ServiceBuilderResources resources)
    892                {
    893                    return sd2.createServiceCreator(resources);
    894                }
    895
    896                public String getServiceId()
    897                {
    898                    return sd2.getServiceId();
    899                }
    900
    901                public Set<Class> getMarkers()
    902                {
    903                    return sd2.getMarkers();
    904                }
    905
    906                public Class getServiceInterface()
    907                {
    908                    return sd2.getServiceInterface();
    909                }
    910
    911                public String getServiceScope()
    912                {
    913                    return sd2.getServiceScope();
    914                }
    915
    916                public boolean isEagerLoad()
    917                {
    918                    return sd2.isEagerLoad();
    919                }
    920            };
    921        }
    922
    923        public static ServiceDef2 toServiceDef2(final ServiceDef sd)
    924        {
    925            if (sd instanceof ServiceDef2)
    926                return (ServiceDef2) sd;
    927
    928            return new ServiceDef2()
    929            {
    930                // ServiceDef2 methods:
    931
    932                public boolean isPreventDecoration()
    933                {
    934                    return false;
    935                }
    936
    937                // ServiceDef methods:
    938
    939                public ObjectCreator createServiceCreator(ServiceBuilderResources resources)
    940                {
    941                    return sd.createServiceCreator(resources);
    942                }
    943
    944                public String getServiceId()
    945                {
    946                    return sd.getServiceId();
    947                }
    948
    949                public Set<Class> getMarkers()
    950                {
    951                    return sd.getMarkers();
    952                }
    953
    954                public Class getServiceInterface()
    955                {
    956                    return sd.getServiceInterface();
    957                }
    958
    959                public String getServiceScope()
    960                {
    961                    return sd.getServiceScope();
    962                }
    963
    964                public boolean isEagerLoad()
    965                {
    966                    return sd.isEagerLoad();
    967                }
    968
    969                @Override
    970                public String toString()
    971                {
    972                    return sd.toString();
    973                }
    974            };
    975        }
    976
    977        public static ModuleDef2 toModuleDef2(final ModuleDef md)
    978        {
    979            if (md instanceof ModuleDef2)
    980                return (ModuleDef2) md;
    981
    982            return new ModuleDef2()
    983            {
    984                public Set<AdvisorDef> getAdvisorDefs()
    985                {
    986                    return Collections.emptySet();
    987                }
    988
    989                public Class getBuilderClass()
    990                {
    991                    return md.getBuilderClass();
    992                }
    993
    994                public Set<ContributionDef> getContributionDefs()
    995                {
    996                    return md.getContributionDefs();
    997                }
    998
    999                public Set<DecoratorDef> getDecoratorDefs()
    1000                {
    1001                    return md.getDecoratorDefs();
    1002                }
    1003
    1004                public String getLoggerName()
    1005                {
    1006                    return md.getLoggerName();
    1007                }
    1008
    1009                public ServiceDef getServiceDef(String serviceId)
    1010                {
    1011                    return md.getServiceDef(serviceId);
    1012                }
    1013
    1014                public Set<String> getServiceIds()
    1015                {
    1016                    return md.getServiceIds();
    1017                }
    1018            };
    1019        }
    1020
    1021        /**
    1022         * @since 5.1.0.2
    1023         */
    1024        public static ServiceLifecycle2 toServiceLifecycle2(final ServiceLifecycle lifecycle)
    1025        {
    1026            if (lifecycle instanceof ServiceLifecycle2)
    1027                return (ServiceLifecycle2) lifecycle;
    1028
    1029            return new ServiceLifecycle2()
    1030            {
    1031                public boolean requiresProxy()
    1032                {
    1033                    return true;
    1034                }
    1035
    1036                public Object createService(ServiceResources resources, ObjectCreator creator)
    1037                {
    1038                    return lifecycle.createService(resources, creator);
    1039                }
    1040
    1041                public boolean isSingleton()
    1042                {
    1043                    return lifecycle.isSingleton();
    1044                }
    1045            };
    1046        }
    1047
    1048        /**
    1049         * @since 5.2.0
    1050         */
    1051        public static <T extends Comparable<T>> List<T> matchAndSort(Collection<? extends T> collection,
    1052                                                                     Predicate<T> predicate)
    1053        {
    1054            assert predicate != null;
    1055
    1056            List<T> result = CollectionFactory.newList();
    1057
    1058            for (T object : collection)
    1059            {
    1060                if (predicate.accept(object))
    1061                    result.add(object);
    1062            }
    1063
    1064            Collections.sort(result);
    1065
    1066            return result;
    1067        }
    1068
    1069        /**
    1070         * @since 5.2.0
    1071         */
    1072        public static ContributionDef2 toContributionDef2(final ContributionDef contribution)
    1073        {
    1074            if (contribution instanceof ContributionDef2)
    1075                return (ContributionDef2) contribution;
    1076
    1077            return new ContributionDef2()
    1078            {
    1079
    1080                public Set<Class> getMarkers()
    1081                {
    1082                    return Collections.emptySet();
    1083                }
    1084
    1085                public Class getServiceInterface()
    1086                {
    1087                    return null;
    1088                }
    1089
    1090                public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources,
    1091                                       Configuration configuration)
    1092                {
    1093                    contribution.contribute(moduleSource, resources, configuration);
    1094                }
    1095
    1096                public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources,
    1097                                       OrderedConfiguration configuration)
    1098                {
    1099                    contribution.contribute(moduleSource, resources, configuration);
    1100                }
    1101
    1102                public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources,
    1103                                       MappedConfiguration configuration)
    1104                {
    1105                    contribution.contribute(moduleSource, resources, configuration);
    1106                }
    1107
    1108                public String getServiceId()
    1109                {
    1110                    return contribution.getServiceId();
    1111                }
    1112
    1113                @Override
    1114                public String toString()
    1115                {
    1116                    return contribution.toString();
    1117                }
    1118            };
    1119        }
    1120
    1121        public static ContributionDef3 toContributionDef3(ContributionDef contribution)
    1122        {
    1123
    1124            if (contribution instanceof ContributionDef2)
    1125            {
    1126                return (ContributionDef3) contribution;
    1127            }
    1128
    1129            final ContributionDef2 cd2 = toContributionDef2(contribution);
    1130
    1131            return new ContributionDef3()
    1132            {
    1133                public boolean isOptional()
    1134                {
    1135                    return false;
    1136                }
    1137
    1138                public String getServiceId()
    1139                {
    1140                    return cd2.getServiceId();
    1141                }
    1142
    1143                public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources, Configuration configuration)
    1144                {
    1145                    cd2.contribute(moduleSource, resources, configuration);
    1146                }
    1147
    1148                public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources, OrderedConfiguration configuration)
    1149                {
    1150                    cd2.contribute(moduleSource, resources, configuration);
    1151                }
    1152
    1153                public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources, MappedConfiguration configuration)
    1154                {
    1155                    cd2.contribute(moduleSource, resources, configuration);
    1156                }
    1157
    1158                public Set<Class> getMarkers()
    1159                {
    1160                    return cd2.getMarkers();
    1161                }
    1162
    1163                public Class getServiceInterface()
    1164                {
    1165                    return cd2.getServiceInterface();
    1166                }
    1167
    1168                @Override
    1169                public String toString()
    1170                {
    1171                    return cd2.toString();
    1172                }
    1173            };
    1174        }
    1175
    1176        /**
    1177         * @since 5.2.2
    1178         */
    1179        public static AdvisorDef2 toAdvisorDef2(final AdvisorDef advisor)
    1180        {
    1181            if (advisor instanceof AdvisorDef2)
    1182                return (AdvisorDef2) advisor;
    1183
    1184            return new AdvisorDef2()
    1185            {
    1186
    1187                public ServiceAdvisor createAdvisor(ModuleBuilderSource moduleSource, ServiceResources resources)
    1188                {
    1189                    return advisor.createAdvisor(moduleSource, resources);
    1190                }
    1191
    1192                public String getAdvisorId()
    1193                {
    1194                    return advisor.getAdvisorId();
    1195                }
    1196
    1197                public String[] getConstraints()
    1198                {
    1199                    return advisor.getConstraints();
    1200                }
    1201
    1202                public boolean matches(ServiceDef serviceDef)
    1203                {
    1204                    return advisor.matches(serviceDef);
    1205                }
    1206
    1207                public Set<Class> getMarkers()
    1208                {
    1209                    return Collections.emptySet();
    1210                }
    1211
    1212                public Class getServiceInterface()
    1213                {
    1214                    return null;
    1215                }
    1216
    1217                @Override
    1218                public String toString()
    1219                {
    1220                    return advisor.toString();
    1221                }
    1222            };
    1223        }
    1224
    1225        /**
    1226         * @since 5.2.2
    1227         */
    1228        public static DecoratorDef2 toDecoratorDef2(final DecoratorDef decorator)
    1229        {
    1230            if (decorator instanceof DecoratorDef2)
    1231                return (DecoratorDef2) decorator;
    1232
    1233            return new DecoratorDef2()
    1234            {
    1235
    1236                public ServiceDecorator createDecorator(ModuleBuilderSource moduleSource, ServiceResources resources)
    1237                {
    1238                    return decorator.createDecorator(moduleSource, resources);
    1239                }
    1240
    1241                public String[] getConstraints()
    1242                {
    1243                    return decorator.getConstraints();
    1244                }
    1245
    1246                public String getDecoratorId()
    1247                {
    1248                    return decorator.getDecoratorId();
    1249                }
    1250
    1251                public boolean matches(ServiceDef serviceDef)
    1252                {
    1253                    return decorator.matches(serviceDef);
    1254                }
    1255
    1256                public Set<Class> getMarkers()
    1257                {
    1258                    return Collections.emptySet();
    1259                }
    1260
    1261                public Class getServiceInterface()
    1262                {
    1263                    return null;
    1264                }
    1265
    1266                @Override
    1267                public String toString()
    1268                {
    1269                    return decorator.toString();
    1270                }
    1271            };
    1272        }
    1273
    1274        /**
    1275         * Determines if the indicated class is stored as a locally accessible file
    1276         * (and not, typically, as a file inside a JAR). This is related to automatic
    1277         * reloading of services.
    1278         *
    1279         * @since 5.2.0
    1280         */
    1281        public static boolean isLocalFile(Class clazz)
    1282        {
    1283            String path = PlasticInternalUtils.toClassPath(clazz.getName());
    1284
    1285            ClassLoader loader = clazz.getClassLoader();
    1286
    1287            // System classes have no visible class loader, and are not local files.
    1288
    1289            if (loader == null)
    1290                return false;
    1291
    1292            URL classFileURL = loader.getResource(path);
    1293
    1294            return classFileURL != null && classFileURL.getProtocol().equals("file");
    1295        }
    1296
    1297        /**
    1298         * Wraps a {@link Coercion} as a {@link Mapper}.
    1299         *
    1300         * @since 5.2.0
    1301         */
    1302        public static <S, T> Mapper<S, T> toMapper(final Coercion<S, T> coercion)
    1303        {
    1304            assert coercion != null;
    1305
    1306            return new Mapper<S, T>()
    1307            {
    1308                public T map(S value)
    1309                {
    1310                    return coercion.coerce(value);
    1311                }
    1312            };
    1313        }
    1314
    1315        private static final AtomicLong uuidGenerator = new AtomicLong(System.nanoTime());
    1316
    1317        /**
    1318         * Generates a unique value for the current execution of the application. This initial UUID value
    1319         * is not easily predictable; subsequent UUIDs are allocated in ascending series.
    1320         *
    1321         * @since 5.2.0
    1322         */
    1323        public static long nextUUID()
    1324        {
    1325            return uuidGenerator.incrementAndGet();
    1326        }
    1327
    1328        /**
    1329         * Extracts the service id from the passed annotated element. First the {@link ServiceId} annotation is checked.
    1330         * If present, its value is returned. Otherwise {@link Named} annotation is checked. If present, its value is
    1331         * returned.
    1332         * If neither of the annotations is present, <code>null</code> value is returned
    1333         *
    1334         * @param annotated annotated element to get annotations from
    1335         * @since 5.3
    1336         */
    1337        public static String getServiceId(AnnotatedElement annotated)
    1338        {
    1339            ServiceId serviceIdAnnotation = annotated.getAnnotation(ServiceId.class);
    1340
    1341            if (serviceIdAnnotation != null)
    1342            {
    1343                return serviceIdAnnotation.value();
    1344            }
    1345
    1346            Named namedAnnotation = annotated.getAnnotation(Named.class);
    1347
    1348            if (namedAnnotation != null)
    1349            {
    1350                String value = namedAnnotation.value();
    1351
    1352                if (InternalUtils.isNonBlank(value))
    1353                {
    1354                    return value;
    1355                }
    1356            }
    1357
    1358            return null;
    1359        }
    1360
    1361        /**
    1362         * Converts old-style Tapestry IoC {@link org.apache.tapestry5.ioc.MethodAdvice} to modern
    1363         * Plastic {@link MethodAdvice}.
    1364         *
    1365         * @param iocMethodAdvice old style advice
    1366         * @return new style advice
    1367         */
    1368        public static MethodAdvice toPlasticMethodAdvice(final org.apache.tapestry5.ioc.MethodAdvice iocMethodAdvice,
    1369                                                         final AnnotationProvider methodAnnotationProvider)
    1370        {
    1371            assert iocMethodAdvice != null;
    1372
    1373            return new MethodAdvice()
    1374            {
    1375                public void advise(final MethodInvocation invocation)
    1376                {
    1377                    org.apache.tapestry5.ioc.Invocation iocInvocation = new org.apache.tapestry5.ioc.Invocation()
    1378                    {
    1379                        public void rethrow()
    1380                        {
    1381                            invocation.rethrow();
    1382                        }
    1383
    1384                        public void proceed()
    1385                        {
    1386                            invocation.proceed();
    1387                        }
    1388
    1389                        public void overrideThrown(Exception thrown)
    1390                        {
    1391                            invocation.setCheckedException(thrown);
    1392                        }
    1393
    1394                        public void overrideResult(Object newResult)
    1395                        {
    1396                            invocation.setReturnValue(newResult);
    1397                        }
    1398
    1399                        public void override(int index, Object newParameter)
    1400                        {
    1401                            invocation.setParameter(index, newParameter);
    1402                        }
    1403
    1404                        public boolean isFail()
    1405                        {
    1406                            return invocation.didThrowCheckedException();
    1407                        }
    1408
    1409                        public <T extends Throwable> T getThrown(Class<T> throwableClass)
    1410                        {
    1411                            return invocation.getCheckedException(throwableClass);
    1412                        }
    1413
    1414                        public Object getParameter(int index)
    1415                        {
    1416                            return invocation.getParameter(index);
    1417                        }
    1418
    1419                        public Object getResult()
    1420                        {
    1421                            return invocation.getReturnValue();
    1422                        }
    1423
    1424                        public Class getResultType()
    1425                        {
    1426                            return method().getReturnType();
    1427                        }
    1428
    1429                        private Method method()
    1430                        {
    1431                            return invocation.getMethod();
    1432                        }
    1433
    1434                        public Class getParameterType(int index)
    1435                        {
    1436                            return method().getParameterTypes()[index];
    1437                        }
    1438
    1439                        public int getParameterCount()
    1440                        {
    1441                            return method().getParameterTypes().length;
    1442                        }
    1443
    1444                        public String getMethodName()
    1445                        {
    1446                            return method().getName();
    1447                        }
    1448
    1449                        public <T extends Annotation> T getMethodAnnotation(Class<T> annotationClass)
    1450                        {
    1451                            return methodAnnotationProvider.getAnnotation(annotationClass);
    1452                        }
    1453                    };
    1454
    1455                    iocMethodAdvice.advise(iocInvocation);
    1456                }
    1457            };
    1458        }
    1459
    1460        public static AnnotationProvider toAnnotationProvider(final Method element)
    1461        {
    1462            if (element == null)
    1463                return NULL_ANNOTATION_PROVIDER;
    1464
    1465            return new AnnotationProvider()
    1466            {
    1467                public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
    1468                {
    1469                    return element.getAnnotation(annotationClass);
    1470                }
    1471            };
    1472        }
    1473
    1474        public static <T> ObjectCreator<T> createConstructorConstructionPlan(final OperationTracker tracker, final ObjectLocator locator,
    1475                                                                             final InjectionResources resources,
    1476                                                                             final Logger logger,
    1477                                                                             final String description,
    1478                                                                             final Constructor<T> constructor)
    1479        {
    1480            return tracker.invoke(String.format("Creating plan to instantiate %s via %s",
    1481                    constructor.getDeclaringClass().getName(),
    1482                    constructor), new Invokable<ObjectCreator<T>>()
    1483            {
    1484                public ObjectCreator<T> invoke()
    1485                {
    1486                    validateConstructorForAutobuild(constructor);
    1487
    1488                    ObjectCreator[] constructorParameters = calculateParameters(locator, resources, constructor.getParameterTypes(), constructor.getGenericParameterTypes(), constructor.getParameterAnnotations(), tracker);
    1489
    1490                    Invokable<T> core = new ConstructorInvoker<T>(constructor, constructorParameters);
    1491
    1492                    Invokable<T> wrapped = logger == null ? core : new LoggingInvokableWrapper<T>(logger, description, core);
    1493
    1494                    ConstructionPlan<T> plan = new ConstructionPlan(tracker, description, wrapped);
    1495
    1496                    extendPlanForInjectedFields(plan, tracker, locator, resources, constructor.getDeclaringClass());
    1497
    1498                    extendPlanForPostInjectionMethods(plan, tracker, locator, resources, constructor.getDeclaringClass());
    1499
    1500                    return plan;
    1501                }
    1502            });
    1503        }
    1504
    1505        private static <T> void extendPlanForInjectedFields(final ConstructionPlan<T> plan, OperationTracker tracker, final ObjectLocator locator, final InjectionResources resources, Class<T> instantiatedClass)
    1506        {
    1507            Class clazz = instantiatedClass;
    1508
    1509            while (clazz != Object.class)
    1510            {
    1511                Field[] fields = clazz.getDeclaredFields();
    1512
    1513                for (final Field f : fields)
    1514                {
    1515                    // Ignore all static and final fields.
    1516
    1517                    int fieldModifiers = f.getModifiers();
    1518
    1519                    if (Modifier.isStatic(fieldModifiers) || Modifier.isFinal(fieldModifiers))
    1520                        continue;
    1521
    1522                    final AnnotationProvider ap = new AnnotationProvider()
    1523                    {
    1524                        public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
    1525                        {
    1526                            return f.getAnnotation(annotationClass);
    1527                        }
    1528                    };
    1529
    1530                    String description = String.format("Calculating possible injection value for field %s.%s (%s)",
    1531                            clazz.getName(), f.getName(),
    1532                            PlasticUtils.toTypeName(f.getType()));
    1533
    1534                    tracker.run(description, new Runnable()
    1535                    {
    1536                        public void run()
    1537                        {
    1538                            final Class<?> fieldType = f.getType();
    1539
    1540                            InjectService is = ap.getAnnotation(InjectService.class);
    1541                            if (is != null)
    1542                            {
    1543                                addInjectPlan(plan, f, locator.getService(is.value(), fieldType));
    1544                                return;
    1545                            }
    1546
    1547                            if (ap.getAnnotation(Inject.class) != null || ap.getAnnotation(InjectResource.class) != null)
    1548                            {
    1549                                Object value = resources.findResource(fieldType, f.getGenericType());
    1550
    1551                                if (value != null)
    1552                                {
    1553                                    addInjectPlan(plan, f, value);
    1554                                    return;
    1555                                }
    1556
    1557                                addInjectPlan(plan, f, locator.getObject(fieldType, ap));
    1558                                return;
    1559                            }
    1560
    1561                            if (ap.getAnnotation(javax.inject.Inject.class) != null)
    1562                            {
    1563                                Named named = ap.getAnnotation(Named.class);
    1564
    1565                                if (named == null)
    1566                                {
    1567                                    addInjectPlan(plan, f, locator.getObject(fieldType, ap));
    1568                                } else
    1569                                {
    1570                                    addInjectPlan(plan, f, locator.getService(named.value(), fieldType));
    1571                                }
    1572
    1573                                return;
    1574                            }
    1575
    1576                            // Ignore fields that do not have the necessary annotation.
    1577
    1578                        }
    1579                    });
    1580                }
    1581
    1582                clazz = clazz.getSuperclass();
    1583            }
    1584        }
    1585
    1586        private static <T> void addInjectPlan(ConstructionPlan<T> plan, final Field field, final Object injectedValue)
    1587        {
    1588            plan.add(new InitializationPlan<T>()
    1589            {
    1590                public String getDescription()
    1591                {
    1592                    return String.format("Injecting %s into field %s of class %s.",
    1593                            injectedValue,
    1594                            field.getName(),
    1595                            field.getDeclaringClass().getName());
    1596                }
    1597
    1598                public void initialize(T instance)
    1599                {
    1600                    inject(instance, field, injectedValue);
    1601                }
    1602            });
    1603        }
    1604
    1605        private static boolean hasAnnotation(AccessibleObject member, Class<? extends Annotation> annotationType)
    1606        {
    1607            return member.getAnnotation(annotationType) != null;
    1608        }
    1609
    1610        private static <T> void extendPlanForPostInjectionMethods(ConstructionPlan<T> plan, OperationTracker tracker, ObjectLocator locator, InjectionResources resources, Class<T> instantiatedClass)
    1611        {
    1612            for (Method m : instantiatedClass.getMethods())
    1613            {
    1614                if (hasAnnotation(m, PostInjection.class) || hasAnnotation(m, PostConstruct.class))
    1615                {
    1616                    extendPlanForPostInjectionMethod(plan, tracker, locator, resources, m);
    1617                }
    1618            }
    1619        }
    1620
    1621        private static void extendPlanForPostInjectionMethod(final ConstructionPlan<?> plan, final OperationTracker tracker, final ObjectLocator locator, final InjectionResources resources, final Method method)
    1622        {
    1623            tracker.run("Computing parameters for post-injection method " + method,
    1624                    new Runnable()
    1625                    {
    1626                        public void run()
    1627                        {
    1628                            final ObjectCreator[] parameters = InternalUtils.calculateParametersForMethod(method, locator,
    1629                                    resources, tracker);
    1630
    1631                            plan.add(new InitializationPlan<Object>()
    1632                            {
    1633                                public String getDescription()
    1634                                {
    1635                                    return "Invoking " + method;
    1636                                }
    1637
    1638                                public void initialize(Object instance)
    1639                                {
    1640                                    Throwable fail = null;
    1641
    1642                                    Object[] realized = realizeObjects(parameters);
    1643
    1644                                    try
    1645                                    {
    1646                                        method.invoke(instance, realized);
    1647                                    } catch (InvocationTargetException ex)
    1648                                    {
    1649                                        fail = ex.getTargetException();
    1650                                    } catch (Exception ex)
    1651                                    {
    1652                                        fail = ex;
    1653                                    }
    1654
    1655                                    if (fail != null)
    1656                                    {
    1657                                        throw new RuntimeException(String
    1658                                                .format("Exception invoking method %s: %s", method, toMessage(fail)), fail);
    1659                                    }
    1660                                }
    1661                            });
    1662                        }
    1663                    });
    1664        }
    1665
    1666
    1667        public static <T> ObjectCreator<T> createMethodInvocationPlan(final OperationTracker tracker, final ObjectLocator locator,
    1668                                                                      final InjectionResources resources,
    1669                                                                      final Logger logger,
    1670                                                                      final String description,
    1671                                                                      final Object instance,
    1672                                                                      final Method method)
    1673        {
    1674
    1675            return tracker.invoke("Creating plan to invoke " + method, new Invokable<ObjectCreator<T>>()
    1676            {
    1677                public ObjectCreator<T> invoke()
    1678                {
    1679                    ObjectCreator[] methodParameters = calculateParametersForMethod(method, locator, resources, tracker);
    1680
    1681                    Invokable<T> core = new MethodInvoker<T>(instance, method, methodParameters);
    1682
    1683                    Invokable<T> wrapped = logger == null ? core : new LoggingInvokableWrapper<T>(logger, description, core);
    1684
    1685                    return new ConstructionPlan(tracker, description, wrapped);
    1686                }
    1687            });
    1688        }
    1689
    1690        /**
    1691         * @since 5.3.1, 5.4
    1692         */
    1693        public static Mapper<ObjectCreator, Object> CREATE_OBJECT = new Mapper<ObjectCreator, Object>()
    1694        {
    1695            public Object map(ObjectCreator element)
    1696            {
    1697                return element.createObject();
    1698            }
    1699        };
    1700
    1701        /**
    1702         * @since 5.3.1, 5.4
    1703         */
    1704        public static Object[] realizeObjects(ObjectCreator[] creators)
    1705        {
    1706            return F.flow(creators).map(CREATE_OBJECT).toArray(Object.class);
    1707        }
    1708    }