/[cvs]/nfo/perl/libs/Data/Storage.pm
ViewVC logotype

Diff of /nfo/perl/libs/Data/Storage.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.4 by joko, Sun Oct 27 18:35:07 2002 UTC revision 1.10 by joko, Sat Dec 7 03:37:23 2002 UTC
# Line 4  Line 4 
4  #  #
5  # See COPYRIGHT section in pod text below for usage and distribution rights.  # See COPYRIGHT section in pod text below for usage and distribution rights.
6  #  #
7  #################################  ############################################
8  #  #
9  #  $Log$  #  $Log$
10    #  Revision 1.10  2002/12/07 03:37:23  joko
11    #  + updated pod
12    #
13    #  Revision 1.9  2002/12/01 22:15:45  joko
14    #  - sub createDb: moved to handler
15    #
16    #  Revision 1.8  2002/11/29 04:48:23  joko
17    #  + updated pod
18    #
19    #  Revision 1.7  2002/11/17 06:07:18  joko
20    #  + creating the handler is easier than proposed first - for now :-)
21    #  + sub testAvailability
22    #
23    #  Revision 1.6  2002/11/09 01:04:58  joko
24    #  + updated pod
25    #
26    #  Revision 1.5  2002/10/29 19:24:18  joko
27    #  - reduced logging
28    #  + added some pod
29    #
30  #  Revision 1.4  2002/10/27 18:35:07  joko  #  Revision 1.4  2002/10/27 18:35:07  joko
31  #  + added pod  #  + added pod
32  #  #
# Line 23  Line 43 
43  #  Revision 1.1  2002/10/10 03:43:12  cvsjoko  #  Revision 1.1  2002/10/10 03:43:12  cvsjoko
44  #  + new  #  + new
45  #  #
46  #################################  ############################################
47    
 # aim_V1: should encapsulate Tangram, DBI, DBD::CSV and LWP:: to access them in an unordinary way ;)  
 # aim_V2: introduce a generic layered structure, refactor *SUBLAYER*-stuff, make (e.g.) this possible:  
 #               - Perl Data::Storage[DBD::CSV]  ->  Perl LWP::  ->  Internet HTTP/FTP/*  ->  Host Daemon  ->  csv-file  
