org.openmrs.module.openhmis.cashier.api.ReceiptNumberGeneratorFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.openhmis.cashier.api.ReceiptNumberGeneratorFactory.java

Source

/*
 * The contents of this file are subject to the OpenMRS Public License
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and
 * limitations under the License.
 *
 * Copyright (C) OpenHMIS.  All Rights Reserved.
 */
package org.openmrs.module.openhmis.cashier.api;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.GlobalProperty;
import org.openmrs.api.APIException;
import org.openmrs.api.context.Context;
import org.openmrs.module.openhmis.cashier.ModuleSettings;
import org.reflections.Reflections;
import org.springframework.transaction.annotation.Transactional;

/**
 * Implements {@link IReceiptNumberGenerator}
 */
@Transactional
public class ReceiptNumberGeneratorFactory {
    private static final Log LOG = LogFactory.getLog(ReceiptNumberGeneratorFactory.class);
    private static volatile IReceiptNumberGenerator generator;

    protected ReceiptNumberGeneratorFactory() {
    }

    /**
     * Returns the currently defined {@link IReceiptNumberGenerator} for the system.
     * @return The {@link IReceiptNumberGenerator}.
     * @should Return the currently defined receipt number generator
     * @should Load the generator if it has not been loaded
     * @should not load the generator if it has been loaded
     * @should Return null if no generator has been defined
     * @should Throw APIException if generator class cannot be found
     * @should Throw APIException if generator class cannot be instantiated
     */
    public static IReceiptNumberGenerator getGenerator() {
        if (generator == null) {
            generator = createGeneratorInstance();
            if (generator == null) {
                return null;
            }
        }

        // Ensure that the generator is loaded
        if (!generator.isLoaded()) {
            generator.load();
        }

        return generator;
    }

    /**
     * Sets the system-wide {@link IReceiptNumberGenerator}.
     * @param generator The generator.
     * @throws APIException
     * @should Set the receipt number generator for the system
     * @should Remove the current generator if set to null
     */
    public static void setGenerator(IReceiptNumberGenerator generator) {
        Class<? extends IReceiptNumberGenerator> cls = (generator == null) ? null : generator.getClass();

        FactoryImpl.INSTANCE.setGeneratorClass(cls);

        ReceiptNumberGeneratorFactory.generator = generator;
    }

    private static IReceiptNumberGenerator createGeneratorInstance() {
        Class<? super IReceiptNumberGenerator> cls = null;
        try {
            cls = FactoryImpl.INSTANCE.getGeneratorClass();
            if (cls == null) {
                return null;
            }

            generator = (IReceiptNumberGenerator) cls.newInstance();
            return generator;
        } catch (ClassNotFoundException classEx) {
            LOG.warn("Attempt to load unknown receipt number generator type", classEx);
            throw new APIException("Could not locate receipt number generator class.", classEx);
        } catch (InstantiationException instantiationEx) {
            throw new APIException("Could not instantiate the '" + cls.getClass().getName() + "' class.",
                    instantiationEx);
        } catch (IllegalAccessException accessEx) {
            throw new APIException("Could not access the '" + cls.getClass().getName() + "' class.", accessEx);
        }
    }

    /**
     * Locates and instantiates all classes that implement {@link IReceiptNumberGenerator} in the current classpath.
     * @return The instantiated receipt number generators.
     * @should Locate all classes that implement IReceiptNumberGenerator
     * @should Not throw an exception if the class instantiation fails
     * @should Use the existing instance for the currently defined generator
     */
    public static IReceiptNumberGenerator[] locateGenerators() {
        // Search for any modules that define classes which implement the IReceiptNumberGenerator interface
        Reflections reflections = new Reflections("org.openmrs.module");
        List<Class<? extends IReceiptNumberGenerator>> classes = new ArrayList<Class<? extends IReceiptNumberGenerator>>();
        for (Class<? extends IReceiptNumberGenerator> cls : reflections
                .getSubTypesOf(IReceiptNumberGenerator.class)) {
            // We only care about public instantiable classes so ignore others
            if (!cls.isInterface() && !Modifier.isAbstract(cls.getModifiers())
                    && Modifier.isPublic(cls.getModifiers())) {
                classes.add(cls);
            }
        }

        // Now attempt to instantiate each found class
        List<IReceiptNumberGenerator> instances = new ArrayList<IReceiptNumberGenerator>();
        for (Class<? extends IReceiptNumberGenerator> cls : classes) {
            if (generator != null && cls.equals(generator.getClass())) {
                instances.add(generator);
            } else {
                try {
                    instances.add(cls.newInstance());
                } catch (Exception ex) {
                    // We don't care about specific exceptions here.  Just log and ignore the class
                    LOG.warn("Could not instantiate the '" + cls.getName() + "' class.  It will be ignored.");
                }
            }
        }

        // Finally, copy the instances to an array
        IReceiptNumberGenerator[] results = new IReceiptNumberGenerator[instances.size()];
        instances.toArray(results);

        return results;
    }

    /**
     * Resets this factory, effectively creating a new instance. If you are using this for anything other than testing you
     * are likely doing something wrong.
     */
    static void reset() {
        generator = null;
    }

    /**
     * Singleton implementation for storing and retrieving the generator in the database.
     */
    private enum FactoryImpl {
        INSTANCE;

        @SuppressWarnings("unchecked")
        public Class<? super IReceiptNumberGenerator> getGeneratorClass() throws ClassNotFoundException {
            Class<? super IReceiptNumberGenerator> result = null;

            String propertyValue = Context.getAdministrationService()
                    .getGlobalProperty(ModuleSettings.SYSTEM_RECEIPT_NUMBER_GENERATOR);
            if (!StringUtils.isEmpty(propertyValue)) {
                LOG.debug("Loading receipt number generator '" + propertyValue + "'...");
                result = (Class<? super IReceiptNumberGenerator>) Class.forName(propertyValue);
                LOG.debug("Receipt number generator loaded.");
            } else {
                LOG.warn("Request for receipt number generator when none has been defined.");
            }

            return result;
        }

        public void setGeneratorClass(Class<? extends IReceiptNumberGenerator> generatorClass) {
            String className = (generatorClass == null) ? "" : generatorClass.getName();
            GlobalProperty property = new GlobalProperty(ModuleSettings.SYSTEM_RECEIPT_NUMBER_GENERATOR, className);

            Context.getAdministrationService().saveGlobalProperty(property);
        }
    }
}