WSRF::Lite - Implementation of the Web Service Resource Framework


WSRF-Lite documentation Contained in the WSRF-Lite distribution.

Index


Code Index:

NAME

Top

WSRF::Lite - Implementation of the Web Service Resource Framework

VERSION

Top

This document refers to version 0.8.3.0 of WSRF::Lite released March, 2011

SYNOPSIS

Top

This is an implementation of the Web Service Resource Framework (WSRF), which is built on SOAP::Lite. It provides support for WSRF, WS-Addressing and for digitally signing a SOAP messages using an X.509 certificate according to the OASIS WS-Security standard.

DESCRIPTION

Top

WSRF::Lite consists of a number of classes for developing WS-Resources. A WS-Resource is an entity that has a Web service interface defined by the WSRF family of specifications that maintains state between calls to the service.

WSRF::Lite provides a number of ways of implementing WS-Resources: one approach uses a process to store the state of the WS-Resource, another approach uses a process to store the state of many WS-Resources and the last approach uses files to store the state of the WS-Resources between calls to the WS-Resource. The different approachs have different benifits, using one process per WS-Resource does not scale very well and isn't very fault tolerant (eg a machine reboot) but is quite easy to develop. The approachs are just examples of how to implement a WS-Resource, it should be possible to use them as a basis to develop tailored solutions for particular applications. For example you could use a relational database to store the state of the WS-Resources.

WSRF::Constants

Top

Defines the set of namespaces used in WSRF::Lite and the directories used to store the named sockets and data files.

$WSRF::Constants::SOCKETS_DIRECTORY

Directory to contain the named sockets of the process based WS-Resources.

$WSRF::Constants::Data

Directory used to store files that hold state of WS-Resoures that use file based storage

$WSRF::Constants::WSA

WS-Addressing namespace.

$WSRF::Constants::WSRL

WS-ResourceLifetimes namespace.

$WSRF::Constants::WSRP

WS-ResourceProperties namespace.

$WSRF::Constants::WSSG

WS-ServiceGroup namespace.

$WSRF::Constants::WSBF

WS-BaseFaults namespace.

$WSRF::Constants::WSU

WS-Security untility namespace.

$WSRF::Constants::WSSE

WS-Security extension namespace.

$WSRF::Constants::WSA_ANON

From the WS-Addressing specification, it is used to indicate an anonymous return address. If you are using a request-response protocol like HTTP which uses the same connection for the request and response you use this as the ReplyTo address in SOAP WS-Addressing header of the request.

WSRF::SOM

Top

Extends SOAP::SOM with one extra method "raw_xml".

METHODS

raw_xml

Returns the raw XML of a message, useful if you want to parse the message using some other tool than provided with SOAP::Lite:

  my $xml = $som->raw_xml;

WSRF::Deserializer

Top

Overrides SOAP::Deserializer to return a WSRF::SOM object, which includes the raw XML of the message, from the deserialize method.

METHODS

The methods are the same as SOAP::Deserializer.

WSRF::WSRFSerializer

Top

Overrides SOAP::Serializer. This class extends the SOAP::Serializer class which creates the XML SOAP Enevlope. WSRF::WSRFSerializer overrides the "envelope" method so that it adds the WSRF, WS-Addressing and WS-Security namespaces to the SOAP Envelope, it also where the message signing happens. The XML SOAP message has to be created before it can be signed.

METHODS

The methods are the same as SOAP::Serializer, the "envelope" method is overridden to include the extra namespaces and to digitally sign the SOAP message if required.

WSRF::SimpleSerializer

Top

Overrides SOAP::Serializer. This is helper class that is based in SOAP::Serializer, it will serialize a SOAP::Data object into XML but without adding the SOAP namespaces etc. It is useful if you want to extra some simple XML from a SOM object, retrieve a SOAP::Data object from the SOM then serialize it to simple XML.

 my $serializer = WSRF::SimpleSerializer->new();
 my $xml = $seriaizer->serialize( $som->dataof('/Envelope/Body/[1]') );

METHODS

All methods are the same as SOAP::Serializer except "serialize".

serialize

This method from SOAP::Serializer is overridden so that it does not add the SOAP namepaces to the XML or set the types of the elements in the XML.

  sub serialize {
     my $self = shift @_;
     $self->autotype(0);
     $self->namespaces({});
     $self->encoding(undef);
     $self->SUPER::serialize(@_);
  }

WSRF::Container

Top

WSRF::Container handles incoming messages and dispatchs them to the appropriate WS-Resource.

METHODS

handle

Takes a HTTP Request object and dispatchs it to the appropriate WS-Resource, handle returns a HTTP Response object from the WS-Resource which should be returned to the client.

WSRF::WS_Address

Top

Class to provide support for WS-Addressing

METHODS

new

Creates a new WSRF::WS_Address object, takes either a SOM object or raw XML that contains a WS-Addressing Endpoint Reference and creates a WSRF::WS_Addressing object.

from_envelope

Creates a new WSRF::WS_Address object from a SOM representation of a SOAP Envelope that contains a WS-Addressing Endpoint Reference.

MessageID

If the WSRF::WS_Address is used to send a message to a service to client this function is used to create a unique identifier for the message. The identifier goes into the WS-Addressing SOAP Header MessageID.

XML

Returns the WS-Addressing Endpoint Reference as a string.

serializeReferenceParameters

Outputs the ReferenceParameters of the WS-Addressing Endpoint Reference.

WSRF::BaseFaults

Top

Class to support the WSRF BaseFaults specification

METHODS

die_with_Fault

To return a WSRF BaseFault call die_with_Fault. die_with_Fault creates a SOAP fault then dies.

	 die_with_Fault(
	    OriginatorReference => $EPR,             
	    ErrorCode           => $errorcode,     
	    dialect             => $dialect,       	
	    Description         => $Description,
	    FaultCause          => $FaultCause  
	  );

OriginatorReference is the WS-Addressing Endpoint Reference of the WS-Resource that the fault orignially came from. ErrorCode allows the WS-Resource to pass an error code back to the client. dialect is the dialect that the error code belongs to. Description provides a description of the fault and FaultCause provides the reason for the fault.

WSRF::Time

Top

WSRF::Time provides two helper sub routines for converting a W3C time to seconds since the Epoch and vice versa.

METHODS

ConvertStringToEpochTime

Converts a W3C date time string to the number of seconds since the UNIX Epoch.

ConvertEpochTimeToString

Converts a time in seconds since the UNIX Epoch to a W3C date time string.

VARIABLES

EXPIRES_IN

You can specify how long until an item expires with $WSRF::TIME::EXPIRES_IN. This variable defaults to 60 seconds.

WSRF::Resource

Top

A process based WS-Resource. The state of the WS-Resource is held in a process, the WSRF::Lite Container talks to the WS-Resource via a named UNIX socket.

METHODS

new

Creates a new WSRF::Resource.

  my $resource = WSRF::Resource->new(
          module    => Counter,       
          path      => /WSRF/Counter/Counter.pm,
	  ID        => M4325324563456,
	  namespace => Counter
          ); 

module is the name of the module that implements the WS-Resource, path is the path to the module relative to $ENV{WSRF_MODULES}, ID is the identifier for your WS-Resource, it will used as part of the URI in the WS-Addressing EPR. If you do not include the ID one will be assigned for you. namespace is the namespace of the WSDL port for any non WSRF operations the WS-Resource supports, if no namespace is provided the name of the module will be used

handle

This subroutine should be called after new. It forks the process that is the WS-Resource. Anything passed to handle is sent to the init method of the WS-Resource after it is created. The WS-Addressing EPR of the WS-Resource is available to the WS-Resource through $ENV{WSA}. handle returns the WSRF identifier for the WS-Resource, this is used to form the URI used in the WS-Addressing EPR.

ID

ID returns the WSRF identifier for the WS-Resource.

WSRF::FileLock

Top

Simple class to provide file locking. It is possible to use fcntl to do file locking but some file systems don't support it. WSRF::FileLock is used to by the file based WS-Resources in WSRF::Lite to prevent concurrent access to the WS-Resource by more than one client.

METHODS

new

new takes a name and tries to create a directory with that name, if there is already a directory with that name it will sleep for half a second and retry. When the directory is created a new WSRF::FileLock object is returned, then the object goes out of scope the directory is removed.

   my $lock = WSRF::FileLock->new($somefilelocation); 

WSRF::File

Top

This class provides support for serializing the state of a WS-Resource to a file.

METHODS

new

Takes a WSRF::SOM envelope, gets the ID of the WS-Resource and then loads the properties of the WS-Resource into the WSRF::WSRP::ResourceProperties hash. new locks the WS-Resource so that no other client can access the WS-Resource while this clients request is being processed. When the WSRF::File object runs out of scope and is destroyed the lock is removed.

ID

Returns the WSRF::Lite indentifier of the WS-Resource.

path

Filename of the file that holds the state of the WS-Resource.

toFile

Serializes the WSRF::WSRP::ResourceProperties hash back to the file. If the properties of the WS-Resource have been modified this should be called before the WSRF::File object goes out of scope.

WSRF::Header

Top

WSRF::Header provides one helper routine header

METHODS

This subroutine takes a WSRF::SOM envelope and creates the appropriate SOAP Headers for the response including the required WS-Addressing SOAP headers.

 


 sub foo {
    my $envelope = pop @_;

    return WSRF::Header::header($envelope); 
  } 

WSRF::WSRP

Top

Provides support for WSRF ResourceProperties, the properties of the WS-Resource are stored in a hash called %WSRF::WSRP::ResourceProperties.

METHODS

xmlizeProperties
GetResourcePropertyDocument
GetResourceProperty
GetMultipleResourceProperties
SetResourceProperties
InsertResourceProperties
UpdateResourceProperties
DeleteResourceProperties

WSRF::WSRL

Top

Provides support for WS-ResourceLifetimes. WS-ResourceLifetime defines a standard mechanism for controlling the lifetime of a WS-Resource. It adds the ResourceProperty TerminationTime to the set of ResourceProerties of the WS-Resource, the TerminationTim cannot be changed through the WS-ResourceProperties - it can only be modified using the WS-ResourceLifetime SetTerminationTime operation.

METHODS

Destroy
SetTerminationTime

WSRF::FileBasedResourceProperties

Top

If a WS-Resource module inherits from this class then its ResourceProperties will be stored in a file.

METHODS

GetResourceProperty
GetMultipleResourceProperties
SetResourceProperties
InsertResourceProperties
UpdateResourceProperties
DeleteResourceProperties
GetResourcePropertyDocument

WSRF::FileBasedResourceLifetimes

Top

If a WS-Resource wants to store its state in a file and wants to support WS-ResourceLifetimes it should inherit from this class. WSRF::FileBasedResourceLifetimes inherits from WSRF::FileBasedResourceProperties.

METHODS

Destroy
SetTerminationTime

WSRF::MultiResourceProperties

Top

In this case a single process acts on behave of a number of WS-Resources. The ResourceProperties are all held in a hash - the WSRF::Lite identifier of the WS-Resource is used as the key to the hash. The WSRF::Lite Container talks to the process through a named UNIX socket - the name of the socket is the same as the name of the module. The WS-Resource module should inherit this class

METHODS

GetResourcePropertyDocument
GetResourceProperty
GetMultipleResourceProperties
SetResourceProperties
InsertResourceProperties
UpdateResourceProperties
DeleteResourceProperties

WSRF::MultiResourceLifetimes

Top

Extends WSRF::MultiResourceProperties to add support for WS-ResourceLifetime.

METHODS

Destroy
SetTerminationTime

WSRF::ServiceGroup

Top

Provides support for WS-ServiceGroups. This implementation of WS-ServiceGroups stores the state of the WS-ServiceGroup in a file, it extends WSRF::FileBasedResourceLifetimes.

METHODS

Add

Adds a WS-Resource to the ServiceGroup

createServiceGroup

Creates a new ServiceGroup

WSRF::ServiceGroupEntry

Top

Provides support for ServiceGroupEntry WS-Resources defined in the WS-ServiceGroup specification. Each ServiceGroupEntry WS-Resource represents an entry in a ServiceGroup, destroy the ServiceGroupEntry and the entry disappears from the ServiceGroup.

METHODS

GetResourcePropertyDocument
GetResourceProperty
GetMultipleResourceProperties
SetResourceProperties
Destroy
SetTerminationTime

WSRF::Lite

Top

Extends SOAP::Lite to provide support for WS-Addressing. WSRF::Lite uses WSRF::WSRFSerializer and WSRF::Deserializer by default, it will also automatically include the WS-Addressing SOAP headers in the SOAP message. If $ENV{WSS} is set to true, $ENV{HTTPS_CERT_FILE} points to the public part of a X.509 certificate and $ENV{HTTPS_KEY_FILE} points to the unencrypted private key of the certificate then WSRF::Lite will digitally sign the message according to the WS-Security specification.

METHODS

WSRF::Lite supports the same set of methods as SOAP::Lite with the addition of wsaddess.

wsaddress

This can be used instead of the proxy method, it takes a WSRF::WS_Address object for the address of the service or WS-Resource:

	$ans=  WSRF::Lite
	  -> uri($uri)
	  -> wsaddress(WSRF::WS_Address->new()->Address($target))              
	  -> createCounterResource(); 

WSRF::WSS

Top

Provides support for digitally signing SOAP messages according to the WS-Security specification.

METHODS

sign
verify

WSRF-Lite documentation Contained in the WSRF-Lite distribution.
# ==============================================================================
#
# Copyright (C) 2000-2008 University of Manchester 
# WSRF::Lite is free software; you can redistribute it
# and/or modify it under the same terms as Perl itself.
#
# version 0.8.2.7
# Author:         Mark Mc Keown (mark.mckeown@manchester.ac.uk)
#
# Stefan Zasada (sjzasada@lycos.co.uk) did most of the work implementing
# WS-Security - a big thanks goes to Savas Parastatidis
# (http://savas.parastatidis.name/) for helping to get it working with
# .NET.
#
# Contributors:   Andrew Porter, Stephen Pickles,
#                 Sven van den Berghe, Jonathan Chin
#                 Jamie Vicary, Bruno Harbulot
#                 Ivan Porro, Ross Nicoll, Luke @ yahoo
#                 Mary Thompson,  Alex Peeters, Bjoern A. Zeeb
#                 Glen Fu, John Newman, Doug Claar, Edward Kawas
#
# Some parts of the this module are taken from SOAP::Lite -
# here is the required copyright
#
# Copyright (C) 2000-2005 Paul Kulchenko (paulclinger@yahoo.com)
#
#===============================================================================

package WSRF::Lite;

use SOAP::Lite;
use strict;

use vars qw{ $VERSION };

BEGIN {
	$VERSION = '0.8.3.0';
}

# WSRF uses WS-Address headers in the SOAP Header - by default
# SOAP::Lite will croak on these so we change the default in
# SOAP::Lite. The SOAP spec defines the mustUnderstand attribute -
# if an element has this attribute then the service must understand
# what to do with this element. See
# http://www.w3.org/TR/soap12-part1/#soapmu
#
# BUG - should ony accept headers we really do understand
$SOAP::Constants::DO_NOT_CHECK_MUSTUNDERSTAND = 1;

# A singleton class to hold the external socket if there is one.
package WSRF::SocketHolder;

my $oneTrueSelf;

sub instance {
	unless ( defined $oneTrueSelf ) {
		my ( $type, $extern_socket ) = @_;
		my $this = { _socket => $extern_socket };
		$oneTrueSelf = bless $this, $type;
	}
	return $oneTrueSelf;
}

sub close {
	my $self = shift;
	if ( defined $oneTrueSelf ) {
		my $foo =
		  defined( $ENV{SSL} )
		  ? $self->{_socket}->close( SSL_no_shutdown => 1 )
		  : $self->{_socket}->close;
	}
	undef $oneTrueSelf;
}

#===============================================================================
package WSRF::Constants;

#
# Where the named Sockets and ResourceProperty files are stored.
# User can overide these in the Container script.
$WSRF::Constants::SOCKETS_DIRECTORY = "/tmp/wsrf";
$WSRF::Constants::Data         = $WSRF::Constants::SOCKETS_DIRECTORY . "/data/";
$WSRF::Constants::ExternSocket = undef;
%WSRF::Constants::ModuleNamespaceMap = ();

#The set of namespaces used throughout.
#$WSRF::Constants::WSA  = 'http://www.w3.org/2005/03/addressing';
$WSRF::Constants::WSA = 'http://www.w3.org/2005/08/addressing';

#$WSRF::Constants::WSRL = 'http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceLifetime';
$WSRF::Constants::WSRL = 'http://docs.oasis-open.org/wsrf/rl-2';

#$WSRF::Constants::WSRP = 'http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceProperties';
$WSRF::Constants::WSRP = 'http://docs.oasis-open.org/wsrf/rp-2';

#$WSRF::Constants::WSSG = 'http://www.ibm.com/xmlns/stdwip/web-services/WS-ServiceGroup';
$WSRF::Constants::WSSG = 'http://docs.oasis-open.org/wsrf/sg-2';

#$WSRF::Constants::WSBF = 'http://www.ibm.com/xmlns/stdwip/web-services/WS-BaseFaults';
$WSRF::Constants::WSBF = 'http://docs.oasis-open.org/wsrf/bf-2';

$WSRF::Constants::WSU =
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';
$WSRF::Constants::WSSE =
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';

#$WSRF::Constants::WSA_ANON = $WSRF::Constants::WSA.'/role/anonymous';
$WSRF::Constants::WSA_ANON = $WSRF::Constants::WSA . '/anonymous';

$WSRF::Constants::DS = 'http://www.w3.org/2000/09/xmldsig#';

#===============================================================================
# We override SOAP::SOM to store the raw XML from a SOAP message - this class is
# used by the WSRF::Deserializer below. SOAP::Lite does not provide you with
# access to the raw XML of a SOAP message (It was on the SOAP::Lite TODO list)
# - here we override the SOAP::SOM module to provide access to the raw XML -
# we override the SOAP::Deserializer which returns the SOAP::SOM object to
# make sure that it actually keeps the XML

package WSRF::SOM;

use strict;
use vars qw(@ISA);

@ISA = qw(SOAP::SOM);

# function to return raw XML
sub raw_xml {
	my $self = shift;
	return $self->{_xml};
}

#===============================================================================
# We override the SOAP::Serializer to store the raw XML of the SOAP message.
# Normally a SOAP::Lite service cannot access the raw XML of a request - this
# is sometimes useful for the Service developer who might want to use
# XML DOM instead of SOM. The Deserializer returns a WSRF::SOM object - wich
# we have defined above.
package WSRF::Deserializer;

use strict;

use vars qw(@ISA);

@ISA = qw(SOAP::Deserializer);

#This is very similar to the SOAP::Deserializer only a couple of lines are added
# Copyright (C) 2000-2005 Paul Kulchenko (paulclinger@yahoo.com)
sub deserialize {
	SOAP::Trace::trace('()');
	my $self = shift->new;

	# initialize
	$self->hrefs( {} );
	$self->ids(   {} );

	# TBD: find better way to signal parsing errors
	# This is returning a parsed body, however, if the message was mime
	# formatted, then the self->ids hash should be populated with mime parts
	# as will the self->mimeparser->parts array
	my $parsed =
	  $self->decode( $_[0] );    # TBD: die on possible errors in Parser?
	  # Thought - decode should return an ARRAY which may contain MIME::Entities
	  # then the SOM object that is created and returned from this will know how
	  # to parse them out

	# Having this code here makes multirefs in the Body work, but multirefs
	# that reference XML fragments in a MIME part do not work.
	if ( keys %{ $self->ids() } ) {
		$self->traverse_ids($parsed);
	} else {
		$self->ids($parsed);
	}
	$self->decode_object($parsed);

	# these are the changes from SOAP::Deserializer
	# otherwise the code is the same. We simply add the raw XML to
	# the som hash
	my $som = WSRF::SOM->new($parsed);
	$som->{'_xml'} = $_[0];

	# first check if MIME parser has been initialized
	# simple $self->mimeparser() call doesn't work because of
	# "lazy initialization" --PK
	if ( defined $self->{'_mimeparser'} && $self->mimeparser->parts ) {

		# This seems like an unnecessary copy... does SOAP::SOM have a handle on
		# the SOAP::Lite->mimeparser instance so that I can skip this?
		$som->{'_parts'} = $self->mimeparser->parts;
	}
	return $som;
}

#===============================================================================
# We override the SOAP::Serializer to add extra namespaces to the SOAP element
# - these are namesapace we will use a lot wsrl, wsrp, wsa. These are placed
# in any SOAP message we return from the service. The user can use the
# prefixs wsrl, wsrp and wsa and not have to worry about defining the
# namespaces
#
# WSRF::WSRFSerializer is were the message is signed - signing is tricky
# because we have to create the XML before we sign it, so the process of
# signing a SOAP message requires two passes through the serializer. The
# first pass (std_envelope) creates the SOAP message, the second actually
# signs it. THIS IS NOT EFFICIENT BUT WHO CARES?!
package WSRF::WSRFSerializer;

use vars qw(@ISA);

@ISA = qw(SOAP::Serializer);

# This function is the same as SOAP::Serializer::envelope except that
# it adds an extra attribute (wsu:Id="myBody") into the Body element -
# this is used by WS-Security to identify the bits of a message that
# have been signed.
#
# We also add extra namespaces for WSRF and WSA into the SOAP Envelope
# element so we do not need to declare them in the message itself
# Copyright (C) 2000-2005 Paul Kulchenko (paulclinger@yahoo.com)
sub old_envelope {
	SOAP::Trace::trace('()');
	my $self = shift->new;

	$self->autotype(0);
	$self->attr(
				 {
				   'xmlns:wsa'  => $WSRF::Constants::WSA,
				   'xmlns:wsrl' => $WSRF::Constants::WSRL,
				   'xmlns:wsrp' => $WSRF::Constants::WSRP,
				   'xmlns:wsu'  => $WSRF::Constants::WSU,
				   'xmlns:wsse' => $WSRF::Constants::WSSE
				 }
	);

	my $type = shift;
	my ( @parameters, @header );
	for (@_) {

		# Find all the SOAP Headers
		if ( defined($_) && ref($_) && UNIVERSAL::isa( $_ => 'SOAP::Header' ) )
		{
			push( @header, $_ );

			# Find all the SOAP Message Parts (attachments)
		} elsif (    defined($_)
				  && ref($_)
				  && $self->context
				  && $self->context->packager->is_supported_part($_) )
		{
			$self->context->packager->push_part($_);

			# Find all the SOAP Body elements
		} else {
			push( @parameters, $_ );
		}
	}
	my $header = @header ? SOAP::Data->set_value(@header) : undef;
	my ( $body, $parameters );
	if ( $type eq 'method' || $type eq 'response' ) {
		SOAP::Trace::method(@parameters);

		my $method = shift(@parameters);

		#         or die "Unspecified method for SOAP call\n";

		$parameters = @parameters ? SOAP::Data->set_value(@parameters) : undef;
		if ( !defined($method) ) {
		} elsif ( UNIVERSAL::isa( $method => 'SOAP::Data' ) ) {
			$body = $method;
		} elsif ( $self->use_prefix ) {
			$body = SOAP::Data->name($method)->uri( $self->uri );
		} else {
			$body =
			  SOAP::Data->name($method)->attr( { 'xmlns' => $self->uri } );

#$body = SOAP::Data->name($method)->uri($self->uri); # original return before use_prefix
		}

		# This is breaking a unit test right now...
		$body->set_value(
				   SOAP::Utils::encode_data( $parameters ? \$parameters : () ) )
		  if $body;
	} elsif ( $type eq 'fault' ) {
		SOAP::Trace::fault(@parameters);
		$body =
		  SOAP::Data->name(
						   SOAP::Utils::qualify( $self->envprefix => 'Fault' ) )

		  # parameters[1] needs to be escaped - thanks to aka_hct at gmx dot de
		  # commented on 2001/03/28 because of failing in ApacheSOAP
		  # need to find out more about it
		  # -> attr({'xmlns' => ''})
		  ->value(
			\SOAP::Data->set_value(
				SOAP::Data->name(
								  faultcode => SOAP::Utils::qualify(
											  $self->envprefix => $parameters[0]
								  )
				  )->type(""),
				SOAP::Data->name(
					   faultstring => SOAP::Utils::encode_data( $parameters[1] )
				  )->type(""),
				defined( $parameters[2] )
				? SOAP::Data->name(
					detail => do {
						my $detail = $parameters[2];
						ref $detail ? \$detail : $detail;
					  }
				  )
				: (),
				defined( $parameters[3] )
				? SOAP::Data->name( faultactor => $parameters[3] )->type("")
				: (),
			)
		  );
	} elsif ( $type eq 'freeform' ) {
		SOAP::Trace::freeform(@parameters);
		$body = SOAP::Data->set_value(@parameters);
	} elsif ( !defined($type) ) {

	 # This occurs when the Body is intended to be null. When no method has been
	 #  passed in of any kind.
	} else {
		die "Wrong type of envelope ($type) for SOAP call\n";
	}

	$self->seen( {} );    # reinitialize multiref table
	                      # Build the envelope
	  # Right now it is possible for $body to be a SOAP::Data element that has not
	  # XML escaped any values. How do you remedy this?
	my ($encoded) = $self->encode_object(
		  SOAP::Data->name(
			  SOAP::Utils::qualify( $self->envprefix => 'Envelope' ) =>
				\SOAP::Data->value(
				  (
					$header ? SOAP::Data->name(
						 SOAP::Utils::qualify( $self->envprefix => 'Header' ) =>
						   \$header
					  ) : ()
				  ),
				  (
					$body
					? SOAP::Data->name(
						   SOAP::Utils::qualify( $self->envprefix => 'Body' ) =>
							 \$body
					  )->attr( { 'wsu:Id' => $WSRF::WSS::ID{myBody}  } )
					: SOAP::Data->name(
							  SOAP::Utils::qualify( $self->envprefix => 'Body' )
					  )->attr( { 'wsu:Id' => $WSRF::WSS::ID{myBody}  } )
				  ),
				)
			)->attr( $self->attr )
	);
	$self->signature( $parameters->signature ) if ref $parameters;

	# IMHO multirefs should be encoded after Body, but only some
	# toolkits understand this encoding, so we'll keep them for now (04/15/2001)
	# as the last element inside the Body
	#      v -------------- subelements of Envelope
	#          vv -------- last of them (Body)
	#                v --- subelements
	push( @{ $encoded->[2]->[-1]->[2] }, $self->encode_multirefs )
	  if ref $encoded->[2]->[-1]->[2];

	# Sometimes SOAP::Serializer is invoked statically when there is no context.
	# So first check to see if a context exists.
	# TODO - a context needs to be initialized by a constructor?
	if ( $self->context && $self->context->packager->parts ) {

	# TODO - this needs to be called! Calling it though wraps the payload twice!
	# return $self->context->packager->package($self->xmlize($encoded));
	}
	return $self->xmlize($encoded);
}

