/[cvs]/nfo/perl/libs/Data/Transfer/Sync/StorageInterface.pm
ViewVC logotype

Contents of /nfo/perl/libs/Data/Transfer/Sync/StorageInterface.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.5 - (show annotations)
Thu Feb 20 20:24:33 2003 UTC (21 years, 4 months ago) by joko
Branch: MAIN
Changes since 1.4: +22 -2 lines
+ additional pre-flight checks

1 ## $Id: StorageInterface.pm,v 1.4 2003/02/14 14:14:38 joko Exp $
2 ##
3 ## Copyright (c) 2002 Andreas Motl <andreas.motl@ilo.de>
4 ##
5 ## See COPYRIGHT section in pod text below for usage and distribution rights.
6 ##
7 ## ----------------------------------------------------------------------------------------
8 ## $Log: StorageInterface.pm,v $
9 ## Revision 1.4 2003/02/14 14:14:38 joko
10 ## + new code refactored here
11 ##
12 ## Revision 1.3 2003/02/11 07:54:55 joko
13 ## + modified module usage
14 ## + debugging trials
15 ##
16 ## Revision 1.2 2003/02/09 05:05:58 joko
17 ## + major structure changes
18 ## - refactored code to sister modules
19 ## + refactored code to this place
20 ##
21 ## Revision 1.1 2003/01/20 16:58:46 joko
22 ## + initial check-in: here they are....
23 ##
24 ## Revision 1.1 2003/01/19 01:23:04 joko
25 ## + new from Data/Transfer/Sync.pm
26 ##
27 ## ----------------------------------------------------------------------------------------
28
29
30 package Data::Transfer::Sync::StorageInterface;
31
32 use strict;
33 use warnings;
34
35 use mixin::with qw( Data::Transfer::Sync );
36
37
38 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - main
39
40 use Data::Dumper;
41 use Hash::Merge qw( merge );
42 use libdb qw( hash2Sql );
43 use Data::Transform::Deep qw( hash2object );
44
45
46 # get logger instance
47 my $logger = Log::Dispatch::Config->instance;
48
49 # this is a shortcut method
50 # ... let's try to avoid _any_ redundant code in here (ok... - at the cost of method lookups...)
51 sub _getNodeList {
52 my $self = shift;
53 my $descent = shift;
54 my $filter = shift;
55 $logger->debug( __PACKAGE__ . "->_getNodeList( descent=$descent, accessorName=$self->{meta}->{$descent}->{accessorName} )" );
56 #$results ||= $self->{source}->getListUnfiltered($self->{meta}->{source}->{node});
57 #$results ||= $self->{meta}->{source}->{storage}->getListUnfiltered($self->{meta}->{source}->{node});
58 my $list = $self->{meta}->{$descent}->{storage}->getListFiltered($self->{meta}->{$descent}->{accessorName}, $filter);
59 #print Dumper($list);
60 return $list;
61 }
62
63 sub _resolveNodeIdent {
64 my $self = shift;
65 my $descent = shift;
66
67 # trace
68 #print Dumper($self->{node}->{$descent});
69 #print Dumper($self);
70 #exit;
71
72 $logger->debug( __PACKAGE__ . "->_resolveNodeIdent( descent=$descent, accessorName=$self->{meta}->{$descent}->{accessorName} )" );
73
74 # get to the payload
75 #my $item = $specifier->{item};
76 my $payload = $self->{node}->{$descent}->{payload};
77
78 # resolve method to get to the id of the given item
79 # we use global metadata and the given descent for this task
80 #my $ident = $self->{$descent}->id($item);
81 #my $ident = $self->{meta}->{$descent}->{storage}->id($item);
82
83 # trace
84 #print Dumper($self->{meta}->{$descent});
85 #exit;
86
87 my $ident;
88 my $provider_method = $self->{meta}->{$descent}->{IdentProvider}->{method};
89 my $provider_arg = $self->{meta}->{$descent}->{IdentProvider}->{arg};
90
91 # trace
92 #print "provider_method: $provider_method", "\n";
93 #print "provider_arg: $provider_arg", "\n";
94 #print Dumper($payload);
95
96 # resolve to ident
97 if (lc $provider_method eq 'property') {
98 $ident = $payload->{$provider_arg};
99
100 } elsif (lc $provider_method eq 'storage_method') {
101 #$ident = $self->{meta}->{$descent}->{storage}->id($item);
102 $ident = $self->{meta}->{$descent}->{storage}->$provider_arg($payload);
103
104 } elsif (lc $provider_method eq 'code') {
105 $ident = 'ac';
106
107 }
108
109 $self->{node}->{$descent}->{ident} = $ident;
110
111 return 1 if $ident;
112
113 }
114
115 # TODO:
116 # this should be split up into...
117 # - a "_statNode" (should just touch the node to check for existance)
118 # - a "_loadNode" (should load node completely)
119 # - maybe additionally a "loadNodeProperty" (may specify properties to load)
120 # - introduce $self->{nodecache} for this purpose
121 # TODO:
122 # should we:
123 # - not pass ident in here but resolve it via "$descent"?
124 # - refactor this and stuff it with additional debug/error message
125 # - this = the way the implicit load mechanism works
126 sub _statloadNode {
127
128 my $self = shift;
129 my $descent = shift;
130 my $ident = shift;
131 my $force = shift;
132
133 =pod
134 #print "isa: ", UNIVERSAL::isa($self->{meta}->{$descent}->{storage}), "\n";
135
136 # this seems to be the first time we access this side,
137 # so just check (again) for a valid storage handle
138 if (! ref $self->{meta}->{$descent}->{storage}) {
139 $logger->critical( __PACKAGE__ . "->_statloadNode( descent=$descent ident=$ident ): Storage handle undefined!" );
140 return;
141 }
142 =cut
143
144 #$logger->debug( __PACKAGE__ . "->_statloadNode( descent=$descent ident=$ident )" );
145
146 # fetch entry to retrieve checksum from
147 # was:
148 if (!$self->{node}->{$descent} || $force) {
149 # is:
150 #if (!$self->{node}->{$descent}->{item} || $force) {
151
152 if (!$ident) {
153 #print "\n", "Attempt to fetch entry implicitely by ident failed: no ident given! This may result in an insert if no write-protection is in the way.", "\n";
154 return;
155 }
156
157 # patch for DBD::CSV
158 if ($ident && $ident eq 'Null') {
159 return;
160 }
161
162 #print Dumper($self->{meta});
163
164 my $query = {
165 node => $self->{meta}->{$descent}->{accessorName},
166 subnodes => [qw( cs )],
167 criterias => [
168 { key => $self->{meta}->{$descent}->{IdentProvider}->{arg},
169 op => 'eq',
170 val => $ident },
171 ]
172 };
173
174 # trace
175 #print "query:", "\n";
176 #print Dumper($query);
177
178 # send query and fetch first entry from result
179 my $result = $self->{meta}->{$descent}->{storage}->sendQuery($query);
180 my $entry = $result->getNextEntry();
181
182 # trace
183 #print Dumper($entry);
184 #print "pers: " . $self->{meta}->{$descent}->{storage}->is_persistent($entry), "\n";
185 #my $state = $self->{meta}->{$descent}->{storage}->_fetch_object_state($entry, { name => 'TransactionHop' } );
186 #print Dumper($state);
187
188 # be informed about the status of the query
189 my $status = $result->getStatus();
190
191 #print Dumper($status);
192
193 # TODO: enhance error handling (store inside tc)
194 #if (!$row) {
195 # print "\n", "row error", "\n";
196 # next;
197 #}
198
199 # these checks run before actually loading payload- and meta-data to node-container
200
201 # 1st level - hard error
202 if ($status && $status->{err}) {
203 $logger->debug( __PACKAGE__ . "->_statloadNode (ident=\"$ident\") failed - hard error (that's ok): $status->{err}" );
204 return;
205 }
206
207 # 2nd level - logical (empty/notfound) error
208 if (($status && $status->{empty}) || !$entry) {
209 $logger->debug( __PACKAGE__ . "->_statloadNode (ident=\"$ident\") failed - logical error (that's ok)" );
210 #print "no entry (logical)", "\n";
211 return;
212 }
213
214 #print Dumper($entry);
215
216 # was:
217 # $self->{node}->{$descent}->{ident} = $ident;
218 # is:
219 # TODO: re-resolve ident from entry via metadata "IdentProvider" here - like elsewhere
220 $self->{node}->{$descent}->{ident} = $ident;
221 $self->{node}->{$descent}->{payload} = $entry;
222
223 }
224
225 return 1;
226
227 }
228
229
230 sub _touchNodeSet {
231 my $self = shift;
232
233 $logger->debug( __PACKAGE__ . "->touchNodeSet" );
234
235 # check descents/nodes: does descent exist / is node available?
236 foreach my $descent (keys %{$self->{meta}}) {
237
238 # 1. check metadata of descent(s)
239 if (!$self->{meta}->{$descent}) {
240 $logger->critical( __PACKAGE__ . "->touchNodeSet: Could not find descent '$descent' in configuration metadata." );
241 next;
242 }
243
244 # 2. check storage handle(s)
245 my $dbkey = $self->{meta}->{$descent}->{dbKey};
246 if (!$self->{meta}->{$descent}->{storage}) {
247 $logger->critical( __PACKAGE__ . "->touchNodeSet: Could not access storage ( descent='$descent', dbKey='$dbkey' ) - configuration-error?" );
248 next;
249 }
250
251 # 2.b check storage handle type
252 my $dbType = $self->{meta}->{$descent}->{storage}->{locator}->{type};
253 if (!$dbType) {
254 $logger->critical( __PACKAGE__ . "->touchNodeSet: Storage ( descent='$descent', dbKey='$dbkey' ) has no 'dbType' - configuration-error?" );
255 next;
256 }
257
258 # 3. check if descents (and nodes?) are actually available....
259 # TODO:
260 # eventually pre-check mode of access-attempt (read/write) here to provide an "early-croak" if possible
261
262 # trace
263 # print Dumper($self->{meta}->{$descent}->{storage}->{locator});
264
265
266 my $nodeName = $self->{meta}->{$descent}->{nodeName};
267 my $accessorType = $self->{meta}->{$descent}->{accessorType};
268 my $accessorName = $self->{meta}->{$descent}->{accessorName};
269
270
271 # 4. check nodeset
272
273 # debug message containing database type and used/determined accessor name
274 $logger->debug( __PACKAGE__ . "->touchNodeSet: Accessing dbType=$dbType, accessorName=$accessorName" );
275
276 # if target node(s) do(es) not exist, check if we should create it automagically
277 if ($dbType ne 'DBI' && !$self->{meta}->{$descent}->{storage}->existsChildNode($accessorName)) {
278
279 if ($descent eq 'target' && $self->{options}->{target}->{autocreateFolders}) {
280 if (!$self->{meta}->{$descent}->{storage}->createChildNode($accessorName)) {
281 $logger->critical( __PACKAGE__ . "->touchNodeSet: Could not create node '$self->{meta}->{$descent}->{nodeName}\@$self->{meta}->{$descent}->{dbKey}' [$self->{meta}->{$descent}->{nodeType}]." );
282 next;
283 }
284 } else {
285 $logger->critical( __PACKAGE__ . "->touchNodeSet: Could not reach node \"$nodeName\" (accessorName=$accessorName, accessorType=$accessorType) at descent \"$descent\"" );
286 next;
287 }
288 }
289
290 }
291
292 # trace
293 #print Dumper($self->{meta});
294 #exit;
295
296
297 return 1;
298
299 }
300
301
302
303 sub _modifyNode {
304 my $self = shift;
305 my $descent = shift;
306 my $action = shift;
307 my $map = shift;
308 my $crit = shift;
309
310 # map for new style callbacks
311 my $map_callbacks = {};
312
313 # checks go first!
314
315 # TODO: this should be reviewed first - before extending ;-)
316 # TODO: this should be extended:
317 # count this cases inside the caller to this sub and provide a better overall message
318 # if this counts still zero in the end:
319 # "No nodes have been touched for modify: Do you have column-headers in your csv file?"
320 if (not defined $self->{node}) {
321 #$logger->critical( __PACKAGE__ . "->_modifyNode failed: \"$descent\" node is empty." );
322 #return;
323 }
324
325 # transfer callback nodes from value map to callback map - handle them afterwards! - (new style callbacks)
326 if (my $callbacks = $self->{meta}->{$descent}->{Callback}) {
327 foreach my $callback (keys %{$callbacks->{write}}) {
328 $map_callbacks->{write}->{$callback} = $map->{$callback};
329 delete $map->{$callback};
330 }
331 }
332
333
334 # trace
335 #print Dumper($self->{meta});
336
337
338 # --------------------------------------------------------------------------
339 # DBI speaks SQL
340 if ($self->{meta}->{$descent}->{storage}->{locator}->{type} eq 'DBI') {
341
342 # trace
343 #print Dumper($map);
344 #delete $map->{cs};
345
346 # transfer data
347 # TODO: wrap this around '$storageHandle->sendQuery(...)'!?
348 my $sql_main;
349 if (lc($action) eq 'insert') {
350 $sql_main = hash2Sql($self->{meta}->{$descent}->{accessorName}, $map, 'SQL_INSERT');
351 } elsif (lc $action eq 'update') {
352 $crit ||= "$self->{meta}->{$descent}->{IdentProvider}->{arg}='$self->{node}->{$descent}->{ident}'";
353 $sql_main = hash2Sql($self->{meta}->{$descent}->{accessorName}, $map, 'SQL_UPDATE', $crit);
354 }
355 my $sqlHandle = $self->{meta}->{$descent}->{storage}->sendCommand($sql_main);
356
357 # handle errors
358 if ($sqlHandle->err) {
359 #if ($self->{args}->{debug}) { print "sql-error with statement: $sql_main", "\n"; }
360 $self->{node}->{status}->{error} = {
361 statement => $sql_main,
362 state => $sqlHandle->state,
363 err => $sqlHandle->err,
364 errstr => $sqlHandle->errstr,
365 };
366 } else {
367 $self->{node}->{status}->{ok} = 1;
368 }
369
370
371 # --------------------------------------------------------------------------
372 # Tangram does it the oo-way (naturally)
373 } elsif ($self->{meta}->{$descent}->{storage}->{locator}->{type} eq 'Tangram') {
374 my $sql_main;
375 my $object;
376
377 # determine classname
378 my $classname = $self->{meta}->{$descent}->{nodeType};
379
380 # attributes/properties to exclude
381 # push declared ones from metadata
382 my @exclude = @{$self->{meta}->{$descent}->{subnodes_exclude}};
383 # push the attributes associated with the identifier
384 if (my $identProvider = $self->{meta}->{$descent}->{IdentProvider}) {
385 push @exclude, $identProvider->{arg};
386 }
387
388 # trace
389 #print Dumper($self->{meta});
390 #exit;
391
392 # new feature:
393 # - check TypeProvider metadata property from other side
394 # - use argument (arg) inside as a classname for object creation on this side
395 #my $otherSide = $self->_otherSide($descent);
396 if (my $typeProvider = $self->{meta}->{$descent}->{TypeProvider}) {
397 #print Dumper($map);
398 $classname = $map->{$typeProvider->{arg}};
399 # remove nodes from map also (push nodes to "subnodes_exclude" list)
400 push @exclude, $typeProvider->{arg};
401 }
402
403 # exclude banned properties (remove from map)
404 #map { delete $self->{node}->{map}->{$_} } @{$self->{args}->{exclude}};
405 map { delete $map->{$_} } @exclude;
406
407 # list of properties
408 my @props = keys %{$map};
409
410 # transfer data
411 if (lc $action eq 'insert') {
412
413 # make the object persistent in four steps:
414 # - raw create (perl / class tangram scope)
415 # - orm insert (tangram scope) ... this establishes inheritance - don't try to fill in inherited properties before!
416 # is this a Tangram bug?
417 # - raw fill-in from hash (perl scope)
418 # - orm update (tangram scope) ... this updates all properties just filled in
419
420
421 # ==========================
422 # TODO: REVIEW HERE!!!
423 # can't we achieve this more elegant?
424 # o use DesignPattern::Object???
425 # o use Hash::Merge!!! (take care about the cloning behaviour)
426
427 # check if object exists (is classname a valid perl package/module?)
428
429 # we can just check if the classname is valid here
430 if (!$classname) {
431 $logger->critical( __PACKAGE__ . "->_modifyNode: classname is undefined" );
432 # FIXME: stop syncing here?
433 return;
434 }
435
436 # try to match against the classes known by Class::Tangram
437 # FIXME: do "/i" on win32 only!
438 my $classname_find = quotemeta($classname);
439 if (!grep(m/$classname_find/i, Class::Tangram::known_classes())) {
440 $logger->critical( __PACKAGE__ . "->_modifyNode: Classname '$classname' is not known by Class::Tangram" );
441 # FIXME: stop syncing here?
442 return;
443 }
444
445 # create new object ...
446 # V1
447 # build array to initialize object
448 #my @initarray = ();
449 #map { push @initarray, $_, undef; } @props;
450 #my $object = $classname->new( @initarray );
451 # V2
452 $object = $classname->new();
453 # V3
454 # $object = DesignPattern::Object->new($data);
455
456 # ... pass to orm first ...
457 $self->{meta}->{$descent}->{storage}->insert($object);
458
459 # ... and initialize with empty (undef'd) properties afterwards.
460 map { $object->{$_} = undef; } @props;
461
462 # trace
463 #print "\n";
464 #print Dumper($map);
465 #print Dumper($object);
466 #exit;
467
468 # mix in (merge) values ...
469 # TODO: use Hash::Merge here? benchmark!
470 # no! we'd need a Object::Merge here! it's *...2object*
471 hash2object($object, $map);
472
473 # trace
474 #print Dumper($object);
475 #exit;
476
477 # TODO: REVIEW HERE!!!
478 # ==========================
479
480 # ... and re-update@orm.
481 $self->{meta}->{$descent}->{storage}->update($object);
482
483 # asymmetry: get ident after insert
484 # TODO:
485 # - just do this if it is an IdentAuthority
486 # - use IdentProvider metadata here
487 #print Dumper($self->{meta}->{$descent});
488 my $oid = $self->{meta}->{$descent}->{storage}->id($object);
489 #print "oid: $oid", "\n";
490 $self->{node}->{$descent}->{ident} = $oid;
491
492
493 } elsif (lc $action eq 'update') {
494
495 # get fresh object from orm first
496 $object = $self->{meta}->{$descent}->{storage}->load($self->{node}->{$descent}->{ident});
497
498 #print Dumper($self->{node});
499
500 # mix in values
501 #print Dumper($object);
502 # TODO: use Hash::Merge here???
503 hash2object($object, $map);
504 #print Dumper($object);
505 #exit;
506
507 # update orm
508 $self->{meta}->{$descent}->{storage}->update($object);
509
510 }
511
512 #exit;
513
514 my $error = 0;
515
516 # handle new style callbacks - this is a HACK - do this without an eval!
517 #print Dumper($map);
518 #print "cb: ", Dumper($self->{meta}->{$descent}->{Callback});
519 #print Dumper($map_callbacks);
520 foreach my $node (keys %{$map_callbacks->{write}}) {
521 #print Dumper($node);
522
523 # ------------ half-redundant: make $self->callCallback($object, $value, $opts)
524 my $perl_callback = $self->{meta}->{$descent}->{nodeType} . '::' . $node . '_write';
525 my $evalstring = $perl_callback . '( { object => $object, value => $map_callbacks->{write}->{$node}, storage => $self->{meta}->{$descent}->{storage} } );';
526 #print $evalstring, "\n"; exit;
527 eval($evalstring);
528 if ($@) {
529 $error = 1;
530 $logger->error( __PACKAGE__ . "->_modifyNode: $@" );
531 next;
532 }
533 # ------------ half-redundant: make $self->callCallback($object, $value, $opts)
534
535 #print "after eval", "\n";
536
537 if (!$error) {
538 # re-update@orm
539 $self->{meta}->{$descent}->{storage}->update($object);
540 }
541 }
542
543 # handle errors
544 if ($error) {
545 #print "error", "\n";
546 =pod
547 my $sqlHandle;
548 #if ($self->{args}->{debug}) { print "sql-error with statement: $sql_main", "\n"; }
549 $self->{node}->{status}->{error} = {
550 statement => $sql_main,
551 state => $sqlHandle->state,
552 err => $sqlHandle->err,
553 errstr => $sqlHandle->errstr,
554 };
555 =cut
556 # rollback....
557 #print "rollback", "\n";
558 $self->{meta}->{$descent}->{storage}->erase($object);
559 #print "after rollback", "\n";
560 } else {
561 $self->{node}->{status}->{ok} = 1;
562 }
563
564 }
565
566 }
567
568 sub _erase_all {
569 my $self = shift;
570 my $descent = shift;
571 #my $node = shift;
572 #print Dumper($self->{meta}->{$descent});
573 #my $node = $self->{meta}->{$descent}->{nodeName};
574 my $node = $self->{meta}->{$descent}->{accessorName};
575 $logger->debug( __PACKAGE__ . "->_erase_all( node $node )" );
576 $self->{meta}->{$descent}->{storage}->eraseAll($node);
577 }
578
579 1;
580 __END__

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