/[meteor]/googlecode.com/svn/trunk/Meteor/Channel.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /googlecode.com/svn/trunk/Meteor/Channel.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 47 - (show annotations)
Mon Feb 4 21:06:42 2008 UTC (16 years, 1 month ago) by knops.gerd
File size: 8033 byte(s)
• syslog change: If `SyslogFacility` is set to `none`, meteord will not put itself into the background and print all syslog messages with a priority higher than `debug` to standard output. The output will be prefixed with a timestamp (unix time in seconds) and a tab character.

• New syslog messges: joinchannel, leavechannel, document


1 #!/usr/bin/perl -w
2 ###############################################################################
3 # Meteor
4 # An HTTP server for the 2.0 web
5 # Copyright (c) 2006 contributing authors
6 #
7 # Subscriber.pm
8 #
9 # Description:
10 # A Meteor Channel
11 #
12 ###############################################################################
13 #
14 # This program is free software; you can redistribute it and/or modify it
15 # under the terms of the GNU General Public License as published by the Free
16 # Software Foundation; either version 2 of the License, or (at your option)
17 # any later version.
18 #
19 # This program is distributed in the hope that it will be useful, but WITHOUT
20 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
22 # more details.
23 #
24 # You should have received a copy of the GNU General Public License along
25 # with this program; if not, write to the Free Software Foundation, Inc.,
26 # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #
28 # For more information visit www.meteorserver.org
29 #
30 ###############################################################################
31
32 package Meteor::Channel;
33 ###############################################################################
34 # Configuration
35 ###############################################################################
36
37 use strict;
38
39 use Meteor::Message;
40
41 our %Channels=();
42 our $MessageID=0;
43
44 ###############################################################################
45 # Class methods
46 ###############################################################################
47 sub channelWithName {
48 my $class=shift;
49 my $channelName=shift;
50 my $avoidCreation=shift;
51
52 unless(exists($Channels{$channelName}))
53 {
54 return undef if($avoidCreation);
55 #
56 # Create new channel
57 #
58 $Channels{$channelName}=$class->newChannel($channelName);
59
60 &::syslog('debug',"New channel $channelName");
61 }
62
63 return $Channels{$channelName};
64 }
65
66 sub listChannels {
67 my $class=shift;
68
69 my $list='';
70 foreach my $channelName (sort keys %Channels)
71 {
72 my $channel=$Channels{$channelName};
73
74 $list.=$channelName.'('.$channel->messageCount().'/'.$channel->subscriberCount().")$::CRLF";
75 }
76
77 $list;
78 }
79
80 sub listChannelsUsingTemplate {
81 my $class=shift;
82 my $template=shift;
83
84 return '' unless(defined($template) && $template ne '');
85
86 my $list='';
87 foreach my $channelName (sort keys %Channels)
88 {
89 my $channel=$Channels{$channelName};
90
91 $list.=$channel->descriptionWithTemplate($template);
92 }
93
94 $list;
95 }
96
97 sub deleteChannel {
98 my $class=shift;
99 my $channelName=shift;
100
101 delete($Channels{$channelName});
102 }
103
104 sub trimMessageStoresByTimestamp {
105 my $class=shift;
106 my $minTimeStamp=shift;
107
108 return unless($minTimeStamp);
109
110 map { $_->trimMessageStoreByTimestamp($minTimeStamp) } (values %Channels);
111 }
112
113 sub clearAllBuffers {
114 my $class=shift;
115
116 map { $_->clearBuffer() } (values %Channels);
117 }
118
119 sub numChannels {
120
121 return scalar(keys %Channels);
122 }
123
124 ###############################################################################
125 # Factory methods
126 ###############################################################################
127 sub new {
128 #
129 # Create a new empty instance
130 #
131 my $class=shift;
132
133 my $obj={};
134
135 bless($obj,$class);
136 }
137
138 sub newChannel {
139 #
140 # new instance from new server connection
141 #
142 my $self=shift->new();
143
144 my $name=shift;
145 $self->{'name'}=$name;
146
147 $self->{'subscribers'}=[];
148 $self->{'messages'}=[];
149
150 $self;
151 }
152
153 sub DESTROY {
154 my $self=shift;
155
156 my @subscribers=@{$self->{'subscribers'}};
157 map { $_->closeChannel($self->{'name'}) } @subscribers;
158 }
159
160 ###############################################################################
161 # Instance methods
162 ###############################################################################
163 sub name {
164 shift->{'name'};
165 }
166
167 sub addSubscriber {
168 my $self=shift;
169 my $subscriber=shift;
170 my $startId=shift;
171 my $persist=shift;
172 my $mode=shift || '';
173 my $userAgent=shift || '';
174
175 # Note: negative $startId means go back that many messages
176
177 push(@{$self->{'subscribers'}},$subscriber) if($persist);
178
179 &::syslog('info','',
180 'joinchannel',
181 $subscriber->{'subscriberID'},
182 $self->{'name'},
183 $mode,
184 $startId,
185 $userAgent
186 );
187
188 my $startIndex=$self->indexForMessageID($startId);
189 return unless(defined($startIndex));
190
191 my $msgCount=scalar(@{$self->{'messages'}});
192 my $txt='';
193
194 $startIndex=0 if($startIndex<0);
195
196 if($startIndex<$msgCount)
197 {
198 $subscriber->sendMessages(@{$self->{'messages'}}[$startIndex,$msgCount-1]);
199 }
200 }
201
202 sub removeSubscriber {
203 my $self=shift;
204 my $subscriber=shift;
205 my $reason=shift ||'unknown';
206
207 my $idx=undef;
208 for(my $i=0;$i<scalar(@{$self->{'subscribers'}});$i++)
209 {
210 if($self->{'subscribers'}->[$i]==$subscriber)
211 {
212 $idx=$i;
213 last;
214 }
215 }
216
217 if(defined($idx))
218 {
219 splice(@{$self->{'subscribers'}},$idx,1);
220
221 &::syslog('info','',
222 'leavechannel',
223 $subscriber->{'subscriberID'},
224 $self->{'name'},
225 $subscriber->{'ConnectionStart'},
226 $subscriber->{'MessageCount'},
227 $subscriber->{'bytesWritten'},
228 $reason
229 );
230 }
231
232 $self->checkExpiration();
233 }
234
235 sub subscriberCount {
236 my $self=shift;
237
238 scalar(@{$self->{'subscribers'}});
239 }
240
241 sub addMessage {
242 my $self=shift;
243 my $messageText=shift;
244
245 my $message=Meteor::Message->newWithID($MessageID++);
246 $message->setText($messageText);
247 $message->setChannelName($self->{'name'});
248 push(@{$self->{'messages'}},$message);
249
250 $self->trimMessageStoreBySize();
251
252 map { $_->sendMessages($message) } @{$self->{'subscribers'}};
253
254 $message;
255 }
256
257 sub messageCount {
258 my $self=shift;
259
260 scalar(@{$self->{'messages'}});
261 }
262
263 sub trimMessageStoreBySize {
264 my $self=shift;
265
266 my $numMessages=scalar(@{$self->{'messages'}});
267
268 if($numMessages>$::CONF{'MaxMessagesPerChannel'})
269 {
270 splice(@{$self->{'messages'}},0,-$::CONF{'MaxMessagesPerChannel'});
271 }
272 }
273
274 sub trimMessageStoreByTimestamp {
275 my $self=shift;
276 my $ts=shift;
277
278 while(scalar(@{$self->{'messages'}})>0 && $self->{'messages'}->[0]->timestamp()<$ts)
279 {
280 my $msg=shift(@{$self->{'messages'}});
281 }
282
283 $self->checkExpiration();
284 }
285
286 sub clearBuffer {
287 my $self=shift;
288
289 $self->{'messages'}=[];
290
291 $self->checkExpiration();
292 }
293
294 sub checkExpiration {
295 my $self=shift;
296
297 if($self->messageCount()==0 && $self->subscriberCount()==0)
298 {
299 my $name=$self->name();
300 &::syslog('debug',"Channel expired: $name");
301 $self->deleteChannel($name);
302 }
303 }
304
305 sub indexForMessageID {
306 my $self=shift;
307 my $id=shift;
308
309 # the messages is always sorted by ID, so we can
310 # use a binary search to find the message.
311 # return undef if there are no messages or the
312 # ID is that of the last message.
313 # Otherwise return the ID of the found message
314 # of if no message with that ID exists the one
315 # with the next higher ID
316 #
317 return undef unless(defined($id));
318
319 my $numMessages=scalar(@{$self->{'messages'}});
320
321 return undef unless($numMessages);
322 return -1 unless($id ne '');
323
324 # Note: negative $id means go back that many messages
325 return $numMessages+$id if($id<0);
326
327 my $low=0;
328 my $high=$numMessages-1;
329 my $mid;
330 my $cond;
331 while($low<=$high)
332 {
333 $mid=($low+$high)>>1;
334 $cond=$id <=> $self->{'messages'}->[$mid]->id();
335 if($cond<0)
336 {
337 $high=$mid-1;
338 }
339 elsif($cond>0)
340 {
341 $low=$mid+1;
342 }
343 else
344 {
345 return $mid;
346 }
347 }
348
349 return undef if($low>=$numMessages);
350
351 return $low;
352 }
353
354 sub lastMsgID {
355 my $self=shift;
356
357 my $numMessages=scalar(@{$self->{'messages'}});
358
359 return 'undefined' unless($numMessages>0);
360
361 @{$self->{'messages'}}[-1]->id();
362 }
363
364 sub descriptionWithTemplate {
365 my $self=shift;
366 my $template=shift;
367
368 $template=~s/~([a-zA-Z0-9_]*)~/
369 if(!defined($1) || $1 eq '')
370 {
371 '~';
372 }
373 elsif($1 eq 'messageCount')
374 {
375 $self->messageCount();
376 }
377 elsif($1 eq 'subscriberCount')
378 {
379 $self->subscriberCount();
380 }
381 elsif($1 eq 'lastMsgID')
382 {
383 $self->lastMsgID();
384 }
385
386 elsif(exists($self->{$1}))
387 {
388 $self->{$1};
389 }
390 else
391 {
392 '';
393 }
394 /gex;
395
396 $template;
397 }
398
399 1;
400 ############################################################################EOF

  ViewVC Help
Powered by ViewVC 1.1.26