sub std_envelope {
	SOAP::Trace::trace('()');
	my $self = shift->new;
	my $type = shift;

	$self->autotype(0);
	$self->attr(
				 {
				   'xmlns:wsa'  => $WSRF::Constants::WSA,
				   'xmlns:wsrl' => $WSRF::Constants::WSRL,
				   'xmlns:wsrp' => $WSRF::Constants::WSRP,
				   'xmlns:wsu'  => $WSRF::Constants::WSU,
				   'xmlns:ds'   => $WSRF::Constants::DS,
				   'xmlns:wsse' => $WSRF::Constants::WSSE
				 }
	);

	my ( @parameters, @header );
	for (@_) {

		# Find all the SOAP Headers
		if ( defined($_) && ref($_) && UNIVERSAL::isa( $_ => 'SOAP::Header' ) )
		{
			push( @header, $_ );

			# Find all the SOAP Message Parts (attachments)
		} elsif (    defined($_)
				  && ref($_)
				  && $self->context
				  && $self->context->packager->is_supported_part($_) )
		{
			$self->context->packager->push_part($_);

			# Find all the SOAP Body elements
		} else {
			push( @parameters, SOAP::Utils::encode_data($_) );
		}
	}
	my $header = @header ? SOAP::Data->set_value(@header) : undef;
	my ( $body, $parameters );
	if ( $type eq 'method' || $type eq 'response' ) {
		SOAP::Trace::method(@parameters);

		my $method = shift(@parameters);

		#	  or die "Unspecified method for SOAP call\n";

		$parameters = @parameters ? SOAP::Data->set_value(@parameters) : undef;
		if ( !defined($method) ) {
		} elsif ( UNIVERSAL::isa( $method => 'SOAP::Data' ) ) {
			$body = $method;
		} elsif ( $self->use_default_ns ) {
			if ( $self->{'_ns_uri'} ) {
				$body =
				  SOAP::Data->name($method)
				  ->attr( { 'xmlns' => $self->{'_ns_uri'}, } );    
			} else {
				$body = SOAP::Data->name($method);
			}
		} else {

 # Commented out by Byrne on 1/4/2006 - to address default namespace problems
 #      $body = SOAP::Data->name($method)->uri($self->{'_ns_uri'});
 #      $body = $body->prefix($self->{'_ns_prefix'}) if ($self->{'_ns_prefix'});

	   # Added by Byrne on 1/4/2006 - to avoid the unnecessary creation of a new
	   # namespace
	   # Begin New Code (replaces code commented out above)
			$body = SOAP::Data->name($method);
			my $pre = $self->find_prefix( $self->{'_ns_uri'} );
			$body = $body->prefix($pre) if ( $self->{'_ns_prefix'} );

			# End new code

		}

# This is breaking a unit test right now...
#$body->set_value(SOAP::Utils::encode_data($parameters ? \$parameters : ())) if $body;
		$body->set_value( $parameters ? \$parameters : () ) if $body;
	} elsif ( $type eq 'fault' ) {
		SOAP::Trace::fault(@parameters);
		$body =
		  SOAP::Data->name(
						   SOAP::Utils::qualify( $self->envprefix => 'Fault' ) )

		  # parameters[1] needs to be escaped - thanks to aka_hct at gmx dot de
		  # commented on 2001/03/28 because of failing in ApacheSOAP
		  # need to find out more about it
		  # -> attr({'xmlns' => ''})
		  ->value(
			\SOAP::Data->set_value(
				SOAP::Data->name(
								  faultcode => SOAP::Utils::qualify(
											  $self->envprefix => $parameters[0]
								  )
				  )->type(""),
				SOAP::Data->name(
					   faultstring => SOAP::Utils::encode_data( $parameters[1] )
				  )->type(""),
				defined( $parameters[2] )
				? SOAP::Data->name(
					detail => do {
						my $detail = $parameters[2];
						ref $detail ? \$detail : $detail;
					  }
				  )
				: (),
				defined( $parameters[3] )
				? SOAP::Data->name( faultactor => $parameters[3] )->type("")
				: (),
			)
		  );
	} elsif ( $type eq 'freeform' ) {
		SOAP::Trace::freeform(@parameters);
		$body = SOAP::Data->set_value(@parameters);
	} elsif ( !defined($type) ) {

	 # This occurs when the Body is intended to be null. When no method has been
	 # passed in of any kind.
	} else {
		die "Wrong type of envelope ($type) for SOAP call\n";
	}

	$self->seen( {} );    # reinitialize multiref table
	                      # Build the envelope
	  # Right now it is possible for $body to be a SOAP::Data element that has not
	  # XML escaped any values. How do you remedy this?
	my ($encoded) = $self->encode_object(
		  SOAP::Data->name(
			  SOAP::Utils::qualify( $self->envprefix => 'Envelope' ) =>
				\SOAP::Data->value(
				  (
					$header ? SOAP::Data->name(
						 SOAP::Utils::qualify( $self->envprefix => 'Header' ) =>
						   \$header
					  ) : ()
				  ),
				  (
					$body
					? SOAP::Data->name(
						   SOAP::Utils::qualify( $self->envprefix => 'Body' ) =>
							 \$body
					  )->attr( { 'wsu:Id' => $WSRF::WSS::ID{myBody}  } )
					: SOAP::Data->name(
							  SOAP::Utils::qualify( $self->envprefix => 'Body' )
					  )->attr( { 'wsu:Id' => $WSRF::WSS::ID{myBody}  } )
				  ),
				)
			)->attr( $self->attr )
	);
	$self->signature( $parameters->signature ) if ref $parameters;

	# IMHO multirefs should be encoded after Body, but only some
	# toolkits understand this encoding, so we'll keep them for now (04/15/2001)
	# as the last element inside the Body
	#                 v -------------- subelements of Envelope
	#                      vv -------- last of them (Body)
	#                            v --- subelements
	push( @{ $encoded->[2]->[-1]->[2] }, $self->encode_multirefs )
	  if ref $encoded->[2]->[-1]->[2];

	# Sometimes SOAP::Serializer is invoked statically when there is no context.
	# So first check to see if a context exists.
	# TODO - a context needs to be initialized by a constructor?
	if ( $self->context && $self->context->packager->parts ) {

	# TODO - this needs to be called! Calling it though wraps the payload twice!
	#  return $self->context->packager->package($self->xmlize($encoded));
	}
	return $self->xmlize($encoded);
}

# This function is called whenever a SOAP message is created using the
# WSRF::Serializer. First it calls std_envelope to create the SOAP message,
# then it takes this message and signs the bits of the message that should
# be signed and adds the extra signing information into the message
sub envelope {
	my $self = shift @_;

	#create an envelope - this returns raw XML
	my $envelope = $self->std_envelope(@_);

	#if the user has defined these env then he wants the envlope signed -
	#we take the envelope  in the above step and do the necessary
	if ( defined( $ENV{WSS_SIGN} ) ) {

		#call the function to sign the envlope - returns the Header and Body
		#as raw XML
		my ( $header, $Body ) = WSRF::WSS::sign($envelope);

		#returns the body and header as XMl - the header does not have its top
		#and tail ie. the <soap:Header> and </soap:Header> are missing so we
		#add them
		my ($encoded) = $self->encode_object(
			 SOAP::Data->name(
				 SOAP::Utils::qualify( $self->envprefix => 'Envelope' ) =>
				   \SOAP::Data->value(
					 SOAP::Data->name(
						 SOAP::Utils::qualify( $self->envprefix => 'Header' ) =>
						   \SOAP::Data->value($header)->type('xml')
					 ),
					 SOAP::Data->value($Body)->type('xml')
				   )
			   )->attr( $self->attr )
		);

		#$encoded is a SOAP::data - we convert it to XML
		$envelope = $self->xmlize($encoded);
	}

	return $envelope;
}

#===============================================================================
# Take a SOAP::Data object and serialise it - if we are given a SOAP::SOM or
# SOAP::Data object and we want to get simple XML without all the SOAP stuff
# added we use this class. Useful if the user wants to use DOM instead of
# SOM to handle the object.
#
# This is useful if we have a SOAP::Data or SOAP::SOM object which we want to
# convert to XML (e.g. to write to a file) without all the SOAP crap.
# Other Perl packages will do this for you (convert a Perl object to XML)
# but I want to reuse the SOAP::Lite stuff.
#
package WSRF::SimpleSerializer;

use strict;
use vars qw(@ISA);

@ISA = qw(SOAP::Serializer);    # derived from the SOAP::Serializer

sub typecast { return; }

#we override the serialize funtion, switching of lots of stuff
sub serialize {
	my $self = shift @_;
	$self->autotype(0);
	$self->namespaces( {} );
	$self->encoding(undef);
	$self->SUPER::serialize(@_);
}

#===============================================================================
# The Container that handles all the connections for us.
#
# All incoming messages arrive at the handle function -
# in previous versions of WSRF::Lite function that was
# way too big. Now we have a hash which allows use to
# map messages to functions depending on the destination
# URI. This makes it easy to add handlers for messages.
#
# BUG - should be Object Orientated
#
package WSRF::Container;

use IO::Socket;
use HTTP::Daemon;
use HTTP::Status;
use HTTP::Response;

# This hash maps incoming messages to functions - the mapping is done
# using the RequestURI in the HTTP Header. It should be very easy to
# add a custom handler!
# The key in this hash is used in a regular expression - it is matched
# to the start of the RequestURI - eg
# http://vermont.mvc.mcc.ac.uk/WSRF/foobar  -> WSRF
# (/WSRF/foobar is the RequestURI)
%WSRF::Container::HandlerMap = (
						'WSRF'         => \&WSRF::Container::WSRFHandler,
						'Session'      => \&WSRF::Container::SessionHandler,
						'MultiSession' => \&WSRF::Container::MultiSessionHandler
);

# All messages should pass through this handle function - $r is a
# HTTP::Request Object
sub handle {
	my ( $r, $socket ) = @_;

	#need to record if this process has an open socket with the world
	#- if we fork we might need to close it
	$WSRF::Constants::ExternSocket = WSRF::SocketHolder->instance($socket);

	if ( !$r ) {
		print STDERR "$$ WSRF::Container HTTP::Request not defined!";
		return;
	}

	my $Path = $r->uri->path;
	if ( $Path =~ m/\.{2,}/og ) {
		print STDERR
		  "$$ WSRF::Container Path $Path contains unacceptable charactors.\n";
		my $fail = new HTTP::Response(RC_NOT_FOUND);
		$fail->header( 'Content-Type' => 'text/xml' );
		$fail->content("Path $Path contains unacceptable charactors.\n");
		return $fail;
	}

	my ($response);

	#walk through the hash until we find a handler for this function - we put
	#the key between / and / and do a reg expression match
	my $found = undef;
  LINE: foreach my $key ( keys %WSRF::Container::HandlerMap ) {
		if ( $Path =~ m/^\/$key\// ) {
			$found = "TRUE";
			print STDERR "$$ WSRF::Container Using $key Handler\n";
			$response = $WSRF::Container::HandlerMap{$key}->($r);
			last LINE;
		}
	}

	#no handler found - return a 404 HTTP error message
	if ( !$found ) {
		$response = HTTP::Response->new(404);
	}

	return $response;
}

