line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
4
|
|
|
4
|
|
67866
|
use strict; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
95
|
|
2
|
4
|
|
|
4
|
|
12
|
use warnings; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
161
|
|
3
|
|
|
|
|
|
|
package Plack::App::DAIA; |
4
|
|
|
|
|
|
|
{ |
5
|
|
|
|
|
|
|
$Plack::App::DAIA::VERSION = '0.45_1'; |
6
|
|
|
|
|
|
|
} |
7
|
|
|
|
|
|
|
#ABSTRACT: DAIA Server as Plack application |
8
|
|
|
|
|
|
|
|
9
|
4
|
|
|
4
|
|
14
|
use feature ':5.10'; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
434
|
|
10
|
|
|
|
|
|
|
|
11
|
4
|
|
|
4
|
|
348
|
use parent 'Plack::Component'; |
|
4
|
|
|
|
|
204
|
|
|
4
|
|
|
|
|
18
|
|
12
|
4
|
|
|
4
|
|
36686
|
use LWP::Simple qw(get); |
|
4
|
|
|
|
|
170384
|
|
|
4
|
|
|
|
|
28
|
|
13
|
4
|
|
|
4
|
|
629
|
use Encode; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
203
|
|
14
|
4
|
|
|
4
|
|
1557
|
use JSON; |
|
4
|
|
|
|
|
24903
|
|
|
4
|
|
|
|
|
25
|
|
15
|
4
|
|
|
4
|
|
1998
|
use DAIA; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
use Scalar::Util qw(blessed); |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
use Plack::Util::Accessor qw(xsd xslt warnings code idformat initialized html); |
19
|
|
|
|
|
|
|
use Plack::Middleware::Static; |
20
|
|
|
|
|
|
|
use File::ShareDir qw(dist_dir); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
use Plack::Request; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
our %FORMATS = DAIA->formats; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub prepare_app { |
27
|
|
|
|
|
|
|
my $self = shift; |
28
|
|
|
|
|
|
|
return if $self->initialized; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
$self->warnings(1) unless defined $self->warnings; |
31
|
|
|
|
|
|
|
$self->idformat(qr{^.*$}) unless defined $self->idformat; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
if ($self->html) { |
34
|
|
|
|
|
|
|
$self->html( Plack::Middleware::Static->new( |
35
|
|
|
|
|
|
|
path => qr{daia\.(xsl|css)$|xmlverbatim\.xsl$|icon/[a-z0-9_-]+\.png$}, |
36
|
|
|
|
|
|
|
root => dist_dir('Plack-App-DAIA') |
37
|
|
|
|
|
|
|
)); |
38
|
|
|
|
|
|
|
$self->xslt( '/daia.xsl' ) unless $self->xslt; # TODO: fix base path |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
$self->init; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
$self->initialized(1); |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub init { |
47
|
|
|
|
|
|
|
# initialization hook |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
sub call { |
51
|
|
|
|
|
|
|
my ($self, $env) = @_; |
52
|
|
|
|
|
|
|
my $req = Plack::Request->new($env); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
my $id = $req->param('id') // ''; |
55
|
|
|
|
|
|
|
my $invalid_id = ''; |
56
|
|
|
|
|
|
|
my %parts; |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
if ( $self->html and $id eq '' ) { |
59
|
|
|
|
|
|
|
my $resp = $self->html->_handle_static( $env ); |
60
|
|
|
|
|
|
|
if ($resp and $resp->[0] eq 200) { |
61
|
|
|
|
|
|
|
return $resp; |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
if ( $id ne '' and ref $self->idformat ) { |
66
|
|
|
|
|
|
|
if ( ref $self->idformat eq 'Regexp' ) { |
67
|
|
|
|
|
|
|
if ( $id =~ $self->idformat ) { |
68
|
|
|
|
|
|
|
%parts = %+; # named capturing groups |
69
|
|
|
|
|
|
|
} else { |
70
|
|
|
|
|
|
|
$invalid_id = $id; |
71
|
|
|
|
|
|
|
$id = ""; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
my $format = lc($req->param('format')) || ""; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
if (!$format) { |
79
|
|
|
|
|
|
|
# TODO: guess format via content negotiation |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
my $status = 200; |
83
|
|
|
|
|
|
|
my $daia = $self->retrieve( $id, %parts ); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
if (!$daia) { |
86
|
|
|
|
|
|
|
$daia = DAIA::Response->new; |
87
|
|
|
|
|
|
|
$status = 500; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
if ( $self->warnings ) { |
91
|
|
|
|
|
|
|
if ( $invalid_id ne '' ) { |
92
|
|
|
|
|
|
|
$daia->addMessage( 'en' => 'unknown identifier format', errno => 400 ); |
93
|
|
|
|
|
|
|
} elsif ( $id eq "" ) { |
94
|
|
|
|
|
|
|
$daia->addMessage( 'en' => 'please provide a document identifier', errno => 400 ); |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
$self->as_psgi( $status, $daia, $format, $req->param('callback') ); |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
sub retrieve { |
102
|
|
|
|
|
|
|
my $self = shift; |
103
|
|
|
|
|
|
|
return $self->code ? $self->code->(@_) : undef; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub as_psgi { |
107
|
|
|
|
|
|
|
my ($self, $status, $daia, $format, $callback) = @_; |
108
|
|
|
|
|
|
|
my ($content, $type); |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
$type = $FORMATS{$format} unless $format eq 'xml'; |
111
|
|
|
|
|
|
|
$content = $daia->serialize($format) if $type; |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
if (!$content) { |
114
|
|
|
|
|
|
|
$type = "application/xml; charset=utf-8"; |
115
|
|
|
|
|
|
|
if ( $self->warnings ) { |
116
|
|
|
|
|
|
|
if ( not $format ) { |
117
|
|
|
|
|
|
|
$daia->addMessage( 'en' => 'please provide an explicit parameter format=xml', 300 ); |
118
|
|
|
|
|
|
|
} elsif ( $format ne 'xml' ) { |
119
|
|
|
|
|
|
|
$daia->addMessage( 'en' => 'unknown or unsupported format', 300 ); |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
$content = $daia->xml( header => 1, xmlns => 1, ( $self->xslt ? (xslt => $self->xslt) : () ) ); |
123
|
|
|
|
|
|
|
} elsif ( $type =~ qr{^application/javascript} and ($callback || '') =~ /^[\w\.\[\]]+$/ ) { |
124
|
|
|
|
|
|
|
$content = "$callback($content)"; |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
return [ $status, [ "Content-Type" => $type ], [ encode('utf8',$content) ] ]; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
1; |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
__END__ |
134
|
|
|
|
|
|
|
=pod |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=head1 NAME |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
Plack::App::DAIA - DAIA Server as Plack application |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=head1 VERSION |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
version 0.45_1 |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=head1 SYNOPSIS |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
To quickly hack a DAIA server, create a simple C<app.psgi>: |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
use Plack::App::DAIA; |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Plack::App::DAIA->new( code => sub { |
151
|
|
|
|
|
|
|
my $id = shift; |
152
|
|
|
|
|
|
|
# ...construct and return DAIA object |
153
|
|
|
|
|
|
|
} ); |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
To create your own DAIA server, you should better derive from this class: |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
package Your::App; |
158
|
|
|
|
|
|
|
use parent 'Plack::App::DAIA'; |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
sub retrieve { |
161
|
|
|
|
|
|
|
my ($self, $id, %parts) = @_; |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# construct DAIA object (you must extend this in your application) |
164
|
|
|
|
|
|
|
my $daia = DAIA::Response->new; |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
return $daia; |
167
|
|
|
|
|
|
|
}; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
1; |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
Then create an C<app.psgi> that returns an instance of your class: |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
use Your::App; |
174
|
|
|
|
|
|
|
Your::App->new; |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
You can also mix this application with L<Plack> middleware. |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
It is highly recommended to test your services! Testing is made as easy as |
179
|
|
|
|
|
|
|
possible with the L<provedaia> command line script. |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
This module contains a dummy application C<app.psgi> and a more detailed |
182
|
|
|
|
|
|
|
example C<examples/daia-ubbielefeld.pl>. |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=head1 DESCRIPTION |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
This module implements a L<DAIA> server as PSGI application. It provides |
187
|
|
|
|
|
|
|
serialization in DAIA/XML and DAIA/JSON and automatically adds some warnings |
188
|
|
|
|
|
|
|
and error messages. The core functionality must be implemented by deriving |
189
|
|
|
|
|
|
|
from this class and implementing the method C<retrieve>. The following |
190
|
|
|
|
|
|
|
serialization formats are supported by default: |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=over 4 |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=item xml |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
DAIA/XML format (default) |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=item json |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
DAIA/JSON format |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=item rdfjson |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
DAIA/RDF in RDF/JSON. |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=back |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
In addition you get DAIA/RDF in several RDF formats (C<rdfxml>, |
209
|
|
|
|
|
|
|
C<turtle>, and C<ntriples> if L<RDF::Trine> is installed. If L<RDF::NS> is |
210
|
|
|
|
|
|
|
installed, you also get known namespace prefixes for RDF/Turtle format. |
211
|
|
|
|
|
|
|
Furthermore the output formats C<svg> and C<dot> are supported if |
212
|
|
|
|
|
|
|
L<RDF::Trine::Exporter::GraphViz> is installed to visualize RDF graphs |
213
|
|
|
|
|
|
|
(you may need to make sure that C<dot> is in your C<$ENV{PATH}>). |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=head1 METHODS |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=head2 new ( [%options] ) |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
Creates a new DAIA server. Supported options are |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=over 4 |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=item xslt |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
Path of a DAIA XSLT client to attach to DAIA/XML responses. Not required if |
226
|
|
|
|
|
|
|
option "html" is set. |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=item html |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Enable HTML client for DAIA/XML via XSLT. The client is returned in form of |
231
|
|
|
|
|
|
|
three files (C<daia.xsl>, C<daia.css>, C<xmlverbatim.xsl>) and approriate |
232
|
|
|
|
|
|
|
icons, that are all shipped with this module. |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=item xsd |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
Path of a DAIA XML Schema to validate DAIA/XML response. |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=item warnings |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
Enable warnings in the DAIA response (enabled by default). |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=item code |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
Code reference to the 'retrieve' method if you prefer not to create a |
245
|
|
|
|
|
|
|
module derived from this module. |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=item idformat |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
Optional regular expression to validate identifiers. Invalid identifiers |
250
|
|
|
|
|
|
|
are set to the empty string before they are passed to the 'retrieve' |
251
|
|
|
|
|
|
|
method. In addition an error message "unknown identifier format" is |
252
|
|
|
|
|
|
|
added to the response, if warnings are enabled. |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
It is recommended to use regular expressions with named capturing groups |
255
|
|
|
|
|
|
|
as introduced in Perl 5.10. The named parts are also passed to the |
256
|
|
|
|
|
|
|
retrieve method. For instance: |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
idformat => qr{^ (?<prefix>[a-z]+) : (?<local>.+) $}x |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
will give you C<$parts{prefix}> and C<$parts{local}> in the retrieve method. |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=item initialized |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
Stores whether the application had been initialized. |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=back |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
=head2 retrieve ( $id [, %parts ] ) |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
Must return a status and a L<DAIA::Response> object. Override this method |
271
|
|
|
|
|
|
|
if you derive an application from Plack::App::DAIA. By default it either |
272
|
|
|
|
|
|
|
calls the retrieve code, as passed to the constructor, or returns undef, |
273
|
|
|
|
|
|
|
so a HTTP 500 error is returned. |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
This method is passed the original query identifier and a hash of named |
276
|
|
|
|
|
|
|
capturing groups from your identifier format. |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=head2 init |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
This method is called by L<Plack::Component>::prepare_app, once before the |
281
|
|
|
|
|
|
|
first request. You can define this method in you subclass as initialization |
282
|
|
|
|
|
|
|
hook, for instance to set default option values. Initialization during runtime |
283
|
|
|
|
|
|
|
can be triggered by setting C<initialized> to false. |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=head2 as_psgi ( $status, $daia [, $format [, $callback ] ] ) |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
Serializes a L<DAIA::Response> in some DAIA serialization format (C<xml> by |
288
|
|
|
|
|
|
|
default) and returns a a PSGI response with given HTTP status code. |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=head1 SEE ALSO |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
L<Plack::App::DAIA::Validator> and L<Plack::DAIA::Test>. |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
=head1 AUTHOR |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
Jakob Voss |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
This software is copyright (c) 2012 by Jakob Voss. |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
303
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=cut |
306
|
|
|
|
|
|
|
|