com.hmsinc.epicenter.service.ClassificationService.java Source code

Java tutorial

Introduction

Here is the source code for com.hmsinc.epicenter.service.ClassificationService.java

Source

/**
 * Copyright (C) 2008 University of Pittsburgh
 * 
 * 
 * This file is part of Open EpiCenter
 * 
 *     Open EpiCenter is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     Open EpiCenter is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with Open EpiCenter.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * 
 *   
 */
package com.hmsinc.epicenter.service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import com.hmsinc.epicenter.classifier.ClassificationEngine;
import com.hmsinc.epicenter.classifier.ClassifierFactory;
import com.hmsinc.epicenter.classifier.NamedItemClassificationEngine;
import com.hmsinc.epicenter.model.analysis.AnalysisRepository;
import com.hmsinc.epicenter.model.analysis.DataType;
import com.hmsinc.epicenter.model.analysis.classify.Classification;
import com.hmsinc.epicenter.model.analysis.classify.ClassificationTarget;
import com.hmsinc.epicenter.model.analysis.classify.Classifier;
import com.hmsinc.epicenter.model.analysis.classify.ClassifierMetadata;
import com.hmsinc.epicenter.model.attribute.AttributeRepository;
import com.hmsinc.epicenter.model.attribute.PatientClass;
import com.hmsinc.epicenter.model.health.Interaction;
import com.hmsinc.epicenter.model.initialization.UpgradeTasks;
import com.hmsinc.epicenter.util.ReflectionUtils;

/**
 * Handles classification of Interactions and management of Classifier
 * objects.
 * 
 * Classifiers are defined in XML, but also require a representation in the
 * database. Additionally, we need to be able to handle classifiers that are not
 * part of EpiCenter.
 * 
 * @author <a href="mailto:steve.kondik@hmsinc.com">Steve Kondik</a>
 * @version $Id: ClassificationService.java 1795 2008-06-27 18:47:54Z steve.kondik $
 */