# handles messages with URI http://blah.com/WSRF/
# this maps to WS-Resources that use a process to manage the
# state of a WS-Resource, one process per WS-Resource. This
# functions sends the message down a UNIX socket to the process
sub WSRFHandler {
	my $request = shift @_;

	#Only Handle GET and POST
	return HTTP::Response->new(RC_FORBIDDEN)
	  if (    $request->method ne 'POST'
		   && $request->method ne 'GET'
		   && $request->method ne 'DELETE'
		   && $request->method ne 'PUT' );

	print STDERR "$$ WSRFHandler called\n";
	my $Path = $request->uri->path;

	#strip extra '/' at start of URL
	$Path =~ s/^\/+//o;

	#remeber the Path - we will put this in our responses so clients
	#will know who sent them the message - part of WS-Addressing
	$ENV{FROM} = $ENV{URL} . $Path;

	#split up Path part of URL - we multiplex on the first part (the base)
	#the module name is the last part
	my @PathArray  = split( /\//, $Path );
	my $ID         = pop @PathArray;
	my $base       = $PathArray[0];
	my $ModuleName = pop @PathArray;
	print "$$ ModuleName= $ModuleName\n";
	my $Directory = join '/', @PathArray;

	#this is the absolute path now
	$Directory = $ENV{WSRF_MODULES} . "/" . $Directory;
	print STDERR "Directory= $Directory\n";

	$Path = $ENV{WSRF_MODULES} . "/" . $Path;

	#check the ID is safe - we do not accept dots,
	#all paths will be relative to $ENV{WRF_MODULES}
	#only allow alphanumeric, underscore and hyphen
	if ( $ID !~ m/^([-\w]+)$/ && $ID !~ m/^$ModuleName\.(xsl|js|css|svg)$/ ) {
		print STDERR "$$ Bad ID $ID\n";
		my $fail = new HTTP::Response(RC_BAD_REQUEST);
		$fail->header( 'Content-Type' => 'text/xml' );
		$fail->content(
						SOAP::Serializer->fault(
								'Bad WS-Resource Identifier',
								"WS-Resource identifier contains bad charactors"
						)
		);

		return $fail;
	}

	my ($PUT);
	if ( $request->method eq 'PUT' ) {
		$PUT = 1;

		my $To = $ENV{URL};
		chop $To;
		$To .= $request->uri;
		my $header =
		  SOAP::Header->value( "<wsa:To>" . $To . "</wsa:To>" )->type('xml');
		my $xml = $request->content;

		print STDERR "$$ Attempt to PUT\n";

		$xml =~ s/^<\?xml[\s\w\.\-].*\?>\n?//o;
		print STDERR "$$ >>>xml>>>\n$xml\n<<<xml<<<\n";

		my $data =
		  SOAP::Data->name('PutResourcePropertyDocument')->prefix('wsrp')
		  ->attr( { 'xmlns:wsrp' => $WSRF::Constants::WSRP } )
		  ->value( \SOAP::Data->value($xml)->type('xml') );

		my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
		print "$$ >>>envelope>>>\n$envelope\n<<<envelope<<<\n";
		$request = HTTP::Request->new();
		$request->method('POST');
		$request->header( "SOAPAction" =>
						 "$WSRF::Constants::WSRP/PutResourcePropertyDocument" );
		$request->header( "Content-Length" => length $envelope );
		$request->content($envelope);
	}

	print "$$ ID= $ID\n";
	my ($GET);
	if ( $request->method eq 'GET' ) {

		#does the client just want the WSDL/XSL/CSS for service
		if ( $request->uri->query eq 'WSDL' ) {
			my $resp = GetWSDL($request);
			return $resp;
		} elsif ( $ID =~ m/^$ModuleName\.(xsl|css|js|svg)$/ )

		  #looking for xsl or css or js
		{
			print "$$ Getting $ID file\n";
			my $resp = HTTP::Response->new();
			my $file = $Directory . "/" . $ID;
			print "$$ File to open is $file\n";
			if ( !( -f $file ) || !( -r $file ) ) {
				$resp->code(404);
				return $resp;
			}
			open FILE, "< $file" or die "$$ Could not open $file";
			my $xsl = join "", <FILE>;
			close FILE or die "Could not close $file file";
			$resp->header( 'Content-Type' => 'text/xml' )
			  if ( $ID =~ m/\.xsl$/ );
			$resp->header( 'Content-Type' => 'text/css' )
			  if ( $ID =~ m/\.css$/ );
			$resp->header( 'Content-Type' => 'text/javascript' )
			  if ( $ID =~ m/\.js$/ );
			$resp->header( 'Content-Type' => 'text/xml' )
			  if ( $ID =~ m/\.svg$/ );

			$resp->content($xsl);
			return $resp;
		}

		#wants ResourceProperties
		$GET = 1;
		my $data =
		  SOAP::Data->name('GetResourcePropertyDocument')->prefix('wsrp')
		  ->attr( { 'xmlns:wsrp' => $WSRF::Constants::WSRP } );
		my $To = $ENV{URL};
		chop $To;
		$To .= $request->uri;
		my $header =
		  SOAP::Header->value( "<wsa:To>" . $To . "</wsa:To>" )->type('xml');
		my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
		$request = HTTP::Request->new();
		$request->method('POST');
		$request->header( "SOAPAction" =>
						 "$WSRF::Constants::WSRP/GetResourcePropertyDocument" );
		$request->header( "Content-Length" => length $envelope );
		$request->content($envelope);
	}

	if ( $request->method eq 'DELETE' ) {
		my $data =
		  SOAP::Data->name('Destroy')->prefix('wsrl')
		  ->attr( { 'xmlns:wsrl' => $WSRF::Constants::WSRL } );
		my $To = $ENV{URL};
		chop $To;
		$To .= $request->uri;
		my $header =
		  SOAP::Header->value( "<wsa:To>" . $To . "</wsa:To>" )->type('xml');
		my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
		$request = HTTP::Request->new();
		$request->method('POST');
		$request->header( "SOAPAction" => "$WSRF::Constants::WSRL/Destroy" );
		$request->header( "Content-Length" => length $envelope );
		$request->content($envelope);
	}

	my $rend = $WSRF::Constants::SOCKETS_DIRECTORY . "/" . $ID;

	#check that the Socket exists for the requested Grid Service
	if ( !-S $rend ) {
		print STDERR "$$ UNIX Socket $rend does not exist\n";
		my $fail = new HTTP::Response(RC_NOT_FOUND);
		$fail->header( 'Content-Type' => 'text/xml' );
		$fail->content(
						SOAP::Serializer->fault(
												 'No such WS-Resource type',
												 "Check Endpoint of service"
						)
		);

		return $fail;
	}

	print STDERR "$$ $Path Child $$ Starting Processing\n";
	print STDERR "$$ Client Rendezvous $rend\n";

	#open a socket to the GS
	my $MyFH = IO::Socket::UNIX->new(
									  Peer    => "$rend",
									  Type    => SOCK_STREAM,
									  Timeout => 10
	  )
	  or die SOAP::Fault->faultcode("Container Fault")
	  ->faultstring("Container Failure - Socket problem $!");
	print STDERR "$$ Client Socket $MyFH\n";

	#if using SSL add the extra information to the HTTP request
	# we stick it into the HTTP Header
	if ( defined( $ENV{SSL_CLIENT_DN} ) ) {
		$request->header( 'Client-SSL-Cert-Subject' => "$ENV{SSL_CLIENT_DN}" );
		$request->header(
						'Client-SSL-Cert-Issuer' => "$ENV{SSL_CLIENT_ISSUER}" );
	}

	#send down socket and wait for response
	my $out = print $MyFH ( $request->as_string() );

	if ( !defined($out) ) { print STDERR "$$ Could not write to $MyFH\n" }

	#read the response from the Socket and turn it into a
	#HTTP::Response
	my $resp = WSRF::Daemon::ResponseHandler($MyFH);
	$MyFH->close;
	print STDERR "$$ $Path Processing Finished\n";

	#   print STDERR "$$ >>>out>>>\n".$resp->content."\n<<<out<<<\n";

	if ( $GET || $PUT )    #Original Request was a GET
	{
		$resp =
		  WSRF::Container::getProperties( $resp, $Directory, $ModuleName );
		$resp->header( "Pragma" => "no-cache" );
		$resp->header(
					"Cache-Control" => "no-cache, max-age=1, must-revalidate" );
	}
	return $resp;
}

# This function handles messages that have a URI like
# http://blah.com/Session/stuff
# Session WS-Resources store their state in a DB/filesystem etc...
# this function loads the function that loads the code to access
# the state and process the message
sub SessionHandler {
	my $request = shift @_;
	print STDERR "$$ SessionHandler called\n";

	#Only Handle GET and POST
	return HTTP::Response->new(RC_FORBIDDEN)
	  if (    $request->method ne 'POST'
		   && $request->method ne 'GET'
		   && $request->method ne 'DELETE'
		   && $request->method ne 'PUT' );

	my $Path = $request->uri->path;

	#strip extra '/' at start of URL
	$Path =~ s/^\/+//o;

	#remeber the Path - we will put this in our responses so clients
	#will know who sent them the message - part of WS-Addressing
	$ENV{FROM} = $ENV{URL} . $Path;

	#split up Path part of URL - we multiplex on the first part (the base)
	#the module name is the last part
	my @PathArray = split( /\//, $Path );
	my $ID = pop @PathArray;
	my ($module);
	if (    $ID =~ /\d+-?d*/o
		 || $ID =~ /^\w+\.(js|xsl|css|svg)$/ )    #a resource identifier
	{
		$module = pop @PathArray;
	} else {
		$module = $ID;
	}
	$ENV{ID} = $ID;

	my $base              = $PathArray[0];
	my $RelativeDirectory = join '/', @PathArray;

	#this is the absolute path now

	my $Directory = $ENV{WSRF_MODULES} . "/" . $RelativeDirectory;
	print STDERR "$$ Directory to modules $Directory\n";

	my $tmpPath = $Directory . '/' . $module . ".pm";
	print STDERR "$$ Path to module $tmpPath\n";
	if ( !-f $tmpPath ) {
		print STDERR "$$ ERROR $tmpPath no such file\n";
		my $fail = new HTTP::Response(RC_OK);
		$fail->header( 'Content-Type' => 'text/xml' );

		#$fail->content("GS::$Path No Such service\n");
		$fail->content(
						SOAP::Serializer->fault(
									   'No Service', "Check Endpoint of Service"
						)
		);
		return $fail;
	}

	my ($PUT);
	if ( $request->method eq 'PUT' ) {
		$PUT = 1;

		my $To = $ENV{URL};
		chop $To;
		$To .= $request->uri;
		my $header =
		  SOAP::Header->value( "<wsa:To>" . $To . "</wsa:To>" )->type('xml');
		my $xml = $request->content;

		print STDERR "$$ Attempt to PUT\n";

		$xml =~ s/^<\?xml[\s\w\.\-].*\?>\n?//o;
		print STDERR "$$ >>>xml>>>\n$xml\n<<<xml<<<\n";

		my $data =
		  SOAP::Data->name('PutResourcePropertyDocument')->prefix('wsrp')
		  ->attr( { 'xmlns:wsrp' => $WSRF::Constants::WSRP } )
		  ->value( \SOAP::Data->value($xml)->type('xml') );

		my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
		print "$$ >>>envelope>>>\n$envelope\n<<<envelope<<<\n";
		$request = HTTP::Request->new();
		$request->method('POST');
		$request->header( "SOAPAction" =>
						 "$WSRF::Constants::WSRP/PutResourcePropertyDocument" );
		$request->header( "Content-Length" => length $envelope );
		$request->content($envelope);
	}

	my ($GET);
	if ( $request->method eq 'GET' ) {

		#does the client just want the WSDL for service
		if ( $request->uri->query eq 'WSDL' ) {
			my $resp = GetWSDL($request);
			return $resp;
		} elsif ( $ID =~ m/^$module\.(xsl|css|js|svg)$/ )

		  #looking for xsl or css or js
		{
			print "$$ Getting $ID file\n";
			my $resp = HTTP::Response->new();
			my $file = $Directory . "/" . $ID;
			print "$$ File to open is $file\n";
			if ( !( -f $file ) || !( -r $file ) ) {
				$resp->code(404);
				return $resp;
			}
			print "$$ File to open is $file\n";
			open FILE, "< $file" or die "$$ Could not open $file";
			my $xsl = join "", <FILE>;
			close FILE or die "Could not close WSDL file";
			$resp->header( 'Content-Type' => 'text/xml' )
			  if ( $ID =~ m/\.xsl$/ );
			$resp->header( 'Content-Type' => 'text/css' )
			  if ( $ID =~ m/\.css$/ );
			$resp->header( 'Content-Type' => 'text/javascript' )
			  if ( $ID =~ m/\.js$/ );
			$resp->header( 'Content-Type' => 'text/xml' )
			  if ( $ID =~ m/\.svg$/ );

			$resp->content($xsl);
			return $resp;
		}

		$GET = 1;
		my $data =
		  SOAP::Data->name('GetResourcePropertyDocument')->prefix('wsrp')
		  ->attr( { 'xmlns:wsrp' => $WSRF::Constants::WSRP } );
		my $To = $ENV{URL};
		chop $To;
		$To .= $request->uri;
		my $header =
		  SOAP::Header->value( "<wsa:To>" . $To . "</wsa:To>" )->type('xml');
		my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
		$request = HTTP::Request->new();
		$request->method('POST');
		$request->header( "SOAPAction" =>
						 "$WSRF::Constants::WSRP/GetResourcePropertyDocument" );
		$request->header( "Content-Length" => length $envelope );
		$request->content($envelope);
	}

	if ( $request->method eq 'DELETE' ) {
		my $data =
		  SOAP::Data->name('Destroy')->prefix('wsrl')
		  ->attr( { 'xmlns:wsrl' => $WSRF::Constants::WSRL } );
		my $To = $ENV{URL};
		chop $To;
		$To .= $request->uri;
		my $header =
		  SOAP::Header->value( "<wsa:To>" . $To . "</wsa:To>" )->type('xml');
		my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
		$request = HTTP::Request->new();
		$request->method('POST');
		$request->header( "SOAPAction" => "$WSRF::Constants::WSRL/Destroy" );
		$request->header( "Content-Length" => length $envelope );
		$request->content($envelope);
	}

	print STDERR "$$ Dispatch path $Directory\n";
	my %namespacemap = (
						 $WSRF::Constants::WSRL => "$module",
						 $WSRF::Constants::WSRP => "$module",
						 $WSRF::Constants::WSSG => "$module"
	);
	%namespacemap = ( %namespacemap, %WSRF::Constants::ModuleNamespaceMap );

	#this loads the module to handle this function, the module
	#will retrieve the state for the WS-Resource from a DB or
	#some other stable storage, process the message and return the
	#state to the stable storage
	my $resp =
	  WSRF::Session->dispatch_to($Directory)->dispatch_with( \%namespacemap )
	  ->serializer( WSRF::WSRFSerializer->new )
	  ->deserializer( WSRF::Deserializer->new )->handle($request);

	print STDERR "$$ >>>out>>>\n" . $resp->content . "\n<<<out<<<\n";
	if ( $GET || $PUT )    #Original Request was a GET
	{
		$resp = WSRF::Container::getProperties( $resp, $Directory, $module );
	}

	return $resp;
}

sub getProperties {
	my $resp   = shift @_;
	my $Dir    = shift @_;
	my $Module = shift @_;
	my $xml    = $resp->content;
	eval { require XML::LibXML };
	if ( !$@ )    #we have XML::LibXML, so we can strip the SOAP stuff
	{
		#my $xpath = '<XPath xmlns:wsrp="'
		# . $WSRF::Constants::WSRP
		# . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsrp:ResourceProperties]</XPath>';
		my $xpath = '(//. | //@* | //namespace::*)[ancestor-or-self::wsrp:ResourceProperties]';
		 
		my $canon = '<?xml version="1.0" encoding="ISO-8859-1"?>' . "\n";
		$canon = $canon
		  . '<?xml-stylesheet type="text/xsl" href="'
		  . $Module
		  . '.xsl"?>' . "\n"
		  if ( -f $Dir . "/$Module.xsl" && -r $Dir . "/$Module.xsl" );
		my $parser = XML::LibXML->new();
		my $doc    = $parser->parse_string($xml);
		$canon .= $doc->toStringEC14N( 0, $xpath, [''] );
		$resp->header( "Content-Length" => length $canon );
		$resp->content($canon);
	}
	return $resp;
}

# This fuction handles message with URIs like
# http://blah.com/MultiSession/foe
# WS-Resources for this use a single process to store the state of multiple
# WS-Resources. The function passes the message onto the process that handles
# messages for all the WS-Resources of a particular type - if the process
# has not been created ie if this is the first call to this type of
# WS-Resource then this function will create the process
sub MultiSessionHandler {
	my $request = shift @_;
	print STDERR "$$ MultiSessionHandler called\n";

	#Only Handle GET and POST
	return HTTP::Response->new(RC_FORBIDDEN)
	  if (    $request->method ne 'POST'
		   && $request->method ne 'GET'
		   && $request->method ne 'DELETE'
		   && $request->method ne 'PUT' );

	my $Path = $request->uri->path;

	#strip extra '/' at start of URL
	$Path =~ s/^\/+//o;

	#remeber the Path - we will put this in our responses so clients
	#will know who sent them the message - part of WS-Addressing
	$ENV{FROM} = $ENV{URL} . $Path;

	#split up Path part of URL - we multiplex on the first part (the base)
	#the module name is the last part
	my @PathArray = split( /\//, $Path );
	my $ID = pop @PathArray;
	my ($module);

	if (    $ID =~ /\d+-?d*/o
		 || $ID =~ /^\w+\.(xsl|js|css|svg)$/o )    #a resource identifier
	{
		$module = pop @PathArray;
	} else {
		$module = $ID;
	}
	$ENV{ID} = $ID;
	my $base              = $PathArray[0];
	my $RelativeDirectory = join '/', @PathArray;

	#this is the absolute path now
	my $Directory = $ENV{WSRF_MODULES} . "/" . $RelativeDirectory;

	#check the message actually maps to a module
	my $tmpPath = $Directory . '/' . $module . ".pm";
	print STDERR "$$ Path to module $tmpPath\n";
	if ( !-f $tmpPath ) {
		print STDERR "$$ ERROR:: $tmpPath No Such File\n";
		my $fail = new HTTP::Response(RC_OK);
		$fail->header( 'Content-Type' => 'text/xml' );
		$fail->content(
						SOAP::Serializer->fault(
									   'No Service', "Check Endpoint of Service"
						)
		);
		return $fail;
	}

	my ($PUT);
	if ( $request->method eq 'PUT' ) {
		$PUT = 1;

		my $To = $ENV{URL};
		chop $To;
		$To .= $request->uri;
		my $header =
		  SOAP::Header->value( "<wsa:To>" . $To . "</wsa:To>" )->type('xml');
		my $xml = $request->content;

		print STDERR "$$ Attempt to PUT\n";

		$xml =~ s/^<\?xml[\s\w\.\-].*\?>\n?//o;
		print STDERR "$$ >>>xml>>>\n$xml\n<<<xml<<<\n";

		my $data =
		  SOAP::Data->name('PutResourcePropertyDocument')->prefix('wsrp')
		  ->attr( { 'xmlns:wsrp' => $WSRF::Constants::WSRP } )
		  ->value( \SOAP::Data->value($xml)->type('xml') );

		my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
		print "$$ >>>envelope>>>\n$envelope\n<<<envelope<<<\n";
		$request = HTTP::Request->new();
		$request->method('POST');
		$request->header( "SOAPAction" =>
						 "$WSRF::Constants::WSRP/PutResourcePropertyDocument" );
		$request->header( "Content-Length" => length $envelope );
		$request->content($envelope);
	}

	my ($GET);
	if ( $request->method eq 'GET' ) {

		#does the client just want the WSDL for service
		if ( $request->uri->query eq 'WSDL' ) {
			my $resp = GetWSDL($request);
			return $resp;
		} elsif ( $ID =~ m/^$module\.(xsl|css|js|svg)$/ )

		  #looking for xsl or css or js
		{
			print "$$ Getting $ID file\n";
			my $resp = HTTP::Response->new();
			my $file = $Directory . "/" . $ID;
			print "$$ File to open is $file\n";
			if ( !( -f $file ) || !( -r $file ) ) {
				$resp->code(404);
				return $resp;
			}
			open FILE, "< $file" or die "$$ Could not open $file";
			my $xsl = join "", <FILE>;
			close FILE or die "Could not close $file file";
			$resp->header( 'Content-Type' => 'text/xml' )
			  if ( $ID =~ m/\.xsl$/ );
			$resp->header( 'Content-Type' => 'text/css' )
			  if ( $ID =~ m/\.css$/ );
			$resp->header( 'Content-Type' => 'text/javascript' )
			  if ( $ID =~ m/\.js$/ );
			$resp->header( 'Content-Type' => 'text/xml' )
			  if ( $ID =~ m/\.svg$/ );

			$resp->content($xsl);
			return $resp;
		}

		$GET = 1;
		my $data =
		  SOAP::Data->name('GetResourcePropertyDocument')->prefix('wsrp')
		  ->attr( { 'xmlns:wsrp' => $WSRF::Constants::WSRP } );
		my $To = $ENV{URL};
		chop $To;
		$To .= $request->uri;
		my $header =
		  SOAP::Header->value( "<wsa:To>" . $To . "</wsa:To>" )->type('xml');
		my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
		$request = HTTP::Request->new();
		$request->method('POST');
		$request->header( "SOAPAction" =>
						 "$WSRF::Constants::WSRP/GetResourcePropertyDocument" );
		$request->header( "Content-Length" => length $envelope );
		$request->content($envelope);
	}

	if ( $request->method eq 'DELETE' ) {
		my $data =
		  SOAP::Data->name('Destroy')->prefix('wsrl')
		  ->attr( { 'xmlns:wsrl' => $WSRF::Constants::WSRL } );
		my $To = $ENV{URL};
		chop $To;
		$To .= $request->uri;
		my $header =
		  SOAP::Header->value( "<wsa:To>" . $To . "</wsa:To>" )->type('xml');
		my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
		$request = HTTP::Request->new();
		$request->method('POST');
		$request->header( "SOAPAction" => "$WSRF::Constants::WSRL/Destroy" );
		$request->header( "Content-Length" => length $envelope );
		$request->content($envelope);
	}

	#check if a process to handle this message has been created
	my $SockPath = $WSRF::Constants::SOCKETS_DIRECTORY . '/' . $module;
	my ($resp);
	if ( !-S $SockPath ) {

		#create the file and fork the process
		print STDERR "$$ Creating a new Service $module\n";
		my $service = WSRF::Resource->new(
										   module => $module,
										   path   => $RelativeDirectory,
										   ID     => $module
		);
		print STDERR "$$ Calling handle() on service\n";
		$service->handle("");
		print STDERR "$$ Connecting to Socket $SockPath\n";
		my $MyFH = IO::Socket::UNIX->new(
										  Peer    => $SockPath,
										  Type    => SOCK_STREAM,
										  Timeout => 10
		  )
		  or die SOAP::Fault->faultcode("Container Fault")
		  ->faultstring("Container Failure - Socket problem $!");

		#if using SSL add the extra information to the HTTP request
		if ( defined( $ENV{SSL_CLIENT_DN} ) ) {
			$request->header(
						   'Client-SSL-Cert-Subject' => "$ENV{SSL_CLIENT_DN}" );
			$request->header(
						'Client-SSL-Cert-Issuer' => "$ENV{SSL_CLIENT_ISSUER}" );
		}

		#print "Ingoing HTTP>>>\n".$r->as_string()."\n<<<HTTP\n";
		my $out = print $MyFH ( $request->as_string() );
		if ( !defined($out) ) {
			print STDERR "$$ ERROR could not write to $MyFH\n";
		}

		#read the response from the Socket and turn it into a
		#HTTP::Response
		$resp = WSRF::Daemon::ResponseHandler($MyFH);
		$MyFH->close;
		print STDERR "$$ $Path Processing Finished\n";
	} else    #no process to handle this message - we need to create one
	{

		#check the socket is up - send SOAP to socket
		my $MyFH = IO::Socket::UNIX->new(
										  Peer    => $SockPath,
										  Type    => SOCK_STREAM,
										  Timeout => 10
		);
		if ( !$MyFH ) {

			#create the file and fork the process
			my $service = WSRF::Resource->new(
											   module => $module,
											   path   => $RelativeDirectory,
											   ID     => $module
			);
			$service->handle();

			$MyFH = IO::Socket::UNIX->new(
										   Peer    => $SockPath,
										   Type    => SOCK_STREAM,
										   Timeout => 10
			  )
			  or die SOAP::Fault->faultcode("Container Fault")
			  ->faultstring("Container Failure - Socket problem $!");
		}

		#if using SSL add the extra information to the HTTP request
		if ( defined( $ENV{SSL_CLIENT_DN} ) ) {
			$request->header(
						   'Client-SSL-Cert-Subject' => "$ENV{SSL_CLIENT_DN}" );
			$request->header(
						'Client-SSL-Cert-Issuer' => "$ENV{SSL_CLIENT_ISSUER}" );
		}

		my $out = print $MyFH ( $request->as_string() );
		if ( !defined($out) ) { print STDERR "ERROR\n" }

		#read the response from the Socket and turn it into a
		#HTTP::Response
		$resp = WSRF::Daemon::ResponseHandler($MyFH);
		$MyFH->close;
		print STDERR "$$ $Path Processing Finished\n";
	}

	#   print STDERR "$$ >>>out>>>\n".$resp->content."\n<<<out<<<\n";
	if ( $GET || $PUT )    #Original Request was a GET
	{
		$resp = WSRF::Container::getProperties( $resp, $Directory, $module );
	}

	return $resp;
}

sub GetWSDL {
	my ($request) = @_;

	#get the path from the HTTP::Request
	my $uri  = $request->uri;
	my $path = $request->uri->path;
	$path =~ s/^\/+//o;
	my $endpoint = $ENV{URL} . $path;

	#strip extra '/' at start of URL
	#$path =~ s/^\/+//o;

	#we only allow certain types of Path
	#alphanumeric, hypen, and forward-slash
	#BUG - this pattern is too restrictive
	if ( $path =~ /^([-\/\w]+)$/ ) {
		$path = $1;
	} else {    #Bad Path
		return HTTP::Response->new(RC_FORBIDDEN);
	}

	my $LongPATH = $ENV{WSRF_MODULES} . "/" . $path . ".WSDL";

	#  print STDERR "WSRF::Container::GetWSDL LongPATH=\"$LongPATH\"\n";

	#BUG - this could be done with reg-ex
	#split up path
	my @patharray = split( /\//, $path );

	#sometimes the path will have an ID at the end - pop it of
	pop @patharray;

	#rebuild path
	$path = join '/', @patharray;
	my $ShortPATH = $ENV{WSRF_MODULES} . "/" . $path . ".WSDL";

	#  print STDERR "WSRF::Container::GetWSDL ShortPATH=\"$ShortPATH\"\n";

	# resp will be a HTTP::Response object
	# ReturnWSDL can throw exceptions, so we catch them
	my ($resp);

	#check if I can read the file
	if ( -r $LongPATH ) {
		eval { $resp = WSRF::WSDL::ReturnWSDL( $LongPATH, $endpoint ); };
		if ($@) {
			print STDERR
"$$ WSRF::Container::GetWSDL could not retrieve WSDL from $LongPATH";
			$resp = HTTP::Response->new(RC_INTERNAL_SERVER_ERROR);
		}
	} elsif ( -r $ShortPATH ) {
		eval { $resp = WSRF::WSDL::ReturnWSDL( $ShortPATH, $endpoint ); };
		if ($@) {
			print STDERR
"$$ WSRF::Container::GetWSDL could not retrieve WSDL from $ShortPATH";
			$resp = HTTP::Response->new(RC_INTERNAL_SERVER_ERROR);
		}
	} else {
		$resp = HTTP::Response->new(RC_NOT_FOUND);
	}

	return $resp;
}

#===============================================================================
# WS_Address
#
#  A class for holding and handling WS-Addressing EPRs
#
package WSRF::WS_Address;

sub new {
	my ( $self, $stuff ) = @_;

	my ( $address, $ref_params, $meta_data, $XML );
	if ( defined($stuff) ) {

		# we accept either a SOM or XML
		my $som =
		  UNIVERSAL::isa( $stuff => 'SOAP::SOM' )
		  ? $stuff
		  : SOAP::Deserializer->new->deserialize($stuff);

#    $XML =  WSRF::SimpleSerializer->new->serialize( $som->dataof("//{$WSRF::Constants::WSA}EndpointReference"));

		$address = $som->valueof("//{$WSRF::Constants::WSA}Address");

		#print STDERR "address= $address\n";

		if ( $som->match("//{$WSRF::Constants::WSA}ReferenceParameters") ) {
			my $i = 1;
			while (
					$som->match(
							"//{$WSRF::Constants::WSA}ReferenceParameters/[$i]")
			  )
			{
				$ref_params .= WSRF::SimpleSerializer->new->serialize(
						$som->dataof(
							"//{$WSRF::Constants::WSA}ReferenceParameters/[$i]")
				);
				$i++;
			}
		}

		if ( $som->match("//{$WSRF::Constants::WSA}Metadata") ) {
			my $i = 1;
			while ( $som->match("//{$WSRF::Constants::WSA}Metadata/[$i]") ) {
				$meta_data .=
				  WSRF::SimpleSerializer->new->serialize(
					   $som->dataof("//{$WSRF::Constants::WSA}Metadata/[$i]") );
				$i++;
			}
		}

	}

	bless {
			_Address             => $address,
			_ReferenceParameters => $ref_params,
			_Metadata            => $meta_data,
			_XML                 => $XML
	}, $self;

}

sub from_envelope {
	my ( $self, $stuff ) = @_;

	return $self unless defined $stuff;

	my ( $address, $ref_params, $meta_data, $XML );
	my $som =
	  UNIVERSAL::isa( $stuff => 'SOAP::SOM' )
	  ? $stuff
	  : SOAP::Deserializer->new->deserialize($stuff);

	$address =
	  $som->match("//Body//EndpointReference/{$WSRF::Constants::WSA}Address")
	  ? $som->valueof(
					 "//Body//EndpointReference/{$WSRF::Constants::WSA}Address")
	  : die
	  "WS_Address::from_envlope No wsa:EndpointReference in Envelope Body\n";

	#  print STDERR "address= $address\n";

	if (
		$som->match(
"//Body//EndpointReference/{$WSRF::Constants::WSA}ReferenceParameters" )
	  )
	{
		my $i = 1;
		while (
			$som->match( "//Body//EndpointReference/{$WSRF::Constants::WSA}ReferenceParameters/[$i]"
			)
		  )
		{
			$ref_params .= WSRF::SimpleSerializer->new->serialize(
				$som->dataof(
"//Body//EndpointReference/{$WSRF::Constants::WSA}ReferenceParameters/[$i]"
				)
			);
			$i++;
		}
	}

	if (
		 $som->match(
					"//Body//EndpointReference/{$WSRF::Constants::WSA}Metadata")
	  )
	{
		my $i = 1;
		while (
			$som->match(
				"//Body//EndpointReference{$WSRF::Constants::WSA}Metadata/[$i]")
		  )
		{
			$meta_data .= WSRF::SimpleSerializer->new->serialize(
				$som->dataof(
"//Body//EndpointRefernce/{$WSRF::Constants::WSA}Metadata/[$i]"
				)
			);
			$i++;
		}
	}

	bless {
			_Address             => $address,
			_ReferenceParameters => $ref_params,
			_Metadata            => $meta_data,
			_XML                 => $XML
	}, $self;
}

sub BEGIN {
	no strict 'refs';

	for my $method (qw(Address ReferenceParameters Metadata )) {
		my $field = '_' . $method;
		*$method = sub {
			my $self = shift;
			@_
			  ? ( $self->{$field} = shift, return $self )
			  : return $self->{$field};
		  }
	}
}

sub MessageID {
	return join '', 'urn:www.sve.man.ac.uk-', int( rand 100000000000 ) + 1,
	  gmtime;
}

sub XML {
	my $self = shift;

	if ( !defined $self->{_XML} ) {
		my $XML = '<?xml version="1.0" encoding="UTF-8"?>';
		$XML .= " <wsa:EndpointReference xmlns:wsa=\"$WSRF::Constants::WSA\">";
		$XML .= '<wsa:Address>' . $self->{_Address} . '</wsa:Address>';
		$XML .=
		  $self->{_ReferenceParameters} ? $self->{_ReferenceParameters} : '';
		$XML .= $self->{_Metadata} ? $self->{_Metadata} : '';
		$XML .= '</wsa:EndpointReference>';
		$self->{_XML} = $XML;
	}

	return $self->{_XML};
}

sub serializeReferenceParameters {
	my $self = shift;

	if ( !defined( $self->{_ReferenceParameters} ) ) {
		return undef;
	}

	#need to wrap the ReferenceParameters to parse
	my $som =
	  SOAP::Deserializer->new->deserialize(
						 '<_foo>' . $self->{_ReferenceParameters} . '</_foo>' );

	my $ans = "";
	my $i   = 1;
	while ( $som->match("/[1]/[$i]") ) {
		my $data = $som->dataof("/[1]/[$i]");
		my %attr = %{ $data->attr };
		$attr{'wsa:isReferenceParameter'} = 'true';
		$data->attr( \%attr );
		$ans .= WSRF::SimpleSerializer->new->serialize($data);
		$i++;
	}

	return $ans;

}

#===============================================================================
# WS-BaseFaults
#
# This function allows you to return a WS-BaseFault.
# Simply call die_with_Fault to case your service to
# through an exception.
#
# The function takes hash with the following:
#   OriginatorReference  (where did the fault originally originate)
#   ErrorCode            (some code number)
#   dialect              (?)
#   Description          (a description of the fault)
#   FaultCause           (?)
# For details check out the BasFault spec.
#
# I am not sure when you should throw a SOAP fault or a BaseFault

package WSRF::BaseFaults;

sub die_with_Fault {
	my %args = @_;

	my $fault = "<wsbf:BaseFault xmlns:wsbf=\"$WSRF::Constants::WSBF\">";
	$fault .=
	    "<wsbf:Timestamp>"
	  . WSRF::Time::ConvertEpochTimeToString(time)
	  . "</wsbf:Timestamp>";

	if ( defined( $args{OriginatorReference} ) ) {
		$fault .=
		    "<wsbf:OriginatorReference>"
		  . $args{OriginatorReference}
		  . "</wsbf:OriginatorReference>";
	}

	#has the client defined an error code & dialect
	if ( defined( $args{ErrorCode} ) ) {
		if ( defined( $args{dialect} ) ) {
			$fault .=
			    "<wsbf:ErrorCode dialect=\""
			  . $args{dialect} . "\">"
			  . $args{ErrorCode}
			  . "</wsbf:ErrorCode>";
		} else {
			$fault .=
			  "<wsbf:ErrorCode>" . $args{ErrorCode} . "</wsbf:ErrorCode>";
		}
	}

	#has the client defined a Description
	if ( defined( $args{Description} ) ) {
		$fault .=
		  "<wsbf:Description>" . $args{Description} . "</wsbf:Description>";
	}

	#has the client defined a BaseCause
	if ( defined( $args{FaultCause} ) ) {
		$fault .=
		  "<wsbf:FaultCause>" . $args{FaultCause} . "</wsbf:FaultCause>";
	}

	$fault .= "</wsbf:BaseFault>";

	die SOAP::Fault->faultdetail($fault);
}

#===============================================================================
# For WSRF services that are Session based - the process that calls
# this function does all the work - it loads the module, does the operation
# and returns the result.
#
package WSRF::Session;

use SOAP::Transport::HTTP;

use vars qw(@ISA);

@ISA = qw(SOAP::Transport::HTTP::Server);

sub DESTROY { SOAP::Trace::objects('()') }

# constructor for the WSRF::Deamon object
sub new {
	my $self = shift;

	unless ( ref $self ) {
		my $class = ref($self) || $self;
		$self = $class->SUPER::new(@_);
		SOAP::Trace::objects('()');
	}
	return $self;
}

sub handle {
	my $self = shift->new;
	$self->request( shift @_ );
	$self->SUPER::handle;
	return $self->response;
}

#===============================================================================
# Similar to the SOAP::Transport::Daemon module except it listens to a UNIX
# Domain Socket rather than an INET port
#
package WSRF::Daemon;

use vars qw(@ISA);

use HTTP::Status;
use SOAP::Transport::HTTP;

@ISA = qw(SOAP::Transport::HTTP::Server);

sub DESTROY { SOAP::Trace::objects('()') }

# constructor for the WSRF::Deamon object
sub new {
	my $self = shift;

	unless ( ref $self ) {
		my $class = ref($self) || $self;
		$self = $class->SUPER::new(@_);
		SOAP::Trace::objects('()');
	}
	return $self;
}

# takes a socket and handles the info coming out of
# it, passes it to the SOAP handler and then returns
# the answer.
sub handle {
	my $self = shift->new;
	my $Hdle = shift;

	while ( my $new_c = $Hdle->accept ) {
		my $req = $self->Requesthandler($new_c);

		#print "CHILD START::\n",$req->as_string, "CHILD END\n";
		$self->request($req);
		$self->SUPER::handle;
		my $resp = $self->response;

		#print "Return>>>\n".$resp->as_string."\n<<<Return\n";
		print $new_c ( $resp->as_string );
	}
	close($Hdle);
}

# A function that takes a HTTP message from a socket $Handle
# and converts it to a HTTP::Request object
# This HTTP handler is not very sophisticated but we know the
# message has already been parsed in the pipeline
sub Requesthandler {
	my ( $self, $Handle ) = @_;
	my $request = HTTP::Request->new();
	chomp( my $method = <$Handle> );
	my ( $Met, $URI, @blah ) = split( / /, $method );
	$request->method($Met);
	$request->uri($URI);
	my $SIZE = 0;
  LINE: while ( my $line = <$Handle> ) {
		last LINE if $line eq "\n";
		my ( $TAG, $VAL ) = split( /: /, $line, 2 );
		if ( $TAG eq "Content-Length" ) {
			$SIZE = $VAL;
		} elsif ( $TAG eq 'Client-SSL-Cert-Subject' ) {
			$ENV{SSL_CLIENT_DN} = $VAL;
		} elsif ( $TAG eq 'Client-SSL-Cert-Issuer' ) {
			$ENV{SSL_CLIENT_ISSUER} = $VAL;
		}
		$request->header( $TAG, $VAL );
	}
	$request->remove_header( 'TE', 'Connection', 'SOAPAction' );
	my $content = "";

	if ( $SIZE != 0 ) {

	  FULL: while ( my $line = <$Handle> ) {
			$content .= $line;
			last FULL if length($content) >= $SIZE;
		}
		$request->content($content);
	}

	return $request;
}

#parses a HTTP message that comes from a Socket called $Handler
#and returns a HTTP::Response object.
#not much error checking but we know the response should be
#good since we created it.
sub ResponseHandler {
	my ($Handler) = @_;
	my $SIZE      = 0;
	my $resp      = HTTP::Response->new(RC_OK);
	chomp( my $result = <$Handler> );

	#    $resp->message($result);
  LINE: while ( my $line = <$Handler> ) {
		last LINE if $line eq "\n";
		my ( $TAG, $VAL ) = split( /:/, $line, 2 );
		my $headers .= $TAG . " " . $VAL;
		if ( $TAG eq "Content-Length" ) {
			$SIZE = $VAL;
		}
		$resp->header( $TAG, $VAL );
	}
	my $content = "";
  FULL: while ( my $line = <$Handler> ) {
		$content .= $line;
		last FULL if length($content) >= $SIZE;
	}
	$resp->content($content);
	return $resp;
}

#===============================================================================
# This class takes a WSDL file and changes the endpoint to match the
# proper endpoint of the service
#
# BUG(FIXED) - "soap:address" is hardcoded, problem with XML::DOM not
#       understanding namespaces - FIXED

package WSRF::WSDL;

use XML::DOM;
use HTTP::Status;

sub ReturnWSDL {
	my ( $FILEPATH, $endpoint ) = @_;

	#  print STDERR "WSDL File Path  = $FILEPATH\n";

	if ( !-r $FILEPATH ) {
		print STDERR "ERROR WSDL file does not exist\n";
		return HTTP::Response->new(RC_NOT_FOUND);
	}

	#open file and read contents
	#print "Creating Response Object\n";
	#if we cannot open file we do NOT throw a SOAP fault
	#because we are not answering a SOAP request but a HTTP
	#GET request for the WSDL. This exception should be caught
	#by however has called this function.
	open FILE, "< $FILEPATH" or die "Could not open WSDL file";

	#read file
	my $wsdl = join "", <FILE>;

	#close file
	close FILE or die "Could not close WSDL file";

	#take a copy of the WSDL
	my $soap = $wsdl;

	#get the prefix for the http://schemas.xmlsoap.org/wsdl/soap/
	#namespace - hacky because XML::DOM does not like namespaces
	$soap =~ s/="http:\/\/schemas\.xmlsoap\.org\/wsdl\/soap\/"(.|\n)*//o;
	$soap =~ s/(.|\n)*xmlns://o;

	#  print STDERR "Soap Namespace= ".$soap."\n";

	my $parser = new XML::DOM::Parser;

	# we used to just parse the file but the above hack screwed that
	# up - we just parse the string.
	# my $doc = $parser->parsefile($FILEPATH);
	my $doc  = $parser->parse($wsdl);
	my $node = $doc->getElementsByTagName( $soap . ":address" );

	if ( !defined $node->item(0) ) {
		print STDERR "$$ ERROR in WSDL file - no " . $soap
		  . ":address element\n";
		return HTTP::Response->new(RC_INTERNAL_SERVER_ERROR);
	}

	#These methods can throw exceptions - please catch them
	$node->item(0)->getAttributeNode("location")->setValue();
	$node->item(0)->getAttributeNode("location")->setValue($endpoint);

	my $ans = $doc->toString;
	$doc->dispose;

	my $resp = HTTP::Response->new(RC_OK);
	$resp->header( 'Content-Type' => 'text/xml' );
	$resp->content($ans);
	return $resp;
}

#===============================================================================
#
# Some helper functions that have been bundled together
#
package WSRF::GSutil;

use IO::Socket;

# function to generate a unique handle for the resource.
# BUG - the name is misleading, GSH is a hangover from OGSI
sub CalGSH_ID {
	my $num = int( rand 100000 ) + 1;
	my $gsh_id = join( '', gmtime ) . $num;
	return $gsh_id;

}

# create a WS-Address
# BUG - we die without throwing proper SOAP faults
# function takes a HASH with the following
#  path    = relative path to module directory (relative to $ENV{WSRF_MODULES})
#  module  = name of module file
#  ID      = the WS-Resource identifier (can be created with CalGSH_ID above)
sub createWSAddress {
	my %args = @_;

	my $URL    = $ENV{'URL'};
	my $path   = $args{path} || die "createWSAddress:: No Module Path\n";
	my $module = $args{module} || die "createWSAddress:: No Module\n";
	my $ID     = $args{ID} || die "createWSAddress:: No ID\n";

	#strip .pm from module name if it is there
	$module =~ s/\.pm$//o;

	#strip leading /
	$path =~ s/^\/+//o;

	#strip trailing /
	$path =~ s/\/+$//o;

	#actual endpoint of service
	my $endpoint = $ENV{'URL'} . $path . '/' . $module . '/' . $ID;

	#here we create the WS-Addressing string
	my $response =
	  "<wsa:EndpointReference xmlns:wsa=\"$WSRF::Constants::WSA\">";
	$response .= "<wsa:Address>" . $endpoint . "</wsa:Address>";
	$response .= "</wsa:EndpointReference>";

	return $response;
}

# send some SOAP down the UNIX socket to the Resource, returns a SOM object
sub SendSOAPToSocket {
	my ( $SocketAddress, $URI, $method, @params ) = @_;

	#print "SendSOAPToSocket: SocketAddress= $SocketAddress\n";
	#print "SendSOAPToSocket: URI= $URI\n";
	#print "SendSOAPToSocket: method= $method\n";
	#foreach my $param ( @params )
	#{
	#  print "SendSOAPToSocket: params= $param\n";
	#}

	#create a SOAP message
	my $my_soap =
	  SOAP::Lite->serializer->uri($URI)->envelope( method => $method, @params );

	#print "SendSOAPToSocket: my_soap= \n".$my_soap."\n";

	#create a HTTP message and put the SOAP into it
	my $request = HTTP::Request->new();
	$request->method('POST');
	$request->uri($URI);
	$request->push_header( 'Content_Length' => length($my_soap) );
	$request->push_header( 'Content-Type'   => 'text/xml; charset=utf-8' );
	$request->content($my_soap);

	#BUG - have we actually checked the socket exists?
	#open the sockect
	my $rendev = $SocketAddress;
	my $MyFH = IO::Socket::UNIX->new(
									  Peer    => "$rendev",
									  Type    => SOCK_STREAM,
									  Timeout => 10
	  )
	  or die SOAP::Fault->faultcode("Container Fault")
	  ->faultstring("Container Failure - Socket problem $!");

   #print "SendSOAPToSocket sending \n".$request->as_string()."\n to $rendev\n";
   #send HTTP request with SOAP messgae down sockect
	my $out = print $MyFH ( $request->as_string() )
	  or die SOAP::Fault->faultcode("Container Fault")
	  ->faultstring("Container Failure - Socket problem $!");

	if ( !defined($out) ) {
		print STDERR
"$$ ERROR - WSRF::GSutil::SendSOAPToSocket did not get response from Socket\n";
		die SOAP::Fault->faultcode("Container Fault")
		  ->faultstring("Container Failure - Socket problem");
	}

	#resp is a HTTP::Response Object
	my $resp = WSRF::Daemon::ResponseHandler($MyFH);

	#$som is a WSRF::SOM object
	my $som = WSRF::Deserializer->deserialize( $resp->content );

	return $som;
}

#===============================================================================
# Some functions to handle time - convert to/from epoch time/W3C time.
# To handle times and compare them we convert all times in W3C format to
# seconds since the epoch (ie. the number of seconds since 1970)
#
# This module provides some helper classes for doing this
#
package WSRF::Time;


use DateTime::Format::W3CDTF;
use DateTime::Format::Epoch;

# THE EXPIRES_IN variable, rather than hard code 60*60 seconds
$WSRF::TIME::EXPIRES_IN = 60;

# convert XML format Time string to time in seconds since epoch
sub ConvertStringToEpochTime {
	my ($StringTime) = @_;

	#print "StringTime = $StringTime\n";
	#$f object used to convert W3CDTF TimeString to DateTime object
	my $f = DateTime::Format::W3CDTF->new;

	#$formatter used to convert DateTime object to seconds from epoch
	#we use the unix epoch here
	my $dt = DateTime->new( year => '1970', month => '1', day => '1' );
	my $formatter = DateTime::Format::Epoch->new( epoch => $dt );

	#convert $StringTime to a DateTime object
	#This will throw an exception if StringTime is not in the correct W3C format
	#BUG(fixed) with DateTime::Format::W3CDTF - does not
	#like subseconds - should patch DateTime::Format::W3CDTF
	#strip of the crap that DateTime::Format::W3CDTF does not understand
	$StringTime =~ s/\.\d+//;

	my $DateTimeObject = $f->parse_datetime($StringTime);

	#calc time in sec from epoch of $DateTimeObject
	my $EpochTime = $formatter->format_datetime($DateTimeObject);

	return $EpochTime;
}

# convert time in secs since Epoch to suitable XML format string
sub ConvertEpochTimeToString {
	my ($EpochTime) = @_;

	#if no input time use now
	if ( !defined($EpochTime) ) {
		$EpochTime = time;
	}

	#use formatter to convert epoch time to W3CDTF TimeString
	my $dt = DateTime->new( year => 1970, month => 1, day => 1 );
	my $formatter = DateTime::Format::Epoch->new( epoch => $dt );

	my $DateTimeObject = $formatter->parse_datetime($EpochTime);

	my $f = DateTime::Format::W3CDTF->new;

	my $TimeString = $f->format_datetime($DateTimeObject);

	return $TimeString;
}

#===============================================================================
# Class that allows us to create a new WSRF reource - uses a process to hold
# the state of the resource. The handle function actually forks the process
# to manage and hold the state of the Resource.
#
package WSRF::Resource;

use IO::Socket;

use vars qw($AUTOLOAD);

# new takes a HASH with
#  module - name of module
#  path   - relative path to module (relative to $ENV{WSRF_MODULES}
#  ID     - idnetifier for resource (if non is provided then it is calc'd
#           for you)
#  namepsace - for your service
sub new {
	my ( $class, %args ) = @_;

	bless {
		_module => $args{module} || die("missing module name\n"),
		_path   => $args{path}   || die("missing module path\n"),
		_ID     => $args{ID}     || WSRF::GSutil::CalGSH_ID(),
		_namespace => $args{namespace}
		  || ""

	}, $class;
}

sub ID {
	my ($self) = @_;
	return $self->{_ID};
}

# function that forks the process that manages the Resource - after
# forking the init function is called on the Service. Allows user to
# put an init funtion into their module which they know will be
# called when the service is first created.
sub handle {
	my ( $self, @Params ) = @_;

	my $ModulePath = $self->{_path};
	my $resourceID = $self->{_ID};
	my $ModuleName = $self->{_module};
	my $Namespace  = $self->{_namespace};

	#strip .pm from end of module if is there
	$ModuleName =~ s/\.pm$//o;

	#print "handle Namespace = $Namespace\n";
	#$SIG{CHLD} = 'IGNORE';

	#my $URL = $ENV{'URL'};
	#chop $URL;
	my $location = $ENV{'URL'} . "$ModulePath";

	#fork the service off here
	if ( my $pid = fork ) {

		#parent process
	} elsif ( defined $pid ) {    #child
		$SIG{ALRM} = sub { die "Alarm went off\n"; };

		#There may be an open connection to the world - need to close it
		if ( defined($WSRF::Constants::ExternSocket) ) {
			$WSRF::Constants::ExternSocket->close;
			undef $WSRF::Constants::ExternSocket;
		}

		#Store the WSA addres in a ENV variable so the
		#service can know its own EPR
		$ENV{WSA} =
		  WSRF::GSutil::createWSAddress(
										 module => $ModuleName,
										 path   => $ModulePath,
										 ID     => $resourceID
		  );

		#the address of the socket were this resource is going to live
		my $rendivous = $WSRF::Constants::SOCKETS_DIRECTORY . "/" . $resourceID;

		#remove any file that is already there...
		if ( -e $rendivous ) {
			unlink "$rendivous"
			  or die SOAP::Fault->faultcode("Container Fault")
			  ->faultstring("Container Failure - Could not remove file");
		}

		print STDERR "$$ Created $resourceID rendezvous:: $rendivous\n";
		my $Handle = IO::Socket::UNIX->new(
											Local  => "$rendivous",
											Type   => SOCK_STREAM,
											Listen => SOMAXCONN
		  )
		  or die SOAP::Fault->faultcode("Container Fault")
		  ->faultstring("Container Failure - Socket problem $!");
		print STDERR "$$ $resourceID Socket: $Handle\n";

		# redirect stderr/stdout to log directory
		open( STDOUT, "> " . $ENV{WSRF_MODULES} . "/logs/$resourceID.log" )
		  or print STDERR "$$ WARNING: Could not open log file "
		  . $ENV{WSRF_MODULES}
		  . "/logs/$resourceID.log in WSRF::Resource::handle\n";
		open( STDERR, ">&STDOUT" );

#my %namespaces = { 'http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceLifetime'
#                    => "$ModuleName",
#                   'http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceProperties'
#                    => "$ModuleName"
#                 };

		#if ($Namespace  ne "" )
		#{
		#   $namespaces{$Namespace} = $ModuleName;
		#}

		#print "handle set $Namespace = ".$namespaces{$Namespace}."\n";

		#create a new service

		# BUG - if Namespace is not set
		# Now start the Resource in the process we have just created.
		%WSRF::WSRP::ResourceProperties   = ();
		%WSRF::WSRP::PropertyNamespaceMap = ();
		%WSRF::WSRP::NotDeletable         = ();
		%WSRF::WSRP::NotModifiable        = ();
		%WSRF::WSRP::NotInsert            = ();
		%WSRF::WSRP::Private              = ();

		my $daemon =
		  WSRF::Daemon->new()->serializer( WSRF::WSRFSerializer->new )
		  ->deserializer( WSRF::Deserializer->new )
		  ->dispatch_to(   "$ENV{WSRF_MODULES}" . "/"
						 . "$ModulePath" )->dispatch_with(
									 {
									   $WSRF::Constants::WSRL => "$ModuleName",
									   $WSRF::Constants::WSRP => "$ModuleName",
									   $WSRF::Constants::WSSG => "$ModuleName",
									   $Namespace             => $ModuleName
									 }
						 );

		#use eval to handle any time out
		eval { $daemon->handle($Handle); };
		print STDERR
"$$ WSRF::Resource::handle caught exception: $@ - if it is \"Alarm went off\" then the WS-Resource's lifetime has expired";
		unlink($rendivous)
		  or print STDERR
		  "$$ WARNING: Could not remove $rendivous in WSRF::Resource::handle\n";
		print STDERR "$$ Resource Shutting Down\n";

		exit;    #should never get here!!
	} else {     #problem forking
		print STDERR
"$$ ERROR: Could perform fork it start Resource in WSRF::Resource::handle\n";
		return "FAILURE";
	}

	#Parent Process Takes Over Here.
	# by default the factory will call init on the service it just
	# created - select is called to allow the child time to set up socket
	my $rend = $WSRF::Constants::SOCKETS_DIRECTORY . "/" . $resourceID;

	#sleep for 0.2 seconds
	select( undef, undef, undef, 0.2 );

	#resp from SendSOAPToSocket is a WSRF::SOM object - here we call init method
	my $resp =
	  WSRF::GSutil::SendSOAPToSocket( $rend, $ModuleName, "init", @Params );

	#Check for a fault from the init method
	if ( $resp->fault ) {
		print STDERR "$$ ERROR: SOAP fault from init: "
		  . $resp->faultstring
		  . "\n in WSRF::Resource::handle\n";
	}

	return ( $resourceID, $resp );
}

# Once a WSRF::Resource is created with new and started using handle
# method we can call operations on the Service using AUTOLOAD
sub AUTOLOAD {
	my ( $self, @params ) = @_;

	#strip class name from method name (Conway p56)
	$AUTOLOAD =~ s/.*:://;

	my $rend = $WSRF::Constants::SOCKETS_DIRECTORY . "/" . $self->ID();

	if ( $AUTOLOAD eq "DESTROY" ) {

		#    print STDERR "Attempt to DESTROY ".$self->ID()."\n";
		return;
	}

	#$resp is WSRF::SOM object
	my $resp =
	  WSRF::GSutil::SendSOAPToSocket( $rend, $self->{_module}, $AUTOLOAD,
									  @params );

	return $resp;
}

#===============================================================================
# This is the module that provides file locking for us - when an object of this
# class is created a lock file is created. The lock file is automatically
# removed when the object is destroyed. We could use  fcntl to do this - I
# decided to actually create lock files so a user could manually create and
# remove lock files themselves.
#
# This`works by creating/checking for/removing a directory
#
# BUG - This is not very sophistcated. We use this class in WSRF::File

package WSRF::FileLock;

#Provides a simple locking tool -

sub new {
	my ( $self, $file ) = @_;

	#$file is the name of the directory to make - the lock
	until ( mkdir $file ) {
		select( undef, undef, undef, 0.5 );
		print STDERR "$$ Lock on $file\n";
	}

	bless { _file => $file }, $self;
}

sub DESTROY {
	my ($self) = @_;
	print STDERR "$$ Removing Lock File ";
	print STDERR $self->{_file} . "\n";
	if ( -d $self->{_file} ) {
		rmdir $self->{_file}
		  or die SOAP::Fault->faultcode("Container Fault")
		  ->faultstring( "Could not remove lock file " . $self->{_file} );
	}
	print STDERR "$$ Lock file " . $self->{_file} . " removed\n";
}

#===============================================================================
# This module supports writing all the resource properties of a Resource to a
# file. Allows the state of the resource to be stored in a file between calls
# to the Resource. Relies on the Serialisers provided by SOAP::Lite to do the
# work
#
# We could use other Perl modules to do this (eg. the Dumper module) - I
# decided to reuse stuff from SOAP::Lite
#
package WSRF::File;
use Storable qw(lock_store lock_nstore lock_retrieve);
use Safe;

# this is made a private function - Resources use files to store their state
# inherit this module along the way, we do not want remote clients to be
# able to invoke this function so we make it private. (SOAP::Lite will not
# allow you to invoke private functions in a module remotely)
# This function takes a SOM object and puts the data from the SOM object
# into the ResourceProperty HASH of the Resource, the resource developer
# only has to program using the hash.
#
my $Insert = sub {
	my ($b) = @_;

	#get the name of the property
	my $name = $b->dataof()->name;

	#print "insert name= ".$name."\n";

	#check there is no user defined function
	#for inserting this property
	if ( defined( $WSRF::WSRP::InsertMap{$name} ) ) {
		$WSRF::WSRP::InsertMap{$name}->($b);
		return;
	}

	#get the value of the property
	my $value = $b->dataof()->value;

	#print "insert $name value= $value\n";

	#check the property actually exists
	if ( defined( $WSRF::WSRP::ResourceProperties{$name} ) ) {

		#check the type of the property (scalar|array)
		my $type = ref( $WSRF::WSRP::ResourceProperties{$name} );
		if ( $type eq "" )    #scalar
		{
			$WSRF::WSRP::ResourceProperties{$name} = $value;
		} elsif ( $type eq "ARRAY" )    #array
		{

			#add property to array
			push( @{ $WSRF::WSRP::ResourceProperties{$name} }, $value );
		} elsif ( $type ne "CODE" ) {
			print STDERR
"$$ ERROR: Property $name is a $type, only ARRAY,SCALAR and CODE are supported in WSRF::File::Insert\n";
		}
	} else {
		print STDERR
"$$ ERROR: Attempting to load property from file that has not been declared in WSRF::File::Insert\n";
	}

	return;
};

# Takes a SOAP::SOM envelope, gets the ID of the Resource and then loads the
# properties into the WSRF::WSRP::ResouceProperties hash for the service. Uses
# the Insert function to load the properties into the hash. Also creates a
# lock file - lock file is removed in the DESTROY operation when the
# WSRF::File object is destroyed
#
sub new {
	my ( $class, $envelope ) = @_;

	my $address = $envelope->headerof("//{$WSRF::Constants::WSA}To");
	if ( defined $address ) {
		$address = $envelope->headerof("//{$WSRF::Constants::WSA}To")->value;
	} else {
		print STDERR "ERROR: No ResourceID in the SOAP Header\n";
		die SOAP::Fault->faultcode("No WS-Resource Identifier")
		  ->faultstring("No WS-Resource identifier in SOAP Header");
	}

	my @PathArray = split( /\//, $address );
	my $ID        = pop @PathArray;

	#my $ID = $ENV{ID};

	#check the ID is safe - we do not accept dots,
	#all paths will be relative to $ENV{WRF_MODULES}
	#only allow alphanumeric, underscore and hyphen
	if ( $ID =~ /^([-\w]+)$/ ) {
		$ID = $1;
	} else {
		print STDERR "$$ WSRF::File ERROR: Bad $ID for WS-Resource\n";
		die SOAP::Fault->faultcode("Badly formed WS-Resource Identifier")
		  ->faultstring("Badly formed WS-Resource Identifier: $ID");
	}

	my $ID_clipped = $ID;

	#ID can be of the form 1341-4565, we use this form to all multiple
	#WS-Resources to share the same state, the state is in the file
	#1341 - we use this with ServiceGroup/ServiceGroupEntry
	$ID_clipped =~ s/-\w*//o;

	my $path = $WSRF::Constants::Data . $ID_clipped;

	if ( !( -e $path ) ) {
		print STDERR "$$ ERROR: No Resource $path\n";
		die SOAP::Fault->faultcode("No WS-Resource")
		  ->faultstring("No WS-Resource with Identifer $ID");
	}

	#The address of the lock file
	my $lock = $path . ".lock";

	#Acquire a lock for the file
	my $Lock = WSRF::FileLock->new($lock);

#   open FILE, "$path" or die SOAP::Fault->faultcode("Container Failure")
#		                        ->faultstring("Container Failure: Could not open WS-Resource file");
#   #read the XML from the file
#   my $XML = join "",<FILE> ;

#   close FILE or die SOAP::Fault->faultcode("Container Failure")
#		                ->faultstring("Container Failure: Could not close WS-Resource file");

	# convert the XML into a SOM object. (the SOM object will still allow access
	# to the raw XML)
	#   my $som = WSRF::Deserializer->deserialize($XML);

	#iterate through the ResourceProperties and call insert for each one
	#   my $k = 1;
	#   while( $som->match("//ResourceProperties/[$k]") )
	#   {
	#print "SOM name= ".$som->dataof("//ResourceProperties/[$k]")->name()."\n";
	#     $Insert->( $som->match("//ResourceProperties/[$k]") );
	#     $k++;
	#   }

	#   my $safe = new Safe;
	#   $safe->permit(qw(:default require));
	#   local $Storable::Eval = sub { $safe->reval($_[0]) };
	my $hashref = Storable::lock_retrieve($path);

	#   print "Thawing...\n";
	#   foreach my $key (keys %$hashref)
	#   {
	#     $WSRF::WSRP::ResourceProperties{$key} = $hashref->{$key};
	#     print $key.": ".$hashref->{$key}."\n";
	#   }
	#print "CurrentTime = ".${$hashref->{CurrentTime}}."\n";

	%WSRF::WSRP::ResourceProperties =
	  ( %WSRF::WSRP::ResourceProperties, %{ $hashref->{Properties} } );

	%WSRF::WSRP::Private = ( %WSRF::WSRP::Private, %{ $hashref->{Private} } );

	#check that the resource is still alive - if TT time is not
	#set then TT is infinity
	if ( defined( $WSRF::WSRP::ResourceProperties{'TerminationTime'} )
		 && ( $WSRF::WSRP::ResourceProperties{'TerminationTime'} ne "" ) )
	{
		if (
			 WSRF::Time::ConvertStringToEpochTime(
							  $WSRF::WSRP::ResourceProperties{'TerminationTime'}
			 ) < time
		  )
		{
			print STDERR "$$ Resource $ID expired\n";
			unlink $path
			  or die SOAP::Fault->faultcode("Container Failure")
			  ->faultstring("Container Failure: Could not remove file");
			rmdir $lock
			  or die SOAP::Fault->faultcode("Container Failure")
			  ->faultstring("Container Failure: Could not remove lock file");
			die SOAP::Fault->faultcode("No such Resource")
			  ->faultstring("No such Resource $ID - Lifetime expired");
		}
	}

	bless {
			_ID   => $ID,
			_path => $path,
			_lock => $Lock
	}, $class;
}

sub ID {
	my ($self) = @_;
	return $self->{_ID};
}

sub path {
	my ($self) = @_;
	return $self->{_path};
}

# Send the ResourceProperties to a file
sub toFile {
	my $class = shift;

	my $filename =
	  ref($class)
	  ? $class->{_path}
	  : $WSRF::Constants::Data . $class;

#   open FILE, ">$filename" or die SOAP::Fault->faultcode("Container Failure")
#		                             ->faultstring("Container Failure: Could open file");

 #  print ">>>>AFTER>>>>\n".WSRF::WSRP::xmlizeProperties()."\n<<<<<<<<<<<<\n\n";

	#   print FILE WSRF::WSRP::xmlizeProperties();

	#   close FILE or die  SOAP::Fault->faultcode("Container Failure")
	#		                 ->faultstring("Container Failure: Could close file");
	#   my $safe = new Safe;
	#   $safe->permit(qw(:default require));
	#   local $Storable::Eval = sub { $safe->reval($_[0]) };
	#   local $Storable::Deparse = 1;

	my %tmpPrivate = (%WSRF::WSRP::Private);

	#should use map?
	foreach my $key ( keys %tmpPrivate ) {
		if ( ref( $tmpPrivate{$key} ) eq "CODE" ) {
			delete $tmpPrivate{$key};
		}
	}

	#take a copy of the ResourceProperties to copy to file
	my %tmphash = (%WSRF::WSRP::ResourceProperties);
	foreach my $key ( keys %tmphash ) {
		if ( ref( $tmphash{$key} ) eq "CODE" ) {
			delete $tmphash{$key};
		}
	}

	my %tmpStore = ( Properties => \%tmphash, Private => \%tmpPrivate );

	local $Storable::forgive_me = "TRUE";
	lock_store \%tmpStore, $filename;

	return;
}

sub unlock {
	my ($self) = @_;
	my $Lock = $self->{_lock};
	$Lock->DESTROY();
}

#===============================================================================
# header function creates a SOAP::Header that should be included
# in the response to the client. Handles the WS-Address stuff.
# Takes the original envelope and creates a Header from it -
# the second paramter will be stuffed into the Header so must
# be XML
#
# BUG This should be better automated - probably in the SOAP serializer,
# not sure how because we need to remember the MessageID
package WSRF::Header;

sub header {
	my ( $envelope, $anythingelse ) = @_;

	#To create the wsa:Action we must find the operation name
	#and its namespace
	my $data     = $envelope->match('/Envelope/Body/[1]')->dataof;
	my $method   = $data->name;
	my $uri      = $data->uri;
	my $Action   = $uri . "/" . $method . "Response";
	my $myHeader = "<wsa:Action wsu:Id=\"Action\">" . $Action . "</wsa:Action>";

	#We only use "anonoymous" for wsa:To
	$myHeader .= "<wsa:To wsu:Id=\"To\">$WSRF::Constants::WSA_ANON</wsa:To>";

	#We use our endpoint to create the wsa:From - the endpoint
	#is an ENV variable
	if ( $envelope->match("/Envelope/Header/{$WSRF::Constants::WSA}To") ) {
		my $from =
		  $envelope->valueof("/Envelope/Header/{$WSRF::Constants::WSA}To");
		$myHeader .=
"<wsa:From wsu:Id=\"From\"><wsa:EndPointReference><wsa:Address>$from</wsa:Address></wsa:EndPointReference></wsa:From>";
	}

	$myHeader .=
	    "<wsa:MessageID wsu:Id=\"MessageID\">"
	  . WSRF::WS_Address::MessageID()
	  . "</wsa:MessageID>";

	#check for wsa:MessageID in envelope - if it is set use it to
	#create a wsa:RelatesTo element
	my $messageID = $envelope->headerof("//{$WSRF::Constants::WSA}MessageID");
	if ( defined $messageID ) {
		$messageID =
		  $envelope->headerof("//{$WSRF::Constants::WSA}MessageID")->value;
		$myHeader .=
		    "<wsa:RelatesTo wsu:Id=\"RelatesTo\">"
		  . $messageID
		  . "</wsa:RelatesTo>";
	}

	#append anything else the user has given us
	$myHeader .= $anythingelse;

	#create the SOAP::Header object and return to client
	return SOAP::Header->value($myHeader)->type('xml');
}

#===============================================================================
# Base class for the process based WSRF services - a Service can inherit from
# this class to pick up GetResourceProperty, GetMultiResourceProperties and
# SetResourceProperty operations.

package WSRF::WSRP;

use vars qw(@ISA);

# we inherit this to gain access to the envelope - see SOAP::Lite
@ISA = qw(SOAP::Server::Parameters);

# Hash to store resource properties - we make this effectively
# a globe variable
%WSRF::WSRP::ResourceProperties = ();

# Hash stores the prefix for the resource property
# eg CurrentTime will use the prefix wsrl, the
# map between tthe prefix and the namespace is
# elsewhere
%WSRF::WSRP::PropertyNamespaceMap = ();

# Hash that maps a property and the fuction that
# should be called when aan attempt is made to
# insert that property. Simple properties are
# handled by default.
%WSRF::WSRP::InsertMap = ();

# Hash that maps property to function that should
# be used to delete it - simple properties are
# handled by default
%WSRF::WSRP::DeleteMap = ();

# Hash to define which properties can be "nil" - by
# default properties can not be nil.
%WSRF::WSRP::Nillable = ();

# Hash to define which properties cannot be Deleted
%WSRF::WSRP::NotDeletable = ();

# Hash to define which properties cannot be changed
%WSRF::WSRP::NotModifiable = ();

# Hash to define which properties cannot be inserted
%WSRF::WSRP::NotInsert = ();

# serach for a resource property - this is used by getResourceProperty
# and getMultipleResourceProperties. Takes the ID of the resource
# and the name of the rsource.
#
# BUG - we do not handle namespaces of property!!
sub searchResourceProperty {
	my $longsearch = shift @_;

	#dump the namespace of property
	my ( $junk, $search );
	if ( $longsearch =~ m/:/ ) {
		( $junk, $search ) = split /:/, $longsearch;
	} else {
		$search = $longsearch;
	}

	#default result!!
	my $ans = "";

	#print "Printing keys\n";
	#foreach my $key ( keys %WSRF::WSRP::ResourceProperties)
	#{
	#   print "  key= <$key>\n";
	#}

	#Check Resource property exists, if it does it can either
	#be a simple scalar, an array or a function.
	if ( defined( $WSRF::WSRP::ResourceProperties{$search} ) ) {

		#get type of property
		my $type = ref( $WSRF::WSRP::ResourceProperties{$search} );
		if ( $type eq "" )    # if scalar
		{

			#check if property set
			if ( $WSRF::WSRP::ResourceProperties{$search} ne "" ) {
				$ans .= "<"
				  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
				  . ":$search ";

				#do we need to add a namespace for this property
				my $ns =
				  defined(
					   $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} )
				  ? " xmlns:"
				  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix} . "=\""
				  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace}
				  . "\">"
				  : ">";
				$ans .= $ns
				  . $WSRF::WSRP::ResourceProperties{$search} . "</"
				  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
				  . ":$search>";
			}

			#property NOT set - is it nillable?
			elsif ( $WSRF::WSRP::ResourceProperties{$search} eq ""
					&& defined( $WSRF::WSRP::Nillable{$search} ) )
			{
				$ans .= "<"
				  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
				  . ":$search";
				my $ns =
				  defined(
					   $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} )
				  ? " xmlns:"
				  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix} . "=\""
				  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace}
				  . "\""
				  : " ";
				$ans .= $ns . " xsi:nil=\"true\"/>";
			}
		}

		#property is array of things
		elsif ( $type eq "ARRAY" ) {

			#check array is not empty - and property is nillable
			if ( !@{ $WSRF::WSRP::ResourceProperties{$search} }
				 && defined( $WSRF::WSRP::Nillable{$search} ) )
			{
				$ans .= "<"
				  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
				  . ":$search";
				my $ns =
				  defined(
					   $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} )
				  ? " xmlns:"
				  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix} . "=\""
				  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace}
				  . "\""
				  : " ";
				$ans .= $ns . " xsi:nil=\"true\"/>";
			}

			#loop over array building result
			else {
				foreach
				  my $entry ( @{ $WSRF::WSRP::ResourceProperties{$search} } )
				{
					$ans .= "<"
					  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
					  . ":$search";

					#do we need to add a namespace for this property
					my $ns =
					  defined( $WSRF::WSRP::PropertyNamespaceMap->{$search}
							   {namespace} )
					  ? " xmlns:"
					  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
					  . "=\""
					  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace}
					  . "\">"
					  : ">";
					$ans .=
					    $ns . $entry . "</"
					  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
					  . ":$search>";
				}
			}
		}

		#property is a subroutine - call it to get result
		#example of this is CurrentTime
		elsif ( $type eq "CODE" ) {
			$ans .= $WSRF::WSRP::ResourceProperties{$search}->();
		}

   #Some type we do not understand yet eg. Hash - attempt to serialize it anyway
		else {
			my $serializer = WSRF::SimpleSerializer->new();
			$ans .= "<"
			  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
			  . ":$search";

			#do we need to add a namespace for this property
			my $ns =
			  defined( $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} )
			  ? " xmlns:"
			  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix} . "=\""
			  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} . "\">"
			  : ">";

			$ans .= $ns
			  . $serializer->serialize(
									  $WSRF::WSRP::ResourceProperties{$search} )
			  . "</"
			  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
			  . ":$search>";

			#       die SOAP::Fault->faultcode("WSRF::Lite Failure")
			#		      ->faultstring("Could not understand type: $type");
		}

	}

	return $ans;
}