48    
49  BEGIN {  BEGIN {
50  $Data::Storage::VERSION = 0.01;    $Data::Storage::VERSION = 0.02;
51  }  }
52    
53    
54  =head1 NAME  =head1 NAME
55    
56  Data::Storage - Interface for accessing various Storage implementations for Perl in an independent way  Data::Storage - Interface for accessing various Storage implementations for Perl in an independent way
57    
58    
59    =head1 AIMS
60    
61      - should encapsulate Tangram, DBI, DBD::CSV and LWP:: to access them in an unordinary (more convenient) way ;)
62      - introduce a generic layered structure, refactor *SUBLAYER*-stuff, make (e.g.) this possible:
63        Perl Data::Storage[DBD::CSV]  ->  Perl LWP::  ->  Internet HTTP/FTP/*  ->  Host Daemon  ->  csv-file
64      - provide generic synchronization mechanisms across arbitrary/multiple storages based on ident/checksum
65        maybe it's possible to have schema-, structural- and semantical modifications synchronized???
66    
67    
68  =head1 SYNOPSIS  =head1 SYNOPSIS
69    
70    ... the basic way:  =head2 BASIC ACCESS
71    
72    =head2 ADVANCED ACCESS
73    
74    ... via inheritance:    ... via inheritance:
75        
# Line 58  Data::Storage - Interface for accessing Line 87  Data::Storage - Interface for accessing
87      $self->{storage}->insert($proxyObj);      $self->{storage}->insert($proxyObj);
88    
89    
90    =head2 SYNCHRONIZATION
91    
92      my $nodemapping = {
93        'LangText' => 'langtexts.csv',
94        'Currency' => 'currencies.csv',
95        'Country'  => 'countries.csv',
96      };
97    
98      my $propmapping = {
99        'LangText' => [
100          [ 'source:lcountrykey'  =>  'target:country' ],
101          [ 'source:lkey'         =>  'target:key' ],
102          [ 'source:lvalue'       =>  'target:text' ],
103        ],
104        'Currency' => [
105          [ 'source:ckey'         =>  'target:key' ],
106          [ 'source:cname'        =>  'target:text' ],
107        ],
108        'Country' => [
109          [ 'source:ckey'         =>  'target:key' ],
110          [ 'source:cname'        =>  'target:text' ],
111        ],
112      };
113    
114      sub syncResource {
115    
116        my $self = shift;
117        my $node_source = shift;
118        my $mode = shift;
119        my $opts = shift;
120        
121        $mode ||= '';
122        $opts->{erase} ||= 0;
123        
124        $logger->info( __PACKAGE__ . "->syncResource( node_source $node_source mode $mode erase $opts->{erase} )");
125      
126        # resolve metadata for syncing requested resource
127        my $node_target = $nodemapping->{$node_source};
128        my $mapping = $propmapping->{$node_source};
129        
130        if (!$node_target || !$mapping) {
131          # loggger.... "no target, sorry!"
132          print "error while resolving resource metadata", "\n";
133          return;
134        }
135        
136        if ($opts->{erase}) {
137          $self->_erase_all($node_source);
138        }
139      
140        # create new sync object
141        my $sync = Data::Transfer::Sync->new(
142          storages => {
143            L => $self->{bizWorks}->{backend},
144            R => $self->{bizWorks}->{resources},
145          },
146          id_authorities        =>  [qw( L ) ],
147          checksum_authorities  =>  [qw( L ) ],
148          write_protected       =>  [qw( R ) ],
149          verbose               =>  1,
150        );
151        
152        # sync
153        # todo: filter!?
154        $sync->syncNodes( {
155          direction       =>  $mode,                 # | +PUSH | +PULL | -FULL | +IMPORT | -EXPORT
156          method          =>  'checksum',            # | -timestamp | -manual
157          source          =>  "L:$node_source",
158          source_ident    =>  'storage_method:id',
159          source_exclude  =>  [qw( id cs )],
160          target          =>  "R:$node_target",
161          target_ident    =>  'property:oid',
162          mapping         =>  $mapping,
163        } );
164    
165      }
166    
167    
168  =head2 NOTE  =head2 NOTE
169    
170  This module heavily relies on DBI and Tangram, but adds a lot of additional bugs and quirks.  This module heavily relies on DBI and Tangram, but adds a lot of additional bugs and quirks.
171  Please look at their documentation and this code for additional information.  Please look at their documentation and/or this code for additional information.
172    
173    
174    =head1 REQUIREMENTS
175    
176      For full functionality:
177        DBI              from CPAN
178        DBD::mysql       from CPAN
179        Tangram 2.04     from CPAN         (hmmm, 2.04 won't do in some cases)
180        Tangram 2.05     from http://...   (2.05 seems okay but there are also additional patches from our side)
181        Class::Tangram   from CPAN
182        DBD::CSV         from CPAN
183        MySQL::Diff      from http://adamspiers.org/computing/mysqldiff/
184        ... and all their dependencies
185    
186  =cut  =cut
187    
# Line 75  use strict; Line 194  use strict;
194  use warnings;  use warnings;
195    
196  use Data::Storage::Locator;  use Data::Storage::Locator;
197    use Data::Dumper;
198    
199    # TODO: actually implement level (integrate with Log::Dispatch)
200    my $TRACELEVEL = 0;
201    
202  # get logger instance  # get logger instance
203  my $logger = Log::Dispatch::Config->instance;  my $logger = Log::Dispatch::Config->instance;
# Line 83  sub new { Line 206  sub new {
206    my $invocant = shift;    my $invocant = shift;
207    my $class = ref($invocant) || $invocant;    my $class = ref($invocant) || $invocant;
208    #my @args = normalizeArgs(@_);    #my @args = normalizeArgs(@_);
209      
210    my $arg_locator = shift;    my $arg_locator = shift;
211    my $arg_options = shift;    my $arg_options = shift;
212      
213    #my $self = { STORAGEHANDLE => undef, @_ };    #my $self = { STORAGEHANDLE => undef, @_ };
214    my $self = { STORAGEHANDLE => undef, locator => $arg_locator, options => $arg_options };    my $self = { STORAGEHANDLE => undef, locator => $arg_locator, options => $arg_options };
215    $logger->debug( __PACKAGE__ . "[$self->{locator}->{type}]" . "->new(@_)" );    #$logger->debug( __PACKAGE__ . "[$self->{locator}->{type}]" . "->new(@_)" );
216      $logger->debug( __PACKAGE__ . "[$arg_locator->{type}]" . "->new(@_)" );
217    return bless $self, $class;    return bless $self, $class;
218  }  }
219    
# Line 100  sub AUTOLOAD { Line 224  sub AUTOLOAD {
224    #     - Deep recursion on subroutine "Data::Storage::AUTOLOAD"    #     - Deep recursion on subroutine "Data::Storage::AUTOLOAD"
225    #     - Deep recursion on subroutine "Data::Storage::Handler::Abstract::AUTOLOAD"    #     - Deep recursion on subroutine "Data::Storage::Handler::Abstract::AUTOLOAD"
226    #     - Deep recursion on anonymous subroutine at [...]    #     - Deep recursion on anonymous subroutine at [...]
227    # we also might filter log messages caused by logging itself in "advanced logging of AUTOLOAD calls"    # we also might filter log messages caused by logging to itself in "advanced logging of AUTOLOAD calls"
228        
229    my $self = shift;    my $self = shift;
230    our $AUTOLOAD;    our $AUTOLOAD;
# Line 111  sub AUTOLOAD { Line 235  sub AUTOLOAD {
235    my $method = $AUTOLOAD;    my $method = $AUTOLOAD;
236    $method =~ s/^.*:://;    $method =~ s/^.*:://;
237    
238    # advanced logging of AUTOLOAD calls    # advanced logging of AUTOLOAD calls ...
239      my $logstring = "";    # ... nice but do it only when TRACING (TODO) is enabled
240      $logstring .= __PACKAGE__ . "[$self->{locator}->{type}]" . "->" . $method;      if ($TRACELEVEL) {
241      #print "count: ", $#_, "\n";        my $logstring = "";
242      #$logstring .= Dumper(@_) if ($#_ != -1);        $logstring .= __PACKAGE__ . "[$self->{locator}->{type}]" . "->" . $method;
243      my $tabcount = int( (80 - length($logstring)) / 10 );        #print "count: ", $#_, "\n";
244      $logstring .= "\t" x $tabcount . "(AUTOLOAD)";        #$logstring .= Dumper(@_) if ($#_ != -1);
245      # TODO: only ok if logstring doesn't contain        my $tabcount = int( (80 - length($logstring)) / 10 );
246      #            e.g. "Data::Storage[Tangram]->insert(SystemEvent=HASH(0x5c0034c))          (AUTOLOAD)"        $logstring .= "\t" x $tabcount . "(AUTOLOAD)";
247      # but that would be way too specific as long as we don't have an abstract handler for this  ;)        # TODO: only ok if logstring doesn't contain
248      $logger->debug( $logstring );        #            e.g. "Data::Storage[Tangram]->insert(SystemEvent=HASH(0x5c0034c))          (AUTOLOAD)"
249          # but that would be _way_ too specific as long as we don't have an abstract handler for this  ;)
250    # filtering AUTOLOAD calls        $logger->debug( $logstring );
251          #print join('; ', @_);
252        }
253        
254      # filtering AUTOLOAD calls and first-time-touch of the actual storage impl
255    if ($self->_filter_AUTOLOAD($method)) {    if ($self->_filter_AUTOLOAD($method)) {
256        #print "_accessStorage\n";
257      $self->_accessStorage();      $self->_accessStorage();
258      $self->{STORAGEHANDLE}->$method(@_);      $self->{STORAGEHANDLE}->$method(@_);
259    }    }
# Line 155  sub normalizeArgs { Line 284  sub normalizeArgs {
284  sub _accessStorage {  sub _accessStorage {
285    my $self = shift;    my $self = shift;
286    # TODO: to some tracelevel!    # TODO: to some tracelevel!
287    $logger->debug( __PACKAGE__ .  "[$self->{locator}->{type}]" . "->_accessStorage()" );    if ($TRACELEVEL) {
288        $logger->debug( __PACKAGE__ .  "[$self->{locator}->{type}]" . "->_accessStorage()" );
289      }
290    if (!$self->{STORAGEHANDLE}) {    if (!$self->{STORAGEHANDLE}) {
291      $self->_createStorageHandle();      $self->_createStorageHandle();
292    }    }
# Line 163  sub _accessStorage { Line 294  sub _accessStorage {
294    
295  sub _createStorageHandle {  sub _createStorageHandle {
296    my $self = shift;    my $self = shift;
   
297    my $type = $self->{locator}->{type};    my $type = $self->{locator}->{type};
298    $logger->debug( __PACKAGE__ .  "[$type]" . "->_createStorageHandle()" );    $logger->debug( __PACKAGE__ .  "[$type]" . "->_createStorageHandle()" );
299    
300    my $pkg = "Data::Storage::Handler::" . $type . "";    my $pkg = "Data::Storage::Handler::" . $type . "";
301        
302    # propagate args to handler    # try to load perl module at runtime
303    # needs some more thoughts! (not only "dbi" to Tangram, when (in future) db is not more the common case)    my $evalstr = "use $pkg;";
304    if ($type eq 'DBI') {    eval($evalstr);
305      use Data::Storage::Handler::DBI;    if ($@) {
306      #my @args = %{$self->{locator}->{dbi}};      $logger->error( __PACKAGE__ .  "[$type]" . "->_createStorageHandle(): $@" );
307      my @args = %{$self->{locator}};      return;
     # create new storage handle  
     $self->{STORAGEHANDLE} = $pkg->new( @args );  
   }  
   if ($type eq 'Tangram') {  
     use Data::Storage::Handler::Tangram;  
     #$self->{STORAGEHANDLE} = $pkg->new( dsn => $self->{locator}->{dbi}->{dsn} );  
     #my @args = %{$self->{locator}->{dbi}};  
     my @args = %{$self->{locator}};  
     # create new storage handle  
     $self->{STORAGEHANDLE} = $pkg->new( @args );  
   
     #$self->{STORAGEHANDLE_UNDERLYING} = $self->{STORAGEHANDLE}->getUnderlyingStorage();  
     #$self->{STORAGEHANDLE_UNDERLYING}->_configureCOREHANDLE();  
308    }    }
309        
310      # build up some additional arguments to pass on
311      #my @args = %{$self->{locator}};
312      my @args = ();
313    
314      # - create new storage handle object
315      # - propagate arguments to handler
316      # - pass locator by reference to be able to store status- or meta-information in it
317      $self->{STORAGEHANDLE} = $pkg->new( locator => $self->{locator}, @args );
318    
319  }  }
320    
321  sub addLogDispatchHandler {  sub addLogDispatchHandler {
# Line 221  sub addLogDispatchHandler { Line 347  sub addLogDispatchHandler {
347  }  }
348    
349  sub removeLogDispatchHandler {  sub removeLogDispatchHandler {
350      my $self = shift;
351        my $self = shift;    my $name = shift;
352        my $name = shift;    #my $logger = shift;
353        #my $logger = shift;    $logger->remove($name);
   
       $logger->remove($name);  
   
354  }  }
355    
356  sub getDbName {  sub getDbName {
# Line 245  sub testDsn { Line 368  sub testDsn {
368    if ( my $dbh = DBI->connect($dsn, '', '', {    if ( my $dbh = DBI->connect($dsn, '', '', {
369                                                        PrintError => 0,                                                        PrintError => 0,
370                                                        } ) ) {                                                        } ) ) {
371        
372        # TODO: REVIEW
373      $dbh->disconnect();      $dbh->disconnect();
374        
375      return 1;      return 1;
376    } else {    } else {
377      $logger->error( __PACKAGE__ .  "[$self->{locator}->{type}]" . "->testDsn(): " . "DBI-error: " . $DBI::errstr );      $logger->warning( __PACKAGE__ .  "[$self->{locator}->{type}]" . "->testDsn(): " . "DBI-error: " . $DBI::errstr );
378    }    }
379  }  }
380    
381  sub createDb {  sub testAvailability {
382    my $self = shift;    my $self = shift;
383    my $dsn = $self->{locator}->{dbi}->{dsn};    my $status = $self->testDsn();
384      $self->{locator}->{status}->{available} = $status;
385    $logger->debug( __PACKAGE__ .  "->createDb( dsn $dsn )" );    return $status;
   
   $dsn =~ s/database=(.+?);//;  
   my $database_name = $1;  
   
   my $ok;  
     
   if ( my $dbh = DBI->connect($dsn, '', '', {  
                                                       PrintError => 0,  
                                                       } ) ) {  
     if ($database_name) {  
       if ($dbh->do("CREATE DATABASE $database_name;")) {  
         $ok = 1;  
       }  
     }  
     $dbh->disconnect();  
   }  
     
   return $ok;  
     
386  }  }
387    
388    
389  sub dropDb {  sub dropDb {
390    my $self = shift;    my $self = shift;
391    my $dsn = $self->{locator}->{dbi}->{dsn};    my $dsn = $self->{locator}->{dbi}->{dsn};
# Line 297  sub dropDb { Line 405  sub dropDb {
405          $ok = 1;          $ok = 1;
406        }        }
407      }      }
408    
409      $dbh->disconnect();      $dbh->disconnect();
410    
411    }    }
412        
413    return $ok;    return $ok;
# Line 314  __END__ Line 424  __END__
424    
425  =head1 DESCRIPTION  =head1 DESCRIPTION
426    
427  Data::Storage is module for a accessing various "data structures" stored inside  Data::Storage is a module for accessing various "data structures" stored inside
428  various "data containers". It sits on top of DBI and/or Tangram.  various "data containers". It sits on top of DBI and/or Tangram.
429    
430    
# Line 329  License or the Artistic License, as spec Line 439  License or the Artistic License, as spec
439    
440  =head1 ACKNOWLEDGEMENTS  =head1 ACKNOWLEDGEMENTS
441    
442  Larry Wall and the C<perl5-porters> for Perl,  Larry Wall for Perl, Tim Bunce for DBI, Jean-Louis Leroy for Tangram and Set::Object,
443  Tim Bunce for DBI, Jean-Louis Leroy for Tangram and Set::Object,  Sam Vilain for Class::Tangram, Jochen Wiedmann and Jeff Zucker for DBD::CSV and related,
444  Sam Vilain for Class::Tangram.  Adam Spiers for MySQL::Diff and all contributors.
445    
446    
447  =head1 SUPPORT / WARRANTY  =head1 SUPPORT / WARRANTY
# Line 342  Data::Storage is free software. IT COMES Line 452  Data::Storage is free software. IT COMES
452  =head1 TODO  =head1 TODO
453    
454    
455  =head2 Handle the following errors/cases:  =head2 BUGS
456    
457  =head3 "DBI-Error [Tangram]: DBD::mysql::st execute failed: Unknown column 't1.requestdump' in 'field list'"  "DBI-Error [Tangram]: DBD::mysql::st execute failed: Unknown column 't1.requestdump' in 'field list'"
458    
459      ... occours when operating on object-attributes not introduced yet:    ... occours when operating on object-attributes not introduced yet:
460      this should be detected and appended/replaced through:    this should be detected and appended/replaced through:
461      "Schema-Error detected, maybe (just) an inconsistency.    "Schema-Error detected, maybe (just) an inconsistency.
462      Please check if your declaration in schema-module "a" matches structure in database "b" or try to run"    Please check if your declaration in schema-module "a" matches structure in database "b" or try to run"
463      db_setup.pl --dbkey=import --action=deploy    db_setup.pl --dbkey=import --action=deploy
464    
465  =head3 Compare schema (structure diff) with database ...  
466    Compare schema (structure diff) with database ...
467    
468    ... when issuing "db_setup.pl --dbkey=import --action=deploy"    ... when issuing "db_setup.pl --dbkey=import --action=deploy"
469    on a database with an already deployed schema, use an additional "--update" then    on a database with an already deployed schema, use an additional "--update" then
# Line 373  Data::Storage is free software. IT COMES Line 484  Data::Storage is free software. IT COMES
484      R retrieve  ->  no, not subject of this aspect since it is about deployment only      R retrieve  ->  no, not subject of this aspect since it is about deployment only
485      U update    ->  yes, just by user-interaction; maybe automatically if it can be determined that data wouldn't be lost      U update    ->  yes, just by user-interaction; maybe automatically if it can be determined that data wouldn't be lost
486      D delete    ->  yes, just by user-interaction      D delete    ->  yes, just by user-interaction
487    It's all about not to be able to loose data simply while this is in alpha stage.    
488      It's all about not to be able to loose data simply while this is in pre-alpha stage.
489      And loosing data by being able to modify and redeploy schemas easily is definitely quite easy.
490      
491      As we can see, creations of Classes and new Class variables is handled
492      automatically and this is believed to be the most common case under normal circumstances.
493    
494    
495    =head2 FEATURES
496    
497  =head2 Introduce some features:    - Get this stuff together with UML (Unified Modeling Language) and/or standards from ODMG.
498      - Make it possible to load/save schemas in XMI (XML Metadata Interchange),
499        which seems to be most commonly used today, perhaps handle objects with OIFML.
500        Integrate/bundle this with a web-/html-based UML modeling tool or
501        some other interesting stuff like the "Co-operative UML Editor" from Uni Darmstadt. (web-/java-based)
502      - Enable Round Trip Engineering. Keep code and diagrams in sync. Don't annoy/bother the programmers.
503      - Add support for some more handlers/locators to be able to
504         access the following standards/protocols/interfaces/programs/apis transparently:
505        +  DBD::CSV (via Data::Storage::Handler::DBI)
506       (-) Text::CSV, XML::CSV, XML::Excel
507        -  MAPI
508        -  LDAP
509        -  DAV (look at PerlDAV: http://www.webdav.org/perldav/)
510        -  Mbox (use formail for seperating/splitting entries/nodes)
511        -  Cyrus (cyrdeliver - what about cyrretrieve (export)???)
512        -  use File::DiffTree, use File::Compare
513        -  Hibernate
514        -  "Win32::UserAccountDb"
515        -  "*nix::UserAccountDb"
516        -  .wab - files (Windows Address Book)
517        -  .pst - files (Outlook Post Storage?)
518        -  XML (e.g. via XML::Simple?)
519      - Move to t3, look at InCASE
520      - some kind of security layer for methods/objects
521        - acls (stored via tangram/ldap?) for functions, methods and objects (entity- & data!?)
522        - where are the hooks needed then?
523          - is Data::Storage & Co. okay, or do we have to touch the innards of DBI and/or Tangram?
524          - an attempt to start could be:
525             - 'sub getACLByObjectId($id, $context)'
526             - 'sub getACLByMethodname($id, $context)'
527             - 'sub getACLByName($id, $context)'
528                ( would require a kinda registry to look up these very names pointing to arbitrary locations (code, data, ...) )
529    
   Get this stuff together with UML (Unified Modeling Language) and/or standards from ODMG.  
   Make it possible to load/save schemas in XMI (XML Metadata Interchange),  
   which seems to be most commonly used today, perhaps handle objects with OIFML.  
   Integrate/bundle this with a web-/html-based UML modeling tool or  
   some other interesting stuff like the "Co-operative UML Editor" from Uni Darmstadt. (web-/java-based)  
   Enable Round Trip Engineering. Keep code and diagrams in sync. Don't annoy/bother the programmer.  
530    
531    
532  =head3 Links:  =head3 LINKS / REFERENCES
533    
534      Specs:
535      UML 1.3 Spec: http://cgi.omg.org/cgi-bin/doc?ad/99-06-08.pdf      UML 1.3 Spec: http://cgi.omg.org/cgi-bin/doc?ad/99-06-08.pdf
536      XMI 1.1 Spec: http://cgi.omg.org/cgi-bin/doc?ad/99-10-02.pdf      XMI 1.1 Spec: http://cgi.omg.org/cgi-bin/doc?ad/99-10-02.pdf
537      XMI 2.0 Spec: http://cgi.omg.org/docs/ad/01-06-12.pdf      XMI 2.0 Spec: http://cgi.omg.org/docs/ad/01-06-12.pdf
538      ODMG: http://odmg.org/      ODMG: http://odmg.org/
539      OIFML: http://odmg.org/library/readingroom/oifml.pdf      OIFML: http://odmg.org/library/readingroom/oifml.pdf
     Co-operative UML Editor: http://www.darmstadt.gmd.de/concert/activities/internal/umledit.html  
540    
541    further readings:    CASE Tools:
542        Rational Rose (commercial): http://www.rational.com/products/rose/
543        Together (commercial): http://www.oi.com/products/controlcenter/index.jsp
544        InCASE - Tangram-based Universal Object Editor
545        Sybase PowerDesigner: http://www.sybase.com/powerdesigner
546      
547      UML Editors:
548        Fujaba (free, university): http://www.fujaba.de/
549        ArgoUML (free): http://argouml.tigris.org/
550        Poseidon (commercial): http://www.gentleware.com/products/poseidonDE.php3
551        Co-operative UML Editor (research): http://www.darmstadt.gmd.de/concert/activities/internal/umledit.html
552        Metamill (commercial): http://www.metamill.com/
553        Violet (university, research, education): http://www.horstmann.com/violet/
554        PyUt (free): http://pyut.sourceforge.net/
555        (Dia (free): http://www.lysator.liu.se/~alla/dia/)
556        UMLet (free, university): http://www.swt.tuwien.ac.at/umlet/index.html
557        Voodoo (free): http://voodoo.sourceforge.net/
558        Umbrello UML Modeller: http://uml.sourceforge.net/
559    
560      UML Tools:
561        http://www.objectsbydesign.com/tools/umltools_byPrice.html
562    
563      Further readings:
564      http://www.google.com/search?q=web+based+uml+editor&hl=en&lr=&ie=UTF-8&oe=UTF-8&start=10&sa=N      http://www.google.com/search?q=web+based+uml+editor&hl=en&lr=&ie=UTF-8&oe=UTF-8&start=10&sa=N
565      http://www.fernuni-hagen.de/DVT/Aktuelles/01FHHeidelberg.pdf      http://www.fernuni-hagen.de/DVT/Aktuelles/01FHHeidelberg.pdf
566      http://www.enhyper.com/src/documentation/      http://www.enhyper.com/src/documentation/
# Line 403  Data::Storage is free software. IT COMES Line 568  Data::Storage is free software. IT COMES
568      http://citeseer.nj.nec.com/vilain00diagrammatic.html      http://citeseer.nj.nec.com/vilain00diagrammatic.html
569      http://archive.devx.com/uml/articles/Smith01/Smith01-3.asp      http://archive.devx.com/uml/articles/Smith01/Smith01-3.asp
570    
   maybe useful for / to be integrated with:  
     ArapXML: http://xml.coverpages.org/ni2001-09-24-b.html  

Legend:
Removed from v.1.4  
changed lines
  Added in v.1.10

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed