CSE 461: Introduction to Computer Communication Networks, Autumn 2012
  CSE Home   About Us   Search   Contact Info 
Home
Overview
Course email
Anonymous feedback
View feedback
Course wiki
Home Virtual Machines
Homework Turnin
Class GoPost Forum
Gradebook
Schedule
Hw/Project List
    Project 5: SNet Proxy

1. General Idea

One problem with using SNet when the community is small is that you seldom find other users online. A proxy can be used to fix this problem. The idea is to sit a proxy process on some always-up machine and to have the proxy act as the user when the user is offline. In theory, just before the user's device (e.g., phone) goes offline, it sends its own most recent community record and my and chosen photos to the proxy. Until the phone comes back online, the proxy answers fetchUpdate calls directed to the user's phone. When the user's phone comes back online, it tells the proxy it's back, and the phone takes over responding to fetchUpdates calls.

In some sense, every node has the potential to proxy for every other node in SNet. Because of that, it turns out to be pretty easy to build a respectable proxy. As an added bonus, the proxy automatically provides a sync'ing facility that lets a user sync her SNet state across multiple devices.

2. SNet Name Space

The proxy takes over for the user by registering registering itself in DDNS as the user. Subsequent lookups by other clients therefore resolve to the proxy. For this to work, the user's name must be resolvable even when the user is down. The DDNS hostnames from Project 4 therefore aren't suitable - resolving a host name requires descending into the host's zone, and since the host is down it's likely its zone is as well. For that reason we introduce a new SNet name space, with all names maintained by the root zone. A name foo.uw12au.cse461 from Project 4 still exists, and still refers to the host, but a new name, foo.snet.uw12au.cse461, has been introduced. (See the DDNS root zone web page.)

Besides providing names that can always be resolved, separating the SNet names from host names provides another benefit: a single SNet user can use multiple devices (e.g., a phone and a laptop). The proxy provides a kind of sync'ing between the devices.

3. Programming Details

  1. Insert these lines (editted in the obvious way) into your config file:
    snet.name=YOURALIAS.snet.uw12au.cse461.
    snet.proxyname=proxy.snet.uw12au.cse461.
    
  2. Insert these routines into SNetController.java:
        //---------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------------
        // Proxy support routines
    
        /**
         * Symbolic names for valid arguments to registerMyNameAs().
         * @author zahorjan
         */
        public enum ProxyRegisterNameType { ME, PROXY };
    
        
        public boolean syncProxy(File galleryDir) {
            String proxyName = NetBase.theNetBase().config().getProperty("snet.proxyname");
            try {
                if ( proxyName != null ) {
                    fetchUpdatesCaller( proxyName, galleryDir );
                    return true;
                }
            } catch (Exception e) {
                Log.e(TAG, "syncProxy: " + e.getMessage());
            }
            return false;
        }
    
        /**
         * Support for SNet proxying.
         * Registers my SNet name with either my address or the proxy's address
         * @param registerAs  Either "me" or "proxy"
         * @throws Exception Throws an exception if anything goes wrong, EXCEPT not if stopProxy() rpc call fails.
         */
        public void registerMyNameAs(ProxyRegisterNameType registerAs) {
            try {
                // verify that rpc and ddns services are available
                RPCService rpcService = (RPCService)NetBase.theNetBase().getService("rpc");
                if ( rpcService == null ) throw new Exception("No RPCService is loaded.");
                DDNSResolverService ddnsResolver = (DDNSResolverService)NetBase.theNetBase().getService("ddnsresolver");
                if ( ddnsResolver == null ) throw new Exception("No DDNSResolver is loaded.");
                
                // Find the proxy
                ARecord proxyAddress = null;
                // It's not a fatal error if the DDNS system isn't able to resolve the name
                String proxyName = NetBase.theNetBase().config().getProperty("snet.proxyname");
                if ( proxyName != null ) try { 
                    proxyAddress = ddnsResolver.resolve(proxyName);
                } catch (DDNSNoAddressException e) {}
                
    
                String mySNetName = NetBase.theNetBase().config().getProperty("snet.name");
                String ddnsPassword = NetBase.theNetBase().config().getProperty("ddnsresolver.password");
                JSONObject args = new JSONObject().put("name", mySNetName)
                                                  .put("password", ddnsPassword);
                
                if ( registerAs.equals(  ProxyRegisterNameType.ME ) ) {
                    // ask proxy to unregister and then register ourselves
                    if ( proxyAddress != null ) try {
                        RPCCall.invoke(proxyAddress.ip(), proxyAddress.port(), "snet", "stopProxy", args);
                    } catch (Exception e) {}
                    ddnsResolver.register(new DDNSFullName(mySNetName), rpcService.localPort());
                } else if ( registerAs.equals( ProxyRegisterNameType.PROXY )) {
                    // unregister our name and then ask the proxy to register
                    ddnsResolver.unregister(new DDNSFullName(mySNetName));
                    if ( proxyAddress == null ) throw new Exception("Can't transfer my name to proxy: proxy has no address");
                    RPCCall.invoke(proxyAddress.ip(), proxyAddress.port(), "snet", "startProxy", args);
                } else throw new Exception("registerMyNameAs(" + registerAs + "): Unrecognized argument");
    
            } catch (Exception e) {
                Log.e(TAG, "Couldn't register my name: " + e.getMessage());
                e.printStackTrace();
            }
        }
        
        // Proxy support routines end
        //---------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------------
    

  3. Alter fetchUpdatesCaller() to pass two additional arguments, as in this code:
      JSONObject args = new JSONObject().put("community", communityMap)
        .put("needphotos", needPhotoArray)
        .put("destname", friend)
        .put("srcname", NetBase.theNetBase().config().getProperty("snet.name") );
    

  4. Speaking generally, wherever your code uses the host name (e.g., NetBase.theNetBase().hostname()) to mean the SNet user's name, it should now use the value of NetBase.theNetBase().config().getProperty("snet.name"). In the code I distributed:
    SNet.java
    There are two calls to hostname() in the constructor that should be replaced with config().getProperty("snet.name").

    SNetController.java
    There are two calls to hostname() in method getStatusMsg() that should be updated.

    Additionally, you may have to update the code you wrote to switch from using the host name as the SNet name to using the SNet name as the SNet name.

  5. When your SNet application starts, it should issue these calls:
      mSNetController.registerMyNameAs(ProxyRegisterNameType.ME);
      mSNetController.syncProxy(mGalleryDir);
    

    Here mSNetController is the name of the SNetController object, and mGalleryDir is the name of the gallery directory variable. Your names may vary.

  6. Each time you update the user's community record, issue this call:
        mSNetController.syncProxy(galleryDir);
    

  7. When your program is about to exit, issue this call:
        mSNetController.registerMyNameAs(ProxyRegisterNameType.PROXY);
    

4. Testing Using the Proxy

When you call syncProxy you'll engage in a typical fetchUpdates exchange. If the proxy learns that you have new photos, it will try to fetch them. It will therefore invoke your fetchPhoto RPC interface.

If you update photos on some DDNS host A, then sync to the proxy from A, then sync to the proxy from some other host B (using the same SNet name), B should come up to date with A. That exercises at least some of your fetchUpdates code and your caller-side fetchPhoto code.


Computer Science & Engineering
University of Washington
Box 352350
Seattle, WA  98195-2350
(206) 543-1695 voice, (206) 543-2969 FAX
[comments to zahorjan at cs.washington.edu]