File Coverage

blib/lib/Net/MCMP.pm
Criterion Covered Total %
statement 26 167 15.5
branch 2 100 2.0
condition 1 12 8.3
subroutine 8 27 29.6
pod 16 19 84.2
total 53 325 16.3


line stmt bran cond sub pod time code
1             package Net::MCMP;
2              
3 1     1   1090 use strict;
  1         3  
  1         42  
4 1     1   5 use warnings;
  1         2  
  1         33  
5              
6 1     1   906 use HTTP::Request;
  1         29527  
  1         135  
7 1     1   1108 use LWP::UserAgent;
  1         21567  
  1         215  
8              
9             our $VERSION = '0.08';
10              
11             sub new {
12 1     1 1 410 my ( $class, $ref ) = @_;
13              
14 1 50       5 unless ( exists $ref->{uri} ) {
15 0         0 die 'missing uri';
16             }
17              
18 1         4 $ref->{uri} =~ s/\s+//g;
19              
20 1         3 my $self = { _uri => $ref->{uri} };
21              
22 1 50 33     7 if ( exists $ref->{debug} && $ref->{debug} ) {
23 0         0 $self->{_debug} = 1;
24             }
25              
26 1         3 bless $self, $class;
27 1         3 return $self;
28             }
29              
30             sub uri {
31 1     1 1 324 return $_[0]->{_uri};
32             }
33              
34             sub debug {
35 0   0 0 1   return ($_[0]->{_debug} || $ENV{MCMP_TRACE} || undef);
36             }
37              
38 1         599 use constant DEFAULT_MCMP_CONFIG => {
39             Balancer => 'mycluster',
40             StickySession => 'yes',
41             StickySessionCookie => 'JSESSIONID',
42             StickySessionPath => 'jsessionid',
43             StickySessionRemove => 'no',
44             StickySessionForce => 'yes',
45             WaitWorker => 0,
46             MaxAttempts => 1,
47             JvmRoute => undef,
48             Domain => 'mycluster',
49             Host => 'localhost',
50             Port => '8009',
51             Type => 'ajp',
52             FlushPackets => 'off',
53             FlushWait => 1,
54             Ping => 10,
55             Smax => undef,
56             Ttl => 60,
57             Timeout => 0,
58             Context => undef,
59             Alias => undef,
60 1     1   11 };
  1         1  
