content: /**
   * ﻿Copyright (C) 2012 - 2019 52°North Initiative for Geospatial Open Source
   * Software GmbH
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published
   * by the Free Software Foundation.
   *
   * If the program is linked with libraries which are licensed under one of
   * the following licenses, the combination of the program with the linked
   * library is not considered a "derivative work" of the program:
   *
   *       • Apache License, version 2.0
   *       • Apache Software License, version 1.0
   *       • GNU Lesser General Public License, version 3
   *       • Mozilla Public License, versions 1.0, 1.1 and 2.0
   *       • Common Development and Distribution License (CDDL), version 1.0
   *
   * Therefore the distribution of the program linked with libraries licensed
   * under the aforementioned licenses, is permitted by the copyright holders
   * if the distribution is compliant with both the GNU General Public
   * License version 2 and the aforementioned licenses.
   *
   * This program 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.
   */
  package org.n52.wps.mc;
  
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.Collections;
  
  import net.opengis.wps.x100.ProcessDescriptionType;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.n52.movingcode.runtime.GlobalRepositoryManager;
  import org.n52.movingcode.runtime.codepackage.MovingCodePackage;
  import org.n52.movingcode.runtime.coderepository.MovingCodeRepository;
  import org.n52.movingcode.runtime.coderepository.RepositoryChangeListener;
  import org.n52.movingcode.runtime.processors.ProcessorFactory;
  import org.n52.wps.PropertyDocument.Property;
  import org.n52.wps.commons.WPSConfig;
  import org.n52.wps.server.IAlgorithm;
  import org.n52.wps.server.IAlgorithmRepository;
  
  /**
   * This class implements an {@link IAlgorithmRepository} for MovingCode Packages. This repository maintains a
   * {@link GlobalRepositoryManager} internally and fires notification events to {@link WPSConfig}.
   * 
   * This class will also load the basic configuration for the MC runtime subsystem (Available Processors,
   * Repository URLs and Folders)
   * 
   * @author Matthias Mueller, TU Dresden
   * 
   */
  public class MCProcessRepository implements IAlgorithmRepository {
  
  	// static string definitions
  	private static final String REPO_FEED_REPO_PARAM = "REMOTE_REPOSITORY";
  	private static final String LOCAL_ZIP_REPO_PARAM = "LOCAL_REPOSITORY";
  	private static final String CACHED_REMOTE_REPO_PARAM = "CACHED_REMOTE_REPOSITORY";
  
  	// use the GlobalRepoManager from mc-runtime for the process inventory
  	private GlobalRepositoryManager rm = GlobalRepositoryManager.getInstance();
  
  	// valid functionIDs
  	// needs to be volatile since this field may be updated by #updateContent()
  	private volatile Collection<String> supportedFunctionIDs = Collections.emptyList();
  
  	private static Logger LOGGER = LoggerFactory.getLogger(MCProcessRepository.class);
  
  	public MCProcessRepository() {
  		super();
  
  		// register a change listener with the GlobalRepositoryManager
  		// to listen for content updates
  		rm.addRepositoryChangeListener(new RepositoryChangeListener() {
  
  			@Override
  			public void onRepositoryUpdate(MovingCodeRepository updatedRepo) {
  				// trigger a content update of this repo
  				updateContent();
  				// and notify the WPS framework to trigger a Capabilities update
  				LOGGER.info("Moving Code repository content has changed. Capabilities update required.");
  				WPSConfig.getInstance().firePropertyChange(WPSConfig.WPSCAPABILITIES_SKELETON_PROPERTY_EVENT_NAME);
  			}
  		});
  
  		// check if the repository is active
  		if (WPSConfig.getInstance().isRepositoryActive(this.getClass().getCanonicalName())) {
  
  			// trigger remote repo init in separate thread
  			Thread tLoadRemote = new LoadRepoThread();
  			tLoadRemote.start();
  		}
  		else {
  			LOGGER.debug("MCProcessRepository is inactive.");
  		}
  	}
  
  	@Override
  	public Collection<String> getAlgorithmNames() {
  		return supportedFunctionIDs;
  	}
  
  	private synchronized void updateContent(){
  
  		// run this block if validFunctionIDs are not yet available
  		// checks which available functions can be executed with current configuration
  
  		// 1. get all available functionIDs
  		String[] fids = rm.getFunctionIDs();
  		ArrayList<String> exFIDs = new ArrayList<String>();
  
  		// 2. for each function ID
  		for (String currentFID : fids) {
  			// 2.a retrieve implementing packages
  			MovingCodePackage[] mcps = rm.getPackageByFunction(currentFID);
  			// 2.b check whether each one of them can be executed
  			//     by the current processor configuration
  			for (MovingCodePackage currentMCP : mcps) {
  				boolean supported = ProcessorFactory.getInstance().supportsPackage(currentMCP);
  				if (supported) {
  					exFIDs.add(currentFID);
  					break;
  				}
  			}
  		}
  
  		supportedFunctionIDs = exFIDs;
  	}
  
  	@Override
  	public IAlgorithm getAlgorithm(String processID) {
  		return new MCProcessDelegator(processID);
  	}
  
  	@Override
  	public ProcessDescriptionType getProcessDescription(String processID) {
  		return filterProcessDescription(rm.getProcessDescription(processID));
  	}
  
  	@Override
  	public boolean containsAlgorithm(String processID) {
  		return rm.providesFunction(processID);
  	}
  
  	@Override
  	public void shutdown() {
  		// TODO Auto-generated method stub
  		// we probably do not need any logic here
  	}
  
  	static ProcessDescriptionType filterProcessDescription(ProcessDescriptionType description){
  		description.setStatusSupported(true);
  		description.setStoreSupported(true);
  		return description;
  	}
  
  	/**
  	 * 
  	 * @author Matthias Mueller
  	 *
  	 */
  	private final class LoadRepoThread extends Thread {
  
  		@Override
  		public void run() {
  
  			// get properties to find out which remote repositories we shall invoke
  			Property[] propertyArray = WPSConfig.getInstance().getPropertiesForRepositoryClass(MCProcessRepository.class.getCanonicalName());
  
  			// for each remote repository: add to RepoManager
  			for (Property property : propertyArray) {
  				if (property.getName().equalsIgnoreCase(REPO_FEED_REPO_PARAM) && property.getActive()) {
  					// convert to URL, check and register
  					try {
  						URL repoURL = new URL(property.getStringValue());
  						rm.addRepository(repoURL);
  						LOGGER.info("Added MovingCode Repository: " + property.getName() + " - "
  								+ property.getStringValue());
  					}
  					catch (MalformedURLException e) {
  						LOGGER.warn("MovingCode Repository is not a valid URL: " + property.getName() + " - "
  								+ property.getStringValue());
  					}
  					catch (Exception e) {
  						// catch any unexpected error; if we get here this is probably an indication for a
  						// bug/flaw in mc-runtime ...
  						LOGGER.error("Error loading repository: " + property.getName() + " - "
  								+ property.getStringValue());
  					}
  
  				}
  			}
  
  			// for each local repository: add to RepoManager
  			for (Property property : propertyArray) {
  				if (property.getName().equalsIgnoreCase(LOCAL_ZIP_REPO_PARAM) && property.getActive()) {
  					// identify Folder, check and register
  					try {
  						String repoFolder = property.getStringValue();
  						rm.addLocalZipPackageRepository(repoFolder);
  						LOGGER.info("Added MovingCode Repository: " + property.getName() + " - "
  								+ property.getStringValue());
  					}
  					catch (Exception e) {
  						// catch any unexpected error; if we get here this is probably an indication for a
  						// bug/flaw in mc-runtime ...
  						LOGGER.error("Error loading repository: " + property.getName() + " - "
  								+ property.getStringValue());
  						e.printStackTrace();
  					}
  
  				}
  			}
  
  
  			LOGGER.info("The following repositories have been loaded:\n{}", Arrays.toString(rm.getRegisteredRepositories()));
  		}
  	}
  
  }
  