]> jfr.im git - irc/rizon/acid.git/blob - acid/src/main/java/net/rizon/acid/core/Acidictive.java
c507f414b2a75261fe804ef74cb95a64a3837033
[irc/rizon/acid.git] / acid / src / main / java / net / rizon / acid / core / Acidictive.java
1 package net.rizon.acid.core;
2
3 import net.rizon.acid.plugins.Plugin;
4 import com.google.common.eventbus.EventBus;
5 import io.netty.util.concurrent.ScheduledFuture;
6 import java.net.MalformedURLException;
7 import java.sql.PreparedStatement;
8 import java.sql.ResultSet;
9 import java.sql.SQLException;
10 import java.util.Arrays;
11 import java.util.Date;
12 import java.util.List;
13 import java.util.concurrent.TimeUnit;
14 import net.rizon.acid.capab.QuitStorm;
15 import net.rizon.acid.conf.Client;
16 import net.rizon.acid.conf.Config;
17 import net.rizon.acid.conf.PluginDesc;
18 import net.rizon.acid.events.EventCTCP;
19 import net.rizon.acid.events.EventCTCPReply;
20 import net.rizon.acid.events.EventChanMode;
21 import net.rizon.acid.events.EventCommandCertFPMismatch;
22 import net.rizon.acid.events.EventEOB;
23 import net.rizon.acid.events.EventEncap;
24 import net.rizon.acid.events.EventInvite;
25 import net.rizon.acid.events.EventJoin;
26 import net.rizon.acid.events.EventKick;
27 import net.rizon.acid.events.EventKill;
28 import net.rizon.acid.events.EventNickChange;
29 import net.rizon.acid.events.EventNotice;
30 import net.rizon.acid.events.EventPart;
31 import net.rizon.acid.events.EventPrivmsg;
32 import net.rizon.acid.events.EventQuit;
33 import net.rizon.acid.events.EventServerLink;
34 import net.rizon.acid.events.EventServerNotice;
35 import net.rizon.acid.events.EventSync;
36 import net.rizon.acid.events.EventUserConnect;
37 import net.rizon.acid.events.EventWebIRC;
38 import net.rizon.acid.events.ExceptionHandler;
39 import net.rizon.acid.logging.LoggerUtils;
40 import net.rizon.acid.plugins.PluginManager;
41 import net.rizon.acid.sql.SQL;
42 import net.rizon.acid.util.Blowfish;
43 import net.rizon.acid.plugins.ClassLoader;
44 import net.rizon.acid.util.CloakGenerator;
45 import net.rizon.acid.util.Util;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 public class Acidictive extends AcidCore
50 {
51 private static final Logger log = LoggerFactory.getLogger(Acidictive.class);
52 // logs here don't go to irc
53 public static final Logger fileLogger = LoggerFactory.getLogger("file");
54
55 public static CloakGenerator cloakGenerator;
56 public static Config conf;
57 public static long startTime;
58 public static int syncTime;
59 public static SQL acidcore_sql;
60 public static char bold = '\u0002';
61
62 public static ClassLoader loader; // loader for the core commands
63
64 public static EventBus eventBus = new EventBus(new ExceptionHandler());
65 private static QuitStorm quitStorm;
66
67 public static String getVersion()
68 {
69 return "acid-" + Version.PROJECT_VERSION + "-" + Version.GIT_REVISION_SHORT;
70 }
71
72 public static void main(String[] args)
73 {
74 LoggerUtils.initUncaughtExceptionHandler();
75
76 log.info(getVersion() + " starting up...");
77
78 try
79 {
80 conf = (Config) Config.load("acidictive.yml", Config.class);
81 }
82 catch (Exception ex)
83 {
84 log.error("Unable to start due to configuration problems", ex);
85 System.exit(1);
86 }
87
88 try
89 {
90 cloakGenerator = new CloakGenerator(conf.serverinfo.cloakkeys, conf.serverinfo.network + "-");
91 }
92 catch (IllegalArgumentException ex)
93 {
94 log.error(null, ex);
95 System.exit(1);
96 }
97
98 quitStorm = new QuitStorm(eventBus, User.getUsersC());
99
100 AcidCore.start(conf.uplink.host, conf.uplink.port, conf.serverinfo.name, conf.serverinfo.description, conf.uplink.pass, conf.serverinfo.id, conf.uplink.ssl);
101
102 acidcore_sql = SQL.getConnection("acidcore");
103 if (acidcore_sql == null)
104 {
105 log.error("Unable to get connection for `acidcore`");
106 System.exit(-1);
107 }
108
109 try
110 {
111 loader = new ClassLoader("net.rizon.acid.commands.");
112 }
113 catch (MalformedURLException ex)
114 {
115 log.error(null, ex);
116 System.exit(-1);
117 }
118
119 if (conf.clients != null)
120 for (Client c : conf.clients)
121 new AcidUser(null, c);
122
123 if (conf.plugins != null)
124 for (PluginDesc desc : conf.plugins)
125 {
126 try
127 {
128 Plugin p = PluginManager.loadPlugin(desc.getGroupId(), desc.getArtifactId(), desc.getVersion());
129 if (p == null)
130 {
131 log.error("Unable to load plugin {}", desc);
132 System.exit(-1);
133 }
134
135 log.info("Loaded plugin " + p.getName());
136 }
137 catch (Exception ex)
138 {
139 log.error("Unable to load plugin " + desc, ex);
140 System.exit(-1);
141 }
142 }
143
144 try
145 {
146 AcidCore.run();
147 }
148 catch (InterruptedException ex)
149 {
150 log.error(null, ex);
151 }
152
153 acidcore_sql.shutdown();
154
155 System.exit(0);
156 }
157
158 public static void onStart()
159 {
160 startTime = new Date().getTime();
161
162 for (String s : User.getUsers())
163 {
164 User u = User.findUser(s);
165 if (!(u instanceof AcidUser))
166 continue;
167
168 ((AcidUser) u).introduce();
169 }
170
171 Protocol.eob();
172 }
173
174 public static void onNick(User user)
175 {
176 EventUserConnect event = new EventUserConnect();
177 event.setUser(user);
178 eventBus.post(event);
179 }
180
181 public static void onNickChange(User user, String oldnick)
182 {
183 EventNickChange event = new EventNickChange();
184 event.setU(user);
185 event.setOldNick(oldnick);
186 eventBus.post(event);
187 }
188
189 public static void onEOB(Server server)
190 {
191 EventEOB event = new EventEOB();
192 event.setServer(server);
193 eventBus.post(event);
194 }
195
196 public static void onJoin(Channel channel, User[] users)
197 {
198 EventJoin event = new EventJoin();
199 event.setChannel(channel);
200 event.setUsers(users);
201 eventBus.post(event);
202 }
203
204 public static void onPart(User user, Channel channel)
205 {
206 EventPart event = new EventPart();
207 event.setChannel(channel);
208 event.setUser(user);
209 eventBus.post(event);
210 }
211
212 public static void onQuit(User user, String msg)
213 {
214 EventQuit event = new EventQuit();
215 event.setUser(user);
216 event.setMsg(msg);
217 eventBus.post(event);
218 }
219
220 public static void onServer(Server server)
221 {
222 EventServerLink event = new EventServerLink();
223 event.setServer(server);
224 eventBus.post(event);
225 }
226
227 public static void onPrivmsg(String creator, String recipient, String msg)
228 {
229 try
230 {
231 User x = User.findUser(creator);
232
233 EventPrivmsg event = new EventPrivmsg();
234 event.setCreator(creator);
235 event.setRecipient(recipient);
236 event.setMsg(msg);
237 eventBus.post(event);
238
239 if (event.isStopProcessing())
240 return;
241
242 // All channel commands start with .
243 if (recipient.startsWith("#") && !msg.startsWith(conf.general.command_prefix))
244 return;
245
246 if (x.getIdentNick().isEmpty())
247 {
248 // Only lookup flags for people who are +o and +r
249 if (x.hasMode("o"))
250 {
251 // However +o and -r can do some commands (identify!)
252 if (!x.getSU().isEmpty())
253 {
254 x.loadAccess(x);
255
256 // Try to save a certfp if possible
257 //if (!x.getFlags().isEmpty())
258 // Access.addCertFP(x, true);
259 }
260 // If +o-r, try to auth via cert; in case services are gone
261 else if (!x.getCertFP().isEmpty())
262 {
263 // There might also be a CertFP for said user to use, check!
264 try
265 {
266 PreparedStatement ps = Acidictive.acidcore_sql.prepare("SELECT `certfp` FROM `access` WHERE `user` = ?");
267 ps.setString(1, x.getSU());
268 ResultSet rs = Acidictive.acidcore_sql.executeQuery(ps);
269 if (rs.next() && !rs.getString("certfp").isEmpty())
270 {
271 if (rs.getString("certfp").equalsIgnoreCase(x.getCertFP()))
272 {
273 x.loadAccess(x);
274 }
275 else
276 {
277 EventCommandCertFPMismatch e = new EventCommandCertFPMismatch();
278 e.setU(x);
279 e.setCertfp(rs.getString("certfp"));
280 eventBus.post(event);
281 }
282 }
283 }
284 catch (SQLException ex)
285 {
286 log.warn("Unable to load certfp access", ex);
287 }
288 }
289 }
290 }
291
292 // Not destined for anything else, must be a command.
293 String[] parts = msg.split("\\s+");
294 // strip leading "." if needed
295 String command = (parts[0].startsWith(conf.general.command_prefix) ? parts[0].substring(conf.general.command_prefix.length()) : parts[0]).toLowerCase();
296 String[] args = Arrays.copyOfRange(parts, 1, parts.length);
297
298 Channel c = null;
299 AcidUser to = null;
300 net.rizon.acid.conf.Command confCommand = null;
301 if (recipient.startsWith("#"))
302 {
303 c = Channel.findChannel(recipient);
304 if (c == null)
305 return;
306
307 // lol?
308 for (String s : c.getUsers())
309 {
310 User u = User.findUser(s);
311 if (u instanceof AcidUser)
312 {
313 AcidUser t = (AcidUser) u;
314 net.rizon.acid.conf.Command cmd = t.findConfCommand(command, c.getName());
315
316 if (cmd != null)
317 {
318 to = t;
319 confCommand = cmd;
320 }
321 }
322 }
323
324 if (to == null)
325 return;
326 }
327 else
328 {
329 int i = recipient.indexOf('@');
330 if (i != -1)
331 recipient = recipient.substring(0, i);
332
333 User targ = User.findUser(recipient);
334 if (targ == null || !(targ instanceof AcidUser))
335 return;
336
337 to = (AcidUser) targ;
338 confCommand = to.findConfCommand(command, null);
339 }
340
341 if (confCommand == null)
342 return;
343
344 // This isn't really necessary, but is faster than causing the core loader to fail and then fall back
345 // by searching the plugin loaders.
346 Plugin p = to.pkg;
347 // Use our loader if there is no plugin
348 ClassLoader cl = p != null ? p.loader : loader;
349 Class<?> commandClass = cl.loadClass(confCommand.clazz);
350 if (commandClass == null)
351 return;
352
353 if (recipient.startsWith("#"))
354 {
355 // In the proper channels commands can go without access checks. (ish)
356 if (!confCommand.allowsChannel(recipient))
357 {
358 return; // Access
359 }
360
361 // Now that we're in the right channel check for just being logged in OR +o OR spoof
362 if (!x.getIdentNick().isEmpty() || x.hasMode("o") || x.getIP().equals("0"))
363 {
364 // ok
365 }
366 else
367 {
368 // Only allow explicit "anyone" commands
369 if (confCommand.privilege == null || !confCommand.privilege.equals("anyone"))
370 return;
371 }
372 }
373 // Message to me, check priv
374 else
375 {
376 if (!x.hasMode("o"))
377 {
378 if (!"anyone".equals(confCommand.privilege))
379 {
380 // Only accept commands that are explicitly accessible
381 // by anyone.
382 return;
383 }
384 }
385 else
386 {
387 // Non priv commands can be used by any oper
388 if (confCommand.privilege == null)
389 {
390 log.info("Denied access to " + confCommand.name + " to " + x + " [command has no privilege]");
391 // Commands with no priv can not be executed in PM
392 return;
393 }
394
395 // Explicitly requires no privilege
396 if (confCommand.privilege.equals("none") || "anyone".equals(confCommand.privilege))
397 ;
398 else if (x.hasFlags(confCommand.privilege) == false)
399 {
400 log.info("Denied access to " + confCommand.name + " to " + x + " [user has no priv]");
401 return;
402 }
403 }
404 }
405
406 Command cmd = (Command) commandClass.newInstance();
407
408 if (args.length < cmd.GetMinArgs())
409 {
410 reply(x, to, c, "Syntax error for \2" +
411 confCommand.name + "\2. Available commands are:");
412 cmd.onHelp(x, to, c);
413 return; // Syntax
414 }
415
416 if (args.length > cmd.GetMaxArgs() && cmd.GetMaxArgs() > 0)
417 {
418 String last = arrayFormat(args, cmd.GetMaxArgs() - 1, args.length - 1);
419 String[] parsed_args = new String[cmd.GetMaxArgs()];
420 for (int i = 0; i < cmd.GetMaxArgs() - 1; ++i)
421 parsed_args[i] = args[i];
422 parsed_args[cmd.GetMaxArgs() - 1] = last;
423 args = parsed_args;
424 }
425
426 try
427 {
428 cmd.Run(x, to, c, args);
429
430 /* log SUCCESSFUL privmsg command usages as well
431 * except help because that's just silly and nobody cares.
432 */
433 if (!command.equalsIgnoreCase("help")
434 && !recipient.startsWith("#"))
435 privmsg(conf.getChannelNamed("cmdlogchan"), x.getNick() + ": " + msg);
436 }
437 catch (Exception ex)
438 {
439 log.warn("Error running command " + confCommand.name, ex);
440 }
441 }
442 catch (Exception e)
443 {
444 log.error("Error processing PRIVMSG: " + msg, e);
445 }
446 }
447
448 public static void onCtcp(String creator, String recipient, String msg)
449 {
450 boolean stop = false;
451
452 if (msg.startsWith("\1VERSION"))
453 {
454 User u = User.findUser(recipient);
455 if (u instanceof AcidUser)
456 {
457 AcidUser au = (AcidUser) u;
458 if (au.client != null && au.client.version != null)
459 Acidictive.notice(au.getUID(), creator, "\1VERSION " + au.client.version + "\1");
460 }
461 }
462
463 EventCTCP eventCtcp = new EventCTCP();
464 eventCtcp.setCreator(creator);
465 eventCtcp.setRecipient(recipient);
466 eventCtcp.setMsg(msg);
467
468 eventBus.post(eventCtcp);
469 }
470
471 public static void onCtcpReply(User source, String target, String message)
472 {
473 EventCTCPReply event = new EventCTCPReply();
474 event.setSource(source);
475 event.setTarget(target);
476 event.setMessage(message);
477 eventBus.post(event);
478 }
479
480 public static void onNotice(String creator, String recipient, String msg)
481 {
482 if (creator.equalsIgnoreCase("nickserv") && msg.matches("^.*registered\\s+and\\s+protected.*$"))
483 {
484 AcidUser au = (AcidUser) User.findUser(recipient);
485 if (au != null && au.getNSPass() != null && !au.getNSPass().isEmpty())
486 Protocol.privmsg(recipient, creator, "IDENTIFY " + au.getNSPass());
487 return;
488 }
489
490 EventNotice eventNotice = new EventNotice();
491 eventNotice.setCreator(creator);
492 eventNotice.setRecipient(recipient);
493 eventNotice.setMsg(msg);
494 eventBus.post(eventNotice);
495 }
496
497 public static void onServerNotice(String source, String recipient, String msg)
498 {
499 EventServerNotice event = new EventServerNotice();
500 event.setSource(source);
501 event.setRecipient(recipient);
502 event.setMsg(msg);
503 eventBus.post(event);
504 }
505
506 public static void onDie(String msg)
507 {
508 log.info(msg);
509 }
510
511 public static void onSync()
512 {
513 syncTime = (int) new Date().getTime() - (int) startTime;
514 log.info("Synced in " + (double) syncTime / 1000 + " seconds.");
515
516 EventSync event = new EventSync();
517 eventBus.post(event);
518 }
519
520 public static void onKill(final String killer, User user, final String reason)
521 {
522 EventKill eventKill = new EventKill();
523 eventKill.setKiller(killer);
524 eventKill.setUser(user);
525 eventKill.setReason(reason);
526 eventBus.post(eventKill);
527 }
528
529 public static int getUptimeTS()
530 {
531 return getTS() - (int) ((double) startTime / 1000);
532 }
533
534 public static String getUptime()
535 {
536 return Util.formatTime(getUptimeTS());
537 }
538
539 public static void onKick(String kicker, User victim, Channel channel, String reason)
540 {
541 EventKick eventKick = new EventKick();
542 eventKick.setKicker(kicker);
543 eventKick.setVictim(victim);
544 eventKick.setChannel(channel);
545 eventKick.setReason(reason);
546 eventBus.post(eventKick);
547
548 if (victim.getServer() == AcidCore.me)
549 {
550 AcidUser au = (AcidUser) victim;
551 au.joinChan(channel.getName());
552 }
553 }
554
555 public static void onInvite(User inviter, User invitee, Channel channel)
556 {
557 EventInvite eventInvite = new EventInvite();
558 eventInvite.setInviter(inviter);
559 eventInvite.setInvitee(invitee);
560 eventInvite.setChannel(channel);
561 eventBus.post(eventInvite);
562 }
563
564 public static void onMode(String creator, String recipient, String modes)
565 {
566 }
567
568 public static void onChanMode(String creator, Channel chan, String modes)
569 {
570 String[] x = modes.split("\\s+");
571 if (x.length >= 1)
572 {
573 boolean give = true;
574 int whatnick = 1;
575 String m;
576 for (int i = 0; i < x[0].length(); i++)
577 {
578 m = x[0].substring(i, i + 1);
579
580 if (m.equals("+"))
581 give = true;
582 else if (m.equals("-"))
583 give = false;
584 else if (m.matches("(b|I|e)"))
585 whatnick++;
586 else if (m.equals("k"))
587 {
588 if (give)
589 chan.setKey(x[whatnick]);
590 else
591 chan.setKey(null);
592
593 ++whatnick;
594
595 if (give)
596 chan.setMode(m.charAt(0));
597 else
598 chan.unsetMode(m.charAt(0));
599 }
600 else if (m.contains("l"))
601 {
602 if (give)
603 {
604 chan.setLimit(Integer.parseInt(x[whatnick]));
605 whatnick++;
606 }
607 else
608 chan.setLimit(0);
609
610 if (give)
611 chan.setMode(m.charAt(0));
612 else
613 chan.unsetMode(m.charAt(0));
614 }
615 else if (m.matches("(v|h|o|a|q)"))
616 {
617 String targStr = x[whatnick];
618 ++whatnick;
619
620 User target = User.findUser(targStr);
621 if (target == null)
622 continue;
623
624 Membership mem = chan.findUser(target);
625 if (mem == null)
626 continue;
627
628 if (give)
629 mem.addMode(m);
630 else
631 mem.remMode(m);
632 }
633 else
634 {
635 if (give)
636 chan.setMode(m.charAt(0));
637 else
638 chan.unsetMode(m.charAt(0));
639 }
640 }
641 }
642
643 EventChanMode event = new EventChanMode();
644 event.setChan(chan);
645 event.setModes(modes);
646 event.setPrefix(creator);
647 eventBus.post(event);
648 }
649
650 public static void onWebIRC(Server source, String operation, String uid, String realhost, String sockhost, String webircPassword, String webircUsername, String fakeHost, String fakeIp)
651 {
652 EventWebIRC event = new EventWebIRC();
653 event.setSource(source);
654 event.setOperation(operation);
655 event.setUid(uid);
656 event.setRealhost(realhost);
657 event.setSockhost(sockhost);
658 event.setWebircPassword(webircPassword);
659 event.setWebircUsername(webircUsername);
660 event.setFakeHost(fakeHost);
661 event.setFakeIp(fakeIp);
662 eventBus.post(event);
663 }
664
665 public static void onEncap(Server sourceServer, User sourceUser, Server target, String command, String[] arguments)
666 {
667 EventEncap event = new EventEncap(sourceServer, sourceUser, target, command, arguments);
668 eventBus.post(event);
669 }
670
671 public static void setMode(String source, String target, String modes)
672 {
673 Channel c = Channel.findChannel(target);
674 if (c != null)
675 onChanMode(source, c, modes);
676
677 // XXX
678 String[] s = modes.split(" ");
679 Object[] o = new Object[s.length];
680 System.arraycopy(s, 0, o, 0, s.length);
681
682 Protocol.mode(source, target, o);
683 }
684
685 // The only calling instance, KKill, already checks if user exists
686 public static void skill(User u, String msg)
687 {
688 Protocol.kill(u.getUID(), msg);
689 u.onQuit();
690 onQuit(u, "Killed (" + me.getName() + " (" + msg + "))");
691 }
692
693 public static void onSquit(Server server)
694 {
695 quitStorm.onServerDelink(server);
696 }
697
698 public static void kick(AcidUser kicker, User kickee, Channel channel, final String reason)
699 {
700 channel.removeUser(kickee);
701 kickee.remChan(channel);
702
703 Protocol.kick(kicker, kickee, channel.getName(), reason);
704 onKick(kicker.getNick(), kickee, channel, reason);
705 }
706
707 public static void privmsg(final Channel recipient, String message)
708 {
709 String crypto_pass = Acidictive.conf.getCryptoPass(recipient.getName());
710 if (crypto_pass != null)
711 {
712 Blowfish bf = new Blowfish(crypto_pass);
713 message = bf.Encrypt(message);
714 log.debug("Blowfish encrypted:" + message);
715 }
716
717 Protocol.privmsg(conf.general.control, recipient.getName(), message);
718 }
719
720 public static void privmsg(final User recipient, String message)
721 {
722 Protocol.privmsg(conf.general.control, recipient.getUID(), message);
723 }
724
725 //@Deprecated This should be deprecated however I need the warnings to stfu atm in eclipse
726 public static void privmsg(final String recipient, String message)
727 {
728 privmsg(conf.general.control, recipient, message);
729 }
730
731 public static void privmsg(String source, String target, String message)
732 {
733 if (target.startsWith("#"))
734 {
735 String crypto_pass = Acidictive.conf.getCryptoPass(target);
736 if (crypto_pass != null)
737 {
738 Blowfish bf = new Blowfish(crypto_pass);
739 message = bf.Encrypt(message);
740 log.debug("Blowfish encrypted:" + message);
741 }
742 }
743 Protocol.privmsg(source, target, message);
744 }
745
746 public static void notice(final Channel recipient, final String message)
747 {
748 Protocol.notice(conf.general.control, recipient.getName(), message);
749 }
750
751 public static void notice(final User recipient, final String message)
752 {
753 Protocol.notice(conf.general.control, recipient.getUID(), message);
754 }
755
756 public static void notice(final String source, final String recipient, final String message)
757 {
758 Protocol.notice(source, recipient, message);
759 }
760
761 //@Deprecated
762 public static void notice(final String recipient, final String message)
763 {
764 Protocol.notice(conf.general.control, recipient, message);
765 }
766
767 //@Deprecated
768 public static void sNotice(final String recipient, final String message)
769 {
770 Protocol.notice(me.getSID(), recipient, message);
771 }
772
773 public static void reply(String source, String target, String msg)
774 {
775 if (!target.startsWith("#"))
776 // Reply to PMs in notice, reply to channel in privmsg
777 /* TODO: How the hell do we avoid using notice(String,String)?
778 * Thinking of adding a "Target" interface to make this at least
779 * target-agnostic.
780 */
781 notice(source, msg);
782 else
783 privmsg(target, msg);
784 }
785
786 public static void reply(User source, AcidUser to, Channel c, String message)
787 {
788 if (c != null && to != null)
789 privmsg(to.getNick(), c.getName(), message);
790 else if (to != null && c == null)
791 notice(to.getNick(), source.getNick(), message);
792 else if (c != null && to == null)
793 privmsg(conf.general.control, c.getName(), message);
794 else
795 notice(conf.general.control, source.getNick(), message);
796 }
797
798 public static void loadClients(Plugin pkg, List<Client> clients)
799 {
800 for (Client c : clients)
801 {
802 User u = User.findUser(c.nick);
803 if (u != null)
804 {
805 if (u instanceof AcidUser)
806 {
807 AcidUser au = (AcidUser) u;
808
809 // update config and plugin
810 au.client = c;
811 au.pkg = pkg;
812 }
813 continue;
814 }
815
816 AcidUser au = new AcidUser(pkg, c);
817 if (syncTime != 0)
818 au.introduce();
819 }
820 }
821
822 private static Runnable catchAll(Runnable r)
823 {
824 return () -> {
825 try
826 {
827 r.run();
828 }
829 catch (Throwable ex)
830 {
831 log.warn("uncaught exception thrown out of task", ex);
832 }
833 };
834 }
835
836 public static ScheduledFuture scheduleWithFixedDelay(Runnable r, long t, TimeUnit unit)
837 {
838 ScheduledFuture future = eventLoop.scheduleWithFixedDelay(catchAll(r), t, t, unit);
839 return future;
840 }
841
842 public static ScheduledFuture scheduleAtFixedRate(Runnable r, long t, TimeUnit unit)
843 {
844 ScheduledFuture future = eventLoop.scheduleAtFixedRate(catchAll(r), t, t, unit);
845 return future;
846 }
847
848 public static ScheduledFuture schedule(Runnable r, long t, TimeUnit unit)
849 {
850 ScheduledFuture future = eventLoop.schedule(catchAll(r), t, unit);
851 return future;
852 }
853 }