61              
62             # FROM: https://community.jboss.org/wiki/Mod-Clusternodebalancer
63             #
64             # JvmRoute: See http://wiki.jboss.org/wiki/Mod-ClusterManagementProtocol Default: Mandatory
65             # Domain: See http://wiki.jboss.org/wiki/Mod-ClusterManagementProtocol Default: "" empty string
66             # Host: See http://wiki.jboss.org/wiki/Mod-ClusterManagementProtocol Default: "localhost"
67             # Port: See http://wiki.jboss.org/wiki/Mod-ClusterManagementProtocol Default: "8009"
68             # Type: See http://wiki.jboss.org/wiki/Mod-ClusterManagementProtocol Default: "ajp"
69             # flushpackets: Tell how to flush the packets. On: Send immediately, Auto wait for flushwait time before sending, Off don't flush. Default: "Off"
70             # flushwait: Time to wait before flushing. Value in milliseconds. Default: 10
71             # ping: Time to wait for a pong answer to a ping. 0 means we don't try to ping before sending. Value in secondes Default: 10
72             # smax: soft max inactive connection over that limit after ttl are closed. Default depends on the mpm configuration (See below for more information)
73             # ttl: max time in seconds to life for connection above smax. Default 60 seconds.
74             # Timeout: Max time httpd will wait for the backend connection. Default 0 no timeout value in seconds.
75              
76             # Balancer: Name of the balancer. max size: 40 Default: "mycluster"
77             # StickySession: Yes: use JVMRoute to stick a request to a node, No: ignore JVMRoute. Default: "Yes"
78             # StickySessionCookie: Name of the cookie containing the sessionid. Max size: 30 Default: "JSESSIONID"
79             # StickySessionPath: Name of the parametre containing the sessionid. Max size: 30. Default: "jsessionid"
80             # StickySessionRemove: Yes: remove the sessionid (cookie or parameter) when the request can't be routed to the right node. No: send it anyway. Default: "No"
81             # StickySessionForce: Yes: Return an error if the request can't be routed according to JVMRoute, No: Route it to another node. Default: "Yes"
82             # WaitWorker: value in seconds: time to wait for an available worker. Default: "0" no wait.
83             # Maxattempts: value: number of attemps to send the request to the backend server. Default: "1".
84              
85             sub config {
86 0     0 1   my ( $self, $ref ) = @_;
87              
88 0 0         unless ( ref $ref eq 'HASH' ) {
89 0           die 'passed reference must be a HASH reference';
90             }
91              
92 0           foreach my $key ( keys %{ $self->DEFAULT_MCMP_CONFIG } ) {
  0            
93 0 0         unless ( defined $ref->{$key} ) {
94 0           $ref->{$key} = $self->DEFAULT_MCMP_CONFIG->{$key};
95             }
96             }
97              
98 0 0         unless ( $ref->{JvmRoute} ) {
99 0           die 'JvmRoute is missing';
100             }
101            
102 0 0         if ( length $ref->{JvmRoute} > 80 ) {
103 0           die 'JvmRoute cannot exceed 80 characters';
104             }
105              
106 0 0         if ( length $ref->{Balancer} > 40 ) {
107 0           die 'Balancer cannot exceed 40 characters';
108             }
109              
110 0 0         if ( $ref->{StickySession} !~ /^(yes|no)$/i ) {
111 0           die 'invalid StickySession value, should be yes|no';
112             }
113              
114 0 0         if ( length $ref->{StickySessionCookie} > 30 ) {
115 0           die 'StickySessionCookie cannot exceed 30 characters';
116             }
117              
118 0 0         if ( length $ref->{StickySessionPath} > 30 ) {
119 0           die 'StickySessionCookie cannot exceed 30 characters';
120             }
121              
122 0 0         if ( $ref->{StickySessionRemove} !~ /^(yes|no)$/i ) {
123 0           die 'invalid StickySessionRemove value, should be yes|no';
124             }
125              
126 0 0         if ( $ref->{StickySessionForce} !~ /^(yes|no)$/i ) {
127 0           die 'invalid StickySessionForce value, should be yes|no';
128             }
129              
130 0 0         if ( $ref->{WaitWorker} < 0 ) {
131 0           die 'WaitWorker cannot be less than 0';
132             }
133              
134 0 0         if ( $ref->{MaxAttempts} < 1 ) {
135 0           die 'MaxAttempts cannot be less than 1';
136             }
137              
138 0 0         if ( length $ref->{Domain} > 20 ) {
139 0           die 'Domain cannot exceed 20 characters';
140             }
141              
142 0 0         if ( length $ref->{Host} > 64 ) {
143 0           die 'Host cannot exceed 64 characters';
144             }
145              
146 0 0 0       if ( length $ref->{Port} < 0 || length $ref->{Port} > 65545 ) {
147 0           die 'Port must be between 0 and 65545';
148             }
149              
150 0 0         if ( $ref->{Type} !~ /^(https|http|ajp)$/i ) {
151 0           die 'invalid Type value, should be https|http|ajp';
152             }
153              
154 0 0         if ( $ref->{FlushPackets} !~ /^(on|off|auto)$/i ) {
155 0           die 'invalid FlushPackets value, should be on|off|auto';
156             }
157              
158 0 0         if ( $ref->{FlushWait} < 0 ) {
159 0           die 'FlushWait cannot be less than 0';
160             }
161              
162 0 0         if ( $ref->{Ping} < 0 ) {
163 0           die 'Ping cannot be less than 0';
164             }
165              
166 0 0         if ( $ref->{Ttl} < 0 ) {
167 0           die 'Ttl cannot be less than 0';
168             }
169              
170 0 0         if ( $ref->{Timeout} < 0 ) {
171 0           die 'Timeout cannot be less than 0';
172             }
173              
174 0           return $self->request( 'CONFIG', $self->uri, $ref );
175              
176             }
177              
178 1         1168 use constant DEFAULT_MCMP_APP => {
179             JvmRoute => undef,
180             Context => undef,
181             Alias => undef,
182 1     1   5 };
  1         1  
