/////////////////////////////////////////////////////////////////////////
//
// © Institute for Software Science, University of Vienna, 2004
//
// Copyright in this software belongs to Institute for Software Science, 
// University of Vienna, Nordbergstrasse 15/C/3, 1090 Vienna, Austria
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
//	Created By :		Aleksandar Dimitrov
//	Created Date :		2004/02/24
//	Created for Project:	GEMSS
//
////////////////////////////////////////////////////////////////////////
//
// Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//	Last commit info:	$Author: dimitrov $
//					$Date: 2004/05/28 10:25:43 $
//					$Revision: 1.6 $
//
/////////////////////////////////////////////////////////////////////////

package at.ac.univie.iss.registry;

import java.util.logging.Logger;
import java.util.logging.Level;
import java.rmi.RemoteException;
import java.util.Enumeration;
import java.util.Vector;

/**
 * The purpose of this class is to construct 
 * a handle to the data store of registry. 
 */
public class DataStoreHandler {
    
    private DataStore dataStore;
    private Vector storeOfData = new Vector();
    private static Logger logger = null;

    /**
     * Creates a new DataStore object
     */	
    public DataStoreHandler() {

        logger = Logger.getLogger("logging.registry");
        dataStore = new DataStore();
    }

    /**
     * Creates a new DataStore object, that is used to operate
     * with the database file (store() and load() methods).
     * 
     * @param	dataFile A pathname string to the data file
     */		
    public DataStoreHandler(String dataFile) {
		
        logger = Logger.getLogger("logging.registry");
        dataStore = new DataStore(dataFile);
    }

    /**
     * Queries the registry to retrieve one or more service 
     * descriptions that meet certain criteria(attribute).
     * Currently the search method is based on the whole match,
     * i.e. as result will be returned only description of services,
     * that match all attributes. If the attribute is null, then
     * the query will return all service INFOrmation from registry.
     *
     * @throws  RemoteException if an error occured
     *			during the query of the database file:
     *          - the specified attribute are incorrect
     *
     * @param	attribute array of service descriptions
     *          in a form of one or more Name/Value pairs
     *
     * @see		at.ac.univie.iss.registry.Attribute	 
     */	
    public Vector inquiry(Attribute[] attribute) throws RemoteException {			
		
        Vector foundServices = new Vector();
		
        ServiceDescription tempService;
        Attribute[] serviceAttributes;	
	    
        String compName;
        String compValue;	    			    
        
        boolean minFlag = false;
        boolean maxFlag = false;
        boolean found; 
	    
        int i, j; 	
			
        if (attribute == null) {		    
            // loads the database file and returns all registered services
            logger.log(Level.INFO, "take all published services!\n");
            storeOfData = dataStore.load();
            return storeOfData;
        } else {
            logger.log(Level.INFO, "take services with the following attributes:\n");
            
            // prints the query attributes and if some attribute's name
            // is null or "" then a remote exception is performed
            for (int count = 0; count < attribute.length; count++) {
                 
                logger.log(Level.INFO, 
                        "NAME - " + attribute[count].getName() + ", VALUE - "
                        + attribute[count].getValue() + "\n");
                
                if (attribute[count].getName() == null) {
                    logger.log(Level.WARNING, 
                            "DB-WARNING: This query can not be performed." 
                                    + " The name of the attribute can not be null!\n");
                    throw new RemoteException(
                            "DB-WARNING: This query can not be performed."
                                    + " The name of the attribute can not be null!\n");
                } else if (attribute[count].getName().trim().equals("")) {
                    logger.log(Level.WARNING, 
                            "DB-WARNING: This query can not be performed." 
                                    + " There is an attribute with unspecified name!\n");
                    throw new RemoteException(
                            "DB-WARNING: This query can not be performed."
                                    + " There is an attribute with unspecified name!\n");
                }
            }
		                   			
            // try {
            // loads the database file
            storeOfData = dataStore.load();			    
			    
            // all registered service descriptions (storeOfData.elements()) will be checked
            // for matching with the query attributes
            for (Enumeration e = storeOfData.elements(); e.hasMoreElements();) {
			        
                // takes the next registered service description 			        
                tempService = (ServiceDescription) e.nextElement();
                found = true;
                i = 0;
                                        
                // if some registered service description match the search criteria
                // then the following loop is broken and the found service is saved
                // to the vector foundServices  
                while (found && i < attribute.length) {
                        
                    // takes the current query attribute - name and value                        
                    serviceAttributes = tempService.getAttributes();
                    compName = attribute[i].getName().trim();
                    compValue = attribute[i].getValue();
                        
                    found = false;
                    j = 0;
                        
                    // check if the query attribute is specified as MIN
                    if (compName.startsWith("MIN_")) {
                        minFlag = true;
                        // takes the searcing attribute's name
                        compName = compName.substring(4).trim();
                    } else {
                        // check if the query attribute is specified as MAX
                        if (compName.startsWith("MAX_")) {
                            maxFlag = true;
                            // takes the searcing attribute's name
                            compName = compName.substring(4).trim();
                        }
                    }
                    if (serviceAttributes != null) {
        
                        while (!found && j < serviceAttributes.length) {
                                
                            // the query attribute's name is incorrect
                            if (compName.equals("")) {
                                logger.log(Level.WARNING, 
                                        "DB-WARNING: This query can not be performed." 
                                                + " There is an attribute with unspecified name!\n");
                                throw new RemoteException(
                                        "DB-WARNING: This query can not be performed."
                                                + " There is an attribute with unspecified name!\n");
                            }    
                                 
                            if (serviceAttributes[j].getName().equals(compName)) {
                                                                        
                                if (compValue == null) {
                                        
                                    if ((minFlag) || (maxFlag)) {
                                        logger.log(Level.WARNING, 
                                                "DB-WARNING: The query attribute value is not specified!\n");
                                        throw new RemoteException(
                                                "DB-WARNING: The query attribute value is not specified!\n");
                                    } else {
                                        found = true;
                                    }  
                                } else {
                                    if ((minFlag) || (maxFlag)) {
                                        try {
                                            // cvonverts the query attributte's value to int 
                                            int attrValue = Integer.parseInt(
                                                    compValue.trim());
                                        } catch (NumberFormatException nfException) {
                                            logger.log(Level.WARNING, 
                                                    "DB-WARNING: The query attributes are incorrect specified!\n");
                                            throw new RemoteException(
                                                    "DB-WARNING: The query attributes are incorrect specified!\n");
                                        }
                                    }    
                                    try {                                           
                                        if (minFlag) {           
                                            // cvonverts the query attributte's value to int and
                                            // compares this value with the registered attriburte's value
                                            if (Integer.parseInt(
                                                    serviceAttributes[j].getValue())
                                                            >= Integer.parseInt(
                                                                    compValue.trim())) {
                                                found = true; 
                                            }                                                 
                                        } else if (maxFlag) {
                                            // cvonverts the query attributte's value to int and
                                            // compares this value with the registered attriburte's value  
                                            if (Integer.parseInt(
                                                    serviceAttributes[j].getValue())
                                                            <= Integer.parseInt(
                                                                    compValue.trim())) {
                                                found = true; 
                                            }                                                 
                                        } else // the compare's value is a string and conversion is not needed                                                
                                        if (serviceAttributes[j].getValue().equals(
                                                compValue.trim())) {
                                            found = true; 
                                        }
                                    } catch (NumberFormatException nfException) {
                                        logger.log(Level.WARNING, 
                                                "DB-WARNING: a published service has a differnt"
                                                        + " type of attribute's value: found String, required int"
                                                        + "\n");
                                    }
                                }     
                            }                                 
                            j++;                                            
                        }
                    }                        
                    minFlag = false;
                    maxFlag = false;                               
                    i++;                  
                }
                    
                // the found service is added to the foundServices vector, 
                // that will be returned after the searching proccess is finished                   
                if (found) {
                    foundServices.add(tempService);
                }
            }
            // } catch(Exception e) {
            // logger.log(Level.WARNING, "DB-Exception - DataStoreHandler.inquiry()", e);
            // throw new RemoteException("DB-Exception");
            // }
        }								
        return foundServices;
    }

