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