@Service
public class ClassificationService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private AnalysisRepository analysisRepository;

    @Resource
    private AttributeRepository attributeRepository;

    @Resource
    private UpgradeTasks upgradeTasks;

    @Resource
    private PlatformTransactionManager transactionManager;

    @Resource
    private JAXBContext jaxbContext;

    @Resource
    private Cache classifierCache;

    private final Map<String, ClassificationEngine> classifiers = new HashMap<String, ClassificationEngine>();

    private org.springframework.core.io.Resource configuration;

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    @PostConstruct
    public void init() throws Exception {

        logger.info("Initializing classifiers..");
        new TransactionTemplate(transactionManager).execute(new TransactionCallbackWithoutResult() {

            /*
             * (non-Javadoc)
             * 
             * @see org.springframework.transaction.support.TransactionCallbackWithoutResult#doInTransactionWithoutResult(org.springframework.transaction.TransactionStatus)
             */
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {

                // Configure default classifiers
                final List<Classifier> allClassifiers = analysisRepository.getList(Classifier.class);

                try {

                    final ClassifierMetadata cm = (ClassifierMetadata) jaxbContext.createUnmarshaller()
                            .unmarshal(configuration.getInputStream());

                    for (Classifier classifier : cm.getClassifiers()) {

                        try {
                            final ClassificationEngine engine = ClassifierFactory
                                    .createClassifier(classifier.getResource());

                            if (engine != null) {

                                Validate.isTrue(engine.getName().equals(classifier.getName()),
                                        "Classifier names must match! (configured: " + classifier.getName()
                                                + " actual: " + engine.getName());

                                boolean isInstalled = false;
                                for (Classifier installed : allClassifiers) {
                                    if (installed.getName().equals(engine.getName())
                                            && installed.getVersion().equals(engine.getVersion())) {
                                        isInstalled = true;
                                        classifier = installed;
                                        break;
                                    }
                                }

                                classifiers.put(engine.getName(), engine);

                                if (!isInstalled) {

                                    // Copy the properties and create
                                    // Classification objects
                                    classifier.setVersion(engine.getVersion());
                                    classifier.setDescription(engine.getDescription());

                                    logger.info("Installing classifier: {}", classifier.getName());

                                    final SortedSet<Classification> classifications = new TreeSet<Classification>();
                                    for (String category : engine.getCategories()) {
                                        if (!category.equalsIgnoreCase("Other")) {
                                            classifications.add(new Classification(classifier, category));
                                        }
                                    }
                                    classifier.setClassifications(classifications);

                                    analysisRepository.save(classifier);

                                }
                            }
                        } catch (IOException e) {
                            logger.warn(e.getMessage());
                        }
                    }

                    // Load the and initialize any unconfigured classifiers..
                    for (Classifier classifier : allClassifiers) {
                        if (classifier.isEnabled() && !classifiers.containsKey(classifier.getName())) {

                            try {
                                final ClassificationEngine engine = ClassifierFactory
                                        .createClassifier(classifier.getResource());
                                if (engine != null) {
                                    classifiers.put(engine.getName(), engine);
                                }
                            } catch (IOException e) {
                                logger.warn(e.getMessage());
                            }
                        }
                    }

                    // Configure DataTypes
                    final List<DataType> dataTypes = cm.getDataTypes();

                    // Hydrate the PatientClasses because the XML descriptor
                    // will only reference the name
                    // Also remove any targets with unconfigured classifiers.
                    for (DataType dataType : dataTypes) {
                        logger.debug("Data type: " + dataType.toString());

                        final List<ClassificationTarget> unconfigured = new ArrayList<ClassificationTarget>();

                        for (ClassificationTarget ct : dataType.getTargets()) {

                            if (ct.getClassifier() == null || ct.getClassifier().getId() == null
                                    && !classifiers.containsKey(ct.getClassifier().getName())) {
                                logger.debug("Skipping unconfigured classification target");
                                unconfigured.add(ct);
                            } else {
                                final PatientClass pc = attributeRepository
                                        .getPatientClassByName(ct.getPatientClass().getName());
                                Validate.notNull(pc, "Unknown patient class: " + ct.getPatientClass().toString());
                                ct.setPatientClass(pc);

                                if (classifiers.containsKey(ct.getClassifier().getName())) {
                                    ct.setClassifier(
                                            analysisRepository.getClassifierByName(ct.getClassifier().getName()));
                                }
                            }
                        }

                        dataType.getTargets().removeAll(unconfigured);
                    }

                    upgradeTasks.validateAttributes(dataTypes, DataType.class, analysisRepository);

                } catch (JAXBException e) {
                    throw new RuntimeException(e);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }

            }
        });

    }

    /**
     * Classify an Interaction using a specific ClassificationTarget
     * 
     * @param interaction
     * @param target
     * @return
     */
    @Transactional(readOnly = true)
    public Set<Classification> classify(final Interaction interaction, final ClassificationTarget target) {

        final Set<Classification> classifications = new HashSet<Classification>();

        if (classifiers.containsKey(target.getClassifier().getName())) {

            final ClassificationEngine c = classifiers.get(target.getClassifier().getName());
            logger.trace("Classifier: {}", c.getName());

            if (c instanceof NamedItemClassificationEngine) {

                // Use a LinkedHashMap because we depend on order in the RegexClassifier.
                final Map<String, Object> properties = new LinkedHashMap<String, Object>();
                for (String propertyName : target.getPropertyName().split(",")) {

                    properties.put(propertyName,
                            ReflectionUtils.getProperty(interaction, Object.class, propertyName));
                    final List<String> categories = ((NamedItemClassificationEngine) c)
                            .classifyNamedItems(properties);
                    if (categories.size() > 0) {
                        classifications.addAll(findClassifications(target.getClassifier(), categories));
                    }
                }
            } else {

                final String property = ReflectionUtils.getProperty(interaction, String.class,
                        target.getPropertyName());
                if (property != null) {
                    final List<String> categories = c.classify(property);
                    if (categories.size() > 0) {
                        classifications.addAll(findClassifications(target.getClassifier(), categories));
                    }
                }
            }
        }

        return classifications;
    }

    /**
     * Classify a Interaction using metadata configuration.
     * 
     * @param interaction
     * @return the classifications
     */
    @Transactional(readOnly = true)
    public Set<Classification> classify(final Interaction interaction) {

        final Set<Classification> classifications = new HashSet<Classification>();
        if (interaction != null) {

            // Search for a matching Classifier..
            for (ClassificationTarget target : getClassifiersForInteraction(interaction)) {
                classifications.addAll(classify(interaction, target));
            }
        }

        logger.debug("classifications: {}", classifications);
        return classifications;
    }

    /**
     * @param interaction
     * @return
     */
    @SuppressWarnings("unchecked")
    private List<ClassificationTarget> getClassifiersForInteraction(final Interaction interaction) {

        final List<ClassificationTarget> cls;

        final Element e = classifierCache
                .get(interaction.getPatientClass().getAbbreviation() + "_" + interaction.getClass().getName());

        if (e == null) {
            cls = analysisRepository.getClassifiersForInteraction(interaction);
            classifierCache.put(new Element(interaction.getClass().getName(), cls));
        } else {
            cls = (List<ClassificationTarget>) e.getObjectValue();
        }

        return cls;
    }

    /**
     * Get the Classification objects for the categories given.
     * 
     * @param classifier
     * @param categories
     * @return
     */
    private Collection<Classification> findClassifications(Classifier classifier, List<String> categories) {
        final Set<Classification> ret = new HashSet<Classification>();
        for (Classification classification : classifier.getClassifications()) {
            if (categories.contains(classification.getCategory())) {
                ret.add(analysisRepository.load(classification.getId(), Classification.class));
            }
        }
        return ret;
    }

    /**
     * @return the classifiers
     */
    public Map<String, ClassificationEngine> getClassifiers() {
        return classifiers;
    }

    /**
     * @param configuration
     *            the configuration to set
     */
    @Required
    public void setConfiguration(org.springframework.core.io.Resource configuration) {
        this.configuration = configuration;
    }

}