    /**
     * Registers the service description to a registry.
     * If a such description (more precisely - service URI) 
     * already exists in the registry, than the registration 
     * will fail. For these purposes at first the whole registry
     * dates are loaded, and then if the service URI does not exist
     * in these dates, the new service description will be saved to
     * registry with the whole registry dates.  
     *
     * @throws  RemoteException if an error occured
     *			during the registration of the service description:
     *          - the service decription has incorrect attributes
     *          - the service decription has been already registered
     *          - the service decription has not a service URI
     *
     * @param	service object that is used for 
     *          description of existing service
     *
     * @see		at.ac.univie.iss.registry.ServiceDescription	
     */	
    public void register(ServiceDescription service) throws RemoteException {

        boolean isRegistered = false;
        ServiceDescription tempService;     		
        Enumeration enum;
        Attribute[] checkAttributes;
        boolean checkService = true;	
        int count = 0;
		
        if ((service != null) && (service.getWsdlEndpoint() != null)) {
			
            if (service.getWsdlEndpoint().trim().equals("")) {
                checkService = false;  
            }
			
            checkAttributes = service.getAttributes();
				    
            if (checkAttributes != null) {
                                
                while (checkService && (count < checkAttributes.length)) {
                                                                                       					              
                    if (checkAttributes[count].getName() == null) {                        
                        checkService = false;                          
                        logger.log(Level.WARNING, 
                                "DB-WARNING: The publishing can not be performed." 
                                        + "The service has an attribute with name null!\n");                                    
                        throw new RemoteException(
                                "DB-WARNING: The publishing can not be performed." 
                                        + "The service has an attribute with name null!\n");     
                    } else if (checkAttributes[count].getName().trim().equals("")) {
                        checkService = false;                              
                        logger.log(Level.WARNING, 
                                "DB-WARNING: The publishing can not be performed." 
                                        + "The service has an attribute with incorrect name!\n");                                   
                        throw new RemoteException(
                                "DB-WARNING: The publishing can not be performed." 
                                        + "The service has an attribute with incorrect name!\n");                   
                    }
                    if (checkAttributes[count].getValue() == null) {                        
                        checkService = false;                          
                        logger.log(Level.WARNING, 
                                "DB-WARNING: The publishing can not be performed." 
                                        + "The service has an attribute with value null!\n");                                    
                        throw new RemoteException(
                                "DB-WARNING: The publishing can not be performed." 
                                        + "The service has an attribute with value null!\n");     
                    } else if (checkAttributes[count].getValue().trim().equals("")) {
                        checkService = false;                              
                        logger.log(Level.WARNING, 
                                "DB-WARNING: The publishing can not be performed." 
                                        + "The service has an attribute with incorrect value!\n");                                   
                        throw new RemoteException(
                                "DB-WARNING: The publishing can not be performed." 
                                        + "The service has an attribute with incorrect value!\n");                   
                    }                    
                    count++;
                }    
            }                
            
            if (checkService) {   																												
                // try {		
                storeOfData = dataStore.load();
							
                if (storeOfData.size() == 0) {
				    										
                    storeOfData.add(service);																					
																
                    dataStore.store(storeOfData);
                    logger.log(Level.INFO, 
                            "The specified service(" + service.getWsdlEndpoint()
                            + ") has been published!\n");    
                } else {            		
                    enum = storeOfData.elements();
                                     
                    while (!isRegistered && enum.hasMoreElements()) {
                        
                        tempService = (ServiceDescription) enum.nextElement();
                        
                        if (service.getWsdlEndpoint() == null) { 
                            isRegistered = true;
                            logger.log(Level.WARNING, 
                                    "DB-WARNING: The publishing can not be performed."
                                            + " The service is incorrect specified: wsdlEndpoint can not be null!");
                            throw new RemoteException(
                                    "DB-WARNING: The publishing can not be performed." 
                                            + "The service is incorrect specified: wsdlEndpoint can not be null!\n");  
                        } else if (service.getWsdlEndpoint().trim().equals(
                                tempService.getWsdlEndpoint().trim())) {  
                            isRegistered = true;
                            logger.log(Level.WARNING, 
                                    "DB-WARNING: The specified service(" 
                                            + service.getWsdlEndpoint().trim()
                                            + ") is already published!\n");
                            throw new RemoteException(
                                    "DB-WARNING: The specified service(" 
                                            + service.getWsdlEndpoint().trim()
                                            + ") is already published!\n");
                        }
                    }                    
                    if (!isRegistered) {
                        
                        storeOfData.add(service);                                             
                        dataStore.store(storeOfData);                         
                        logger.log(Level.INFO, 
                                "The specified service("
                                        + service.getWsdlEndpoint()
                                        + ") has been published!\n");    
                    }                    									
                }				
                // } catch(Exception e) {
                // logger.log(Level.WARNING, "DB-Exception - DataStoreHandler.register()", e);
                // throw new RemoteException("DB-Exception");
                // }
            }
        } else {
            logger.log(Level.WARNING, "DB-WARNING: The specified service is incorrect!\n");
            throw new RemoteException(
                    "DB-WARNING: The specified service is incorrect!\n");    
        }		
    }