183              
184             sub enable_app {
185 0     0 1   shift->_app( 'ENABLE-APP', @_ );
186             }
187              
188             sub disable_app {
189 0     0 1   shift->_app( 'DISABLE-APP', @_ );
190             }
191              
192             sub stop_app {
193 0     0 1   shift->_app( 'STOP-APP', @_ );
194             }
195              
196             sub remove_app {
197 0     0 1   shift->_app( 'REMOVE-APP', @_ );
198             }
199              
200             sub _app {
201 0     0     my ( $self, $method, $ref ) = @_;
202              
203 0 0         unless ( ref $ref eq 'HASH' ) {
204 0           die 'passed reference must be a HASH reference';
205             }
206              
207 0           foreach my $key ( keys %{ $self->DEFAULT_MCMP_APP } ) {
  0            
208 0 0         unless ( defined $ref->{$key} ) {
209 0           $ref->{$key} = $self->DEFAULT_MCMP_APP->{$key};
210             }
211             }
212              
213 0 0         unless ( $ref->{JvmRoute} ) {
214 0           die 'JvmRoute is missing';
215             }
216              
217 0 0         unless ( $ref->{Context} ) {
218 0           die 'Context is missing';
219             }
220              
221 0 0         unless ( $ref->{Alias} ) {
222 0           die 'Alias is missing';
223             }
224              
225 0           return $self->request( $method, $self->uri, $ref );
226             }
227              
228             sub enable_route {
229 0     0 1   shift->_route( 'ENABLE-APP', @_ );
230             }
231              
232             sub disable_route {
233 0     0 1   shift->_route( 'DISABLE-APP', @_ );
234             }
235              
236             sub stop_route {
237 0     0 1   shift->_route( 'STOP-APP', @_ );
238             }
239              
240             sub remove_route {
241 0     0 1   shift->_route( 'REMOVE-APP', @_ );
242             }
243              
244             sub _route {
245 0     0     my ( $self, $method, $ref ) = @_;
246              
247 0 0         unless ( ref $ref eq 'HASH' ) {
248 0           die 'passed reference must be a HASH reference';
249             }
250              
251 0 0         unless ( $ref->{JvmRoute} ) {
252 0           die 'JvmRoute is missing';
253             }
254              
255 0           return $self->request( $method, $self->uri . '/*', $ref );
256             }
257              
258             sub status {
259 0     0 1   my ( $self, $ref ) = @_;
260              
261 0 0         unless ( ref $ref eq 'HASH' ) {
262 0           die 'passed reference must be a HASH reference';
263             }
264              
265 0 0         unless ( $ref->{JvmRoute} ) {
266 0           die 'JvmRoute is missing';
267             }
268              
269 0 0         unless ( $ref->{Load} ) {
270 0           die 'Load is missing';
271             }
272 0           return $self->request( 'STATUS', $self->uri, $ref );
273             }
274              
275             sub ping {
276 0     0 1   my ( $self, $ref ) = @_;
277              
278 0 0         unless ( ref $ref eq 'HASH' ) {
279 0           die 'passed reference must be a HASH reference';
280             }
281              
282 0 0         unless ( $ref->{JvmRoute} ) {
283 0           die 'JvmRoute is missing';
284             }
285              
286 0           return $self->request( 'PING', $self->uri, $ref );
287             }
288              
289             sub dump {
290 0     0 0   my ($self) = @_;
291              
292 0           return $self->request( 'DUMP', $self->uri );
293             }
294              
295             sub info {
296 0     0 1   my ($self) = @_;
297              
298 0           return $self->request( 'INFO', $self->uri );
299             }
300              
301             sub request {
302 0     0 0   my ( $self, $method, $uri, $params ) = @_;
303              
304 0 0         unless ( exists $self->{_ua} ) {
305 0           $self->{_ua} = LWP::UserAgent->new;
306             }
307              
308 0           my $ua = $self->{_ua};
309 0           my $path = URI->new();
310 0 0         if ( defined $params ) {
311 0           foreach my $key ( qw/Context Alias/ ) {
312 0 0         next unless defined $params->{$key};
313 0           $params->{$key} =~ s/\s+//g;
314             }
315            
316 0           $path->query_form($params);
317             }
318              
319 0 0         if ( $self->debug ) {
320 0 0         if ( $path->query ) {
321 0           warn "Making a $method request to $uri with these params: "
322             . $path->query;
323             }
324             else {
325 0           warn "Making a $method request to $uri";
326             }
327              
328             }
329 0   0       my $req = HTTP::Request->new( $method, $uri, undef, $path->query || undef );
330 0           $req->header( 'Accept' => 'text/plain' );
331 0           $req->header( 'Content-Type' => 'application/x-www-form-urlencoded' );
332 0           my $response = $ua->request($req);
333              
334 0 0         if ( $response->is_success ) {
335 0 0         if ( $response->content ) {
336              
337 0 0         if ( $self->debug ) {
338 0           warn "RESPONSE: " . $response->content;
339             }
340              
341 0 0         if ( $method eq 'DUMP' ) {
    0          
342              
343             # dump parser
344 0           return $response->content;
345             }
346             elsif ( $method eq 'INFO' ) {
347              
348             # info parser
349 0           return $response->content;
350             }
351             else {
352 0           my $resp_uri = URI->new( '?' . $response->content );
353 0           my %parsed_response = $resp_uri->query_form;
354              
355             # fix return inconsistencies
356 0           foreach my $key ( keys %parsed_response ) {
357 0 0         if ( $key =~ /jvmroute/i ) {
358 0           $parsed_response{JvmRoute} = $parsed_response{$key};
359 0           delete $parsed_response{$key};
360             }
361             }
362              
363 0           return \%parsed_response;
364              
365             }
366             }
367             else {
368 0           return 1;
369             }
370             }
371             else {
372 0           $self->error( $response->header('mess') );
373 0 0         if ( $self->debug ) {
374 0 0         if ( $path->query ) {
375 0           warn "CURL for debugging: curl -X $method '$uri' -d '"
376             . $path->query . "'";
377             }
378             else {
379 0           warn "CURL for debugging: curl -X $method '$uri'";
380             }
381              
382             }
383 0           return undef;
384             }
385             }
386              
387             sub has_error {
388 0     0 0   return exists $_[0]->{_error};
389             }
390              
391             sub error {
392 0     0 1   my ( $self, $error ) = @_;
393 0 0         if ($error) {
394 0 0         if ( $self->debug ) {
395 0           warn "FAILURE: $error";
396             }
397 0           $self->{_error} = $error;
398             }
399             else {
400 0   0       return $self->{_error} || undef;
401             }
402             }
403              
404             1;
405              
406             __END__