# This creates  XML with all the ResourceProperties in it - we can then
# use the XPath query from queryResourceProperty on it.
# BUG (FIXED(?) But we have not written queryResourceProperty yet - its a
# bad idea anyway so lets  not worry about it.
#
sub xmlizeProperties {

	#my $ans = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	my $ans =
	    "<wsrp:ResourceProperties"
	  . " xmlns:wsrp=\"$WSRF::Constants::WSRP\" "
	  . " xmlns:wsrl=\"$WSRF::Constants::WSRL\" "
	  . " xmlns:wssg=\"$WSRF::Constants::WSSG\" "
	  . " xmlns:wsa=\"$WSRF::Constants::WSA\" "
	  . " xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\" "
	  . " xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\">";

	foreach my $key ( keys %WSRF::WSRP::ResourceProperties ) {
		$ans .= searchResourceProperty($key);
	}

	$ans .= "</wsrp:ResourceProperties>";

	return $ans;
}

sub GetResourcePropertyDocument {
	my $envelope = pop @_;
	my $xml      = xmlizeProperties();
	return WSRF::Header::header($envelope),
	  SOAP::Data->value($xml)->type('xml');
}

# delete property
# BUG we do not handle namespaces
my $mydelete = sub {
	my ($name) = @_;

	#strip namespace
	$name =~ s/\w*://o;

	#   #check for user defined delete function for this property
	if ( defined( $WSRF::WSRP::DeleteMap{$name} ) ) {
		$WSRF::WSRP::DeleteMap{$name}->();
		return;
	}

	#check we are allowed to delete this function
	#   if( defined( $WSRF::WSRP::NotDeletable{$name} ) )
	#   {
	#     die SOAP::Fault->faultcode("setResourceproperty: Delete Failure")
	#		    ->faultstring("Could not delete $name");
	#   }

	#check property exists
	if ( defined( $WSRF::WSRP::ResourceProperties{$name} ) ) {

		#check type either array or scalar
		my $type = ref( $WSRF::WSRP::ResourceProperties{$name} );
		if ( $type eq "" )    #scalar
		{
			$WSRF::WSRP::ResourceProperties{$name} = "";
		} elsif ( $type eq "ARRAY" )    # array
		{

			#set contents to nothing
			@{ $WSRF::WSRP::ResourceProperties{$name} } = ();
		} else {
			die SOAP::Fault->faultcode("setResourceproperty: Delete Failure")
			  ->faultstring("Could not delete $name");
		}
	} else {
		die SOAP::Fault->faultcode("setResourceproperty: Delete Failure")
		  ->faultstring("No ResourceProperty: $name");
	}
	return;
};

