File Coverage

blib/lib/Apache/Session/DBMS.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             #############################################################################
2             #
3             # Apache::Session::DBMS
4             # Apache persistent user sessions using DBMS
5             # Copyright(c) 2005 Asemantics S.r.l.
6             # Alberto Reggiori (alberto@asemantics.com)
7             # Distribute under a BSD license (see LICENSE file in main dir)
8             #
9             ############################################################################
10              
11             package Apache::Session::DBMS;
12              
13 2     2   1640 use strict;
  2         8  
  2         79  
14 2     2   9 use vars qw(@ISA $VERSION $incl);
  2         3  
  2         214  
15              
16             $VERSION = '0.32';
17             @ISA = qw(Apache::Session);
18              
19             $incl = {};
20              
21 2     2   4033 use Apache::Session;
  2         4418  
  2         62  
22 2     2   3114 use Apache::Session::Lock::Null;
  2         421  
  2         54  
23 2     2   1447 use Apache::Session::Store::DBMS;
  0            
  0            
24             use Apache::Session::Generate::DBMS;
25              
26             use Apache::Session::Serialize::DBMS::Storable;
27              
28             sub populate {
29             my $self = shift;
30              
31             $self->{object_store} = new Apache::Session::Store::DBMS $self;
32             $self->{lock_manager} = new Apache::Session::Lock::Null $self;
33             $self->{generate} = \&Apache::Session::Generate::DBMS::generate;
34             $self->{validate} = \&Apache::Session::Generate::DBMS::validate;
35              
36             if( exists $self->{args}->{Serialize} ) {
37             my $ser = "Apache::Session::Serialize::$self->{args}->{Serialize}";
38              
39             if (!exists $incl->{$ser}) {
40             eval "require $ser" || die $@;
41             eval '$incl->{$ser}->[0] = \&' . $ser . '::serialize' || die $@;
42             eval '$incl->{$ser}->[1] = \&' . $ser . '::unserialize' || die $@;
43              
44             $self->{serialize} = $incl->{$ser}->[0];
45             $self->{unserialize} = $incl->{$ser}->[1];
46             };
47             } else {
48             # Storable is the default
49             $self->{serialize} = \&Apache::Session::Serialize::DBMS::Storable::serialize;
50             $self->{unserialize} = \&Apache::Session::Serialize::DBMS::Storable::unserialize;
51             };
52              
53             $self->{ isObjectPerKey } = ( ( defined $self->{data}->{_session_id} ) and
54             ( $self->{data}->{_session_id} =~ m|^\s*dbms://([^:]+):(\d+)/([^\s]+)| or
55             $self->{data}->{_session_id} =~ m|^\s*dbms://([^/]+)/([^\s]+)| ) ) ? 1 : 0 ;
56              
57             return $self;
58             };
59              
60             # override perltie part
61             sub FETCH {
62             my $self = shift;
63             my $key = shift;
64              
65             if( $self->{isObjectPerKey} ) {
66             &{$self->{unserialize}}( $self, $self->{object_store}->{dbh}->FETCH( $key ) ); # yep we do unserialize it each time
67             } else {
68             $self->SUPER::FETCH( $key );
69             };
70             };
71              
72             sub STORE {
73             my $self = shift;
74             my $key = shift;
75             my $value = shift;
76              
77             if( $self->{isObjectPerKey} ) {
78             $self->{object_store}->{dbh}->STORE( $key, &{$self->{serialize}}( $self, $value ) ); # yep we do serialize it each time
79             } else {
80             $self->SUPER::STORE( $key, $value );
81             };
82             };
83              
84             sub DELETE {
85             my $self = shift;
86             my $key = shift;
87              
88             if( $self->{isObjectPerKey} ) {
89             $self->{object_store}->{dbh}->DELETE( $key );
90             } else {
91             $self->SUPER::DELETE( $key );
92             };
93             };
94              
95             sub CLEAR {
96             my $self = shift;
97              
98             if( $self->{isObjectPerKey} ) {
99             $self->{object_store}->{dbh}->CLEAR();
100             } else {
101             $self->SUPER::CLEAR();
102             };
103             };
104              
105             sub EXISTS {
106             my $self = shift;
107             my $key = shift;
108              
109             if( $self->{isObjectPerKey} ) {
110             $self->{object_store}->{dbh}->EXISTS( $key );
111             } else {
112             $self->SUPER::EXISTS( $key );
113             };
114             };
115              
116             sub FIRSTKEY {
117             my $self = shift;
118              
119             if( $self->{isObjectPerKey} ) {
120             $self->{object_store}->{dbh}->FIRSTKEY();
121             } else {
122             $self->SUPER::FIRSTKEY();
123             };
124             };
125              
126             sub NEXTKEY {
127             my $self = shift;
128              
129             if( $self->{isObjectPerKey} ) {
130             $self->{object_store}->{dbh}->NEXTKEY( shift );
131             } else {
132             $self->SUPER::NEXTKEY( shift );
133             };
134             };
135              
136             sub DESTROY {
137             my $self = shift;
138              
139             if( $self->{isObjectPerKey} ) {
140             #$self->{object_store}->{dbh}->sync();
141             } else {
142             $self->SUPER::DESTROY();
143             };
144             };
145              
146             # override persistence methods if object-per-key mode on
147             # NOTE: basically we bypass the whole Apache::Session "caching" one-key-object layer
148              
149             sub restore {
150             my $self = shift;
151              
152             if( $self->{isObjectPerKey} ) {
153             $self->{object_store}->connection($self);
154             } else {
155             $self->SUPER::restore();
156             };
157             };
158              
159             sub save {
160             my $self = shift;
161              
162             if( $self->{isObjectPerKey} ) {
163             $self->{object_store}->connection($self);
164             } else {
165             $self->SUPER::save();
166             };
167             };
168              
169             sub delete {
170             my $self = shift;
171              
172             if( $self->{isObjectPerKey} ) {
173             $self->{object_store}->connection($self);
174              
175             $self->{object_store}->{dbh}->DROP()
176             or die $DBMS::ERROR."\n"; #shall we do a fire-safe check here?
177             } else {
178             $self->SUPER::delete();
179             };
180             };
181              
182             1;
183              
184             =pod
185              
186             =head1 NAME
187              
188             Apache::Session::DBMS - An implementation of Apache::Session using DBMS
189              
190             =head1 SYNOPSIS
191              
192             use Apache::Session::DBMS;
193              
194             tie %s, 'Apache::Session::DBMS', $sessionid, {
195             'DataSource => 'sessions',
196             'Host' => 'localhost',
197             'Port' => 1234
198             };
199              
200             # or
201             use DBMS;
202             tie %s, 'Apache::Session::DBMS', $sessionid, {
203             'DataSource => 'dbms://localhost:1234/sessions',
204             'Mode' => &DBMS::XSMODE_RDONLY #makes write operations failing
205             };
206            
207             # or if you want to deal with 'object-per-key'
208             tie %s, 'Apache::Session::DBMS', "dbms://localhost:1234/sessions/$sessionid";
209              
210             #or, if your handles are already opened:
211              
212             tie %s, 'Apache::Session::DBMS', $sessionid, {
213             'Handle' => tied(%mydbms)
214             };
215              
216             undef %s;
217              
218             =head1 DESCRIPTION
219              
220             This module is an implementation of Apache::Session. It uses DBMS to store session variables on a remote hashed storage
221             and no locking.
222              
223             The advantage of this is that it is fairly fast and allow to share session information across different machines in very
224             cheap way without requiring a full-blown RDBMS solution. The backend storage is implemented using BerkeleyDB database files.
225              
226             See also the documentation for Apache::Session::Store::DBMS for more details.
227              
228             =head1 OBJECT-PER-KEY
229              
230             The Apache::Session::DBMS module extends the core Apache::Session to deal object-per-key storage; to explain, the built in
231             Apache::Session::Store::DB_File by default just store one single key per DB file which corresponds to the actual
232             session identifier. This is can be too restrictive if the session DB is being used to store misc information like a more
233             persistent user profile for example or some global information to exchange between Apache processes. By using the original
234             Apache::Session model one would need to "invent" a session-identifer and use that to refer to ad-hoc info stored into it
235             (see the Apache:Session documentation for some hints). And then store all information into that key as a single, possibly big, BLOB
236             serialized/de-serialzied as needed. Instead, what would be more useful is to "go one level down" and let the session model
237             to deal with the perltie tied hash keys and serialize/de-serialize those separatly. This of course has the drawback that
238             each write operation on the virtual hash (STORE basically) need to serialize/de-serialize the object associated to the key.
239             To achive this the Apache::Session::DBMS module allows to define custom session-identifiers using the following notation:
240              
241             dbms://:/
242              
243             HOSTNAME is the tcp/ip IP/FQHN of the machine running the dbmsd deamon - PORT is the port is listening to. While IDENTIFIER
244             is the name of the DB (which might or might not correspond to a unique session-identifier). For example, the following would
245             store into an Apache::Session some global information on 'foo.bar.com' port '1234' DB name 'global':
246              
247             tie %global, "Apache::Session::DBMS", 'dbms://foo.bar.com:1234/global';
248              
249             $global{ 'some preference' } = 'some value';
250             $global{ 'some struct' } = { 'foo' => [ 'bar', 2, 3], 'baz' => 'value' };
251              
252             undef %global;
253              
254             or if we would have one unique session DB_File one could write
255              
256             tie %session, "Apache::Session::DB_File", $session_id, {
257             'DataSource' => 'sessions',
258             };
259              
260             $session{ 'user preference' } = 'some value';
261             $session{ 'some user defined struct' } = { 'foo' => [ 'bar', 2, 3], 'baz' => 'value' };
262              
263             undef %session;
264              
265             which would be the similarly mapped into a remote DBMS hash as:
266              
267             tie %session, "Apache::Session::DBMS", $session_id, {
268             'DataSource' => 'sessions',
269             'Port' => 1234,
270             'Host' => 'foo.bar.com'
271             };
272              
273             or even
274              
275             tie %session, "Apache::Session::DBMS", $session_id, {
276             'DataSource' => 'dbms://foo.bar.com:1234/sessions'
277             };
278              
279             If one need am 'object-per-key' remote hash instead:
280              
281             tie %session, "Apache::Session::DBMS", 'dbms://foo.bar.com:1234/sessions';
282              
283             $session{ $session_id } = {
284             'user preference' => 'some value',
285             'some user defined struct' => { 'foo' => [ 'bar', 2, 3], 'baz' => 'value' }
286             };
287              
288             undef %session;
289              
290             When the 'object-per-key' mode is on the invocation of delete() method will trigger a physical DROP
291             operation on the corresponding dbmsd database.
292              
293             =head1 USAGE
294              
295             The special Apache::Session arguments for this module are Host, Port, Mode....
296              
297             =head1 AUTHOR
298              
299             This module was written by Alberto Reggiori
300              
301             =head1 SEE ALSO
302              
303             L, L,
304             http://rdfstore.sf.net/dbms.html