Bug 3482 changed name of notices file
[koha.git] / misc / cronjobs / zebraqueue_start.pl
1 #!/usr/bin/perl
2 # script that starts the zebraquee
3 #  Written by TG on 01/08/2006
4 use strict;
5 BEGIN {
6     # find Koha's Perl modules
7     # test carefully before changing this
8     use FindBin;
9     eval { require "$FindBin::Bin/../kohalib.pl" };
10 }
11
12 use C4::Context;
13 use C4::Biblio;
14 use C4::Search;
15 use C4::AuthoritiesMarc;
16 use XML::Simple;
17 use utf8;
18 ### ZEBRA SERVER UPDATER
19 ##Uses its own database handle
20 my $dbh=C4::Context->dbh;
21 my $readsth=$dbh->prepare("SELECT id,biblio_auth_number,operation,server FROM zebraqueue WHERE done=0 
22                            ORDER BY id DESC"); # NOTE - going in reverse order to catch deletes that
23                                                # occur after a string of updates (e.g., user deletes
24                                                # the items attached to a bib, then the items.
25                                                # Having a specialUpdate occur after a recordDelete
26                                                # should not occur.
27 #my $delsth=$dbh->prepare("delete from zebraqueue where id =?");
28
29
30 #AGAIN:
31
32 #my $wait=C4::Context->preference('zebrawait') || 120;
33 my $verbose = 0;
34 print "starting with verbose=$verbose\n" if $verbose;
35
36 my ($id,$biblionumber,$operation,$server,$marcxml);
37
38 $readsth->execute;
39 while (($id,$biblionumber,$operation,$server)=$readsth->fetchrow){
40     print "read in queue : $id : biblio $biblionumber for $operation on $server\n" if $verbose;
41     my $ok;
42     eval{
43         # if the operation is a deletion, zebra requires that we give it the xml.
44         # as it is no more in the SQL db, retrieve it from zebra itself.
45         # may sound silly, but that's the way zebra works ;-)
46             if ($operation =~ /delete/i) { # NOTE depending on version, delete operation
47                                        #      was coded 'delete_record' or 'recordDelete'.
48                                        #      'recordDelete' is the preferred one, as that's
49                                        #      what the ZOOM API wants.
50                # 1st read the record in zebra
51             my $Zconn=C4::Context->Zconn($server, 0, 1,'','xml');
52             my $query = $Zconn->search_pqf( '@attr 1=Local-Number '.$biblionumber);
53             # then, delete the record
54                 $ok=zebrado($query->record(0)->render(),$operation,$server,$biblionumber);
55         # if it's an add or a modif
56         } else {
57             # get the XML
58             if ($server eq "biblioserver") {
59                 my $marc = GetMarcBiblio($biblionumber);
60                 $marcxml = $marc->as_xml_record() if $marc;
61             } elsif ($server eq "authorityserver") {
62                 $marcxml =C4::AuthoritiesMarc::GetAuthorityXML($biblionumber);
63             }
64             if ($verbose) {
65                 if ($marcxml) {
66                     print "XML read : $marcxml\n" if $verbose >1;
67                 } else {
68                 # workaround for zebra bug needing a XML even for deletion
69                 $marcxml= "<dummy/>";
70                     print "unable to read MARCxml\n" if $verbose;
71                 }
72             }
73             # check it's XML, just in case
74             eval {
75                 my $hashed=XMLin($marcxml);
76             }; ### is it a proper xml? broken xml may crash ZEBRA- slow but safe
77             ## it's Broken XML-- Should not reach here-- but if it does -lets protect ZEBRA
78             if ($@){
79                 warn $@;
80                 my $delsth=$dbh->prepare("UPDATE zebraqueue SET done=1 WHERE id =?");
81                 $delsth->execute($id);
82                 next;
83             }
84             # ok, we have everything, do the operation in zebra !
85             $ok=zebrado($marcxml,$operation,$server);
86         }
87     };
88     print "ZEBRAopserver returned : $ok \n" if $verbose;
89     if ($ok ==1) {
90         $dbh=C4::Context->dbh;
91         my $delsth;
92         # if it's a deletion, we can delete every request on this biblio : in case the user
93         # did a modif (or item deletion) just before biblio deletion, there are some specialUpdage
94         # that are pending and can't succeed, as we don't have the XML anymore
95         # so, delete everything for this biblionumber
96         my $reset_readsth = 0;
97         if ($operation eq 'recordDelete') {
98             print "deleting biblio deletion $biblionumber\n" if $verbose;
99             $delsth =$dbh->prepare("UPDATE zebraqueue SET done=1 WHERE biblio_auth_number =?");
100             $delsth->execute($biblionumber);
101             $reset_readsth = 1 if $delsth->rows() > 0;
102         # if it's not a deletion, delete every pending specialUpdate for this biblionumber
103         # in case the user add biblio, then X items, before this script runs
104         # this avoid indexing X+1 times where just 1 is enough.
105         } else {
106             print "deleting special date for $biblionumber\n" if $verbose;
107             $delsth =$dbh->prepare("UPDATE zebraqueue SET done=1 WHERE biblio_auth_number =? and operation='specialUpdate'");
108             $delsth->execute($biblionumber);
109             $reset_readsth = 1 if $delsth->rows() > 0;
110         }
111         if ($reset_readsth) {
112             # if we can ignore rows in zebraqueue because we've already
113             # touched a record, reset the query. 
114             $readsth->finish();
115             $readsth->execute();
116         }
117     }
118 }
119
120 sub zebrado {
121     
122     ###Accepts a $server variable thus we can use it to update  biblios, authorities or other zebra dbs
123     my ($record,$op,$server,$biblionumber)=@_;
124     
125     my @port;
126     
127     my $tried=0;
128     my $recon=0;
129     my $reconnect=0;
130 #    $record=Encode::encode("UTF-8",$record);
131     my $shadow=$server."shadow";
132     $op = 'recordDelete' if $op eq 'delete_record';
133 reconnect:
134     
135     my $Zconn=C4::Context->Zconn($server, 0, 1);
136     if ($record){
137         print "updating $op on $biblionumber for server $server\n $record\n" if $verbose;
138         my $Zpackage = $Zconn->package();
139         $Zpackage->option(action => $op);
140         $Zpackage->option(record => $record);
141 #           $Zpackage->option(recordIdOpaque => $biblionumber) if $biblionumber;
142 retry:
143         $Zpackage->send("update");
144         my($error, $errmsg, $addinfo, $diagset) = $Zconn->error_x();
145         if ($error==10007 && $tried<3) {## timeout --another 30 looonng seconds for this update
146             print "error 10007\n" if $verbose;
147             sleep 1;    ##  wait a sec!
148             $tried=$tried+1;
149             goto "retry";
150         }elsif ($error==2 && $tried<2) {## timeout --temporary zebra error !whatever that means
151             print "error 2\n" if $verbose;
152             sleep 2;    ##  wait two seconds!
153             $tried=$tried+1;
154             goto "retry";
155         }elsif($error==10004 && $recon==0){##Lost connection -reconnect
156             print "error 10004\n" if $verbose;
157             sleep 1;    ##  wait a sec!
158             $recon=1;
159             $Zpackage->destroy();
160             $Zconn->destroy();
161             goto "reconnect";
162         }elsif ($error){
163         #       warn "Error-$server   $op  /errcode:, $error, /MSG:,$errmsg,$addinfo \n";       
164             print "error $error\n" if $verbose;
165             $Zpackage->destroy();
166             $Zconn->destroy();
167             return 0;
168         }
169         $Zpackage->send('commit');
170 #     $Zpackage->destroy();
171 #     $Zconn->destroy();
172     return 1;
173     }
174     return 0;
175 }