Package ru.aristar.jnuget.sources

Source Code of ru.aristar.jnuget.sources.PackageSourceFactory

package ru.aristar.jnuget.sources;

import com.google.common.collect.Multimap;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.activation.UnsupportedDataTypeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.aristar.jnuget.common.CollectionGenericType;
import ru.aristar.jnuget.common.OptionConverter;
import ru.aristar.jnuget.common.Options;
import ru.aristar.jnuget.common.StorageOptions;
import ru.aristar.jnuget.common.TriggerOptions;
import ru.aristar.jnuget.files.Nupkg;
import ru.aristar.jnuget.sources.push.AfterTrigger;
import ru.aristar.jnuget.sources.push.BeforeTrigger;
import ru.aristar.jnuget.sources.push.ModifyStrategy;

/**
* Фабрика источников данных
*
* @author sviridov
*/
public class PackageSourceFactory {

    //TODO Переписать фабрику под использование ObjectDescriptor
    /**
     * Экземпляр фабрики
     */
    protected volatile static PackageSourceFactory instance;
    /**
     * Настройки сервера
     */
    private final Options options;
    /**
     * Активные хранилища пакетов
     */
    private volatile ConcurrentHashMap<String, PackageSource<Nupkg>> packageSources;
    /**
     * Активные хранилища пакетов
     */
    private volatile ConcurrentHashMap<String, PackageSource<Nupkg>> publicPackageSources;

    /**
     * Конструктор, перечитывающий настройки
     */
    public PackageSourceFactory() {
        this.options = Options.loadOptions();
    }
    /**
     * Логгер
     */
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * Создает индексируемую обертку для хранилища пакетов
     *
     * @param packageSource хранилище, которое необходимо индексировать
     * @param storageName имя, используемое для сохранения индекса
     * @param refreshInterval интервал обновления информации в индекск
     * @param cronString строка cron (планирование обновления индекса)
     * @param saveIndex сохранять или нет индекс на диске
     * @return индексируемое хранилище
     */
    protected PackageSource<Nupkg> createIndexForStorage(PackageSource<Nupkg> packageSource,
            String storageName, Integer refreshInterval, String cronString, boolean saveIndex) {
        logger.debug("Создание индекса для хранилища {}", new Object[]{packageSource});
        IndexedPackageSource indexedPackageSource = new IndexedPackageSource();
        boolean refreshNow = cronString == null;
        indexedPackageSource.setUnderlyingSource(packageSource, refreshNow);
        if (saveIndex) {
            File storageFile = IndexedPackageSource.getIndexSaveFile(Options.getNugetHome(), storageName);
            indexedPackageSource.setIndexStoreFile(storageFile);
        }
        if (cronString != null) {
            logger.info("Расписание обновления индекса для хранилища {} установлено в \"{}\"",
                    new Object[]{packageSource, cronString});
            indexedPackageSource.setCronSheduller(cronString);
        } else if (refreshInterval != null) {
            logger.info("Интервал обновления для хранилища {} установлен в {}",
                    new Object[]{packageSource, refreshInterval});
            indexedPackageSource.setRefreshInterval(refreshInterval);
        }
        return indexedPackageSource;
    }

    /**
     * Создание хранилищ на основе настроек
     *
     * @param serviceOptions настройки приложения
     */
    protected void createPackageSources(Options serviceOptions) {
        //Создание корневого хранилища
        logger.info("Инициализация файлового хранища");
        packageSources = new ConcurrentHashMap<>();
        publicPackageSources = new ConcurrentHashMap<>();
        for (StorageOptions storageOptions : serviceOptions.getStorageOptionsList()) {
            try {
                if (storageOptions.getStorageName() == null) {
                    throw new IllegalArgumentException("Имя хранилища должно быть указано");
                }
                PackageSource<Nupkg> childSource = createPackageSource(storageOptions);
                packageSources.put(storageOptions.getStorageName(), childSource);
                if (storageOptions.isPublic()) {
                    publicPackageSources.put(storageOptions.getStorageName(), childSource);
                }
            } catch (Exception e) {
                logger.warn("Ошибка создания хранилища пакетов", e);
            }
        }
        logger.info("Создано {} хранилищ из них публичных {}", new Object[]{packageSources.size(), publicPackageSources.size()});
    }

    /**
     * Производит поиск сеттера для свойства
     *
     * @param sourceClass класс, в котором производится поиск сеттера
     * @param propertyName имя свойства
     * @return метод - сеттер
     * @throws NoSuchMethodException метод не найден
     */
    protected Method findSetter(Class<?> sourceClass, String propertyName) throws NoSuchMethodException {
        String setterName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
        for (Method method : sourceClass.getMethods()) {
            if (method.getName().equals(setterName) && method.getParameterTypes().length == 1) {
                return method;
            }
        }
        throw new NoSuchMethodException("Метод " + setterName + " не найден в классе " + sourceClass.getName());
    }

    /**
     * Создание нового дочернего хранилища на основе настроек
     *
     * @param storageOptions настройки хранилища
     * @return хранилище пакетов
     * @throws Exception ошибка создания хранилища
     */
    protected PackageSource<Nupkg> createPackageSource(StorageOptions storageOptions)
            throws Exception {
        //Создание файлового хранилища
        logger.info("Инициализация хранилища типа {}", new Object[]{storageOptions.getClassName()});
        Class<?> sourceClass = Class.forName(storageOptions.getClassName());
        Object object = sourceClass.newInstance();
        if (!(object instanceof PackageSource)) {
            throw new UnsupportedDataTypeException("Класс " + sourceClass + " не является хранилищем пакетов");
        }
        @SuppressWarnings("unchecked")
        PackageSource<Nupkg> newSource = (PackageSource) object;
        newSource.setName(storageOptions.getStorageName());
        setObjectProperties(storageOptions.getProperties(), object);
        ModifyStrategy pushStrategy = createPushStrategy(storageOptions);
        newSource.setPushStrategy(pushStrategy);
        logger.info("Установлена стратегия фиксации");
        if (storageOptions.isIndexed()) {
            newSource = createIndexForStorage(
                    newSource,
                    storageOptions.getStorageName(),
                    storageOptions.getRefreshInterval(),
                    storageOptions.getCronString(),
                    storageOptions.isSaveIndex());
        }
        logger.info("Хранилище создано");
        return newSource;
    }

    /**
     * Создает стратегию фиксации пакетов
     *
     * @param storegeOptions настройки стратегии
     * @return стратегия фиксации
     * @throws Exception ошибка создания стратегии
     */
    protected ModifyStrategy createPushStrategy(StorageOptions storegeOptions) throws Exception {
        //Создание стратегии фиксации
        //TODO убрать настройки стратегии, оставить только разрешение/запрещение публикации
        logger.info("Инициализация стратегии фиксации и удаления");
        ModifyStrategy pushStrategy = new ModifyStrategy(storegeOptions.isCanPush(), storegeOptions.isCanDelete());
        //Триггеры BEFORE
        pushStrategy.getBeforePushTriggers().addAll(createTriggers(storegeOptions.getBeforeTriggersOptions(), BeforeTrigger.class));
        //Триггеры Afther
        pushStrategy.getAftherPushTriggers().addAll(createTriggers(storegeOptions.getAftherTriggersOptions(), AfterTrigger.class));
        logger.info("Стратегия создана");
        return pushStrategy;
    }

    /**
     * Создает коллекцию триггеров из коллекции настроек
     *
     * @param <T> класс требуемого объекта
     * @param options коллекция настроек
     * @param triggerClass класс требуемого объекта
     * @return коллекция настроек
     * @throws Exception ошибка создания триггера
     */
    protected <T> Collection<T> createTriggers(Collection<TriggerOptions> options, Class<T> triggerClass) throws Exception {
        ArrayList<T> pushTriggers = new ArrayList<>();
        for (TriggerOptions triggerOptions : options) {
            T pushTrigger = createTrigger(triggerOptions, triggerClass);
            pushTriggers.add(pushTrigger);
        }
        return pushTriggers;
    }

