Java: Hopefully working HubListeners.
[doldaconnect.git] / lib / java / dolda / dolcon / Session.java
1 package dolda.dolcon;
2
3 import java.util.*;
4 import dolda.dolcon.protocol.*;
5
6 public class Session {
7     Connection conn;
8     private String state;
9     private boolean listening = false;
10     private Dispatcher dispatcher;
11     HubManager hm = null;
12     
13     public Session(String aspec, String username, List<Authenticator> auth) throws AuthException, ProtocolException, InterruptedException {
14         state = "connecting";
15         conn = new Connection(aspec);
16         conn.expectVersion(2);
17         try {
18             conn.syncConnect();
19         } catch(ConnectException e) {
20             throw(new ProtocolException(e));
21         }
22         state = "auth";
23         authenticate(username, auth);
24         state = "";
25         dispatcher = new Dispatcher();
26         dispatcher.start();
27     }
28     
29     public Session(String aspec, String username, Authenticator... auth) throws AuthException, ProtocolException, InterruptedException {
30         this(aspec, username, Arrays.asList(auth));
31     }
32     
33     private void authenticate(String username, List<Authenticator> auth) throws AuthException, ProtocolException, InterruptedException {
34         Response resp;
35         
36         try {
37             resp = ResponseException.check(conn.ecmd("lsauth"), 200);
38             List<String> mechs = new LinkedList<String>();
39             for(List<String> mech : resp.lines)
40                 mechs.add(mech.get(0).intern());
41             String use = null;
42             Authenticator au = null;
43             for(Authenticator a : auth) {
44                 use = a.handles(mechs);
45                 if(use != null) {
46                     au = a;
47                     break;
48                 }
49             }
50             if(use == null)
51                 throw(new NoMechException());
52             resp = conn.ecmd("login", use, username);
53             while(true) {
54                 if(resp.code == 200) {
55                     return;
56                 } else if((resp.code / 100) == 3) {
57                     resp = conn.ecmd(au.step(resp));
58                 } else if((resp.code / 100) == 5) {
59                     throw(new AuthException(resp.token(0, 0)));
60                 } else {
61                     throw(new ResponseException(resp, 0));
62                 }
63             }
64         } catch(ClosedException e) {
65             throw(new ProtocolException(e));
66         }
67     }
68     
69     private HubManager gethm() {
70         if(hm == null) {
71             hm = new HubManager(this);
72         }
73         return(hm);
74     }
75     
76     public synchronized void addHubListener(HubListener hl, boolean addexisting) {
77         gethm().addls(hl, addexisting);
78     }
79     
80     public synchronized void removeHubListener(HubListener hl) {
81         gethm().rmls(hl);
82     }
83     
84     public synchronized Collection<Hub> getHubs() throws InterruptedException {
85         return(gethm().gethubs());
86     }
87     
88     public void close() {
89         conn.close();
90         state = "closed";
91     }
92     
93     protected void finalize() {
94         if(state != "closed")
95             close();
96         dispatcher.interrupt();
97     }
98     
99     void dispatch(Runnable ev) {
100         dispatcher.dispatch(ev);
101     }
102
103     private static class Dispatcher extends Thread {
104         private Queue<Runnable> q = new LinkedList<Runnable>();
105         
106         private Dispatcher() {
107             setDaemon(true);
108         }
109         
110         public void dispatch(Runnable ev) {
111             synchronized(q) {
112                 q.offer(ev);
113                 q.notifyAll();
114             }
115         }
116         
117         public void run() {
118             while(true) {
119                 try {
120                     Runnable r;
121                     synchronized(q) {
122                         while((r = q.poll()) == null)
123                             q.wait();
124                     }
125                     r.run();
126                 } catch(Throwable t) {
127                     t.printStackTrace();
128                 }
129             }
130         }
131     }
132 }