# insert property - this function is used by the Insert and Update
# in the SetResourceProperty operation. This operation takes
# the ID of the resource and a SOAP::SOM object that has been set
# at the property that should be inserted
# Only one property can be inserted at a time using the function -
# SetResourceProperty of course loops over it
my $insert = sub {
	my ($b) = @_;

	#get the name of the property
	my $name = $b->dataof()->name;

	#   #check there is no user defined function
	#   #for inserting this property
	if ( defined( $WSRF::WSRP::InsertMap{$name} ) ) {
		$WSRF::WSRP::InsertMap{$name}->($b);
		return;
	}

	#check this property can be changed
	#   if( defined( $WSRF::WSRP::NotModifiable{$name} ))
	#   {
	#     die SOAP::Fault->faultcode("setResourceproperty: Insert Failure")
	#		    ->faultstring("Could not insert $name");
	#   }

	#get the value of the property
	my $value = $b->dataof()->value;

	#check the property actually exists
	if ( defined( $WSRF::WSRP::ResourceProperties{$name} ) ) {

		#check the type of the property (scalar|array)
		my $type = ref( $WSRF::WSRP::ResourceProperties{$name} );
		if ( $type eq "" )    #scalar
		{
			$WSRF::WSRP::ResourceProperties{$name} = $value;
		} elsif ( $type eq "ARRAY" )    #array
		{

			#add property to array
			push( @{ $WSRF::WSRP::ResourceProperties{$name} }, $value );
		} else                          #perhaps subroutine?
		{
			die SOAP::Fault->faultcode("setResourceproperty: Insert Failure")
			  ->faultstring("Could not insert $name");
		}
	} else {
		die SOAP::Fault->faultcode(
								"setResourceproperty: No such ResourceProperty")
		  ->faultstring("$name is not a ResourceProperty of this WS-Resource");
	}
	return;
};

# we provide an init method in case the service writer does bother - this
# will be called whenever the WS-Resource is created
sub init { return; }

# wsrp GetResourceProperty
sub GetResourceProperty {
	my $envelope = pop @_;

	#print "XML>>>\n".xmlizeProperties()."\n<<<XML\n";

	#search through envelope to the GetResourceProperty bit
	#and get the resource property name
	my $search = $envelope->valueof('//GetResourceProperty/');

	#print "GetResourceProperty = $search\n";
	my $ans = searchResourceProperty($search);

	#print "GetResourceProperty Ans= $ans\n";

	return WSRF::Header::header($envelope),
	  SOAP::Data->value($ans)->type('xml');
}

# wsrp GetMultipleResourceProperties
sub GetMultipleResourceProperties {
	my $envelope = pop @_;

	my $ans = "";    #we will just cat the answers together

	#    print "XML>>>\n".$xmlizeProperties->($ID)."\n<<<XML\n";

	#loop over each ResourceProperty request
	foreach my $search ( $envelope->valueof('//ResourceProperty/') ) {
		$ans .= searchResourceProperty($search);
	}

	return WSRF::Header::header($envelope),
	  SOAP::Data->value($ans)->type('xml');

}

# wsrp SetResourceProperties - the client can request that properties
# are inserted, updated and deleted in the one operation. The commands
# must happen in the order they come in the request, all stop when we
# hit a problem
sub SetResourceProperties {

	#get the envelope
	my $som = pop @_;

	#the base point of all our searchs.
	my $base = "//SetResourceProperties";

	#find the start of commands - should think
	#of this as an array of arries - that is why we have [$jj]/[$kk]
	if ( $som->match($base) ) {
		my $jj = 1;

		#now we loop over commands - $jj records our postion
		while ( $som->dataof("$base/[$jj]") ) {

			#get the command name
			my $Function = $som->dataof("$base/[$jj]")->name();
			if ( $Function eq "Insert" )    #an Insert
			{
				my $kk = 1;

				#loop over the things that have to be inserted
				while ( $som->match("$base/[$jj]/[$kk]") ) {

			 #print "Inserting ".$som->dataof("$base/[$jj]/[$kk]")->name()."\n";
			 #insert the thing - note we pass a SOM object becasue the
					if (
						 !defined(
								   $WSRF::WSRP::NotInsert{ $som->dataof(
												  "$base/[$jj]/[$kk]")->name() }
						 )
					  )
					{
						$insert->( $som->match("$base/[$jj]/[$kk]") );
					}    #thing could be pretty complex.

					$kk++;
				}
			} elsif ( $Function eq "Update" )    #an Update
			{
				my $kk      = 1;
				my %tmpHash = ();

				#loop over things to Update - an update is a Delete followed
				#by an Insert in a single atomic operation
				while ( $som->match("$base/[$jj]/[$kk]") ) {

					#get name of thing we are updating
					my $name = $som->dataof("$base/[$jj]/[$kk]")->name();

			   #print "Updating $name\n";
			   #check we have not deleted it before else delete before inserting
					if ( !defined( $WSRF::WSRP::NotModifiable{$name} ) ) {
						if ( !defined( $tmpHash{$name} ) ) {
							$mydelete->($name);
							$tmpHash{$name} = 1;
						}

						#insert value
						$insert->( $som->match("$base/[$jj]/[$kk]") );
					}
					$kk++;
				}
			} elsif ( $Function eq "Delete" )    #a Delete
			{

				#the property to delete is actually an attribute
				#in the delete element
				my $propname =
				  $som->dataof("$base/[$jj]")->attr->{'resourceProperty'};

				#print "Delete $propname\n";
				#delete property
				if ( !defined( $WSRF::WSRP::NotDeletable{$propname} ) ) {
					$mydelete->($propname);
				}
			} else {    #something other than Insert|Update|Delete
				die SOAP::Fault->faultcode(
										"setResourceproperty: Unkown operation")
				  ->faultstring("$Function not supported - only Insert,Update and Delete are supported"
				  );
			}
			$jj++;
		}
	}

	return WSRF::Header::header($som);
}

sub InsertResourceProperties {
	my $som  = pop @_;
	my $base = "//InsertResourceProperties";
	if ( $som->match($base) ) {
		my $kk = 1;
		while ( $som->match("$base/[1]/[$kk]") ) {
			my $name = $som->dataof("$base/[1]/[$kk]")->name();
			print "Inserting $name\n";

			#insert the thing - note we pass a SOM object becasue the
			#thing could be pretty complex.
			if ( !defined( $WSRF::WSRP::NotInsert{$name} ) ) {
				$insert->( $som->match("$base/[1]/[$kk]") );
			} else {
				die "InvalidInsertResourcePropertiesRequestContent\n";
			}
			$kk++;
		}
	}
	return WSRF::Header::header($som);
}

sub UpdateResourceProperties {
	my $som  = pop @_;
	my $base = "//UpdateResourceProperties";
	if ( $som->match($base) ) {
		my $kk      = 1;
		my %tmpHash = ();
		while ( $som->match("$base/[1]/[$kk]") ) {

			#get name of thing we are updating
			my $name = $som->dataof("$base/[1]/[$kk]")->name();
			print "Updating $name\n";
			if ( !defined( $WSRF::WSRP::NotModifiable{$name} ) ) {

			   #check we have not deleted it before else delete before inserting
				if ( !defined( $tmpHash{$name} ) ) {
					$mydelete->($name);
					$tmpHash{$name} = 1;
				}

				#insert value
				$insert->( $som->match("$base/[1]/[$kk]") );
			} else {
				die "InvalidUpdateResourcePropertiesRequestContent\n";
			}
			$kk++;
		}
	}

	return WSRF::Header::header($som);
}

sub DeleteResourceProperties {
	my $som  = pop @_;
	my $base = "//DeleteResourceProperties";
	if ( $som->match($base) ) {
		my $kk = 1;
		while ( $som->match("$base/[$kk]") ) {
			print "Into Loop inner...\n";

			#the property to delete is actually an attribute
			#in the delete element
			my $propname =
			  $som->dataof("$base/[$kk]")->attr->{'ResourceProperty'};
			$propname =~ s/\w*://o;

			#delete property
			if ( !defined( $WSRF::WSRP::NotDeletable{$propname} ) ) {
				$mydelete->($propname);
			} else {
				die "InvalidDeleteResourcePropertiesRequestContent\n";
			}
			$kk++;
		}
	}

	return WSRF::Header::header($som);
}

#===============================================================================
# The WSRL class, inherits from the WSRF::WSRP class and adds Destroy
# and SetTerminationTime operations. Adds the resource properties
# required wsrl:TerminationTime and wsrl:CurrentTime
#
package WSRF::WSRL;

use vars qw(@ISA);

@ISA = qw(WSRF::WSRP);

sub init {
	my $self = shift @_;

	# Add TerminationTime as a resource property -
	# initalise to nothing (ie. set TT to infinity)
	$WSRF::WSRP::ResourceProperties{'TerminationTime'} = "";

	# belongs to RsourceLiftetime namespace - defined
	# elsewhere to be wsrl
	$WSRF::WSRP::PropertyNamespaceMap->{TerminationTime}{prefix} = "wsrl";

	# the TerminationTime can be nil.
	$WSRF::WSRP::Nillable{TerminationTime}      = 1;
	$WSRF::WSRP::NotModifiable{TerminationTime} = 1;

	# add resource property CurrentTime - in this
	# case a subroutine that returns the current
	# time in the correct format
	$WSRF::WSRP::ResourceProperties{'CurrentTime'} = sub {
		return "<wsrl:CurrentTime>"
		  . WSRF::Time::ConvertEpochTimeToString()
		  . "</wsrl:CurrentTime>";
	};
	$WSRF::WSRP::PropertyNamespaceMap->{CurrentTime}{prefix} = "wsrl";

	# By default if a resource property is a subroutine
	# then you cannot change it or delete it - however
	# for completeness we set the following
	$WSRF::WSRP::NotDeletable{CurrentTime}  = 1;
	$WSRF::WSRP::NotModifiable{CurrentTime} = 1;
	$WSRF::WSRP::NotInsert{CurrentTime}     = 1;

	$self->SUPER::init();

}

sub Destroy {

	#set alarm to 1, gives us time to return a result
	#before we die
	alarm(1);

	#return nothing except a SOAP HEADER
	return WSRF::Header::header( pop @_ );
}

# wsrl SetTerminationTime - if you want to make a max limit your Resource
# you should override this function in your module.
sub SetTerminationTime {
	my $envelope = pop @_;
	shift @_;    #the first paramter is always the class of the object
	my $time = shift @_;    #the new TerminationTime

	#check for null time - allowed by wsrl, means TT is infinity
	if ( $time eq "" ) {
		$WSRF::WSRP::ResourceProperties{'TerminationTime'} = "";

		#disable alarm
		alarm;
		my $ans =
		    "<wsrl:NewTerminationTime xsi:nil=\"true\" />"
		  . "<wsrl:CurrentTime>"
		  . WSRF::Time::ConvertEpochTimeToString()
		  . "</wsrl:CurrentTime>";

		return WSRF::Header::header($envelope),
		  SOAP::Data->value($ans)->type('xml');
	}

	#BUG this is handled by WSRF::Time::ConvertStringToEpochTime now - should
	#BUG be removed from here
	$time =~ s/\.\d+//;

	#print "Setting TerminationTime to: $time\n";
	#test time is good - this will die if the string is faulty, causing
	#a SOAP fault to be sent to the cli
	#ent
	DateTime::Format::W3CDTF->new->parse_datetime($time);

	my $SecsToLive = WSRF::Time::ConvertStringToEpochTime($time);

	if ( $SecsToLive < time )    # TT is sometime in the past, die now
	{

		#give us time to reply - then die
		alarm 1;
	} else {

		#reset the alarm, this is were you can set a max TT.
		alarm( $SecsToLive - time );
	}

	#reset TerminationTime
	$WSRF::WSRP::ResourceProperties{'TerminationTime'} = $time;

	my $result = "<wsrl:NewTerminationTime>$time</wsrl:NewTerminationTime>";
	$result .=
	    "<wsrl:CurrentTime>"
	  . WSRF::Time::ConvertEpochTimeToString()
	  . "</wsrl:CurrentTime>";

	return WSRF::Header::header($envelope),
	  SOAP::Data->value($result)->type('xml');
}

#===============================================================================
# If the Service inherits from this class then the ResourceProperties are
# stored in a file between calls.
#
package WSRF::FileBasedResourceProperties;

use vars qw(@ISA);

@ISA = qw(WSRF::WSRP);

# Load the ResourceProperties from the file into the ResourceProperties hash
# then call the super operation.
sub GetResourceProperty {
	my $self     = shift @_;
	my $envelope = pop @_;
	my $lock     = WSRF::File->new($envelope);

	#print "TT= ".$WSRF::WSRP::ResourceProperties{TerminationTime}."\n";
	#print "calling SUPER::GetResourceProperty\n";
	my @resp = $self->SUPER::GetResourceProperty($envelope);
	$lock->toFile();
	return @resp;
}

# Load the ResourceProperties from the file into the ResourceProperties hash
# then call the super operation.
sub GetMultipleResourceProperties {
	my $self     = shift @_;
	my $envelope = pop @_;
	my $lock     = WSRF::File->new($envelope);
	my @resp     = $self->SUPER::GetMultipleResourceProperties($envelope);
	$lock->toFile();
	return @resp;
}

# Load the ResourceProperties from the file into the ResourceProperties hash
# then call the super operation.
sub SetResourceProperties {
	my $self     = shift @_;
	my $envelope = pop @_;
	my $lock     = WSRF::File->new($envelope);
	my @resp     = $self->SUPER::SetResourceProperties($envelope);
	$lock->toFile();
	return @resp;
}

# Load the ResourceProperties from the file into the ResourceProperties hash
# then call the super operation.
sub InsertResourceProperties {
	my $self     = shift @_;
	my $envelope = pop @_;
	my $lock     = WSRF::File->new($envelope);
	my @resp     = $self->SUPER::InsertResourceProperties($envelope);
	$lock->toFile();
	return @resp;
}

# Load the ResourceProperties from the file into the ResourceProperties hash
# then call the super operation.
sub UpdateResourceProperties {
	my $self     = shift @_;
	my $envelope = pop @_;
	my $lock     = WSRF::File->new($envelope);
	my @resp     = $self->SUPER::UpdateResourceProperties($envelope);
	$lock->toFile();
	return @resp;
}

# Load the ResourceProperties from the file into the ResourceProperties hash
# then call the super operation.
sub DeleteResourceProperties {
	my $self     = shift @_;
	my $envelope = pop @_;
	my $lock     = WSRF::File->new($envelope);
	my @resp     = $self->SUPER::DeleteResourceProperties($envelope);
	$lock->toFile();
	return @resp;
}

# Load the ResourceProperties from the file into the ResourceProperties hash
# then call the super operation.
sub GetResourcePropertyDocument {
	my $self     = shift @_;
	my $envelope = pop @_;
	my $lock     = WSRF::File->new($envelope);
	my @resp     = $self->SUPER::GetResourcePropertyDocument($envelope);
	$lock->toFile();
	return @resp;
}

#=============================================================================
# Inherits from WSRF::FileBasedResourceProperties, adds the WSRL operations
# to the Service. Again all the ResourceProperties are stored in a file
# between calls - the name of the file is the same as the Resource ID
#

package WSRF::FileBasedResourceLifetimes;

use vars qw(@ISA);

@ISA = qw(WSRF::FileBasedResourceProperties);

#Add TerminationTime as a reource property -
#initalise to nothing (infinity)
$WSRF::WSRP::ResourceProperties{'TerminationTime'} = "";

#belongs to RsourceLiftetime namespace - defined
#elsewhere to be wsrl
$WSRF::WSRP::PropertyNamespaceMap->{TerminationTime}{prefix} = "wsrl";

#the TerminationTime can be nil
$WSRF::WSRP::Nillable{TerminationTime}      = 1;
$WSRF::WSRP::NotModifiable{TerminationTime} = 1;

#add resource property CurrentTime - in this
#case a subroutine that returns the current
#time in the correct format
$WSRF::WSRP::ResourceProperties{'CurrentTime'} = sub {
	return "<wsrl:CurrentTime>"
	  . WSRF::Time::ConvertEpochTimeToString()
	  . "</wsrl:CurrentTime>";
};
$WSRF::WSRP::PropertyNamespaceMap->{CurrentTime}{prefix} = "wsrl";

#By default if a resource property is a subroutine
#then you cannot change it or delete it - however
#for completeness we set the following
$WSRF::WSRP::NotDeletable{CurrentTime}  = 1;
$WSRF::WSRP::NotModifiable{CurrentTime} = 1;

# remove the file with the resource properties in it.
sub Destroy {
	my $envelope = pop @_;
	my $lock     = WSRF::File->new($envelope);
	my $file     = $WSRF::Constants::Data . $lock->ID();
	unlink $file
	  or die SOAP::Fault->faultcode("Container Failure")
	  ->faultstring("Container Failure: could not remove file");
	return WSRF::Header::header($envelope);
}

# load the properties from the file into the hash then
# set the termination time and store back to the file.
sub SetTerminationTime {
	my $envelope = pop @_;
	my $lock     = WSRF::File->new($envelope);
	shift @_;    #the first paramter is always the class of the object
	my $time = shift @_;    #the new TerminationTime

	#check for null time - allowed by wsrl
	my ($ans);
	if ( $time eq "" ) {
		$WSRF::WSRP::ResourceProperties{'TerminationTime'} = "";

		my $ans =
		    "<wsrl:NewTerminationTime xsi:nil=\"true\" />"
		  . "<wsrl:CurrentTime>"
		  . WSRF::Time::ConvertEpochTimeToString(time)
		  . "</wsrl:CurrentTime>";
	} else {

		#BUG - this is done in ConvertEpochTimeToString now so we can drop it
		$time =~ s/\.\d+//;

		#print "Setting TerminationTime to: $time\n";

		#test time is good - this will die if the string is faulty, causing
		#a SOAP fault to be sent to the client
		DateTime::Format::W3CDTF->new->parse_datetime($time);

		#reset TerminationTime
		$WSRF::WSRP::ResourceProperties{'TerminationTime'} = $time;

		$ans = "<wsrl:NewTerminationTime>$time</wsrl:NewTerminationTime>";
		$ans .=
		    "<wsrl:CurrentTime>"
		  . WSRF::Time::ConvertEpochTimeToString()
		  . "</wsrl:CurrentTime>";
	}

	$lock->toFile();
	return WSRF::Header::header($envelope),
	  SOAP::Data->value($ans)->type('xml');
}

#===============================================================================
# In this case a single process acts on behave of a number of
# Resources - the resource properties are all held in a hash - the
# ID of the resource is used as the key to the hash. The Container
# talks to the process through a named UNIX socket - the name of the
# socket is the same as the name of the module.
#
package WSRF::MultiResourceProperties;

use vars qw(@ISA);

#we inherit this to gain access to the envelope - see SOAP::Lite
@ISA = qw(SOAP::Server::Parameters);

# For this example all Resources are managed by one process,
# a hash holds an entry for each resource, the same hash
# also holds all the resource properties for each resource

#Hash to store each resource and its properties
%WSRF::MultiResourceProperties::ResourceProperties = ();

# Hash stores the prefix for the resource property
# eg CurrentTime will use the prefix wsrl, the
# map between tthe prefix and the namespace is
# elsewhere
%WSRF::MultiResourceProperties::PropertyNamespaceMap = ();

# Hash that maps a property and the fuction that
# should be called when aan attempt is made to
# insert that property. Simple properties are
# handled by default.
%WSRF::MultiResourceProperties::InsertMap = ();

# Hash that maps property to function that should
# be used to delete it - simple properties are
# handled by default
%WSRF::MultiResourceProperties::DeleteMap = ();

# Hash to define which properties can be "nil" - by
# default properties can not be nil.
%WSRF::MultiResourceProperties::Nillable = ();

# Hash to define which properties cannot be Deleted
%WSRF::MultiResourceProperties::NotDeletable = ();

# Hash to define which properties cannot be changed
%WSRF::MultiResourceProperties::NotModifiable = ();

%WSRF::MultiResourceProperties::NotInsert = ();

# get the Resource ID from the envelope - check that it is in the
# hash and check the termination time for the resource.
# BUG - should we check the TT for all resources and do Garbag Collection
#       pro-actively
sub getID {
	my $envelope = shift;

	#print STDERR "Calling getID...\n";
	#search for ResourceID in Header
	my $ID = $envelope->headerof("//{$WSRF::Constants::WSA}To");
	if ( defined $ID ) {
		$ID = $envelope->headerof("//{$WSRF::Constants::WSA}To")->value;
	} else {
		die SOAP::Fault->faultcode('No WS-Resource Identifier')
		  ->faultstring('No Resource Identifier in SOAP Header');
	}

	my @PathArray = split( /\//, $ID );
	$ID = pop @PathArray;

	#print STDERR "ID => $ID\n";

	#check the Resource actually exists or die
	if ( !defined( $WSRF::MultiResourceProperties::ResourceProperties->{$ID} ) )
	{
		die SOAP::Fault->faultcode('No WS-Resource')
		  ->faultstring("No Resource with Identifier $ID");
	}

	#check that the resource is still alive - if TT time is not
	#set then TT is infinity
	foreach
	  my $key ( keys %{$WSRF::MultiResourceProperties::ResourceProperties} )
	{
		if (
			 defined(
					  $WSRF::MultiResourceProperties::ResourceProperties->{$key}
						{'TerminationTime'}
			 )
			 && ( $WSRF::MultiResourceProperties::ResourceProperties->{$key}
				  {'TerminationTime'} ne "" )
		  )
		{
			if (
				 WSRF::Time::ConvertStringToEpochTime(
					  $WSRF::MultiResourceProperties::ResourceProperties->{$key}
						{'TerminationTime'}
				 ) < time
			  )
			{
				print STDERR "MultiResourceProperties Resource $key Expired\n";
				delete
				  $WSRF::MultiResourceProperties::ResourceProperties->{$key};
			}
		}
	}

	#check the Resource actually exists or die
	if ( !defined( $WSRF::MultiResourceProperties::ResourceProperties->{$ID} ) )
	{
		die SOAP::Fault->faultcode('No WS-Resource')
		  ->faultstring("No Resource with Identifier $ID");
	}

	#could set as ENV variable?
	return $ID;
}

# serach for a resource property - this is used by getResourceProperty
# and getMultipleResourceProperties. Takes the ID of the resource
# and the name of the rsource.
# BUG - we do not handle namespaces of peroperty!!
my $MultisearchResourceProperty = sub {
	my %args       = @_;
	my $ID         = $args{ID};
	my $longsearch = $args{property};

	#dump the namespace of property
	my ( $junk, $search );
	if ( $longsearch =~ m/:/ ) {
		( $junk, $search ) = split /:/, $longsearch;
	} else {
		$search = $longsearch;
	}

	#default result!!
	my $ans = "";

	#Check Resource property exists, if it does it can either
	#be a simple scalar, an array or a function.
	if (
		 defined(   $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$search}
		 )
	  )
	{

		#get type of property
		my $type =
		  ref( $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
			   {$search} );
		if ( $type eq "" )    # if scalar
		{

			#check if property set
			if ( $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
				 {$search} ne "" )
			{
				$ans .= "<"
				  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
				  ->{$search}{prefix} . ":$search ";

				#do we need to add a namespace for this property
				my $ns =
				  defined( $WSRF::MultiResourceProperties::PropertyNamespaceMap
						   ->{$search}{namespace} )
				  ? " xmlns:"
				  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
				  ->{$search}{prefix} . "=\""
				  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
				  ->{$search}{namespace} . "\">"
				  : ">";
				$ans .= $ns
				  . $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
				  {$search} . "</"
				  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
				  ->{$search}{prefix} . ":$search>";
			}

			#property NOT set - is it nillable?
			elsif ( $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
				 {$search} eq ""
				 && defined( $WSRF::MultiResourceProperties::Nillable{$search} )
			  )
			{
				$ans .= "<"
				  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
				  ->{$search}{prefix} . ":$search";
				my $ns =
				  defined( $WSRF::MultiResourceProperties::PropertyNamespaceMap
						   ->{$search}{namespace} )
				  ? " xmlns:"
				  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
				  ->{$search}{prefix} . "=\""
				  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
				  ->{$search}{namespace} . "\""
				  : " ";
				$ans .= $ns . " xsi:nil=\"true\"/>";
			}
		}

		#property is array of things
		elsif ( $type eq "ARRAY" ) {

			#check array is not empty - and property is nillable
			if (
				 !@{
					 $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
					   {$search}
				 }
				 && defined( $WSRF::MultiResourceProperties::Nillable{$search} )
			  )
			{
				$ans .= "<"
				  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
				  ->{$search}{prefix} . ":$search";
				my $ns =
				  defined( $WSRF::MultiResourceProperties::PropertyNamespaceMap
						   ->{$search}{namespace} )
				  ? " xmlns:"
				  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
				  ->{$search}{prefix} . "=\""
				  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
				  ->{$search}{namespace} . "\""
				  : " ";
				$ans .= $ns . " xsi:nil=\"true\"/>";
			}

			#loop over array building result
			else {
				foreach my $entry (
						  @{
							  $WSRF::MultiResourceProperties::ResourceProperties
								->{$ID}{$search}
						  }
				  )
				{
					$ans .= "<"
					  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
					  ->{$search}{prefix} . ":$search";

					#do we need to add a namespace for this property
					my $ns =
					  defined(
							$WSRF::MultiResourceProperties::PropertyNamespaceMap
							  ->{$search}{namespace} )
					  ? " xmlns:"
					  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
					  ->{$search}{prefix} . "=\""
					  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
					  ->{$search}{namespace} . "\">"
					  : ">";
					$ans .=
					    $ns . $entry . "</"
					  . $WSRF::MultiResourceProperties::PropertyNamespaceMap
					  ->{$search}{prefix} . ":$search>";
				}
			}
		}

		#property is a subroutine - call it to get result
		#example of this is CurrentTime
		elsif ( $type eq "CODE" ) {
			$ans .=
			  $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$search}
			  ->();
		}

		#Some type we do not understand yet eg. Hash
		else {

			my $serializer = WSRF::SimpleSerializer->new();
			$ans .= "<"
			  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
			  . ":$search";

			#do we need to add a namespace for this property
			my $ns =
			  defined( $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} )
			  ? " xmlns:"
			  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix} . "=\""
			  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} . "\">"
			  : ">";

			$ans .= $ns
			  . $serializer->serialize(
							   $WSRF::WSRP::ResourceProperties->{$ID}{$search} )
			  . "</"
			  . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
			  . ":$search>";

			#die "Do not understand type\n";
		}

	}

	return $ans;
};

# This creates  XML with all the ResourceProperties in it - we can then
# use the XPath query from queryResourceProperty on it.
# BUG - we have not written queryResourceProperty
my $xmlizeProperties = sub {
	my $ID = shift @_;

	if ( !defined($ID) || $ID eq "" ) {
		die "Attempt to call xmlizeProperties without ID\n";
	}

	#print "$$ MultiSession xmlizeProperties called for $ID\n";

	#my $ans = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	my $ans =
	    "<wsrp:ResourceProperties"
	  . " xmlns:wsrp=\"$WSRF::Constants::WSRP\" "
	  . " xmlns:wsrl=\"$WSRF::Constants::WSRL\" "
	  . " xmlns:wsa=\"$WSRF::Constants::WSA\" >";

	foreach my $key (
		   keys %{ $WSRF::MultiResourceProperties::ResourceProperties->{$ID} } )
	{
		$ans .= $MultisearchResourceProperty->( ID => $ID, property => $key );
	}

	$ans .= "</wsrp:ResourceProperties>";

	return $ans;
};

sub GetResourcePropertyDocument {
	my $envelope = pop @_;
	my $ID       = getID($envelope);
	print "$$ Called GetResourcePropertyDocument, ID= $ID\n";
	my $xml = $xmlizeProperties->($ID);
	return WSRF::Header::header($envelope),
	  SOAP::Data->value($xml)->type('xml');
}

# insert property - this function is used by the Insert and Update
# in the SetResourceProperty operation. This operation takes
# the ID of the resource and a SOAP::SOM object that has been set
# at the property that should be inserted
# Only one property can be inserted at a time using the function -
# SetResourceProperty of course loops over it
my $Multiinsert = sub {
	my %args = @_;
	my $ID   = $args{ID};
	my $b    = $args{som};

	#get the name of the property
	my $name = $b->dataof()->name;

	#check there is no user defined function
	#for inserting this property
	if ( defined( $WSRF::MultiResourceProperties::InsertMap{$name} ) ) {
		$WSRF::MultiResourceProperties::InsertMap{$name}->( $ID, $b );
		return;
	}

	#check this property can be changed
	#   if( defined( $WSRF::MultiResourceProperties::NotModifiable{$name} ))
	#   {
	#     die SOAP::Fault->faultcode("setResourceproperty: Failure")
	#		    ->faultstring("Could not modify $name");
	#   }

	#get the value of the property
	my $value = $b->dataof()->value;

	#check the property actually exists
	if (
		 defined(     $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$name}
		 )
	  )
	{

		#check the type of the property (scalar|array)
		my $type =
		  ref(
			 $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$name} );
		if ( $type eq "" )    #scalar
		{
			$WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$name} =
			  $value;
		} elsif ( $type eq "ARRAY" )    #array
		{

			#add property to array
			push(
				  @{
					  $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
						{$name}
					},
				  $value
			);
		} else                          #perhaps subroutine?
		{
			die SOAP::Fault->faultcode("setResourceproperty: Failure")
			  ->faultstring("Could not modify $name");
		}
	} else {
		die SOAP::Fault->faultcode("No such WS-Resource")
		  ->faultstring("No such WS-Resource with identifier $ID");
	}
	return;
};

# delete property
# BUG we do not handle namespaces
my $Multimydelete = sub {
	my %args = @_;
	my $ID   = $args{ID};
	my $name = $args{property};

	#strip namespace
	$name =~ s/\w*://;

	#check for user defined delete function for this property
	if ( defined( $WSRF::MultiResourceProperties::DeleteMap{$name} ) ) {
		$WSRF::MultiResourceProperties::DeleteMap{$name}->($ID);
		return;
	}

	#check we are allowed to delete this function
	#   if( defined( $WSRF::MultiResourceProperties::NotDeletable{$name} ) )
	#   {
	#     die SOAP::Fault->faultcode("setResourceproperty: Delete Failure")
	#		     ->faultstring("Could not delete $name");
	#   }

	#check property exists
	if (
		 defined(     $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$name}
		 )
	  )
	{

		#check type either array or scalar
		my $type =
		  ref(
			 $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$name} );
		if ( $type eq "" )    #scalar
		{
			$WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$name} =
			  "";
		} elsif ( $type eq "ARRAY" )    # array
		{

			#set contents to nothing
			@{ $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
				  {$name} } = ();
		} else {
			die SOAP::Fault->faultcode("setResourceproperty: Delete Failure")
			  ->faultstring("Could not delete $name");
		}
	} else {
		die SOAP::Fault->faultcode("No such WS-Resource")
		  ->faultstring("No WS-Resource with identifier $ID");
	}
	return;
};

# provide a default init - incase the service developer doesn't bother
sub init { return; }

# wsrp GetResourceProperty
sub GetResourceProperty {
	my $envelope = pop @_;
	my $ID       = getID($envelope);

	#search through envelope to the GetResourceProperty bit
	#and get the resource property name
	my $search = $envelope->valueof('//GetResourceProperty/');

	my $ans = $MultisearchResourceProperty->(    ID       => $ID,
											  property => $search );

	return WSRF::Header::header($envelope),
	  SOAP::Data->value($ans)->type('xml');
}

# wsrp GetMultipleResourceProperties
sub GetMultipleResourceProperties {
	my $envelope = pop @_;
	my $ID       = getID($envelope);

	my $ans = "";    #we will just cat the answers together

	#    print "XML>>>\n".$xmlizeProperties->($ID)."\n<<<XML\n";

	#loop over each ResourceProperty request
	foreach my $search ( $envelope->valueof('//ResourceProperty/') ) {
		$ans .= $MultisearchResourceProperty->(       ID       => $ID,
												property => $search );
	}

	return WSRF::Header::header($envelope),
	  SOAP::Data->value($ans)->type('xml');

}

# wsrp SetResourceProperties - the client can request that properties
# are inserted, updated and deleted in the one operation. The commands
# must happen in the order they come in the request, all stop when we
# hit a problem
sub SetResourceProperties {

	#get the envelope
	my $som = pop @_;
	my $ID  = getID($som);

	#the base point of all our searchs.
	my $base = "//SetResourceProperties";

	#find the start of commands - should think
	#of this as an array of arries - that is why we have [$jj]/[$kk]
	if ( $som->match($base) ) {
		my $jj = 1;

		#now we loop over commands - $jj records our postion
		while ( $som->dataof("$base/[$jj]") ) {

			#get the command name
			my $Function = $som->dataof("$base/[$jj]")->name();
			if ( $Function eq "Insert" )    #an Insert
			{
				my $kk = 1;

				#loop over the things that have to be inserted
				while ( $som->match("$base/[$jj]/[$kk]") ) {

			 #print "Inserting ".$som->dataof("$base/[$jj]/[$kk]")->name()."\n";
			 #insert the thing - note we pass a SOM object becasue the
			 #thing could be pretty complex.
					if (
						 !defined(
								 $WSRF::MultiResourceProperties::NotInsert{ $som
									   ->dataof("$base/[$jj]/[$kk]")->name() }
						 )
					  )
					{
						$Multiinsert->(                   ID  => $ID,
										som => $som->match("$base/[$jj]/[$kk]") );
					}
					$kk++;
				}
			} elsif ( $Function eq "Update" )    #an Update
			{
				my $kk      = 1;
				my %tmpHash = ();

				#loop over things to Update - an update is a Delete followed
				#by an Insert in a single atomic operation
				while ( $som->match("$base/[$jj]/[$kk]") ) {

					#get name of thing we are updating
					my $name = $som->dataof("$base/[$jj]/[$kk]")->name();

			   #print "Updating $name\n";
			   #check we have not deleted it before else delete before inserting
					if (
						!defined(             $WSRF::MultiResourceProperties::NotModifiable{$name}
						)
					  )
					{
						if ( !defined( $tmpHash{$name} ) ) {
							$Multimydelete->(                      ID       => $ID,
											  property => $name );
							$tmpHash{$name} = 1;
						}

						#insert value
						$Multiinsert->(                   ID  => $ID,
										som => $som->match("$base/[$jj]/[$kk]") );
					}
					$kk++;
				}
			} elsif ( $Function eq "Delete" )    #a Delete
			{

				#the property to delete is actually an attribute
				#in the delete element
				my $propname =
				  $som->dataof("$base/[$jj]")->attr->{'resourceProperty'};

				#print "Delete $propname\n";
				#delete property
				if (
					 !defined(          $WSRF::MultiResourceProperties::NotDeletable{$propname}
					 )
				  )
				{
					$Multimydelete->(                ID       => $ID,
									  property => $propname );
				}
			} else {    #something other than Insert|Update|Delete
				die SOAP::Fault->faultcode("setResourceproperty: Failure")
				  ->faultstring("setResourceProperty does not support $Function: only Insert, Update and Delete are supported"
				  );
			}
			$jj++;
		}
	}

	return WSRF::Header::header($som);
}

sub InsertResourceProperties {
	my $som  = pop @_;
	my $ID   = getID($som);
	my $base = "//InsertResourceProperties";
	if ( $som->match($base) ) {
		my $kk = 1;
		while ( $som->match("$base/[1]/[$kk]") ) {
			my $name = $som->dataof("$base/[1]/[$kk]")->name();
			print "Inserting $name\n";

			#insert the thing - note we pass a SOM object becasue the
			#thing could be pretty complex.
			if ( !defined( $WSRF::MultiResourceProperties::NotInsert{$name} ) )
			{
				$Multiinsert->(             ID  => $ID,
								som => $som->match("$base/[1]/[$kk]") );
			} else {
				die "InvalidInsertResourcePropertiesRequestContent\n";
			}
			$kk++;
		}
	}
	return WSRF::Header::header($som);
}

sub UpdateResourceProperties {
	my $som  = pop @_;
	my $ID   = getID($som);
	my $base = "//UpdateResourceProperties";
	if ( $som->match($base) ) {
		my $kk      = 1;
		my %tmpHash = ();
		while ( $som->match("$base/[1]/[$kk]") ) {

			#get name of thing we are updating
			my $name = $som->dataof("$base/[1]/[$kk]")->name();
			print "Updating $name\n";
			if (
				 !defined(             $WSRF::MultiResourceProperties::NotModifiable{$name}
				 )
			  )
			{

			   #check we have not deleted it before else delete before inserting
				if ( !defined( $tmpHash{$name} ) ) {
					$Multimydelete->(                ID       => $ID,
									  property => $name );
					$tmpHash{$name} = 1;
				}

				#insert value
				$Multiinsert->(             ID  => $ID,
								som => $som->match("$base/[1]/[$kk]") );
			} else {
				die "InvalidUpdateResourcePropertiesRequestContent\n";
			}
			$kk++;
		}
	}

	return WSRF::Header::header($som);
}

sub DeleteResourceProperties {
	my $som  = pop @_;
	my $ID   = getID($som);
	my $base = "//DeleteResourceProperties";
	if ( $som->match($base) ) {
		my $kk = 1;
		while ( $som->match("$base/[$kk]") ) {
			print "Into Loop inner...\n";

			#the property to delete is actually an attribute
			#in the delete element
			my $propname =
			  $som->dataof("$base/[$kk]")->attr->{'ResourceProperty'};
			$propname =~ s/\w*://o;

			#delete property
			if (
				 !defined(           $WSRF::MultiResourceProperties::NotDeletable{$propname}
				 )
			  )
			{
				$Multimydelete->(             ID       => $ID,
								  property => $propname );
			} else {
				die "InvalidDeleteResourcePropertiesRequestContent\n";
			}
			$kk++;
		}
	}

	return WSRF::Header::header($som);
}

#===============================================================================
# The extension to WSRF::MultiResourceProperties that supports WSRL - adding
# the operations Destroy and SetTerminationTime
#
package WSRF::MultiResourceLifetimes;

use vars qw(@ISA);

@ISA = qw(WSRF::MultiResourceProperties);

# wsrl Destroy
sub Destroy {
	my $envelope = pop @_;
	my $ID       = WSRF::MultiResourceProperties::getID($envelope);

	delete $WSRF::MultiResourceProperties::ResourceProperties->{$ID};

	#return nothing except a SOAP HEADER
	return WSRF::Header::header($envelope);
}

# wsrl SetTerminationTime
sub SetTerminationTime {
	my $envelope = pop @_;
	shift @_;    #the first paramter is always the class of the object
	my $time = shift @_;    #the new TerminationTime
	my $ID = WSRF::MultiResourceProperties::getID($envelope);

	#check for null time - allowed by wsrl
	if ( $time eq "" ) {
		$WSRF::MultiResourceProperties::ResourceProperties->{$ID}
		  {'TerminationTime'} = "";

		my $ans =
		    "<wsrl:NewTerminationTime xsi:nil=\"true\" />"
		  . "<wsrl:CurrentTime>"
		  . WSRF::Time::ConvertEpochTimeToString(time)
		  . "</wsrl:CurrentTime>";

		return WSRF::Header::header($envelope),
		  SOAP::Data->value($ans)->type('xml');
	}

	#BUG - with DateTime::Format::W3CDTF - does not
	#like subseconds - should patch DateTime::Format::W3CDTF
	#print "Called SetTerminationTime: $time\n";
	$time =~ s/\.\d+//;

	#print "Setting TerminationTime to: $time\n";

	#test time is good - this will die if the string is faulty, causing
	#a SOAP fault to be sent to the client
	DateTime::Format::W3CDTF->new->parse_datetime($time);

	#reset TerminationTime
	$WSRF::MultiResourceProperties::ResourceProperties->{$ID}
	  {'TerminationTime'} = $time;

	my $result = "<wsrl:NewTerminationTime>$time</wsrl:NewTerminationTime>";
	$result .=
	    "<wsrl:CurrentTime>"
	  . WSRF::Time::ConvertEpochTimeToString()
	  . "</wsrl:CurrentTime>";

	return WSRF::Header::header($envelope),
	  SOAP::Data->value($result)->type('xml');
}

#===============================================================================
# This package is for supporting ServiceGroups:
# http://www.globus.org/wsrf/specs/ws-servicegroup.pdf
#
# ServiceGroups allows you to bunch a set of WS-Resources
# together. They are the building blocks of Registries
#
#
package WSRF::ServiceGroup;

use vars qw(@ISA);

@ISA = qw(WSRF::FileBasedResourceLifetimes);

# foo is an array of things
$WSRF::WSRP::ResourceProperties{Entry}                = [];
$WSRF::WSRP::PropertyNamespaceMap->{Entry}{prefix}    = "wssg";
$WSRF::WSRP::PropertyNamespaceMap->{Entry}{namespace} = $WSRF::Constants::WSSG;
$WSRF::WSRP::NotDeletable{Entry} = 1; #Cannot delete through SetResourceProperty
$WSRF::WSRP::NotModifiable{Entry} =
  1;                                  #Cannot modify through SetResourceProperty

$WSRF::WSRP::ResourceProperties{ServiceGroupEPR}                = "";
$WSRF::WSRP::PropertyNamespaceMap->{ServiceGroupEPR}{prefix}    = "wssg";
$WSRF::WSRP::PropertyNamespaceMap->{ServiceGroupEPR}{namespace} =
  $WSRF::Constants::WSSG;
$WSRF::WSRP::NotDeletable{ServiceGroupEPR} =
  1;                                  #Cannot delete through SetResourceProperty
$WSRF::WSRP::NotModifiable{ServiceGroupEPR} =
  1;                                  #Cannot modify through SetResourceProperty

# The module name and path to use when creating a new entry
# in the SG.  Can be overridden by any module that subclasses this one.
$WSRF::ServiceGroup::ServiceGroupEntryModule = "ServiceGroupEntry";
$WSRF::ServiceGroup::ServiceGroupEntryPath   = "Session/ServiceGroupEntry/";

$WSRF::WSRP::InsertMap{ServiceGroupEPR} = sub {
	my ($som) = @_;

	print STDERR
	  "ServiceGroup WSRF::WSRP::InsertMap{ServiceGroupEPR}  called\n";

	my $serializer = new WSRF::SimpleSerializer;

	#print STDERR "$$ WSRF::ServiceGroup serializing ServiceGroupEPR\n";
	$WSRF::WSRP::ResourceProperties{ServiceGroupEPR} =
	  $serializer->serialize( $som->dataof('[1]') );
};

$WSRF::WSRP::InsertMap{Entry} = sub {
	my ($som) = @_;

	print STDERR "ServiceGroup WSRF::WSRP::InsertMap{Entry}  called\n";

	my $serializer = new WSRF::SimpleSerializer;

	#We store the entry as follows
	#   MemberServiceEPR
	#   ServiceGroupEntryEPR
	#   Content (optional)
	#   EntryTerminationTime
	#We will use EntryTerminationTime as a marker

	#get MemberServiceEPR
	my $Entry = $serializer->serialize( $som->dataof('[1]') );

	#get ServiceGroupEntryEPR
	$Entry .= $serializer->serialize( $som->dataof('[2]') );

	#Get the Content
	my $ContentorTime = $serializer->serialize( $som->dataof('[3]') );

	my $Time = "";
	if ( $ContentorTime =~ m/EntryTerminationTime/o ) {
		$Time = $ContentorTime;
		$Entry .= $Time;
	} else {
		$Entry .= $ContentorTime;
		$Time = $serializer->serialize( $som->dataof('[4]') );
		$Entry .= $Time;
	}

	#print STDERR "$$ Entry= $Entry\n\n";

	#strip xml tags away from time
	$Time =~ s/<\/?EntryTerminationTime\/?>//og;

	#print STDERR "$$ TerminationTime for Entry= $Time\n";

	if ( $Time eq "nil" )    #No TerminationTime
	{
		push( @{ $WSRF::WSRP::ResourceProperties{Entry} }, $Entry );
	} else {

		#check TerminationTime
		if ( WSRF::Time::ConvertStringToEpochTime($Time) > time ) {
			push( @{ $WSRF::WSRP::ResourceProperties{Entry} }, $Entry );
		}
	}

};

my $strip_old_Entries = sub {
	my $parser = new XML::DOM::Parser;
	my @tmp    = @{ $WSRF::WSRP::ResourceProperties{Entry} };
	@{ $WSRF::WSRP::ResourceProperties{Entry} } = ();
	foreach my $entry (@tmp) {
		my $tmpentry = "<t>" . $entry . "</t>";
		my $doc      = $parser->parse($tmpentry);

		#print STDERR "Parsed document..\n";
		my $TermTime =
		  defined( $doc->getElementsByTagName("EntryTerminationTime")->item(0)
				   ->getFirstChild )
		  ? $doc->getElementsByTagName("EntryTerminationTime")->item(0)
		  ->getFirstChild->getNodeValue
		  : "";

		next
		  if (    ( $TermTime ne "nil" )
			   && ( WSRF::Time::ConvertStringToEpochTime($TermTime) < time ) );

		push @{ $WSRF::WSRP::ResourceProperties{Entry} }, $entry;
		$doc->dispose;
	}

};

# wsrp GetResourceProperty
sub GetResourceProperty {
	my $self     = shift @_;
	my $envelope = pop @_;

	my $lock = WSRF::File->new($envelope);
	$strip_old_Entries->();

	my $search = $envelope->valueof('//GetResourceProperty/');

	#strip namespace - BUG we should handle namespaces properly and
	#not just ignore them
	$search =~ s/\w*://o;

	my $ans = "";

	#print STDERR "GetResourceProperty = $search\n";
	if ( $search eq "Entry" ) {
		foreach my $entry ( @{ $WSRF::WSRP::ResourceProperties{Entry} } ) {
			$ans .= "<wssg:Entry xmlns:wssg=\"$WSRF::Constants::WSSG\">";

			#BUG - why must we take a copy?
			my $tmp = $entry;
			$tmp =~ s/<EntryTerminationTime\/>//o;
			$tmp =~ s/<EntryTerminationTime>\w*<\/EntryTerminationTime>//o;
			$ans .= $tmp;
			$ans .= "</wssg:Entry>";
		}
	} else {
		$ans = WSRF::WSRP::searchResourceProperty($search);
	}

	$lock->toFile();
	return WSRF::Header::header($envelope),
	  SOAP::Data->value($ans)->type('xml');
}

# wsrp GetMultipleResourceProperties
sub GetMultipleResourceProperties {
	my $self     = shift @_;
	my $envelope = pop @_;
	my $lock     = WSRF::File->new($envelope);
	$strip_old_Entries->();

  #print ">>>>BEFORE>>>>\n".WSRF::WSRP::xmlizeProperties()."\n<<<<<<<<<<<<\n\n";

	my $ans = "";    #we will just cat the answers together

	#    print "XML>>>\n".$xmlizeProperties->($ID)."\n<<<XML\n";

	#loop over each ResourceProperty request
	foreach my $search ( $envelope->valueof('//ResourceProperty/') ) {

		#strip namespace
		$search =~ s/\w*://o;
		if ( $search eq "Entry" ) {
			foreach my $entry ( @{ $WSRF::WSRP::ResourceProperties{Entry} } ) {
				$ans .= "<wssg:Entry xmlns:wssg=\"$WSRF::Constants::WSSG\">";

				#BUG - why must we take a copy?
				my $tmp = $entry;
				$tmp =~ s/<EntryTerminationTime\/>//o;
				$tmp =~ s/<EntryTerminationTime>\w*<\/EntryTerminationTime>//o;
				$ans .= $tmp;
				$ans .= "</wssg:Entry>";
			}
		} else {
			$ans .= WSRF::WSRP::searchResourceProperty($search);
		}
	}

#print STDERR ">>>>AFTER>>>>\n".WSRF::WSRP::xmlizeProperties()."\n<<<<<<<<<<<<\n\n";

	$lock->toFile();
	return WSRF::Header::header($envelope),
	  SOAP::Data->value($ans)->type('xml');
}

# operation to create a new File based Counter
sub createServiceGroup {
	my $envelope = pop @_;
	my ( $class, @params ) = @_;

	# get an ID for the Resource
	my $ID = WSRF::GSutil::CalGSH_ID();

	#create a WS-Address for the Resource
	my $wsa = WSRF::GSutil::createWSAddress(
											 module => 'ServiceGroup',
											 path   => 'Session/ServiceGroup/',
											 ID     => $ID
	);

	$WSRF::WSRP::ResourceProperties{ServiceGroupEPR} = $wsa;

	#write the properties to a file
	WSRF::File::toFile($ID);

	#return the WS-Address
	return WSRF::Header::header($envelope),
	  SOAP::Data->value($wsa)->type('xml');
}

# add an entry to the SG
sub Add {
	my $envelope = pop @_;                     #get the SOAP envelope
	my $lock     = WSRF::File->new($envelope); #get the properties from the file
	$strip_old_Entries->();
	my ( $class, $val ) = @_;                  #get the operation paramaters

	my $serializer = new WSRF::SimpleSerializer;

#print "$$ Message::\n".$serializer->serialize( $envelope->dataof('/') )."\n\n";

	# BUG
	# We cannot use the following to get the MemberEPR
	# my $mepr = $serializer->serialize( $envelope->dataof('//MemberEPR/[1]') );
	# because it screws up the namespaces - SimpleSerializer cannot
	# handle more than one namespace in a message.

	my $mepraddress =
	    $envelope->match("//MemberEPR//{$WSRF::Constants::WSA}Address")
	  ? $envelope->valueof("//MemberEPR//{$WSRF::Constants::WSA}Address")
	  : die "No MemberEPR in Add message\n";    #BUG - BaseFault

	#check for ReferenceParameters
	my ($RefParam);
	if ( $envelope->dataof('//MemberEPR//ReferenceParameters/*') ) {
		my $i = 0;
		foreach
		  my $a ( $envelope->dataof('//MemberEPR//ReferenceParameters/*') )
		{
			$i++;
			my $name  = $a->name();
			my $uri   = $a->uri();
			my $value = $a->value();
			$RefParam .=
			    "<myns" . $i . ":" . $name
			  . " xmlns:myns"
			  . $i . "=\""
			  . $uri . "\">"
			  . $value
			  . "</myns"
			  . $i . ":"
			  . $name . ">";
		}
	}

	my $mepr = "<wsa:EndpointReference xmlns:wsa=\"$WSRF::Constants::WSA\">";
	$mepr .= "<wsa:Address>$mepraddress</wsa:Address>";
	$mepr .= $RefParam ? $RefParam : "";
	$mepr .= "</wsa:EndpointReference>";

	$mepr = "<wssg:MemberServiceEPR>$mepr</wssg:MemberServiceEPR>";

	#print STDERR "$$ MEPR = $mepr\n";

	my $content = "";
	if ( defined( $envelope->dataof('//Content/[1]') ) ) {

		#print "Content!! ". $envelope->dataof('//Content')  ."\n";
		$content = $serializer->serialize( $envelope->dataof('//Content/[1]') );

		$content = "<wssg:Content>$content</wssg:Content>";
	}

	#  print STDERR "Content = $content\n";

	my $termTime = "nil";
	if ( defined( $envelope->valueof('//InitialTerminationTime') ) ) {
		$termTime = $envelope->valueof('//InitialTerminationTime');

		#BUG with DateTime::Format::W3CDTF - does not
		#like subseconds - should patch DateTime::Format::W3CDTF
		#print "Called SetTerminationTime: $time\n";
		$termTime =~ s/\.\d+//;

		#print "Setting TerminationTime to: $time\n";

		#test time is good - this will die if the string is faulty, causing
		#a SOAP fault to be sent to the client
		#BUG should eval this and throw a WS-BaseFault
		DateTime::Format::W3CDTF->new->parse_datetime($termTime);
	}

	$termTime = "<EntryTerminationTime>$termTime</EntryTerminationTime>";

	# get an ID for the new ServiceGroupEntry
	my $ID = WSRF::GSutil::CalGSH_ID();
	$ID = $lock->ID() . "-" . $ID;

	#print STDERR "ServiceGroup ID = ".$lock->ID()."\n";
	#print STDERR "ServiceGroupEntry ID = $ID\n";

	my $sge_wsa = WSRF::GSutil::createWSAddress(
						 module => $WSRF::ServiceGroup::ServiceGroupEntryModule,
						 path   => $WSRF::ServiceGroup::ServiceGroupEntryPath,
						 ID     => $ID
	);

	my $ans = $sge_wsa;
	$sge_wsa =
	  "<wssg:ServiceGroupEntryEPR>$sge_wsa</wssg:ServiceGroupEntryEPR>";

	my $Entry = $mepr . $sge_wsa . $content . $termTime;

	push( @{ $WSRF::WSRP::ResourceProperties{Entry} }, $Entry );

	$lock->toFile();                        #put the properties back in the file
	return WSRF::Header::header($envelope), #return result
	  SOAP::Data->value($ans)->type('xml');
}

#===============================================================================

package WSRF::ServiceGroupEntry;

use vars qw(@ISA);
use XML::DOM;
use Storable qw(lock_store lock_nstore lock_retrieve);

@ISA = qw(WSRF::WSRL);

# foo is an array of things
$WSRF::WSRP::ResourceProperties{Content}                = "";
$WSRF::WSRP::PropertyNamespaceMap->{Content}{prefix}    = "wssg";
$WSRF::WSRP::PropertyNamespaceMap->{Content}{namespace} =
  $WSRF::Constants::WSSG;
$WSRF::WSRP::NotDeletable{Content} =
  1;    #Cannot delete through SetResourceProperty
$WSRF::WSRP::NotModifiable{Content} =
  1;    #Cannot modify through SetResourceProperty

$WSRF::WSRP::ResourceProperties{ServiceGroupEPR}                = "";
$WSRF::WSRP::PropertyNamespaceMap->{ServiceGroupEPR}{prefix}    = "wssg";
$WSRF::WSRP::PropertyNamespaceMap->{ServiceGroupEPR}{namespace} =
  $WSRF::Constants::WSSG;
$WSRF::WSRP::NotDeletable{ServiceGroupEPR} =
  1;    #Cannot delete through SetResourceProperty
$WSRF::WSRP::NotModifiable{ServiceGroupEPR} =
  1;    #Cannot modify through SetResourceProperty

$WSRF::WSRP::ResourceProperties{MemberEPR}                = "";
$WSRF::WSRP::PropertyNamespaceMap->{MemberEPR}{prefix}    = "wssg";
$WSRF::WSRP::PropertyNamespaceMap->{MemberEPR}{namespace} =
  $WSRF::Constants::WSSG;
$WSRF::WSRP::NotDeletable{MemberEPR} =
  1;    #Cannot delete through SetResourceProperty
$WSRF::WSRP::NotModifiable{MemberEPR} =
  1;    #Cannot modify through SetResourceProperty

