All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.exactpro.sf.configuration.DictionaryManager Maven / Gradle / Ivy

There is a newer version: 3.4.260
Show newest version
/******************************************************************************
 * Copyright 2009-2018 Exactpro (Exactpro Systems Limited)
 *
 * Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.exactpro.sf.configuration;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.exactpro.sf.aml.Dictionaries;
import com.exactpro.sf.aml.Dictionary;
import com.exactpro.sf.aml.DictionarySettings;
import com.exactpro.sf.aml.generator.AlertCollector;
import com.exactpro.sf.center.IVersion;
import com.exactpro.sf.center.impl.PluginLoader;
import com.exactpro.sf.common.impl.messages.AbstractMessageFactory;
import com.exactpro.sf.common.impl.messages.DefaultMessageFactory;
import com.exactpro.sf.common.impl.messages.DummyMessageFactory;
import com.exactpro.sf.common.messages.IMessageFactory;
import com.exactpro.sf.common.messages.structures.IDictionaryStructure;
import com.exactpro.sf.common.messages.structures.loaders.IDictionaryStructureLoader;
import com.exactpro.sf.common.messages.structures.loaders.XmlDictionaryStructureLoader;
import com.exactpro.sf.common.util.EPSCommonException;
import com.exactpro.sf.configuration.suri.SailfishURI;
import com.exactpro.sf.configuration.suri.SailfishURIException;
import com.exactpro.sf.configuration.suri.SailfishURIRule;
import com.exactpro.sf.configuration.suri.SailfishURIUtils;
import com.exactpro.sf.configuration.workspace.FolderType;
import com.exactpro.sf.configuration.workspace.IWorkspaceDispatcher;
import com.exactpro.sf.configuration.workspace.WorkspaceSecurityException;
import com.exactpro.sf.configuration.workspace.WorkspaceStructureException;
import com.exactpro.sf.scriptrunner.ManagerUtils;
import com.exactpro.sf.scriptrunner.ScriptRunException;
import com.exactpro.sf.scriptrunner.utilitymanager.UtilityClass;
import com.exactpro.sf.scriptrunner.utilitymanager.UtilityInfo;
import com.exactpro.sf.scriptrunner.utilitymanager.UtilityManager;
import com.exactpro.sf.scriptrunner.utilitymanager.exceptions.UtilityManagerException;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;

public class DictionaryManager implements IDictionaryManager, ILoadableManager {

	private static final Logger logger = LoggerFactory.getLogger(DictionaryManager.class);

	private final IWorkspaceDispatcher workspaceDispatcher;

	private final UtilityManager utilityManager;

	// URI -> file_name (relative to DICTIONARIES)
	private final Map location = new HashMap<>();
	// URI -> DictionarySettings
	private final Map dictSettings = new HashMap<>();
	// URI -> DictionaryStructure
	private final Map dicts = new HashMap<>();
	// URI -> IMessageFactory
	private final Map factories = new HashMap<>();
	// plugin alias -> list of dictionary URI's
	private final SetMultimap pluginDictTitles = HashMultimap.create();

    private long dictLoadedCounter;

    private final Map dictionaryIds = new ConcurrentHashMap<>();

	private final List eventListeners;

    public DictionaryManager(IWorkspaceDispatcher workspaceDispatcher, UtilityManager utilityManager) {
        this.workspaceDispatcher = Objects.requireNonNull(workspaceDispatcher, "workspaceDispatcher cannot be null");
        this.utilityManager = Objects.requireNonNull(utilityManager, "utilityManager cannot be null");
		this.eventListeners = new CopyOnWriteArrayList<>();
	}

    @Override
    public synchronized Set getDictionaryURIs() {
        return new HashSet<>(location.keySet());
    }

    @Override
    public synchronized Set getDictionaryURIs(String pluginAlias) {
        if(pluginAlias == null || !pluginDictTitles.containsKey(pluginAlias)) {
            logger.error("Dictionary titles for plugin alias '{}' not found", pluginAlias);
            return null;
        }
        return pluginDictTitles.get(pluginAlias);
    }

    @Override
	public synchronized List getCachedDictURIs() {
        return new ArrayList<>(dicts.keySet());
	}

    @Override
    public synchronized Map getDictionaryLocations() {
        return new HashMap<>(location);
    }

    @Override
	public void load(ILoadableManagerContext context) {
		try {
            ClassLoader loader = context.getClassLoaders()[0];
            InputStream stream = context.getResourceStream();
            String dictionaryFolderPath = context.getResourceFolder();
            IVersion version = context.getVersion();
		    
			JAXBContext jc = JAXBContext.newInstance(Dictionaries.class);
			Unmarshaller u = jc.createUnmarshaller();

			JAXBElement root = u.unmarshal(new StreamSource(stream), Dictionaries.class);
			Dictionaries dictionaries = root.getValue();

			for (Dictionary dict : dictionaries.getDictionary()) {

			    if (dict.getTitle() == null) {
                    throw new EPSCommonException("Null Title in config " + stream);
                }

                SailfishURI dictionaryURI = new SailfishURI(version.getAlias(), null, SailfishURIUtils.sanitize(dict.getTitle()));
                DictionarySettings settings = dictSettings.get(dictionaryURI);

                pluginDictTitles.put(dictionaryURI.getPluginAlias(), dictionaryURI);

			    if (settings == null) {
			        settings = new DictionarySettings();
			        settings.setURI(dictionaryURI);
                    dictSettings.put(dictionaryURI, settings);
			    }

			    for (String className : dict.getUtilityClassName()){
			        try {
			        UtilityClass utilityClass = utilityManager.load(loader, className, version);

			        for(String utilityClassAlias : utilityClass.getClassAliases()) {
			            SailfishURI utilityClassURI = new SailfishURI(version.getAlias(), utilityClassAlias);
			            settings.addUtilityClassURI(utilityClassURI);
                        }
                    } catch (UtilityManagerException e) {
                        if (context.getVersion().isLightweight()) {
                            logger.warn("Can't load utility class '{}'", className, e);
                        } else {
                            throw e;
                        }
			        }
                }

                for (String utilityURI : dict.getUtilityURI()) {
                    SailfishURI uri = SailfishURI.parse(utilityURI, SailfishURIRule.REQUIRE_PLUGIN, SailfishURIRule.REQUIRE_CLASS);
                    if (utilityManager.getUtilityClassByURI(uri) == null) {
                        logger.warn("SailfishURI {} is not registered", uri);
                    } else {
                        settings.addUtilityClassURI(uri);
                    }
                }

                String currentResource = location.get(dictionaryURI);
                String resource = dict.getResource();
                if (resource == null) {
                	logger.warn("resource (xml dictionary) not specified for dictionary {}", dictionaryURI);
                } else {
                	resource = dictionaryFolderPath + File.separator + resource;
                    if (currentResource != null && !currentResource.equals(resource)) {
                        logger.warn("Resources '{}' wasn't sent, because current value '{}' not null", resource, currentResource);
                    } else {
                        location.put(dictionaryURI, resource);
                    }
                }

                String factoryClassName = dict.getFactoryClassName();
                if (factoryClassName != null) {
                    if (settings.getFactoryClass() != null && !settings.getFactoryClass().getCanonicalName().equals(factoryClassName)) {
                        logger.warn("Factory class '{}' wasn't sent, because current value '{}' not null", factoryClassName, settings.getFactoryClass());
                    } else {
                        try {
                            @SuppressWarnings("unchecked")
                            Class factoryClass = (Class) loader.loadClass(factoryClassName);
                            settings.setFactoryClass(factoryClass);
                        } catch (ClassNotFoundException e) {
                            logger.warn("{} dictionary has incorrect factory [{}]. DefaultMessageFactory will be used",
                                    dict.getTitle(), dict.getFactoryClassName());
                        }
                    }
                }
            }
        } catch (JAXBException | SailfishURIException e) {
			throw new EPSCommonException("Failed to load dictionary", e);
		}
	}

	@Override
    public void finalize(ILoadableManagerContext context) throws Exception {
        // TODO Auto-generated method stub
    }

	@Override
	public synchronized IDictionaryStructure getDictionary(SailfishURI uri) throws RuntimeException {
        IDictionaryStructure dict = SailfishURIUtils.getMatchingValue(uri, dicts, SailfishURIRule.REQUIRE_RESOURCE);

		if (dict == null) {

			this.dictLoadedCounter++;
            dictionaryIds.put(uri, dictLoadedCounter);

            String resource = SailfishURIUtils.getMatchingValue(uri, location, SailfishURIRule.REQUIRE_RESOURCE);

			if (resource == null) {
				throw new RuntimeException("No dictionary found for URI: " + uri);
			}

            dict = createMessageDictionary(resource);

			if (dict == null) {
				throw new RuntimeException("Can not create dictionary for URI: " + uri +", resource = "+resource);
			}

            dicts.put(uri, dict);

			logger.info("Dictionary {} was loaded", uri);
		}

		return dict;
	}

	@Override
	public synchronized DictionarySettings getSettings(SailfishURI uri) {
	    return SailfishURIUtils.getMatchingValue(uri, dictSettings, SailfishURIRule.REQUIRE_RESOURCE);
	}

	@Override
	public synchronized  IMessageFactory getMessageFactory(SailfishURI uri) {
		IMessageFactory factory = SailfishURIUtils.getMatchingValue(uri, factories, SailfishURIRule.REQUIRE_RESOURCE);
		if (factory == null) {
            factory = ObjectUtils.defaultIfNull(loadFactory(uri), DefaultMessageFactory.getFactory());
			factories.put(uri, factory);
		}
		return factory;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
        for(Entry e : dicts.entrySet()) {
            if(sb.length() > 0) {
                sb.append(", ");
            }
			sb.append(e.getKey());
			sb.append(" = ");
			sb.append(e.getValue().getNamespace());
		}
		return sb.toString();
	}

	@Override
	public synchronized void invalidateDictionaries(SailfishURI ... uris) {
	    if (uris == null || uris.length == 0) {
            dicts.clear();
            dictionaryIds.clear();
            factories.clear();
            invalidateEvent(Collections.unmodifiableSet(dicts.keySet()));
            logger.info("All dictionaries have been invalidated");
	    } else {
	        for (SailfishURI uri : uris) {
                dicts.remove(uri);
                dictionaryIds.remove(uri);
                factories.remove(uri);
            }
            invalidateEvent(new HashSet<>(Arrays.asList(uris)));
            StringBuilder builder = new StringBuilder("Dictionaries ")
                .append(Arrays.toString(uris))
                .append(" have been invalidated");

            logger.info("{}", builder);
	    }
	}


    private IMessageFactory loadFactory(SailfishURI uri) {
        try {
            IMessageFactory iMessageFactory = null;
            DictionarySettings settings = SailfishURIUtils.getMatchingValue(uri, dictSettings, SailfishURIRule.REQUIRE_RESOURCE);

            if((settings != null) && (settings.getFactoryClass() != null)) {
                Class clazz = settings.getFactoryClass();
                iMessageFactory = clazz.newInstance();
            } else {
                iMessageFactory = new AbstractMessageFactory() {
                    @Override
                    public String getProtocol() {
                        return "Unknown";
                    }
                };
            }

            iMessageFactory.init(uri, getDictionary(uri));

            return iMessageFactory;
        } catch (Exception e) {
            logger.warn("Can not create message factory for SailfishURI: " + uri, e);
        }
        return null;
    }

	private synchronized void createDictionary(String filename, DictionarySettings settings, boolean overwrite) {

	    Objects.requireNonNull(filename, "Dictionary file is null");
	    Objects.requireNonNull(settings, "Dictionary settings is null");
	    Objects.requireNonNull(settings.getURI(), "Dictionary Sailfish URI is null");

        SailfishURI uri = settings.getURI();

        if(getDictionaryURIs().contains(uri)) {
            if (!overwrite) {
                throw new EPSCommonException(String.format("Concurrent dictionary registration with SailfishURI: '%s' and name: '%s' already exists", uri, filename));
            }
            invalidateDictionaries(uri);
            return;
        }


		Dictionary dictionary = new Dictionary();

		dictionary.setTitle(uri.getResourceName());
		dictionary.setResource(filename);

        for (SailfishURI suri : settings.getUtilityClassURIs()) {
            dictionary.getUtilityURI().add(suri.toString());
		}

		Dictionaries dictionaries = null;

        if(workspaceDispatcher.exists(FolderType.CFG, PluginLoader.CUSTOM_DICTIONARIES_XML)) {

			try {
                File customDictionariesXml = workspaceDispatcher.getFile(FolderType.CFG, PluginLoader.CUSTOM_DICTIONARIES_XML);

				JAXBContext jc = JAXBContext.newInstance(Dictionaries.class);
				Unmarshaller u = jc.createUnmarshaller();

				dictionaries = (Dictionaries) u.unmarshal(customDictionariesXml);

			} catch (Exception e) {
				throw new RuntimeException("Can't read " + PluginLoader.CUSTOM_DICTIONARIES_XML, e);
			}

		} else {
			dictionaries = new Dictionaries();
		}

		dictionaries.getDictionary().add(dictionary);

		try {
            File customDictionariesXml = workspaceDispatcher.createFile(FolderType.CFG, true, PluginLoader.CUSTOM_DICTIONARIES_XML);

			JAXBContext jc = JAXBContext.newInstance(Dictionaries.class);
			Marshaller m = jc.createMarshaller();
			m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

			m.marshal(dictionaries, customDictionariesXml);

		} catch (Exception e) {
			throw new RuntimeException("Can't write to " + PluginLoader.CUSTOM_DICTIONARIES_XML, e);
		}

		dictSettings.put(uri, settings);
		location.put(uri, "cfg" + File.separator + "dictionaries" + File.separator + filename);
        pluginDictTitles.put(ObjectUtils.defaultIfNull(uri.getPluginAlias(), IVersion.GENERAL), uri);
		createEvent(uri);
	}

	/**
	 *
	 * Load dictionary from xml/xsd file
	 * @param pathName file path (relative to {ROOT})
	 * @return
	 */
	@Override
	public IDictionaryStructure createMessageDictionary(String pathName) throws RuntimeException {
	    try {
	        IDictionaryStructureLoader loader = createStructureLoader(pathName);

            File targetFile = workspaceDispatcher.getFile(FolderType.ROOT, pathName);

        	try (InputStream in = new BufferedInputStream(new FileInputStream(targetFile))) {
        		return loader.load(in);
        	}

	    } catch (Exception e) {
            throw new ScriptRunException("Could not create dictionary [" + pathName + "]", e);
        }
	}


	@Override
	public IMessageFactory createMessageFactory() {
		return DefaultMessageFactory.getFactory();
	}

	@Override
	public UtilityInfo getUtilityInfo(SailfishURI dictionaryURI, SailfishURI utilityURI, Class... argTypes) throws SailfishURIException {
	    if(utilityURI.isAbsolute()) {
	        return utilityManager.getUtilityInfo(utilityURI, argTypes);
	    }

	    DictionarySettings settings = SailfishURIUtils.getMatchingValue(dictionaryURI, dictSettings, SailfishURIRule.REQUIRE_RESOURCE);

	    if(settings == null) {
	        return null;
	    }

	    for(SailfishURI utilityClassURI : settings.getUtilityClassURIs()) {
	        UtilityInfo utilityInfo = utilityManager.getUtilityInfo(utilityURI.merge(utilityClassURI), argTypes);

            if(utilityInfo != null) {
                return utilityInfo;
            }
	    }

	    return null;
	}

    @Override
    public UtilityInfo getUtilityInfo(SailfishURI dictionaryURI, SailfishURI utilityURI, long line, long uid, String column, AlertCollector alertCollector, Class... argTypes) throws SailfishURIException {
        if (utilityURI.isAbsolute()) {
            return utilityManager.getUtilityInfo(utilityURI, argTypes);
        }

        DictionarySettings settings = SailfishURIUtils.getMatchingValue(dictionaryURI, dictSettings, SailfishURIRule.REQUIRE_RESOURCE);

        if (settings == null) {
            return null;
        }

        return ManagerUtils.getUtilityInfo(settings.getUtilityClassURIs(), utilityManager, utilityURI, line, uid, column, alertCollector, argTypes);
    }

    @Override
    public Set getUtilityURIs(SailfishURI dictionaryURI) {
        DictionarySettings settings = SailfishURIUtils.getMatchingValue(dictionaryURI, dictSettings, SailfishURIRule.REQUIRE_RESOURCE);

        if (settings == null) {
            return Collections.emptySet();
        }

        Set result = new HashSet<>();

        for (SailfishURI utilityClassURI : settings.getUtilityClassURIs()) {
            Set infos = utilityManager.getUtilityInfos(utilityClassURI);
            for (UtilityInfo info : infos) {
                result.add(info.getURI());
            }
        }

        return result;
    }

	@Override
	public long getDictionaryId(SailfishURI uri) {
	    return SailfishURIUtils.getMatchingValue(uri, dictionaryIds, SailfishURIRule.REQUIRE_RESOURCE);
	}


	@Override
	public void subscribeForEvents(IDictionaryManagerListener listener){
        eventListeners.add(listener);
	}

	@Override
	public void unSubscribeForEvents(IDictionaryManagerListener listener){
        eventListeners.remove(listener);
	}

    public void invalidateEvent(Set uris) {
        for (IDictionaryManagerListener listener : eventListeners) {
            try {          
                listener.invalidateEvent(uris);
            } catch (RuntimeException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }


    public void createEvent(SailfishURI uri) {
        for (IDictionaryManagerListener listener : eventListeners){
            listener.createEvent(uri);
        }
    }


	@Override
    public IDictionaryRegistrator registerDictionary(String title, boolean overwrite) throws WorkspaceStructureException, WorkspaceSecurityException {

        SailfishURI suri;
	    try {
            suri = new SailfishURI(IVersion.GENERAL, null, title);
        } catch (SailfishURIException e) {
            throw new EPSCommonException(String.format("Name '%s' is incorrect", title));
        }
	    synchronized (this) {
            if(!overwrite && getDictionaryURIs().contains(suri)) {
	            throw new EPSCommonException(String.format("Dictionary with title %s and suri %s already registred", title, suri));
	        }
        }

	    Path relativePath = Paths.get("cfg", "dictionaries", title + ".xml");
        workspaceDispatcher.createFile(FolderType.ROOT, overwrite, relativePath.toString());
        return new DictionaryRegistrator(suri, relativePath, overwrite);
	}

	private IDictionaryStructureLoader createStructureLoader(String pathName) throws FileNotFoundException, WorkspaceSecurityException {
        String extension = FilenameUtils.getExtension(pathName);

        if ("xml".equals(extension.toLowerCase())) {
            return new XmlDictionaryStructureLoader();
        }

        throw new EPSCommonException("Unresolved dictionary extension: '" + extension + "'");
	}

    private class DictionaryRegistrator implements IDictionaryRegistrator {

	    private final DictionarySettings dictionarySettings;
	    private final boolean overwrite;
	    private final Path relativePath;

        public DictionaryRegistrator(SailfishURI dictionarySURI, Path relativePath, boolean overwrite) {
            this.relativePath = relativePath;
            this.overwrite = overwrite;
            this.dictionarySettings = new DictionarySettings();
            dictionarySettings.setURI(dictionarySURI);
            dictionarySettings.setFactoryClass(DummyMessageFactory.class);
        }

        @Override
        public SailfishURI registrate() {

            try {
                createDictionary(relativePath.getFileName().toString(), dictionarySettings, overwrite);
            } catch (Exception e) {
                throw new EPSCommonException(String.format("Could not create dictionary with SailfishURI: '%s'", dictionarySettings.getURI()), e);
            }

            return dictionarySettings.getURI();
        }

        @Override
        public String getPath() {
            return relativePath.toString();
        }

        @Override
        public IDictionaryRegistrator addUtilityClassURI(SailfishURI uri) {
            dictionarySettings.addUtilityClassURI(uri);
            return this;
        }

        @Override
        public IDictionaryRegistrator addUtilityClassURI(Collection uri) {
            for (SailfishURI sailfishURI : uri) {
                addUtilityClassURI(sailfishURI);
            }
            return this;
        }

        @Override
        public IDictionaryRegistrator setFactoryClass(Class factoryClass) {
            dictionarySettings.setFactoryClass(factoryClass);
            return this;
        }
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy