--- /dev/null
+package Net::Soma;
+
+use strict;
+use vars qw($DEBUG $VERSION @uris $AUTOLOAD %schemas);
+use SOAP::Lite 0.71 #+trace => debug, objects
+;
+
+$VERSION = '0.01';
+
+$DEBUG = 0;
+
+@uris = qw( CPECollection AppCatalog AdminService CPESearch Version Applications
+ CPEAccess ApplicationsV2
+ );
+
+=head1 NAME
+
+Net::Soma - Perl client interface to SOMA iWireless platform
+
+=head1 SYNOPSIS
+
+use Net::Soma;
+use Net::Soma qw( AttributeInstance FeatureInstance ApplicationInstance
+ ChoiceItem AttributeDef FeatureDef ApplicationDef
+ ApplicationDefV2 FeatureDefV2 AttributeDefV2 CPEInfoDefV2
+ CPESearchStruct CPESearchResult CPEInfo CPEInfoDef
+ HardwarePort
+ NoSuchCPEException DataAccessException InternalFault
+ BadAppParameterException BadAppParameterExceptionV2
+ NoSuchAppException NoSuchFeatureException
+ NoSuchAttributeException BadCPEParameterException
+ ActiveApplicationsException );
+
+$soma = new Net::Soma { url => 'https://soma.example.net:8088/ossapi/services',
+ namespace => 'AppCatalog',
+ }
+
+$err_or_som = $soma->getApplicationDefinitions();
+
+if (ref($err_or_som)){
+ my $result = $err_or_som->result;
+ foreach my $definition (@$result) {
+ print $definition->name, "\n";
+ }
+}else{
+ print "$err_or_som\n";
+}
+
+=head1 DESCRIPTION
+
+Net::Soma is a module implementing a Perl interface to SOMA's iWireless
+SOAP interface (ossapi). It is compatible with release 1.5 of that software
+and requires the WSDLs from SOMA.
+
+Net::Soma enables you to simply access the SOAP interface of your SOMA
+networks softair platform server.
+
+=head1 BASIC USAGE
+
+Import the Net::Soma module with
+
+use Net::Soma (@list_of_classes);
+
+Net::Soma will create any of the following classes for you
+
+AttributeInstance FeatureInstance ApplicationInstance ChoiceItem
+AttributeDef FeatureDef ApplicationDef ApplicationDefV2 FeatureDefV2
+AttributeDefV2 CPEInfoDefV2 CPESearchStruct CPESearchResult CPEInfo
+CPEInfoDef HardwarePort NoSuchCPEException DataAccessException InternalFault
+BadAppParameterException BadAppParameterExceptionV2 NoSuchAppException
+NoSuchFeatureException NoSuchAttributeException BadCPEParameterException
+ActiveApplicationsException
+
+=cut
+
+sub import {
+ my $class = shift;
+ my @classes = @_;
+ my $me = __PACKAGE__;
+ my @classlist = qw( AttributeInstance FeatureInstance ApplicationInstance
+ ChoiceItem AttributeDef FeatureDef ApplicationDef
+ ApplicationDefV2 FeatureDefV2 AttributeDefV2 CPEInfoDefV2
+ CPESearchStruct CPESearchResult CPEInfo CPEInfoDef
+ HardwarePort
+ NoSuchCPEException DataAccessException InternalFault
+ BadAppParameterException BadAppParameterExceptionV2
+ NoSuchAppException NoSuchFeatureException
+ NoSuchAttributeException BadCPEParameterException
+ ActiveApplicationsException
+ );
+ my (%EXPORT_OK) = map { $_ => 1 } @classlist;
+
+ {
+ no strict 'refs'; #hmmm force 'use' of all to be serialized?
+ foreach my $class (@classlist) {
+
+ *{"SOAP::Serializer::as_$class"} = sub {
+ my ($self, $value, $name, $type, $attr) = @_;
+
+ $self->register_ns("urn:ossapi.services.core.soma.com", "netsoma");
+ $self->encode_object( \SOAP::Data->value(
+ SOAP::Data->name($name => map { SOAP::Data->name($_ => $value->{$_}) }
+ keys %$value
+ )
+ ), $name, $type, {'xsi:type' => "netsoma:$class", %$attr});
+ };
+
+ *{"SOAP::Serializer::as_ArrayOf$class"} = sub {
+ my ($self, $value, $name, $type, $attr) = @_;
+
+ $self->register_ns("urn:ossapi.services.core.soma.com", "netsoma");
+ if (@$value) {
+ $self->encode_object( \SOAP::Data->value(
+ SOAP::Data->name($name => map { SOAP::Data->name(item => $_) }
+ @$value
+ )
+ ), $name, $type, {'xsi:type' => "netsoma:ArrayOf$class", %$attr});
+ } else {
+ $self->encode_object( [], $name, $type, {'xsi:type' => "netsoma:ArrayOf$class", %$attr});
+ }
+
+ };
+
+ }
+
+ }
+
+ foreach $class ( map { $_, "ArrayOf$_" }
+ grep { exists( $EXPORT_OK{$_} )
+ or die "$_ is not exported by module $me"
+ }
+ @classes)
+ {
+ no strict 'refs';
+
+ *{"$class\::NEW"} = sub {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = { @_ };
+ return bless($self, $class);
+ };
+ *{"$class\::AUTOLOAD"} = sub {
+ my $field = $AUTOLOAD;
+ $field =~ s/.*://;
+ return if $field eq 'DESTROY';
+ if ( defined($_[1]) ) {
+ $_[0]->{$field} = $_[1];
+ } else {
+ $_[0]->{$field};
+ }
+ };
+ }
+
+ # $me =~ s/::/\//g;
+ # $INC{"$me.pm"} =~ /^(.*)\.pm$/;
+ # $me = $1;
+ # for (@uris){
+ # $schemas{$_."Service"} = SOAP::Schema
+ # ->schema_url("file:$me/wsdls/$_.wsdl")
+ # ->parse->services->{$_."Service"};
+ # }
+
+}
+
+=head1 CONSTRUCTOR
+
+=over 4
+
+=item new HASHREF
+
+Creates a new Soma object. HASHREF should contain the keys url and namespace
+for the URL of the Soma SOAP proxy and the namespace of the methods you would
+like to call. You may optionally define the key die_on_fault to cause that
+behavior for methods.
+
+=cut
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = { @_ };
+
+ for (@uris){
+ $schemas{$_."Service"} = SOAP::Schema
+ ->schema_url($self->{url}."$_?wsdl")
+ ->parse->services->{$_."Service"};
+ }
+
+ return bless($self, $class);
+}
+
+=head1 METHODS
+
+All Soma methods may be invoked as methods of the Net::Soma object.
+The return value is either the fault string in the event of an error
+or a SOAP::SOM object.
+
+If the option die_on_fault was set for the Net::Soma object, then
+instead the method dies on error and returns the result component
+of the SOAP::SOM object on success.
+
+=cut
+
+sub AUTOLOAD {
+ my $self = shift; #hmmm... test this?
+
+ my $method = $AUTOLOAD;
+ $method =~ s/.*://;
+ return if $method eq 'DESTROY';
+
+ my $nscount = 1;
+ my $uri = $self->{namespace};
+ $uri =~ s/Service$//;
+ my $soap = SOAP::Lite
+ -> autotype(1)
+ -> readable(1)
+ -> uri($uri)
+ -> proxy($self->{url});
+
+ $soap->serializer(Net::Soma::Serializer->rebless($soap->serializer));
+
+# local *SOAP::Transport::HTTP::Client::get_basic_credentials = sub {
+# return $self->{user} => $self->{password};
+# };
+
+ my $param = 0;
+ my $som =
+ $soap->$method( map {
+ my $paramdata =
+ $schemas{$self->{namespace}}{$method}{'parameters'}[$param++];
+ my ($pre,$type) = SOAP::Utils::splitqname($paramdata->type);
+ SOAP::Data->name($paramdata->name => $_ )
+ ->type(${[SOAP::Utils::splitqname($paramdata->type)]}[1]) } @_
+ );
+
+ if ($som) {
+ if ($som->fault){
+ if ($self->{die_on_fault}){
+ die $som->faultstring;
+ } else {
+ return $som->faultstring;
+ }
+ }else{
+ if ($self->{die_on_fault}){
+ return $som->result;
+ } else {
+ return $som;
+ }
+ }
+ }
+
+ die "Net::Soma failed to $method for $self->{namespace} at " . $self->{url};
+}
+
+
+=back
+
+=head1 SEE ALSO
+
+ SOAP::Lite, SOAP::SOM
+
+ http://www.somanetworks.com/ for information about SOMA and iWireless.
+
+ http://www.sisd.com/freeside/ for the ISP billing and provisioning system
+ which provoked the need for this module.
+
+=head1 BUGS
+
+Namespace promiscuous.
+Lax handling of arguments and return values.
+In fact, calling a bogus method with arguments causes complaints about
+accessing methods on undefined values (at line 233, paramdata->name)
+
+Quite probably others. Use at your own risk.
+
+=head1 AUTHOR AND COPYRIGHT
+
+Copyright (c) 2008 Jeff Finucane jeff-net-soma@weasellips.com
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+This software is neither authorized, sponsored, endorsed, nor supported
+by Soma Networks.
+
+=cut
+
+1;