my $fromFile = sub {

	# get ID
	my ( $envelope, %args ) = @_;

	foreach my $key ( keys %args ) {
		print "$$ fromFile $key => " . $args{$key} . "\n";
	}
	if ( defined( $args{Destroy} ) ) {
		print "$$ fromFile Attempt to Destroy\n";
	}

	my $address = $envelope->headerof("//{$WSRF::Constants::WSA}To");
	if ( defined $address ) {
		$address = $envelope->headerof("//{$WSRF::Constants::WSA}To")->value;
	} else {
		print STDERR "ERROR: No ResourceID in the SOAP Header\n";
		die SOAP::Fault->faultcode("No WS-Resource Identifier")
		  ->faultstring("No WS-Resource identifier in SOAP Header");
	}

	my @PathArray = split( /\//, $address );
	my $ID        = pop @PathArray;

	#check the ID is safe - we do not accept dots,
	#all paths will be relative to $ENV{WRF_MODULES}
	#only allow alphanumeric, underscore and hyphen
	if ( $ID =~ /^([-\w]+)$/ ) {
		$ID = $1;
	} else {
		print STDERR "ERROR: Bad ResourceID $ID in SOAP Header\n";
		die SOAP::Fault->faultcode("Badly formed WS-Resource Identifier")
		  ->faultstring("Badly formed WS-Resource Identifier in SOAP Header");
	}

	$ENV{ID} = $ID;

	my $ID_clipped = $ID;

	#ID can be of the form 1341-4565, we use this form to all multiple
	#WS-Resources to share the same state, the state is in the file
	#1341 - we use this with ServiceGroup/ServiceGroupEntry
	$ID_clipped =~ s/-\w*//o;

	my $path = $WSRF::Constants::Data . $ID_clipped;

	if ( !( -e $path ) ) {
		print STDERR "ERROR: No Resource $path\n";
		die SOAP::Fault->faultcode("No such WS-Resource")
		  ->faultstring("No WS-Resource with identifier $ID");
	}

	my $lock = $path . ".lock";

	my $Lock = WSRF::FileLock->new($lock);

	my $hashref = Storable::lock_retrieve($path);

	%WSRF::WSRP::ResourceProperties =
	  ( %WSRF::WSRP::ResourceProperties, %{ $hashref->{Properties} } );

	%WSRF::WSRP::Private = ( %WSRF::WSRP::Private, %{ $hashref->{Private} } );

	#   print STDERR "$$ fromFile about to enter loop\n";
	my $parser = new XML::DOM::Parser;
	my $found  = 0;
	my ( $doc, $TerminationTime, $MEPR, $Content, $Destroyed );
	my @tmp = @{ $WSRF::WSRP::ResourceProperties{Entry} };
	@{ $WSRF::WSRP::ResourceProperties{Entry} } = ();

	#   print "$$ Number of Entries= @tmp\n";
	foreach my $entry (@tmp) {

		#      print STDERR $entry."\n";
		my $tmpentry = "<t>" . $entry . "</t>";
		$doc = $parser->parse($tmpentry);

		#print STDERR "Parsed document..\n";
		my $TermTime =
		  defined( $doc->getElementsByTagName("EntryTerminationTime")->item(0)
				   ->getFirstChild )
		  ? $doc->getElementsByTagName("EntryTerminationTime")->item(0)
		  ->getFirstChild->getNodeValue
		  : "";

		if (    ( $TermTime ne "nil" )
			 && ( WSRF::Time::ConvertStringToEpochTime($TermTime) < time ) )
		{
			print STDERR "Deleting Node\n";
			next;
		}

		my $subnodes = $doc->getElementsByTagName("wssg:ServiceGroupEntryEPR");

		#      print "Length= ".$subnodes->getLength."\n";
		my $ResourceID = $subnodes->item(0)->getElementsByTagName("Address");
		if ( $ResourceID->getLength == 0 ) {
			$ResourceID =
			  $subnodes->item(0)->getElementsByTagName("wsa:Address");
		}

		#      print "$$ ResourceID Length= ".$ResourceID->getLength."\n";
		$ResourceID = $ResourceID->item(0)->getFirstChild->getNodeValue;

		#      print STDERR "$$ ResourceID = $ResourceID\n";
		if ( $ResourceID eq $address )    #found node we want
		{
			print STDERR "$$ ResourceIDs match\n";
			$TerminationTime = ( $TermTime eq "nil" ) ? "" : $TermTime;
			$Content =
			  $doc->getElementsByTagName("wssg:Content")->item(0)
			  ->getFirstChild->toString;
			$MEPR =
			  $doc->getElementsByTagName("wssg:MemberServiceEPR")->item(0)
			  ->getFirstChild->toString;
			$found = 1;
			if ( defined( $args{Destroy} ) ) {

			  #            print STDERR "$$ Destroying ServiceGroupEntry $ID\n";
				$Destroyed = "True";
				next;
			}
			if ( defined( $args{TerminationTime} ) ) {
				$doc->getElementsByTagName("EntryTerminationTime")->item(0)
				  ->getFirstChild->setNodeValue( $args{TerminationTime} );
			}
			my $foo = $doc->toString;
			$foo =~ s/<\/?t>//og;
			$entry = $foo;
		}
		push @{ $WSRF::WSRP::ResourceProperties{Entry} }, $entry;
		$doc->dispose;
	}

	my %tmpPrivate = (%WSRF::WSRP::Private);

	#should use map?
	foreach my $key ( keys %tmpPrivate ) {
		if ( ref( $tmpPrivate{$key} ) eq "CODE" ) {
			delete $tmpPrivate{$key};
		}
	}

	#take a copy of the ResourceProperties to copy to file
	my %tmphash = (%WSRF::WSRP::ResourceProperties);
	foreach my $key ( keys %tmphash ) {
		if ( ref( $tmphash{$key} ) eq "CODE" ) {
			delete $tmphash{$key};
		}
	}

	my %tmpStore = ( Properties => \%tmphash, Private => \%tmpPrivate );

	local $Storable::forgive_me = "TRUE";
	lock_store \%tmpStore, $path;

	#ServiceGroupEntry not found
	if ( !$found && !$Destroyed ) {
		die SOAP::Fault->faultcode("No such WS-Resource")
		  ->faultstring("No WS-Resource with identifier $address");
	}

	$WSRF::WSRP::ResourceProperties{TerminationTime} = $TerminationTime;
	$WSRF::WSRP::ResourceProperties{Content}         = $Content;
	$WSRF::WSRP::ResourceProperties{MemberEPR}       = $MEPR;

	return $path;
};

sub GetResourceProperty {
	my $self     = shift @_;
	my $envelope = pop @_;
	$fromFile->($envelope);

#   print STDERR "ServiceGroupEntry::GetResourceProperty Dumping Properties..\n";
#   foreach my $key ( keys %WSRF::WSRP::ResourceProperties )
#   {
#      print "  $key: ".$WSRF::WSRP::ResourceProperties{$key}."\n";
#   }
	my @resp = $self->SUPER::GetResourceProperty($envelope);
	return @resp;
}

sub GetResourcePropertyDocument {
	my $self     = shift @_;
	my $envelope = pop @_;
	$fromFile->($envelope);
	my @resp = $self->SUPER::GetResourcePropertyDocument($envelope);
	return @resp;
}

sub SetResourceProperties {
	my $self     = shift @_;
	my $envelope = pop @_;
	my $path     = $fromFile->($envelope);
	my @resp     = $self->SUPER::SetResourceProperties($envelope);
	return @resp;
}

sub GetMultipleResourceProperties {
	my $self     = shift @_;
	my $envelope = pop @_;
	my $path     = $fromFile->($envelope);
	my @resp     = $self->SUPER::GetMultipleResourceProperties($envelope);
	return @resp;
}

sub Destroy {

	# get ID
	my ($envelope) = pop @_;
	print STDERR "$$ WSRF::ServiceGroupEntry Destroy invoked\n";
	$fromFile->( $envelope, Destroy => 1 );
	return WSRF::Header::header($envelope);
}

sub SetTerminationTime {

	# get ID
	my ($envelope) = pop @_;
	shift @_;    #the first paramter is always the class of the object
	my $time = shift @_;

	#print STDERR "time= $time\n";

	#BUG with DateTime::Format::W3CDTF - does not
	#like subseconds - should patch DateTime::Format::W3CDTF
	#print "Called SetTerminationTime: $time\n";
	$time =~ s/\.\d+//;

	#check time is in good format - otherwise die!
	DateTime::Format::W3CDTF->new->parse_datetime($time);

	$fromFile->( $envelope, TerminationTime => $time );

	my $result = "<wsrl:NewTerminationTime>$time</wsrl:NewTerminationTime>";
	$result .=
	    "<wsrl:CurrentTime>"
	  . WSRF::Time::ConvertEpochTimeToString()
	  . "</wsrl:CurrentTime>";

	return WSRF::Header::header($envelope),
	  SOAP::Data->value($result)->type('xml');

}

# ======================================================================

package WSRF;

use vars qw($AUTOLOAD);
require URI;

my $soap;    # shared between SOAP and SOAP::Lite packages

{
	no strict 'refs';
	*AUTOLOAD = sub {
		local ( $1, $2 );
		my ( $package, $method ) = $AUTOLOAD =~ m/(?:(.+)::)([^:]+)$/;
		return if $method eq 'DESTROY';

		my $soap =
		  ref $_[0] && UNIVERSAL::isa( $_[0] => 'SOAP::Lite' ) ? $_[0] : $soap
		  || die
"SOAP:: prefix shall only be used in combination with +autodispatch option\n";

		my $uri        = URI->new( $soap->uri );
		my $currenturi = $uri->path;
		$package =
		  ref $_[0] && UNIVERSAL::isa( $_[0] => 'SOAP::Lite' )
		  ? $currenturi
		  : $package eq 'SOAP'
		  ? ref $_[0]
		  || ( $_[0] eq 'SOAP'
			 ? $currenturi || Carp::croak "URI is not specified for method call"
			 : $_[0] )
		  : $package eq 'main'
		  ? $currenturi || $package
		  : $package;

		# drop first parameter if it's a class name
		{
			my $pack = $package;
			for ($pack) { s!^/!!; s!/!::!g; }
			shift @_
			  if @_ && !ref $_[0] && ( $_[0] eq $pack || $_[0] eq 'SOAP' )
			  || ref $_[0] && UNIVERSAL::isa( $_[0] => 'SOAP::Lite' );
		}

		for ($package) { s!::!/!g; s!^/?!/!; }
		$uri->path($package);

		my $som = $soap->uri( $uri->as_string )->call( $method => @_ );
		UNIVERSAL::isa( $som => 'SOAP::SOM' )
		  ? wantarray ? $som->paramsall : $som->result
		  : $som;
	};
}

# ======================================================================
# Copyright (C) 2000-2004 Paul Kulchenko (paulclinger@yahoo.com)
# SOAP::Lite is free software; you can redistribute it
# and/or modify it under the same terms as Perl itself.

package WSRF::Lite;

use vars qw($AUTOLOAD @ISA);
use Carp ();

use SOAP::Packager;

@ISA = qw(SOAP::Cloneable);

# provide access to global/autodispatched object
sub self { @_ > 1 ? $soap = $_[1] : $soap }

# no more warnings about "used only once"
*UNIVERSAL::AUTOLOAD if 0;

sub autodispatched { \&{*UNIVERSAL::AUTOLOAD} eq \&{*SOAP::AUTOLOAD} }

sub soapversion {
	my $self    = shift;
	my $version = shift or return $SOAP::Constants::SOAP_VERSION;

	($version) =
	  grep { $SOAP::Constants::SOAP_VERSIONS{$_}->{NS_ENV} eq $version }
	  keys %SOAP::Constants::SOAP_VERSIONS
	  unless exists $SOAP::Constants::SOAP_VERSIONS{$version};

	die qq!$SOAP::Constants::WRONG_VERSION Supported versions:\n@{[
                join "\n", map {"  $_ ($SOAP::Constants::SOAP_VERSIONS{$_}->{NS_ENV})"} keys %SOAP::Constants::SOAP_VERSIONS
                ]}\n!
	  unless defined($version)
	  && defined( my $def = $SOAP::Constants::SOAP_VERSIONS{$version} );

	foreach ( keys %$def ) {
		eval
"\$SOAP::Constants::$_ = '$SOAP::Constants::SOAP_VERSIONS{$version}->{$_}'";
	}

	$SOAP::Constants::SOAP_VERSION = $version;
	$self;
}

BEGIN { WSRF::Lite->soapversion(1.1) }

sub import {
	my $pkg    = shift;
	my $caller = caller;
	no strict 'refs';

	# emulate 'use SOAP::Lite 0.99' behavior
	$pkg->require_version(shift) if defined $_[0] && $_[0] =~ /^\d/;

	while (@_) {
		my $command = shift;

		my @parameters =
		  UNIVERSAL::isa( $_[0] => 'ARRAY' ) ? @{ shift() } : shift
		  if @_ && $command ne 'autodispatch';
		if ( $command eq 'autodispatch' || $command eq 'dispatch_from' ) {
			$soap = ( $soap || $pkg )->new;
			no strict 'refs';
			foreach ( $command eq 'autodispatch' ? 'UNIVERSAL' : @parameters ) {
				my $sub = "${_}::AUTOLOAD";
				defined &{*$sub}
				  ? ( \&{*$sub} eq \&{*SOAP::AUTOLOAD}
					? ()
					: Carp::croak
					  "$sub already assigned and won't work with DISPATCH. Died"
				  )
				  : ( *$sub = *SOAP::AUTOLOAD );
			}
		} elsif ( $command eq 'service' ) {
			foreach (
					  keys %{ SOAP::Schema->schema_url( shift(@parameters) )
							->parse(@parameters)->load->services
					  }
			  )
			{
				$_->export_to_level( 1, undef, ':all' );
			}
		} elsif ( $command eq 'debug' || $command eq 'trace' ) {
			SOAP::Trace->import( @parameters ? @parameters : 'all' );
		} elsif ( $command eq 'import' ) {
			local $^W;    # supress warnings about redefining
			my $package = shift(@parameters);
			$package->export_to_level( 1, undef,
									   @parameters ? @parameters : ':all' )
			  if $package;
		} else {
			Carp::carp
			  "Odd (wrong?) number of parameters in import(), still continue"
			  if $^W && !( @parameters & 1 );
			$soap = ( $soap || $pkg )->$command(@parameters);
		}
	}
}

sub DESTROY { SOAP::Trace::objects('()') }

sub new {
	my $self = shift;
	return $self if ref $self;
	unless ( ref $self ) {
		my $class = ref($self) || $self;

	   # Check whether we can clone. Only the SAME class allowed, no inheritance
		$self = ref($soap) eq $class ? $soap->clone : {
			_transport    => SOAP::Transport->new,
			_serializer   => WSRF::WSRFSerializer->new,
			_deserializer => WSRF::Deserializer->new,
			_packager     => SOAP::Packager::MIME->new,
			_schema       => undef,
			_wsaddress    => undef,
			_autoresult   => 0,
			_on_action    => sub { sprintf '"%s#%s"', shift || '', shift },
			_on_fault => sub {
				ref $_[1]                                    ? return $_[1]
				  : Carp::croak $_[0]->transport->is_success ? $_[1]
				  : $_[0]->transport->status;
			},
		};
		bless $self => $class;
		$self->on_nonserialized(    $self->on_nonserialized
								 || $self->serializer->on_nonserialized );
		SOAP::Trace::objects('()');
	}

	Carp::carp "Odd (wrong?) number of parameters in new()"
	  if $^W && ( @_ & 1 );
	while (@_) {
		my ( $method, $params ) = splice( @_, 0, 2 );
		$self->can($method)
		  ? $self->$method( ref $params eq 'ARRAY' ? @$params : $params )
		  : $^W && Carp::carp "Unrecognized parameter '$method' in new()";
	}

	return $self;
}

sub init_context {
	my $self = shift->new;
	$self->{'_deserializer'}->{'_context'} = $self;
	$self->{'_serializer'}->{'_context'}   = $self;
}

sub destroy_context {
	my $self = shift;
	delete( $self->{'_deserializer'}->{'_context'} );
	delete( $self->{'_serializer'}->{'_context'} );
}

# Naming? wsdl_parser
sub schema {
	my $self = shift;
	if (@_) {
		$self->{'_schema'} = shift;
		return $self;
	} else {
		if ( !defined $self->{'_schema'} ) {
			$self->{'_schema'} = SOAP::Schema->new;
		}
		return $self->{'_schema'};
	}
}

sub BEGIN {
	no strict 'refs';
	for my $method (qw(serializer deserializer)) {
		my $field = '_' . $method;
		*$method = sub {
			my $self = shift->new;
			if (@_) {
				my $context =
				  $self->{$field}->{'_context'};    # save the old context
				$self->{$field} = shift;
				$self->{$field}->{'_context'} =
				  $context;                         # restore the old context
				return $self;
			} else {
				return $self->{$field};
			}
		  }
	}
	for my $method (
				 qw(endpoint transport outputxml autoresult packager wsaddress))
	{
		my $field = '_' . $method;
		*$method = sub {
			my $self = shift->new;
			@_
			  ? ( $self->{$field} = shift, return $self )
			  : return $self->{$field};
		  }
	}
	for my $method (qw(on_action on_fault on_nonserialized)) {
		my $field = '_' . $method;
		*$method = sub {
			my $self = shift->new;
			return $self->{$field} unless @_;
			local $@;

			# commented out because that 'eval' was unsecure
			# > ref $_[0] eq 'CODE' ? shift : eval shift;
			# Am I paranoid enough?
			$self->{$field} = shift;
			Carp::croak $@ if $@;
			Carp::croak
"$method() expects subroutine (CODE) or string that evaluates into subroutine (CODE)"
			  unless ref $self->{$field} eq 'CODE';
			return $self;
		  }
	}

	# SOAP::Transport Shortcuts
	# TODO - deprecate proxy() in favor of new language endpoint_url()
	for my $method (qw(proxy)) {
		*$method = sub {
			my $self = shift->new;
			if (@_) {
				my $endpoint = shift @_;
				if ( UNIVERSAL::isa( $endpoint => 'WSRF::WS_Address' ) ) {
					$self->{_wsaddress} = $endpoint;
					$endpoint = $endpoint->Address();
				}
				$self->transport->$method( $endpoint, @_ );
				return $self;
			}
			return $self->transport->$method();
		  }
	}

	# SOAP::Seriailizer Shortcuts
	for my $method (
		qw(autotype readable envprefix encodingStyle
		encprefix multirefinplace encoding typelookup uri
		header maptype xmlschema use_prefix ns default_ns)
	  )
	{
		*$method = sub {
			my $self = shift->new;
			@_
			  ? ( $self->serializer->$method(@_), return $self )
			  : return $self->serializer->$method();
		  }
	}

	# SOAP::Schema Shortcuts
	for my $method (qw(cache_dir cache_ttl)) {
		*$method = sub {
			my $self = shift->new;
			@_
			  ? ( $self->schema->$method(@_), return $self )
			  : return $self->schema->$method();
		  }
	}
}

sub parts {
	my $self = shift;
	$self->packager->parts(@_);
	return $self;
}

# Naming? wsdl
sub service {
	my $self = shift->new;
	return $self->{'_service'} unless @_;
	$self->schema->schema_url( $self->{'_service'} = shift );
	my %services = %{ $self->schema->parse(@_)->load->services };

	Carp::croak
"More than one service in service description. Service and port names have to be specified\n"
	  if keys %services > 1;
	my $service = ( keys %services )[0]->new;
	return $service;
}

sub AUTOLOAD {
	my $method = substr( $AUTOLOAD, rindex( $AUTOLOAD, '::' ) + 2 );
	return if $method eq 'DESTROY';

	ref $_[0]
	  or Carp::croak qq!Can\'t locate class method "$method" via package \"!
	  . __PACKAGE__ . '\"';

	no strict 'refs';
	*$AUTOLOAD = sub {
		my $self = shift;
		my $som = $self->call( $method => @_ );
		return $self->autoresult
		  && UNIVERSAL::isa( $som => 'SOAP::SOM' )
		  ? wantarray ? $som->paramsall : $som->result
		  : $som;
	};
	goto &$AUTOLOAD;
}

sub call {
	SOAP::Trace::trace('()');
	my $self = shift;

	if (
		 !(
			defined $self->proxy
			&& UNIVERSAL::isa( $self->proxy => 'SOAP::Client' )
		 )
		 && defined( $self->wsaddress )
		 && UNIVERSAL::isa( $self->wsaddress => 'WSRF::WS_Address' )
	  )
	{
		$self->proxy( $self->wsaddress->Address() );
	}

# Why is this here? Can't call be null? Indicating that there are no input arguments?
#return $self->{_call} unless @_;
	die
"A service address has not been specified either by using SOAP::Lite->proxy() or a service description)\n"
	  unless defined $self->proxy
	  && UNIVERSAL::isa( $self->proxy => 'SOAP::Client' );

	$self->init_context();
	my $serializer = $self->serializer;
	$serializer->on_nonserialized( $self->on_nonserialized );
	if ( defined $self->wsaddress ) {
		my $header =
		    "<wsa:Action wsu:Id=\"Action\">"
		  . scalar( $self->on_action->( $serializer->uriformethod( $_[0] ) ) )
		  . "</wsa:Action>";
		$header .=
		  "<wsa:To wsu:Id=\"To\">" . $self->wsaddress->Address() . "</wsa:To>";
		$header .=
		    "<wsa:MessageID wsu:Id=\"MessageID\">"
		  . $self->wsaddress->MessageID()
		  . "</wsa:MessageID>";
		$header .=
		    $self->wsaddress->serializeReferenceParameters()
		  ? $self->wsaddress->serializeReferenceParameters()
		  : '';

		#bug fix - John Newman
		$header .=
"<wsa:ReplyTo wsu:Id=\"ReplyTo\"><wsa:Address>$WSRF::Constants::WSA_ANON</wsa:Address></wsa:ReplyTo>";
		@_ = ( @_, SOAP::Header->value($header)->type('xml') );
	}

	my $response = $self->transport->send_receive(
		context  => $self,             # this is provided for context
		endpoint => $self->endpoint,
		action   =>
		  scalar( $self->on_action->( $serializer->uriformethod( $_[0] ) ) ),

		# leave only parameters so we can later update them if required
		envelope => $serializer->envelope( method => shift, @_ ),

		#    envelope => $serializer->envelope(method => shift, @_),
		encoding => $serializer->encoding,
		parts => @{ $self->packager->parts } ? $self->packager->parts : undef,
	);

	#BUG fix by Luke AT yahoo.com
	#return $response if $self->outputxml;
	# if ( $self->outputxml ) { $self->destroy_context(); return $response; }

	# deserialize and store result
	my $result = $self->{'_call'} =
	  eval { $self->deserializer->deserialize($response) }
	  if $response;

	if (
		!$self->transport->is_success ||    # transport fault
		$@                            ||    # not deserializible
		                                    # fault message even if transport OK
		  # or no transport error (for example, fo TCP, POP3, IO implementations)
		UNIVERSAL::isa( $result => 'SOAP::SOM' ) && $result->fault
	  )
	{
		return $self->{'_call'} =
		  ( $self->on_fault->( $self, $@ ? $@ . ( $response || '' ) : $result )
			|| $result );
	}

	return unless $response;    # nothing to do for one-ways

	# little bit tricky part that binds in/out parameters
	if (    UNIVERSAL::isa( $result => 'SOAPSOM' )
		 && ( $result->paramsout || $result->headers )
		 && $serializer->signature )
	{
		my $num = 0;
		my %signatures = map { $_ => $num++ } @{ $serializer->signature };
		for ( $result->dataof(SOAP::SOM::paramsout),
			  $result->dataof(SOAP::SOM::headers) )
		{
			my $signature = join $;, $_->name, $_->type || '';
			if ( exists $signatures{$signature} ) {
				my $param = $signatures{$signature};
				my ($value) = $_->value;    # take first value
				UNIVERSAL::isa( $_[$param] => 'SOAP::Data' )
				  ? $_[$param]->SOAP::Data::value($value)
				  : UNIVERSAL::isa( $_[$param] => 'ARRAY' )
				  ? ( @{ $_[$param] } = @$value )
				  : UNIVERSAL::isa( $_[$param] => 'HASH' )
				  ? ( %{ $_[$param] } = %$value )
				  : UNIVERSAL::isa( $_[$param] => 'SCALAR' )
				  ? ( ${ $_[$param] } = $$value )
				  : ( $_[$param] = $value );
			}
		}
	}
	$self->destroy_context();

    if ( $self->outputxml ) {
      return ($result, $response);
    } else {
	  return $result;
    }
}    # end of call()

# ======================================================================

package WSRF::WSS;

%WSRF::WSS::ASNMTAP = ();
$WSRF::WSS::ASNMTAP{UsernameToken}    = undef;
$WSRF::WSS::ASNMTAP{SAML}             = undef;
$WSRF::WSS::ASNMTAP{Assertion}        = undef;
$WSRF::WSS::ASNMTAP{SAMLAssertionID}  = undef;

%WSRF::WSS::ID = (); 
$WSRF::WSS::ID{X509Token} = "X509Token-" . time(); 
$WSRF::WSS::ID{TimeStamp} = "TimeStamp-" . time(); 
$WSRF::WSS::ID{myBody} = "myBody-" . time(); 

%WSRF::WSS::Sign                      = ();
$WSRF::WSS::Sign{BinarySecurityToken} = 1;
$WSRF::WSS::Sign{Timestamp}           = 1;
$WSRF::WSS::Sign{MessageID}           = 1;
$WSRF::WSS::Sign{To}                  = 1;
$WSRF::WSS::Sign{Action}              = 1;
$WSRF::WSS::Sign{From}                = 1;
$WSRF::WSS::Sign{RelatesTo}           = 1;
$WSRF::WSS::Sign{ReplyTo}             = 1;
$WSRF::WSS::Sign{Body}                = 1;

%WSRF::WSS::ID_Xpath = ();

#XPaths to the parts of the SOAP message we want to sign
$WSRF::WSS::sec_xpath = 
	  '(//. | //@* | //namespace::*)[ancestor-or-self::wsse:BinarySecurityToken]';

#$WSRF::WSS::sec_xpath = 
#	  '<XPath xmlns:wsse="' 
#	. $WSRF::Constants::WSSE
#	. '">(//. | //@* | //namespace::*)[ancestor-or-self::wsse:BinarySecurityToken]</XPath>';

$WSRF::WSS::si_xpath = 
#	'<XPath xmlns:ds="' . $WSRF::Constants::DS . '">(//. | //@* | //namespace::*)[ancestor-or-self::ds:SignedInfo]</XPath>';
	'(//. | //@* | //namespace::*)[ancestor-or-self::ds:SignedInfo]';
$WSRF::WSS::timestamp_xpath = 
#	  '<XPath xmlns:wsu="' 
#	. $WSRF::Constants::WSU 
#	. '">(//. | //@* | //namespace::*)[ancestor-or-self::wsu:Timestamp]</XPath>';
	'(//. | //@* | //namespace::*)[ancestor-or-self::wsu:Timestamp]';

$WSRF::WSS::ID_Xpath{MessageID} =
#  '<XPath xmlns:wsa="'
#  . $WSRF::Constants::WSA 
#  . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsa:MessageID]</XPath>';
   '(//. | //@* | //namespace::*)[ancestor-or-self::wsa:MessageID]';

$WSRF::WSS::ID_Xpath{To} = 
#  '<XPath xmlns:wsa="'
#  . $WSRF::Constants::WSA 
#  . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsa:To]</XPath>';
   '(//. | //@* | //namespace::*)[ancestor-or-self::wsa:To]';

$WSRF::WSS::ID_Xpath{Action} =
#  '<XPath xmlns:wsa="'
#  . $WSRF::Constants::WSA 
#  . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsa:Action]</XPath>'; 
  '(//. | //@* | //namespace::*)[ancestor-or-self::wsa:Action]';

$WSRF::WSS::ID_Xpath{From} = 
#  '<XPath xmlns:wsa="'
#   . $WSRF::Constants::WSA
#   . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsa:From]</XPath>';
   '(//. | //@* | //namespace::*)[ancestor-or-self::wsa:From]';

$WSRF::WSS::ID_Xpath{ReplyTo} =
#  '<XPath xmlns:wsa="'
#  . $WSRF::Constants::WSA
#  . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsa:ReplyTo]</XPath>';
  '(//. | //@* | //namespace::*)[ancestor-or-self::wsa:ReplyTo]';

$WSRF::WSS::ID_Xpath{RelatesTo} =
#  '<XPath xmlns:wsa="'
#  . $WSRF::Constants::WSA 
#  . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsa:RelatesTo]</XPath>';
  '(//. | //@* | //namespace::*)[ancestor-or-self::wsa:RelatesTo]';

$WSRF::WSS::body_xpath =
#"<XPath xmlns:$SOAP::Constants::PREFIX_ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">"
#  . '(//. | //@* | //namespace::*)'
#  . "[ancestor-or-self::$SOAP::Constants::PREFIX_ENV:Body]</XPath>";
  '(//. | //@* | //namespace::*)' . "[ancestor-or-self::$SOAP::Constants::PREFIX_ENV:Body]";

$WSRF::WSS::priv_key = undef;
$WSRF::WSS::pub_key  = undef;

sub load_priv_key {

	if ( defined($WSRF::WSS::priv_key) ) {
		if ( ref($WSRF::WSS::priv_key) eq 'CODE' ) {
			return $WSRF::WSS::priv_key->();
		} else {
			return $WSRF::WSS::priv_key;
		}
	}

	eval { require Crypt::OpenSSL::RSA };
	die "Failed to access class Crypt::OpenSSL::RSA: $@" if $@;

	my $key_file_name =
	  $ENV{HTTPS_KEY_FILE} ? $ENV{HTTPS_KEY_FILE} : die "No Private Key\n";
	open( PRIVKEY, $key_file_name )
	  || die("Could not open file $key_file_name");
	my $privkey = join "", <PRIVKEY>;
	close(PRIVKEY);
	Crypt::OpenSSL::RSA->new_private_key($privkey);
}

#returns the cert block between the begin and end delimiters
sub load_cert {

	if ( defined($WSRF::WSS::pub_key) ) {
		if ( ref($WSRF::WSS::pub_key) eq 'CODE' ) {
			return $WSRF::WSS::pub_key->();
		} else {
			return $WSRF::WSS::pub_key;
		}
	}

	my $cert_file_name =
	  $ENV{HTTPS_CERT_FILE} ? $ENV{HTTPS_CERT_FILE} : die "No Public Key\n";
	open( CERT, $cert_file_name )
	  || die("Could not open certificate file $cert_file_name");
	my $start = 0;
	my $cert  = "";
	while (<CERT>) {
		if ( !m/-----END CERTIFICATE-----/ && $start == 1 ) {
			$cert = $cert . $_;
		}
		if (/-----BEGIN CERTIFICATE-----/) {
			$start = 1;
		}
	}
	close(CERT);
	return $cert;
}

sub sign {
	my $envelope = shift;

	eval { require XML::LibXML };
	die "Failed to access class XML::LibXML: $@" if $@;
	eval { require MIME::Base64 };
	die "Failed to access class MIME::Base64: $@" if $@;

	#Get Certificate
	my $certificate = WSRF::WSS::load_cert();

	my $header = "";

	my $for_signing =
	    '<ds:SignedInfo xmlns:ds="' . $WSRF::Constants::DS . '">'
	  . '<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />'
	  . '<ds:SignatureMethod Algorithm="' . $WSRF::Constants::DS . 'rsa-sha1"/>';

	#search through the envelope for things to sign
	foreach my $key ( keys(%WSRF::WSS::ID_Xpath) ) {
		next unless (defined $WSRF::WSS::ID_Xpath{$key});
		$for_signing .=
		  WSRF::WSS::make_token( $envelope, $WSRF::WSS::ID_Xpath{$key}, $key )
		  if defined( $WSRF::WSS::Sign{$key} );
		my $parser = XML::LibXML->new();
		my $doc    = $parser->parse_string($envelope);
		my $canon = undef;
		eval {$canon  = $doc->toStringEC14N( 0, $WSRF::WSS::ID_Xpath{$key}, [''] );};
		$header .= defined($canon) ? $canon : "";
	}

	$for_signing .=
	  WSRF::WSS::make_token( $envelope, $WSRF::WSS::body_xpath, $WSRF::WSS::ID{myBody}  )
	  if defined( $WSRF::WSS::Sign{Body} );

	#create a security token using the certificate
	my $sec_token =
'<wsse:BinarySecurityToken xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="' . $WSRF::WSS::ID{X509Token} . '">'
	  . $certificate
	  . '</wsse:BinarySecurityToken>';
	if (    defined( $WSRF::WSS::Sign{BinarySecurityToken} )
		 && defined($WSRF::WSS::sec_xpath) )
	{
		$for_signing .=
		  WSRF::WSS::make_token( $sec_token, $WSRF::WSS::sec_xpath,
								 $WSRF::WSS::ID{X509Token} );
	}

	#create a timestamp
	my $timestamp = '';
	if ( defined($WSRF::WSS::timestamp_xpath) ) {
		$timestamp =
'<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="' . $WSRF::WSS::ID{TimeStamp} . '">';
		$timestamp .=
		    '<wsu:Created>'
		  . WSRF::Time::ConvertEpochTimeToString(time)
		  . '</wsu:Created>';
		$timestamp .=
		    '<wsu:Expires>'
		  . WSRF::Time::ConvertEpochTimeToString( time + ($WSRF::TIME::EXPIRES_IN ? $WSRF::TIME::EXPIRES_IN : 60))
		  . '</wsu:Expires>';

		#$timestamp .= '<wsu:Created>2004-02-07T14:31:59Z</wsu:Created>';
		#$timestamp .= '<wsu:Expires>2006-02-07T14:36:59Z</wsu:Expires>';
		$timestamp .= '</wsu:Timestamp>';

		#canonicalize,digest + Base64 the timestamp
		$for_signing .=
		  WSRF::WSS::make_token( $timestamp, $WSRF::WSS::timestamp_xpath,
								 $WSRF::WSS::ID{TimeStamp} )
		  if defined( $WSRF::WSS::Sign{Timestamp} );
	}

	$for_signing .= '</ds:SignedInfo>';

	my $parser          = XML::LibXML->new();
	my $doc             = $parser->parse_string($for_signing);
	my $can_signed_info = $doc->toStringEC14N( 0, $WSRF::WSS::si_xpath, [''] );

#   print ">>>can_signed>>>>".MIME::Base64::encode(sha1($can_signed_info))."<<<<<can_aigned<<<<<\n";
#   print ">>>can_signed_info>>>>\n$can_signed_info\n<<<<<can_signed_info<<<<<\n";

	my $rsa_priv  = WSRF::WSS::load_priv_key();
	my $signature = $rsa_priv->sign($can_signed_info);
	$signature = MIME::Base64::encode($signature);

  my $sec_token_reference = '<wsse:Reference  ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="#' . $WSRF::WSS::ID{X509Token} . '"/>';

  if ( defined $WSRF::WSS::ASNMTAP{Assertion} and $WSRF::WSS::ASNMTAP{SAMLAssertionID} ) {
    $sec_token = $WSRF::WSS::ASNMTAP{Assertion};
    $WSRF::WSS::ASNMTAP{Assertion} =~ $WSRF::WSS::ASNMTAP{SAMLAssertionID};
    $sec_token_reference = '<wsse:KeyIdentifier  ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID">' . ( defined $1 ? $1 : '?' ) . '</wsse:KeyIdentifier>';
  }

	my $extraheader =
'<wsse:Security xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">'
	  . $sec_token . "\n"
	  . '<ds:Signature xmlns:ds="' . $WSRF::Constants::DS . '">' 
	  . $can_signed_info . '<ds:SignatureValue>' 
	  . $signature . '</ds:SignatureValue><ds:KeyInfo>' 
    . '<wsse:SecurityTokenReference>' . $sec_token_reference . '</wsse:SecurityTokenReference>'
    . '</ds:KeyInfo></ds:Signature>';

	$extraheader .= $WSRF::WSS::ASNMTAP{UsernameToken} if ( $WSRF::WSS::ASNMTAP{UsernameToken} );

	  if ( defined($WSRF::WSS::timestamp_xpath) ) {
		$extraheader .= $timestamp;
	}
	$extraheader .= '</wsse:Security>';
	$header = $extraheader . $header;

	$doc = $parser->parse_string($envelope);
  my $Body = $doc->toStringEC14N( 0, $WSRF::WSS::body_xpath, ((defined $WSRF::WSS::ASNMTAP{SAML}) ? ['saml', 'samlp'] : ['']));
	# TODO: replace ['saml', 'samlp'] with the array created from the content of $WSRF::WSS::ASNMTAP{SAML}!!!
	#my $Body = $doc->toStringEC14N( 0, $WSRF::WSS::body_xpath, [''] );
	#my $Body = $doc->toStringC14N(0,$WSRF::WSS::body_xpath);
	
	#print ">>>header newline body>>>>\n$header\n\n$Body\n<<<<<header newline body<<<<<\n";
	return $header, $Body;
}

sub make_token {
	my ( $XML, $Path, $ID ) = @_;

	eval { require XML::LibXML };
	die "Failed to access class XML::LibXML: $@" if $@;
	eval { require Digest::SHA1 };
	die "Failed to access class Digest::SHA1: $@" if $@;
	eval { require MIME::Base64 };
	die "Failed to access class MIME::Base64: $@" if $@;

	#   print "make_token $ID\n";
	#   print "Xpath=> $Path\n";
	my $parser    = XML::LibXML->new();
	my $doc       = $parser->parse_string($XML);
	my $can_token = undef;
	eval {$can_token = $doc->toStringEC14N( 0, $Path, [''] );};
	return '' unless $can_token;

#	print ">>>token-$ID>>>\n$can_token\n<<<token-$ID<<<<\n";

	#take digest of token
	my $token_digest = Digest::SHA1::sha1($can_token);

	#base64 encode digest
	$token_digest = MIME::Base64::encode($token_digest);
	chomp($token_digest);

#print ">>>>token-$ID-digest>>>".$token_digest."<<<token-$ID-digest<<<<\n";

	return '<ds:Reference URI="#' . $ID . '">'
	  . '<ds:Transforms>'
	  . '<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>'
	  #. '</ds:Transform>'
	  . '</ds:Transforms>'
	  . '<ds:DigestMethod Algorithm= "' . $WSRF::Constants::DS . 'sha1"/>'
	  . '<ds:DigestValue>'
	  . $token_digest
	  . '</ds:DigestValue>'
	  . '</ds:Reference>';

}

%WSRF::WSS::ThingsThatShouldBeSigned = ();

$WSRF::WSS::ThingsThatShouldBeSigned{Body} = $SOAP::Constants::NS_ENV;
$WSRF::WSS::Xpath{Body}                    = $WSRF::WSS::body_xpath;

$WSRF::WSS::ThingsThatShouldBeSigned{To} = $WSRF::Constants::WSA;
$WSRF::WSS::Xpath{To}                    = $WSRF::WSS::ID_Xpath{To};

$WSRF::WSS::ThingsThatShouldBeSigned{MessageID} = $WSRF::Constants::WSA;
$WSRF::WSS::Xpath{MessageID} = $WSRF::WSS::ID_Xpath{MessageID};

$WSRF::WSS::ThingsThatShouldBeSigned{ReplyTo} = $WSRF::Constants::WSA;
$WSRF::WSS::Xpath{ReplyTo}                    = $WSRF::WSS::ID_Xpath{ReplyTo};

$WSRF::WSS::ThingsThatShouldBeSigned{Action} = $WSRF::Constants::WSA;
$WSRF::WSS::Xpath{Action}                    = $WSRF::WSS::ID_Xpath{Action};

$WSRF::WSS::ThingsThatShouldBeSigned{Timestamp} = $WSRF::Constants::WSU;
$WSRF::WSS::Xpath{Timestamp}                    = $WSRF::WSS::timestamp_xpath;

$WSRF::WSS::ThingsThatShouldBeSigned{BinarySecurityToken} =
  $WSRF::Constants::WSSE;
$WSRF::WSS::Xpath{BinarySecurityToken} = $WSRF::WSS::sec_xpath;

$WSRF::WSS::ThingsThatShouldBeSigned{From} = $WSRF::Constants::WSA;
$WSRF::WSS::Xpath{From}                    = $WSRF::WSS::ID_Xpath{From};

$WSRF::WSS::ThingsThatShouldBeSigned{RelatesTo} = $WSRF::Constants::WSA;
$WSRF::WSS::Xpath{RelatesTo} = $WSRF::WSS::ID_Xpath{RelatesTo};

sub verify {
	my $envelope = shift;

	eval { require XML::LibXML };
	die "Failed to access class XML::LibXML: $@" if $@;
	eval { require Digest::SHA1 };
	die "Failed to access class Digest::SHA1: $@" if $@;
	eval { require Crypt::OpenSSL::RSA };
	die "Failed to access class Crypt::OpenSSL::RSA: $@" if $@;
	eval { require Crypt::OpenSSL::X509 };
	die "Failed to access class Crypt::OpenSSL::X509: $@" if $@;
	eval { require MIME::Base64 };
	die "Failed to access class MIME::Base64: $@" if $@;

	my %results = ();

	#get Security Token
	my $Token =
	  $envelope->match(
		"/Envelope/Header/Security/{$WSRF::Constants::WSSE}BinarySecurityToken")
	  ? $envelope->valueof(
		"/Envelope/Header/Security/{$WSRF::Constants::WSSE}BinarySecurityToken")
	  : die "WSRF::WSS::verify Fault - No Security Token in SOAP Header\n";

    $Token =~ s/\s+$//;
	$Token =
	  "-----BEGIN CERTIFICATE-----\n" . $Token . "\n-----END CERTIFICATE-----";

	#   print ">>>>Token>>>\n$Token\n<<<<Token<<<<<\n";

#create an X509 object from the string - this will die if it is not an X509 cert
	my $x509 = Crypt::OpenSSL::X509->new_from_string($Token);

	#if we get here then $Token IS a X509 cert
	$results{X509} = $Token;

	my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key( $x509->pubkey() );

	#get the piece of XML that has been signed
	my $parser          = XML::LibXML->new();
	my $doc             = $parser->parse_string( $envelope->raw_xml );
	my $can_signed_info = $doc->toStringEC14N( 0, $WSRF::WSS::si_xpath, [''] );

	#get the Signature value
	my $SignatureValue =
	  $envelope->match(
		 "/Envelope/Header//{$WSRF::Constants::DS}SignatureValue")
	  ? $envelope->valueof(
		 "/Envelope/Header//{$WSRF::Constants::DS}SignatureValue")
	  : die "WSRF::WSS::verify Fault - No Signature Value in SOAP Header\n";

	$SignatureValue = MIME::Base64::decode($SignatureValue);

	if ( $rsa_pub->verify( $can_signed_info, $SignatureValue ) ) {
		$results{Signed} = 'true';

		#print STDERR "WSRF::WSS::verify Message Signature is Correct\n";
	} else {
		die "WSRF::WSS::verify Fault - Message Signature is NOT Correct\n";
	}

	my $i           = 1;
	my %SignedStuff = ();
	while (
		 $envelope->match("/Envelope/Header/Security/Signature/SignedInfo/[$i]")
	  )
	{
		my $data =
		  $envelope->dataof(
						 "/Envelope/Header/Security/Signature/SignedInfo/[$i]");
		if ( $data->name eq "Reference" ) {
			my $attr        = $data->attr;
			my $name        = $attr->{URI};
			my $DigestValue =
			  $envelope->match(
"/Envelope/Header/Security/Signature/SignedInfo/[$i]//{$WSRF::Constants::DS}DigestValue"
			  )
			  ? $envelope->valueof(
"/Envelope/Header/Security/Signature/SignedInfo/[$i]//{$WSRF::Constants::DS}DigestValue"
			  )
			  : die "WSRF::WSS::verify No DigestValue for $name";

#strip the # that is part of the XLink stuff for pointing to other parts of the XML doc
			$name =~ s/^\#//o;
			$SignedStuff{$name} = $DigestValue;
		}
		$i++;
	}

	my %Signed = ();
	foreach my $key ( keys %WSRF::WSS::ThingsThatShouldBeSigned ) {
		if (
			 $envelope->match(
				  "/Envelope//{$WSRF::WSS::ThingsThatShouldBeSigned{$key}}$key")
		  )
		{
			my $data =
			  $envelope->dataof(
				 "/Envelope//{$WSRF::WSS::ThingsThatShouldBeSigned{$key}}$key");
			my $attr = $data->attr;
			my $ID   = $attr->{"{$WSRF::Constants::WSU}Id"};
			$Signed{$key} = $ID;
		}
	}

	foreach my $key ( keys %Signed ) {
		my $parser        = XML::LibXML->new();
		my $doc           = $parser->parse_string( $envelope->raw_xml );
		my $CanonicalForm =
		  $doc->toStringEC14N( 0, $WSRF::WSS::Xpath{$key}, [''] );
		die "Could not get the Canonicalize $key from Envelope\n"
		  unless $CanonicalForm;
		my $token_digest = Digest::SHA1::sha1($CanonicalForm);
		$token_digest = MIME::Base64::encode($token_digest);
		chomp($token_digest);
		if ( $SignedStuff{ $Signed{$key} } eq $token_digest ) {

			#print "WSRF::WSS::verify Message \"$key\" is signed\n";
			$results{PartsSigned}{$key} = 'true';
		} else {
			die "WSRF::WSS::verify $key digest hashs do not match\n";
		}
	}

	$results{Created} =
	  $envelope->match(
		   "/Envelope/Header/Security/Timestamp/{$WSRF::Constants::WSU}Created")
	  ? $envelope->valueof(
		   "/Envelope/Header/Security/Timestamp/{$WSRF::Constants::WSU}Created")
	  : undef;

#print STDERR "WSRF::WSS::verify Message Created at $results{Created} (should be GMT)\n" if $results{Created};

	$results{Expires} =
	  $envelope->match(
		   "/Envelope/Header/Security/Timestamp/{$WSRF::Constants::WSU}Expires")
	  ? $envelope->valueof(
		   "/Envelope/Header/Security/Timestamp/{$WSRF::Constants::WSU}Expires")
	  : undef;

#print STDERR "WSRF::WSS::verify Message Expires at \"$results{Expires}\" (should be GMT)\n" if  $results{Expires};

	return %results;
}

1;