DekGenius.com
Previous Section  < Day Day Up >  Next Section

9.8 The Console Interface

Console interfaces generally share a common usage idiom: provide a method for invoking the interface without arguments, but also allow for runtime decisions by providing for command flags. If the number of arguments does not match your expectations, return a usage statement.

If a user provides no command flags to our console interface, it launches into search mode. We'll allow users to enter search terms on the command line and return results. To quit, they just press Enter.

In order to launch an indexing event, the user passes in a flag in this format: /i: http://www.somedomain.com/. This simply provides the starting URL from which the rest of the indexable pages are eventually discovered. If the user invokes the indexer, we'll bypass the search. If not, we go into search mode.

According to typical standards, we should allow the user to enter a help flag (we'll use /?) that produces the usage message. Any other number of or type of flag cause us to write out the usage message and quit.

Here are the utility methods:

private static void writeUsage( )
{
    System.out.println("Usage: java ConsoleSearch");
    System.out.println(" -- /i:http://YOUR_INITIAL_INDEX_PATH");
    System.out.println(" -- /?:show this message");
}

private static boolean checkArgs(String[] args)
{
    if(args.length < 2 && args.length > 0 && args != null)
    {
        if(args[0].startsWith("/i") || args[0].startsWith("/?"))
        {
            return true;
        }

    }
    return false;
}

private static String getIndexFlag(String[] args)
{
  for(int i=0;i<args.length;i++)
  {
      if(args[i].startsWith("/i:"))
      {
          return args[i].substring(3, args[i].length( ));
      }
  }
  return "";
}

The last method, getIndexFlag, examines the args collection to see if the user passed in the /i flag. If so, it returns the URL that is passed in after the flag.

That only leaves the entry point to the application:

public static void main(String[] args)
    {
        // get configured index path from IndexPathBean
        ConfigBean config = new ConfigBean( );
        String indexPath = config.getCurIndexPath( );
        if(indexPath == "") return;

        // check args for index flag, retrieve initial page, execute index
        if(!checkArgs(args)) {
            writeUsage( );
            return;
        }

        String indexInitialPage = getIndexFlag(args);
        if(indexInitialPage != "")
        {
            doIndex(indexInitialPage);
            return;
        }

        // Allow multiple queries from command line
        BufferedReader rdr = new BufferedReader(new InputStreamReader(System.in));
        try {
             while (true) {
              System.out.print("Query: ");
              String line = rdr.readLine( );
                if (line.length( ) == 0) {
                  break;
                }
             QueryBean query = new QueryBean("contents", indexPath, line);
             query.execute( );
             HitBean[] hits = query.getResults( );
             for(int i = 0;i<hits.length;i++)
             {
                 System.out.println(hits[i].getScoreAsString( ) + " " + 
                     hits[i].getUrl( ));
             }
           }
        }
        catch(Exception e)
        {
             e.printStackTrace( );
        }
    }

This single method provides everything our human and machine users could want. The live users can execute the main method and start interactively querying the index. Conversely, they can pass in the /i: flag and operate just the indexing/crawling functionality.

First, make sure the arguments are correct; if they're not, call writeUsage and then break. Check to see if the user is asking for an index instead of a search. If she is, call doIndex, then return. If not, allow the user to enter a query and execute the search, returning a simply-formatted result set. If anything goes wrong, print out the stack trace.

Here's the doIndex method:

private static void doIndex(String indexflag)
    {
        try
        {
            ConfigBean config = new ConfigBean( );
            String nextIndex;
            try
            {
               nextIndex = config.getNextIndexPath( );
            }
            catch(Exception ex)
            {
                return;
            }

            IndexLinks lts = new IndexLinks(nextIndex, config.getMaxLinks( ), 
                                            config.getSkippedLinksFile( ));
            lts.initFollowPrefixesFromSystemProperties( );
            lts.initAvoidPrefixesFromSystemProperties( );
            lts.setInitialLink(indexflag);

            config.flipIndexPath( );
        }
        catch(Exception e)
        {
            // handle error
        }
    }

First, we retrieve the alternate index path from the ConfigBean. Remember, we need to create the index, but still allow other users to coninue to search it. Create a new instance of our IndexLinks class, passing in the alternate index path plus the MaxLinks and SkippedLinksFile values from our ConfigBean. Set up the avoid and follow link collections and kick off the index process. If everything succeeds, flip the indexes via the ConfigBean.flipIndexPath( ) method.

The console interface is simple enough that it doesn't need a controller layer between it and our business logic. In fact, the console interface looks a lot like a simple controller layer. It pieces together all three services without knowing anything about their internal configuration, demonstrating good interface separation. It is entirely focused on providing entry points to the application services to our users and returning results.

9.8.1 Principles in Action

  • Keep it simple: no configurable schedule for invoking services, allow indexing output to show through to user

  • Choose the right tools: JUnit

  • Do one thing, and do it well: just read from command lines and return search results

  • Strive for transparency: no internal knowledge of services

  • Allow for extension: none

    Previous Section  < Day Day Up >  Next Section