content: /**
   * ﻿Copyright (C) 2007 - 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.server.database;
  
  import java.beans.PropertyChangeEvent;
  import java.beans.PropertyChangeListener;
  import java.io.File;
  import java.io.InputStream;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * 
   */
  public class DatabaseFactory implements IDatabase
  {
  	private static Logger LOGGER = LoggerFactory.getLogger(DatabaseFactory.class);
  	// Property of the name of the database. Used to define the database implementation.
  	public static final String PROPERTY_NAME_DATABASE_CLASS_NAME = "databaseClass";
  	private static IDatabase database;
  
      private static PropertyChangeListener propertyChangeListener;
  	
  	public static IDatabase getDatabase() {
  
          // FvK: create and register listener to the WPSConfig if not yet happend.
          if (propertyChangeListener == null){
              propertyChangeListener =  new PropertyChangeListener(){
                  public void propertyChange(
                      final PropertyChangeEvent propertyChangeEvent) {
                          //shutdown Database connection and instance
                          DatabaseFactory.database.shutdown();
                          DatabaseFactory.database = null;
                          DatabaseFactory.getDatabase();
                          LOGGER.info(this.getClass().getName() + ": Received Property Change Event: " + propertyChangeEvent.getPropertyName());
                      }
              }; 
              org.n52.wps.commons.WPSConfig.getInstance().addPropertyChangeListener(org.n52.wps.commons.WPSConfig.WPSCONFIG_PROPERTY_EVENT_NAME, propertyChangeListener);
          }
  
          if(DatabaseFactory.database == null) {
  			try {
  				String databaseClassName = 
  					AbstractDatabase.getDatabaseProperties(PROPERTY_NAME_DATABASE_CLASS_NAME);
  				// if databaseClassName is not defined take derby.
  				if(databaseClassName == null || databaseClassName.equals("")) {
  					LOGGER.info("Database class name was not found in properties. FlatFileDatabase will be used.");
  					databaseClassName = "org.n52.wps.server.database.FlatFileDatabase";
  				}
  				Class<?> cls = Class.forName(databaseClassName, true, DatabaseFactory.class.getClassLoader());
  				Method method = cls.getMethod("getInstance", new Class[0]);
  				IDatabase db = (IDatabase) method.invoke(cls, new Object[0]);
  				DatabaseFactory.database = db;
  			} catch(NoSuchMethodException nsm_ex) {
  				LOGGER.error("Instance returning method was not found while creating database instance. " + 
  						nsm_ex.getMessage());
  				return null;
  			} catch(InvocationTargetException it_ex) {
  				LOGGER.error("Invocation target exception while creating database instance. " + 
  						it_ex.getMessage());
  				return null;
  			} catch(IllegalAccessException ia_ex) {
  				LOGGER.error("Illegal access exception while creating database instance. " + 
  						ia_ex.getMessage());
  				return null;
  			} catch(ClassNotFoundException cnf_ex) {
  				LOGGER.error("Database class could not be found. " + 
  						cnf_ex.getMessage());
  				return null;
  			} catch(Exception ex) {
  				LOGGER.error("Database class could not be found.");
  				return null;
  			}
  		}
  		return DatabaseFactory.database;
  	}
  
  	@Override
      protected void finalize() throws Throwable {
  		super.finalize();
  		DatabaseFactory.database.shutdown();
  	}
  	
  	/**
  	 * Shutdown the database in a clean, safe way.
  	 */
  	@Override
      public void shutdown() {
  		DatabaseFactory.database.shutdown();
  	}
      
      /**
  	 * Insert a new Request into the Database.
  	 * 
  	 * @param request  The Response to insert.
  	 * @see #storeResponse(Response)
  	 */
      @Override
      public synchronized void insertRequest(String id, InputStream inputStream, boolean xml) {
  		DatabaseFactory.database.insertRequest(id, inputStream, xml);
  	}
  
  	/**
  	 * Insert a new Response into the Database.
  	 * 
  	 * @param response  The Response to insert.
  	 * @see #storeResponse(Response)
  	 */
      @Override
      public synchronized String insertResponse(String id, InputStream outputStream) {
  		return DatabaseFactory.database.insertResponse(id, outputStream);
  	}
  
  	/**
  	 * Update the Response in the Database, based on the Identifier.
  	 * 
  	 * @param response  The Response to update
  	 * @see #storeResponse(Response)
  	 */
      @Override
      public synchronized void updateResponse(String id, InputStream outputStream) {
  		DatabaseFactory.database.updateResponse(id, outputStream);
  	}
  
  	/**
  	 * Store the Response of a deferred Request. It either gets inserted into
  	 * the database, or it updates a previous Response, based on the identifier.
  	 * 
  	 * @param response  The Response to store.
  	 */
      @Override
      public synchronized String storeResponse(String id, InputStream outputStream) {
  		return DatabaseFactory.database.storeResponse(id, outputStream);
  	}
  
      @Override
      public synchronized InputStream lookupRequest(String request_id) {
  		return DatabaseFactory.database.lookupRequest(request_id);
  	}
      
  	/**
  	 * Retrieve the Response on a previous Request, based on an unique
  	 * identifier, which was already given to the client for reference.
  	 * 
  	 * @param request_id  The identifier of the Request
  	 * @return null, if an SQLException occurred, else an InputStream with the
  	 *         Response
  	 */
  	@Override
      public synchronized InputStream lookupResponse(String request_id) {
  		return DatabaseFactory.database.lookupResponse(request_id);
  	}
  	
  	@Override
      public synchronized String storeComplexValue(String id, InputStream stream, String type, String mimeType) {
  		return DatabaseFactory.database.storeComplexValue(id, stream, type, mimeType);
  	}
  	
  	/**
  	 * The URL referencing the location from which the ExecuteResponse can be retrieved. 
  	 * If "status" is "true" in the Execute request, the ExecuteResponse should also be 
  	 * found here as soon as the process returns the initial response to the client. 
  	 * It should persist at this location as long as the outputs are accessible from the server. 
  	 * The outputs may be stored for as long as the implementer of the server decides. If the 
  	 * process takes a long time, this URL can be repopulated on an ongoing basis in order to 
  	 * keep the client updated on progress. Before the process has succeeded, the ExecuteResponse 
  	 * contains information about the status of the process, including whether or not processing 
  	 * has started, and the percentage completed. It may also optionally contain the inputs and 
  	 * any ProcessStartedType interim results. When the process has succeeded, the ExecuteResponse 
  	 * found at this URL shall contain the output values or references to them.
  	 */	
  	@Override
      public String generateRetrieveResultURL(String id) {
  		return DatabaseFactory.database.generateRetrieveResultURL(id);
  	}
  	
  	/** 
  	 * Returns some name for the database, like DERBY, HSQL, ...
  	 */
  	@Override
      public String getDatabaseName() {
  		return DatabaseFactory.database.getDatabaseName();
  	}
  
      @Override
  	public String getMimeTypeForStoreResponse(String id) {
  		return DatabaseFactory.database.getMimeTypeForStoreResponse(id);
  	}
  	
      @Override
  	public long getContentLengthForStoreResponse(String id) {
  		return DatabaseFactory.database.getContentLengthForStoreResponse(id);
  	}
  
      @Override
  	public boolean deleteStoredResponse(String id) {
  		return DatabaseFactory.database.deleteStoredResponse(id);
  	}
  
      @Override
  	public File lookupRequestAsFile(String id) {
  		return DatabaseFactory.database.lookupRequestAsFile(id);
  	}
  
      @Override
  	public File lookupResponseAsFile(String id) {
  		return DatabaseFactory.database.lookupResponseAsFile(id);
  	}
  	
  }
  