1 package org.apache.maven.continuum.notification.irc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.continuum.model.project.ProjectScmRoot;
23 import org.apache.maven.continuum.configuration.ConfigurationService;
24 import org.apache.maven.continuum.model.project.BuildDefinition;
25 import org.apache.maven.continuum.model.project.BuildResult;
26 import org.apache.maven.continuum.model.project.Project;
27 import org.apache.maven.continuum.model.project.ProjectNotifier;
28 import org.apache.maven.continuum.notification.AbstractContinuumNotifier;
29 import org.apache.maven.continuum.notification.ContinuumNotificationDispatcher;
30 import org.apache.maven.continuum.notification.MessageContext;
31 import org.apache.maven.continuum.notification.NotificationException;
32 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;
33 import org.codehaus.plexus.util.StringUtils;
34 import org.schwering.irc.lib.IRCConnection;
35 import org.schwering.irc.lib.IRCConstants;
36 import org.schwering.irc.lib.IRCEventListener;
37 import org.schwering.irc.lib.IRCModeParser;
38 import org.schwering.irc.lib.IRCUser;
39 import org.schwering.irc.lib.ssl.SSLDefaultTrustManager;
40 import org.schwering.irc.lib.ssl.SSLIRCConnection;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.springframework.stereotype.Service;
44
45 import java.io.IOException;
46 import java.util.ArrayList;
47 import java.util.HashMap;
48 import java.util.List;
49 import java.util.Map;
50
51 import javax.annotation.Resource;
52
53
54
55
56
57
58
59
60 @Service("notifier#irc")
61 public class IrcContinuumNotifier
62 extends AbstractContinuumNotifier
63 implements Disposable
64 {
65 private static final Logger log = LoggerFactory.getLogger( IrcContinuumNotifier.class );
66
67
68
69
70
71 @Resource
72 private ConfigurationService configurationService;
73
74 private int defaultPort = 6667;
75
76
77
78
79 private Map<String, IRCConnection> hostConnections = new HashMap<String, IRCConnection>();
80
81 private Map<String, List<String>> channelConnections = new HashMap<String, List<String>>();
82
83
84
85
86
87 public void dispose()
88 {
89
90 for ( String key : hostConnections.keySet() )
91 {
92 IRCConnection connection = hostConnections.get( key );
93 if ( connection.isConnected() )
94 {
95 connection.doQuit( "Continuum shutting down" );
96 connection.close();
97 }
98 }
99
100 }
101
102
103
104
105 private IRCConnection getIRConnection( String host, int port, String password, String nick, String alternateNick,
106 String userName, String realName, String channel, boolean ssl )
107 throws IOException
108 {
109 String key = getConnectionKey( host, port, nick, alternateNick );
110 IRCConnection conn = hostConnections.get( key );
111 if ( conn != null )
112 {
113 checkConnection( conn, key );
114 return conn;
115 }
116
117 if ( !ssl )
118 {
119 conn = new IRCConnection( host, new int[]{port}, password, nick, userName, realName );
120 }
121 else
122 {
123 conn = new SSLIRCConnection( host, new int[]{port}, password, nick, userName, realName );
124 ( (SSLIRCConnection) conn ).addTrustManager( new SSLDefaultTrustManager() );
125 }
126
127 conn.addIRCEventListener( new Listener( conn, nick, alternateNick ) );
128 checkConnection( conn, key );
129 checkChannel( conn, key, channel );
130 hostConnections.put( key, conn );
131 return conn;
132 }
133
134 private String getConnectionKey( String host, int port, String nick, String alternateNick )
135 {
136 String nickname = nick;
137 String alternateNickName = alternateNick;
138 if ( nick == null )
139 {
140 nickname = "null";
141 }
142 if ( alternateNick == null )
143 {
144 alternateNickName = "null";
145 }
146 return host.toUpperCase() + Integer.toString( port ) + nickname.toUpperCase() + alternateNickName.toUpperCase();
147 }
148
149 private void checkConnection( IRCConnection conn, String key )
150 throws IOException
151 {
152 if ( !conn.isConnected() )
153 {
154 conn.connect();
155
156 try
157 {
158 Thread.sleep( 5000 );
159 }
160 catch ( InterruptedException e )
161 {
162
163 }
164
165
166 List<String> channels = channelConnections.get( key );
167 if ( channels != null )
168 {
169 for ( String channel : channels )
170 {
171 connectToChannel( conn, channel );
172 }
173 }
174 }
175 }
176
177 private void checkChannel( IRCConnection conn, String key, String channel )
178 {
179 List<String> channels = channelConnections.get( key );
180 if ( channels == null )
181 {
182 connectToChannel( conn, channel );
183 channels = new ArrayList<String>();
184 channels.add( channel );
185 channelConnections.put( key, channels );
186 }
187 else
188 {
189 boolean found = false;
190 for ( String c : channels )
191 {
192 if ( c.equalsIgnoreCase( channel ) )
193 {
194 found = true;
195 }
196 }
197 if ( !found )
198 {
199 channels.add( channel );
200 channelConnections.put( key, channels );
201 }
202
203
204 connectToChannel( conn, channel );
205 }
206 }
207
208 private void connectToChannel( IRCConnection conn, String channel )
209 {
210 conn.doJoin( channel );
211 }
212
213
214
215
216
217 public String getType()
218 {
219 return "irc";
220 }
221
222 public void sendMessage( String messageId, MessageContext context )
223 throws NotificationException
224 {
225 Project project = context.getProject();
226
227 List<ProjectNotifier> notifiers = context.getNotifiers();
228
229 BuildDefinition buildDefinition = context.getBuildDefinition();
230
231 BuildResult build = context.getBuildResult();
232
233 ProjectScmRoot projectScmRoot = context.getProjectScmRoot();
234
235 boolean isPrepareBuildComplete =
236 messageId.equals( ContinuumNotificationDispatcher.MESSAGE_ID_PREPARE_BUILD_COMPLETE );
237
238 if ( projectScmRoot == null && isPrepareBuildComplete )
239 {
240 return;
241 }
242
243
244
245
246
247 if ( build == null && !isPrepareBuildComplete )
248 {
249 return;
250 }
251
252
253
254
255
256 if ( messageId.equals( ContinuumNotificationDispatcher.MESSAGE_ID_BUILD_COMPLETE ) )
257 {
258 for ( ProjectNotifier notifier : notifiers )
259 {
260 buildComplete( project, notifier, build, buildDefinition );
261 }
262 }
263 else if ( isPrepareBuildComplete )
264 {
265 for ( ProjectNotifier notifier : notifiers )
266 {
267 prepareBuildComplete( projectScmRoot, notifier );
268 }
269 }
270 }
271
272 private void buildComplete( Project project, ProjectNotifier projectNotifier, BuildResult build,
273 BuildDefinition buildDef )
274 throws NotificationException
275 {
276
277
278
279
280 BuildResult previousBuild = getPreviousBuild( project, buildDef, build );
281
282 if ( !shouldNotify( build, previousBuild, projectNotifier ) )
283 {
284 return;
285 }
286
287 sendMessage( projectNotifier.getConfiguration(), generateMessage( project, build, configurationService ) );
288 }
289
290 private void prepareBuildComplete( ProjectScmRoot projectScmRoot, ProjectNotifier projectNotifier )
291 throws NotificationException
292 {
293
294
295
296
297 if ( !shouldNotify( projectScmRoot, projectNotifier ) )
298 {
299 return;
300 }
301
302 sendMessage( projectNotifier.getConfiguration(), generateMessage( projectScmRoot, configurationService ) );
303 }
304
305 private void sendMessage( Map<String, String> configuration, String message )
306 throws NotificationException
307 {
308
309
310
311
312 String host = configuration.get( "host" );
313
314 String portAsString = configuration.get( "port" );
315 int port = defaultPort;
316 if ( portAsString != null )
317 {
318 port = Integer.parseInt( portAsString );
319 }
320 String channel = configuration.get( "channel" );
321
322 String nickName = configuration.get( "nick" );
323
324 if ( StringUtils.isEmpty( nickName ) )
325 {
326 nickName = "continuum";
327 }
328
329 String alternateNickName = configuration.get( "alternateNick" );
330
331 if ( StringUtils.isEmpty( alternateNickName ) )
332 {
333 alternateNickName = "continuum_";
334 }
335
336 String userName = configuration.get( "username" );
337
338 if ( StringUtils.isEmpty( userName ) )
339 {
340 userName = nickName;
341 }
342
343 String fullName = configuration.get( "fullName" );
344
345 if ( StringUtils.isEmpty( fullName ) )
346 {
347 fullName = nickName;
348 }
349
350 String password = configuration.get( "password" );
351
352 boolean isSsl = Boolean.parseBoolean( configuration.get( "ssl" ) );
353
354 try
355 {
356 IRCConnection ircConnection = getIRConnection( host, port, password, nickName, alternateNickName, userName,
357 fullName, channel, isSsl );
358 ircConnection.doPrivmsg( channel, message );
359 }
360 catch ( IOException e )
361 {
362 throw new NotificationException( "Exception while checkConnection to irc ." + host, e );
363 }
364 }
365
366
367
368
369 class Listener
370 implements IRCEventListener
371 {
372 private String nick;
373
374 private String alternateNick;
375
376 private IRCConnection conn;
377
378 public Listener( IRCConnection conn, String nick, String alternateNick )
379 {
380 this.conn = conn;
381 this.nick = nick;
382 this.alternateNick = alternateNick;
383 }
384
385 public void onRegistered()
386 {
387 log.info( "Connected" );
388 }
389
390 public void onDisconnected()
391 {
392 log.info( "Disconnected" );
393 }
394
395 public void onError( String msg )
396 {
397 log.error( "Error: " + msg );
398 }
399
400 public void onError( int num, String msg )
401 {
402 log.error( "Error #" + num + ": " + msg );
403 if ( num == IRCConstants.ERR_NICKNAMEINUSE )
404 {
405 if ( alternateNick != null )
406 {
407 log.info( "reconnection with alternate nick: '" + alternateNick + "'" );
408 try
409 {
410 boolean ssl = false;
411 if ( conn instanceof SSLIRCConnection )
412 {
413 ssl = true;
414 }
415 String key = getConnectionKey( conn.getHost(), conn.getPort(), nick, alternateNick );
416 conn = getIRConnection( conn.getHost(), conn.getPort(), conn.getPassword(), alternateNick, null,
417 conn.getUsername(), conn.getRealname(), "#foo", ssl );
418 hostConnections.put( key, conn );
419 }
420 catch ( IOException e )
421 {
422 e.printStackTrace();
423 }
424 }
425 }
426 }
427
428 public void onInvite( String chan, IRCUser u, String nickPass )
429 {
430 if ( log.isDebugEnabled() )
431 {
432 log.debug( chan + "> " + u.getNick() + " invites " + nickPass );
433 }
434 }
435
436 public void onJoin( String chan, IRCUser u )
437 {
438 if ( log.isDebugEnabled() )
439 {
440 log.debug( chan + "> " + u.getNick() + " joins" );
441 }
442 }
443
444 public void onKick( String chan, IRCUser u, String nickPass, String msg )
445 {
446 if ( log.isDebugEnabled() )
447 {
448 log.debug( chan + "> " + u.getNick() + " kicks " + nickPass );
449 }
450 }
451
452 public void onMode( IRCUser u, String nickPass, String mode )
453 {
454 if ( log.isDebugEnabled() )
455 {
456 log.debug( "Mode: " + u.getNick() + " sets modes " + mode + " " + nickPass );
457 }
458 }
459
460 public void onMode( String chan, IRCUser u, IRCModeParser mp )
461 {
462 if ( log.isDebugEnabled() )
463 {
464 log.debug( chan + "> " + u.getNick() + " sets mode: " + mp.getLine() );
465 }
466 }
467
468 public void onNick( IRCUser u, String nickNew )
469 {
470 if ( log.isDebugEnabled() )
471 {
472 log.debug( "Nick: " + u.getNick() + " is now known as " + nickNew );
473 }
474 }
475
476 public void onNotice( String target, IRCUser u, String msg )
477 {
478 log.info( target + "> " + u.getNick() + " (notice): " + msg );
479 }
480
481 public void onPart( String chan, IRCUser u, String msg )
482 {
483 if ( log.isDebugEnabled() )
484 {
485 log.debug( chan + "> " + u.getNick() + " parts" );
486 }
487 }
488
489 public void onPrivmsg( String chan, IRCUser u, String msg )
490 {
491 if ( log.isDebugEnabled() )
492 {
493 log.debug( chan + "> " + u.getNick() + ": " + msg );
494 }
495 }
496
497 public void onQuit( IRCUser u, String msg )
498 {
499 if ( log.isDebugEnabled() )
500 {
501 log.debug( "Quit: " + u.getNick() );
502 }
503 }
504
505 public void onReply( int num, String value, String msg )
506 {
507 log.info( "Reply #" + num + ": " + value + " " + msg );
508 }
509
510 public void onTopic( String chan, IRCUser u, String topic )
511 {
512 if ( log.isDebugEnabled() )
513 {
514 log.debug( chan + "> " + u.getNick() + " changes topic into: " + topic );
515 }
516 }
517
518 public void onPing( String p )
519 {
520 if ( log.isDebugEnabled() )
521 {
522 log.debug( "Ping:" + p );
523 }
524 }
525
526 public void unknown( String a, String b, String c, String d )
527 {
528 if ( log.isDebugEnabled() )
529 {
530 log.debug( "UNKNOWN: " + a + " b " + c + " " + d );
531 }
532 }
533 }
534 }