    /**
     * Создает триггер фиксации пакета
     *
     * @param <T> класс требуемого объекта
     * @param triggerOptions настройки триггера
     * @param triggerClass класс требуемого объекта
     * @return триггер
     * @throws Exception ошибка создания триггера
     */
    protected <T> T createTrigger(TriggerOptions triggerOptions, Class<T> triggerClass) throws Exception {
        logger.info("Создание триггера типа {}", new Object[]{triggerOptions.getClassName()});
        Class<?> sourceClass = Class.forName(triggerOptions.getClassName());
        if (!triggerClass.isAssignableFrom(sourceClass)) {
            throw new UnsupportedDataTypeException("Класс " + sourceClass
                    + " не является " + BeforeTrigger.class.getCanonicalName());
        }
        Object object = sourceClass.newInstance();
        @SuppressWarnings("unchecked")
        T trigger = (T) object;
        setObjectProperties(triggerOptions.getProperties(), trigger);
        logger.info("Триггер создан");
        return trigger;
    }

    /**
     * Устанавливает свойства объекту
     *
     * @param properties карта свойств
     * @param newObject объект
     * @throws Exception ошибка установки свойств
     */
    private void setObjectProperties(Multimap<String, String> properties, Object newObject)
            throws Exception {
        Class<?> sourceClass = newObject.getClass();
        for (String key : properties.keySet()) {
            Method method = findSetter(sourceClass, key);
            Class<?> valueType = method.getParameterTypes()[0];
            Object value;
            if (Collection.class.isAssignableFrom(valueType)) {
                value = getCollectionValue(method, valueType, properties.get(key));
            } else {
                value = getSingleValue(valueType, properties.get(key));
            }
            method.invoke(newObject, value);

        }
    }

    /**
     * Возвращает экземпляр фабрики, или создает новый
     *
     * @return экземпляр фабрики
     */
    public static PackageSourceFactory getInstance() {
        if (instance == null) {
            synchronized (PackageSourceFactory.class) {
                if (instance == null) {
                    instance = new PackageSourceFactory();
                }
            }
        }
        return instance;
    }

    /**
     * @return хранилища пакетов
     */
    private Map<String, PackageSource<Nupkg>> getPackageSourcesMap() {
        if (packageSources == null) {
            synchronized (this) {
                if (packageSources == null) {
                    createPackageSources(options);
                }
            }
        }
        return packageSources;
    }

    /**
     *
     * @return публичные хранилища пакетов
     */
    private Map<String, PackageSource<Nupkg>> getPublicPackageSourcesMap() {
        if (publicPackageSources == null) {
            synchronized (this) {
                if (publicPackageSources == null) {
                    createPackageSources(options);
                }
            }
        }
        return publicPackageSources;
    }

    /**
     * @return источники пакетов
     */
    public List<PackageSource<Nupkg>> getPackageSources() {
        ArrayList<PackageSource<Nupkg>> result = new ArrayList<>(getPackageSourcesMap().values());
        return result;
    }

    /**
     * @return публичное доступные источники пакетов
     */
    public List<PackageSource<Nupkg>> getPublicPackageSources() {
        ArrayList<PackageSource<Nupkg>> result = new ArrayList<>(getPublicPackageSourcesMap().values());
        return result;
    }

    /**
     * @param storageName имя хранилища
     * @return хранилище пакетов или null
     */
    public PackageSource<Nupkg> getPackageSource(String storageName) {
        return getPackageSourcesMap().get(storageName);
    }

    /**
     * @param storageName имя хранилища
     * @return публичное хранилище пакетов или null
     */
    public PackageSource<Nupkg> getPublicPackageSource(String storageName) {
        return getPublicPackageSourcesMap().get(storageName);
    }

    /**
     * @return настройки приложения
     */
    public Options getOptions() {
        return options;
    }