    /**
     * Unregisters the service description from a registry.
     * If a serviceURI does not exist in the registry, than
     * the unregistration will fail.	  
     *
     * @throws  RemoteException if an error occured
     *          during the unregistration of the service description:
     *          - the specified service URI is not found
     *
     * @param   serviceURI URI of the service that will be 
     *          unregister from registry
     */	
    public void unregister(String serviceURI) throws RemoteException {
		
        boolean deleted = false;
        ServiceDescription tempService;               
        Enumeration enum;		

        if (serviceURI != null) {
		
            if (!serviceURI.trim().equals("")) {
    			
                // try {		
                storeOfData = dataStore.load();
                              
                enum = storeOfData.elements();
                   
                while (!deleted && enum.hasMoreElements()) {
                        
                    tempService = (ServiceDescription) enum.nextElement();
                        
                    if (serviceURI.equals(tempService.getWsdlEndpoint())) {
                        storeOfData.remove(tempService);
                        deleted = true;
                        dataStore.store(storeOfData);	
                        logger.log(Level.INFO, 
                                "The specified service(" + serviceURI
                                + ") has been removed!\n");    
                    }
                }
                if (!deleted) {
                    logger.log(Level.WARNING, 
                            "DB-WARNING: The specified service(" + serviceURI
                            + ") does not exist in registry!\n");
                    throw new RemoteException(
                            "DB-WARNING: The specified service(" + serviceURI
                            + ") does not exist in registry!\n");     
                }
                // } catch(Exception e) {
                // logger.log(Level.WARNING, "DB-Exception - DataStoreHandler.unregister()", e);
                // throw new RemoteException("DB-Exception");
                // }
            }
        }		
    }
}
