| 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 | } |