    /**
     * Производит попытку создать значение указанного типа из строки
     *
     * @param <T> тип значения
     * @param valueType тип значения
     * @param stringValue строковое представление типа значения
     * @return распознанное значение
     * @throws InstantiationException ошибка вызова конструктора
     * @throws NoSuchMethodException не найден конструктор, принимающий как
     * аргумент строку
     * @throws IllegalArgumentException не найден конструктор, принимающий как
     * аргумент строку
     * @throws InvocationTargetException ошибка вызова конструктора
     * @throws SecurityException конструктор с указанными параметрами не
     * является публичным
     * @throws IllegalAccessException ошибка вызова конструктора
     */
    public static <T> T getValueFromString(Class<T> valueType, String stringValue) throws
            InstantiationException,
            NoSuchMethodException,
            IllegalArgumentException,
            InvocationTargetException,
            SecurityException,
            IllegalAccessException {
        T value;
        if (valueType.isPrimitive()) {
            value = getPrimitiveValue(stringValue, valueType);
        } else {
            final Constructor<T> constructor = valueType.getConstructor(String.class);
            value = constructor.newInstance(stringValue);
        }
        return value;
    }

    /**
     * Возвращает значение для примитивного типа
     *
     * @param <T> тип значения
     * @param string строковое значение
     * @param targetClass тип значения, в которое требуется преобразовать строку
     * @return преобразованное значение примитивного типа
     */
    @SuppressWarnings("unchecked")
    private static <T> T getPrimitiveValue(String string, Class<T> targetClass) {
        if (targetClass == java.lang.Boolean.TYPE) {
            return (T) Boolean.valueOf(string);
        } else if (targetClass == java.lang.Character.TYPE) {
            return (T) Character.valueOf(string.charAt(0));
        } else if (targetClass == java.lang.Byte.TYPE) {
            return (T) Byte.valueOf(string);
        } else if (targetClass == java.lang.Short.TYPE) {
            return (T) Short.valueOf(string);
        } else if (targetClass == java.lang.Integer.TYPE) {
            return (T) Integer.valueOf(string);
        } else if (targetClass == java.lang.Long.TYPE) {
            return (T) Long.valueOf(string);
        } else if (targetClass == java.lang.Float.TYPE) {
            return (T) Float.valueOf(string);
        } else if (targetClass == java.lang.Double.TYPE) {
            return (T) Double.valueOf(string);
        } else {
            throw new UnsupportedOperationException("Primitive type "
                    + targetClass + " is unsupported");
        }
    }

    /**
     * Получение значения, если параметр является коллекцией
     *
     * @param method метод сеттер.
     * @param valueType тип коллекции.
     * @param values строковые представления значений коллекции.
     * @return коллекция значений.
     * @throws Exception ошибка преобразования.
     */
    private Collection getCollectionValue(Method method, Class<?> valueType, Collection<String> values) throws Exception {
        CollectionGenericType annotation = method.getAnnotation(CollectionGenericType.class);
        Class<?> elementType = annotation == null ? String.class : annotation.type();
        @SuppressWarnings("unchecked")
        Collection<? super Object> result = (Collection) valueType.getConstructor().newInstance();
        for (String stringValue : values) {
            stringValue = OptionConverter.replaceVariables(stringValue);
            Object value = getValueFromString(elementType, stringValue);
            result.add(value);
        }
        return result;
    }

    /**
     * Получение значения, для простого параметра (не коллекции)
     *
     * @param valueType тип параметра
     * @param values строковое значение
     * @return значение
     * @throws Exception ошибка преобразования.
     */
    private Object getSingleValue(Class<?> valueType, Collection<String> values) throws Exception {
        Object value;
        String stringValue = values.iterator().next();
        stringValue = OptionConverter.replaceVariables(stringValue);
        value = getValueFromString(valueType, stringValue);
        return value;
    }
}
TOP

Related Classes of ru.aristar.jnuget.sources.PackageSourceFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.