/* * 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); } } }