1 | package dmg.cells.services; |
2 | |
3 | import java.util.*; |
4 | import java.io.*; |
5 | import dmg.cells.nucleus.*; |
6 | import dmg.util.*; |
7 | |
8 | import org.slf4j.Logger; |
9 | import org.slf4j.LoggerFactory; |
10 | |
11 | /** |
12 | * |
13 | * The dmg.cells.services.RoutingManager is a ready to use |
14 | * service Cell, performing the following services : |
15 | * <ul> |
16 | * <li>Watching a specified tunnel cell and setting the |
17 | * default route to this cell as soon as this tunnel |
18 | * cell establishes its domain route. |
19 | * <li>Assembling downstream routing informations and |
20 | * the exportCell EventListener Event and maitaining |
21 | * a wellknown Cell list. |
22 | * <li>Sending its wellknown cell list upstream as soon |
23 | * as a default route is available and whenever |
24 | * the wellknown cell list changes. |
25 | * </ul> |
26 | * @author Patrick Fuhrmann |
27 | * @version 0.1, 15 Feb 1998 |
28 | */ |
29 | public class RoutingManager |
30 | extends CellAdapter |
31 | implements CellEventListener |
32 | { |
33 | private final static Logger _log = |
34 | LoggerFactory.getLogger(RoutingManager.class); |
35 | |
36 | private final CellNucleus _nucleus; |
37 | private final Args _args; |
38 | private final Set<String> _localExports = new HashSet(); |
39 | private final Map<String,Set<String>> _domainHash = new HashMap(); |
40 | private final String _watchCell; |
41 | private boolean _defaultInstalled = false; |
42 | |
43 | public RoutingManager(String name, String args) |
44 | { |
45 | super(name,"System", args, false); |
46 | _nucleus = getNucleus(); |
47 | _args = getArgs(); |
48 | |
49 | _nucleus.addCellEventListener(this); |
50 | _watchCell = _args.argc() == 0 ? null : _args.argv(0); |
51 | |
52 | start(); |
53 | } |
54 | |
55 | public synchronized void getInfo(PrintWriter pw) |
56 | { |
57 | pw.println(" Our routing knowledge :"); |
58 | pw.append(" Local : ").println(_localExports); |
59 | |
60 | for (Map.Entry<String,Set<String>> e : _domainHash.entrySet()) { |
61 | pw.append(" ").append(e.getKey()).append(" : ").println(e.getValue()); |
62 | } |
63 | } |
64 | |
65 | private synchronized void setDefaultInstalled(boolean value) |
66 | { |
67 | _defaultInstalled = value; |
68 | } |
69 | |
70 | private synchronized boolean isDefaultInstalled() |
71 | { |
72 | return _defaultInstalled; |
73 | } |
74 | |
75 | private void addWellknown(String cell, String domain) |
76 | { |
77 | if (cell.startsWith("@")) |
78 | return; |
79 | try { |
80 | _nucleus.routeAdd(new CellRoute(cell, |
81 | "*@"+domain, |
82 | CellRoute.WELLKNOWN)); |
83 | } catch (IllegalArgumentException e) { |
84 | _log.warn("Couldn't add wellknown route : " + e.getMessage()); |
85 | } |
86 | } |
87 | |
88 | private void removeWellknown(String cell, String domain) |
89 | { |
90 | if (cell.startsWith("@")) |
91 | return; |
92 | try { |
93 | _nucleus.routeDelete(new CellRoute(cell, |
94 | "*@"+domain, |
95 | CellRoute.WELLKNOWN)); |
96 | } catch (IllegalArgumentException e) { |
97 | _log.warn("Couldn't delete wellknown route : " + e.getMessage()); |
98 | } |
99 | } |
100 | |
101 | private synchronized void updateUpstream() |
102 | { |
103 | List<String> all = new ArrayList(); |
104 | _log.info("update requested to upstream Domains"); |
105 | // |
106 | // the protocol requires the local DomainName |
107 | // first |
108 | // |
109 | all.add(_nucleus.getCellDomainName()); |
110 | // |
111 | // here we add our own exportables |
112 | // |
113 | all.addAll(_localExports); |
114 | |
115 | // |
116 | // and now all the others |
117 | // |
118 | |
119 | for (Set<String> cells : _domainHash.values()) { |
120 | all.addAll(cells); |
121 | } |
122 | |
123 | String destinationManager = _nucleus.getCellName(); |
124 | _log.info("Resending to " + destinationManager + " : " + all); |
125 | try { |
126 | CellPath path = new CellPath(destinationManager); |
127 | String[] arr = all.toArray(new String[0]); |
128 | _nucleus.resendMessage(new CellMessage(path, arr)); |
129 | } catch (NoRouteToCellException e) { |
130 | /* This normally happens when there is no default route. |
131 | */ |
132 | _log.info("Cannot send routing information to RoutingMgr: " + e.getMessage()); |
133 | } |
134 | } |
135 | |
136 | private synchronized void addRoutingInfo(String[] info) |
137 | { |
138 | String domain = info[0]; |
139 | Set<String> oldCells = _domainHash.get(domain); |
140 | Set<String> newCells = new HashSet<String>(); |
141 | for (int i = 1; i < info.length; i++){ |
142 | newCells.add(info[i]); |
143 | } |
144 | |
145 | if (oldCells == null) { |
146 | _log.info("Adding new domain : " + domain); |
147 | for (String cell : newCells) { |
148 | addWellknown(cell, domain); |
149 | } |
150 | } else { |
151 | _log.info("Updating domain : " + domain); |
152 | for (String cell : newCells) { |
153 | _log.info("Adding : " + cell); |
154 | if (!oldCells.remove(cell)) { |
155 | // entry not found, so make it |
156 | addWellknown(cell, domain); |
157 | } |
158 | } |
159 | // all additional route added now, need to remove the rest |
160 | for (String cell : oldCells) { |
161 | _log.info("Removing : " + cell); |
162 | removeWellknown(cell, domain); |
163 | } |
164 | } |
165 | _domainHash.put(domain, newCells); |
166 | if (isDefaultInstalled()) |
167 | updateUpstream(); |
168 | } |
169 | |
170 | private synchronized void removeRoutingInfo(String domain) |
171 | { |
172 | _log.info("Removing all routes to domain : " + domain); |
173 | Set<String> cells = _domainHash.remove(domain); |
174 | if (cells == null){ |
175 | _log.info("No entry found for domain : " + domain); |
176 | return; |
177 | } |
178 | for (String cell : cells) |
179 | removeWellknown(cell, domain); |
180 | } |
181 | |
182 | public void messageArrived(CellMessage msg) |
183 | { |
184 | Object obj = msg.getMessageObject(); |
185 | if (obj instanceof String[]){ |
186 | String[] info = (String[])obj; |
187 | if (info.length < 1){ |
188 | _log.warn("Protocol error 1 in routing info"); |
189 | return; |
190 | } |
191 | _log.info("Routing info arrived for Domain : " + info[0]); |
192 | addRoutingInfo(info); |
193 | } else { |
194 | _log.warn("Unidentified message ignored : " + obj); |
195 | } |
196 | } |
197 | |
198 | public void cellCreated(CellEvent ce) |
199 | { |
200 | String name = (String)ce.getSource(); |
201 | _log.info("cellCreated : " + name); |
202 | } |
203 | |
204 | public synchronized void cellDied(CellEvent ce) |
205 | { |
206 | String name = (String) ce.getSource(); |
207 | _log.info("cellDied : "+name); |
208 | _localExports.remove(name); |
209 | updateUpstream(); |
210 | } |
211 | |
212 | public synchronized void cellExported(CellEvent ce) |
213 | { |
214 | String name = (String)ce.getSource(); |
215 | _log.info("cellExported : " + name); |
216 | _localExports.add(name); |
217 | updateUpstream(); |
218 | } |
219 | |
220 | public void routeAdded(CellEvent ce) |
221 | { |
222 | CellRoute cr = (CellRoute)ce.getSource(); |
223 | CellAddressCore gate = new CellAddressCore(cr.getTargetName()); |
224 | _log.info("Got 'route added' event : " + cr); |
225 | if (cr.getRouteType() == CellRoute.DOMAIN){ |
226 | if ((_watchCell != null) && gate.getCellName().equals(_watchCell)) { |
227 | // |
228 | // the upstream route (we only support one) |
229 | // |
230 | try { |
231 | CellRoute defRoute = |
232 | new CellRoute("", |
233 | "*@"+cr.getDomainName(), |
234 | CellRoute.DEFAULT); |
235 | _nucleus.routeAdd(defRoute); |
236 | } catch (IllegalArgumentException e) { |
237 | _log.warn("Couldn't add default route : " + e.getMessage()); |
238 | } |
239 | } else { |
240 | // |
241 | // possible downstream routes |
242 | // |
243 | // _log.info("Downstream route added : "+ cr); |
244 | _log.info("Downstream route added to Domain : " + cr.getDomainName()); |
245 | // |
246 | // If the locationManager takes over control |
247 | // the default route may be installed before |
248 | // the actual domainRouted is added. Therefore |
249 | // we have to 'updateUpstream' for each route. |
250 | updateUpstream(); |
251 | } |
252 | } else if (cr.getRouteType() == CellRoute.DEFAULT) { |
253 | _log.info("Default route was added"); |
254 | setDefaultInstalled(true); |
255 | updateUpstream(); |
256 | } |
257 | } |
258 | |
259 | public void routeDeleted(CellEvent ce) |
260 | { |
261 | CellRoute cr = (CellRoute)ce.getSource(); |
262 | CellAddressCore gate = new CellAddressCore(cr.getTargetName()); |
263 | if (cr.getRouteType() == CellRoute.DOMAIN) { |
264 | if ((_watchCell != null) && gate.getCellName().equals(_watchCell)) { |
265 | CellRoute defRoute = |
266 | new CellRoute("", |
267 | "*@"+cr.getDomainName(), |
268 | CellRoute.DEFAULT); |
269 | _nucleus.routeDelete(defRoute); |
270 | } else { |
271 | removeRoutingInfo(cr.getDomainName()); |
272 | } |
273 | } else if (cr.getRouteType() == CellRoute.DEFAULT) { |
274 | setDefaultInstalled(false); |
275 | } |
276 | } |
277 | |
278 | public String ac_update(Args args) |
279 | { |
280 | updateUpstream(); |
281 | return "Done"; |
282 | } |
283 | |
284 | /** |
285 | * This method returns the current state of the RoutingMgr cell as a (binary) Object. |
286 | * <p> |
287 | * NB. <b>This is a hack</b>. The correct method of receiving information from a |
288 | * Cell is via a Vehicle. However, as the RoutingMgr is within the cells module (which |
289 | * does not have the concept of Vehicles) this cannot be (easily) done. Instead, we |
290 | * use the existing mechanism of obtaining a binary object via the admin interface and |
291 | * flag this functionality as something that should be improved later. |
292 | * |
293 | * @return a representation of the RoutingManager's little brain. |
294 | */ |
295 | @Deprecated |
296 | public Object ac_ls_$_0( Args args) { |
297 | |
298 | Object info; |
299 | |
300 | if (args.getOpt("x") == null) { |
301 | // Throw together some meaningful output. |
302 | ByteArrayOutputStream os = new ByteArrayOutputStream(); |
303 | PrintWriter pw = new PrintWriter( os); |
304 | getInfo( pw); |
305 | pw.flush(); |
306 | info = os.toString(); |
307 | } else { |
308 | Object infoArray[] = new Object[3]; |
309 | |
310 | infoArray[0] = _nucleus.getCellDomainName(); |
311 | infoArray[1] = _localExports; |
312 | infoArray[2] = _domainHash; |
313 | |
314 | info = infoArray; |
315 | } |
316 | |
317 | return info; |
318 | } |
319 | |
320 | public String hh_ls = "[-x]"; |
321 | |
322 | } |