/**
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package org.mifosplatform.infrastructure.jobs.annotation;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.mifosplatform.infrastructure.jobs.service.JobName;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

/**
 * Parser to find method which is marked with CronTargetMethod annotation
 */
public class CronMethodParser {

    public static class ClassMethodNamesPair {

        public String className;
        public String methodName;
    }

    private static final String SEARCH_PACKAGE = "org.mifosplatform.";

    private static final String CRON_ANNOTATION_ATTRIBUTE_NAME = "jobName";

    private static final String RESOURCE_PATTERN = "**/*.class";

    private static final Map<String, ClassMethodNamesPair> targetMethosMap = new HashMap<>();

    private static final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

    private static final MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

    public static ClassMethodNamesPair findTargetMethodDetails(final String attributeValue) throws IOException {
        if (!targetMethosMap.containsKey(attributeValue)) {
            findAnnotationMethods(CronTarget.class, CRON_ANNOTATION_ATTRIBUTE_NAME);
        }
        return targetMethosMap.get(attributeValue);
    }

    /**
     * method adds all the method names to map with annotation attribute value
     * as key
     */
    private static void findAnnotationMethods(final Class<? extends Annotation> annotationClass, final String attributeName)
            throws IOException {
        final String basePackagePath = ClassUtils.convertClassNameToResourcePath(new StandardEnvironment()
                .resolveRequiredPlaceholders(SEARCH_PACKAGE));
        final String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + basePackagePath + "/" + RESOURCE_PATTERN;
        final Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
        for (final Resource resource : resources) {
            if (resource.isReadable()) {
                final MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                final Set<MethodMetadata> metadataSet = metadataReader.getAnnotationMetadata().getAnnotatedMethods(
                        annotationClass.getName());
                if (metadataSet != null && metadataSet.size() > 0) {
                    for (final MethodMetadata metadata : metadataSet) {
                        final Map<String, Object> attributes = metadata.getAnnotationAttributes(annotationClass.getName());
                        final JobName attributeValue = (JobName) attributes.get(attributeName);
                        final String className = metadata.getDeclaringClassName();
                        final ClassMethodNamesPair pair = new ClassMethodNamesPair();
                        pair.className = className;
                        pair.methodName = metadata.getMethodName();
                        targetMethosMap.put(attributeValue.toString(), pair);
                    }
                }
            }
        }
    }
}

@@ -2595,30 +2595,24 @@
 ));%0A        
-final 
 String packa
@@ -2719,16 +2719,130 @@
 ATTERN;%0A
+        packageSearchPath = packageSearchPath.replace(%22//%22, %22/%22); // else it doesn't work if *.class are in WAR!!%0A
         

Fix for start-up problem if WAR is NOT unpacked (relevant e.g. for upcoming Spring Boot + MariaDB4j java -jar mifos.war