Apache OODT 0.10 release maintenance branch.

git-svn-id: https://svn.apache.org/repos/asf/oodt/branches/0.10@1702810 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/0.10/CHANGES.txt b/0.10/CHANGES.txt
new file mode 100644
index 0000000..8badb67
--- /dev/null
+++ b/0.10/CHANGES.txt
@@ -0,0 +1,1226 @@
+Apache OODT Change Log
+======================
+Release 0.10 - 08/30/2015
+
+* OODT-871 Issues with OODT 0.10 RC#1 (lewismc, mattmann)
+
+* OODT-246 Give user the ability to print a detailed report on what jobs in 
+  the Resource Manager are running on what nodes (Gabe Resneck, mattmann)
+
+* OODT-245 List results from the Resource Manager client should be sorted 
+  alphabetically (Gabe Resneck via mattmann)
+
+* OODT-248 Give user the ability to print a detailed report on the load, capacity 
+  and queues of all nodes in the Resource Manager (Gabe Resneck, mattmann)
+
+* OODT-247 Resource Manager client should allow users to see what jobs are 
+  currently in the queue (Gabe Resneck, mattmann)
+
+* OODT-244 Allow the Resource Manager scheduler to cycle on a non-integer 
+  number of seconds (Gabe Resneck via mattmann)
+
+* OODT-864 Upgrade to Tika 1.10 (mattmann,tpalsulich)
+
+* OODT-686 AddProductTypeCliAction should prompt for ProductTypeId (rverma, mattmann)
+
+* OODT-302 Add support in the xmlquery package to handle HTTP Redirection (Michael Cayanan via mattmann)
+
+* OODT-303 Add support in the Grid Package to throw an HTTP Servlet Response (Michael Cayanan via mattmann)
+
+* OODT-480 Balance: default view not found (Nesbert Hidalgo via mattmann)
+
+* OODT-598 XMLQuery DIS-style query parser needs to treat "#" as a word character (kelly via mattmann)
+
+* OODT-870 Integrate Curator into OPSUI (mattmann)
+
+* OODT-842 create restful version of cas curator services (mattmann)
+
+* OODT-838 Separate RESTful Servers and Clients (Michael Starch, mattmann)
+
+* OODT-824 Port the Curator to Apache Wicket (mattmann)
+
+* OODT-861 Workflow Manager client will print the list of task ids for a given workflow, if requested (luca)
+
+* OODT-854 Enable File Manager and Resource Manager to collect configuration files by parsing a directory tree recursively (luca)
+
+
+Release 0.9 - 05/31/2015
+
+* OODT-841 Update Maven instructions in README to include clean command (Ethan Wang via mattmann)
+
+* OODT-844 Scala maven plugin for streaming only (Jane Wyngaard via mattmann)
+
+* OODT-829 Implement an Apache Tika based Server Side Extractor (Radu Manole via mattmann)
+
+* OODT-826 Add the capability to check external preconditions before ingesting a file.
+
+* OODT-832 Move streaming items to seperate top-level component (starchmd)
+
+* OODT-831 CAS PGE adapter framework build failed (Aditya Dhulipala via mattmann)
+
+* OODT-830 Queue Assertion failure of XmlQueueRepository under CAS Resource Manager (Aditya Dhulipala via mattmann)
+
+* OODT-823 OODT JDK8 Maven3 build errors (Aditya Dhulipala via mattmann)
+
+* OODT-715 SolrIndexer fails to index when numProducts == pageSize (pramirez, mdstarch via mattmann)
+
+* OODT-821 OODT-821 FMProd RDF conf shouldn't use EDRN as a default namespace for keys and for types (mattmann)
+
+* OODT-818 CmdLineIngester should log there was an exception and move on during ingest (mattmann)
+
+* OODT-817 TikaCmdLineExtractor needs to add Filename and FileLocation fields (mattmann)
+
+* OODT-813 CAS-PGE incorrectly passes varargs to DynamicConfigFile implementations (mattmann)
+
+* OODT-812 RADIX ingest script (mattmann)
+
+* OODT-811 RADIX incorrectly sets up PCS REST services app config (mattmann)
+
+* OODT-810 Metadata PreCondition Comparators based on FileName and LastModified time (Luca Cinquini via mattmann)
+
+* OODT-809 A CrawlerAction that indexes to Solr (Luca Cinquini via mattmann)
+
+
+Release 0.8.1 - 01/28/2015
+-------------------------------------------
+* OODT-804 Broken trunk build on Jenkins
+
+* OODT-699 Mesos cluster manager backend to resource manager
+
+* OODT-780 Spark backend to resource manager
+
+* OODT-802 Create Dockerfile for OODT Radix.
+
+* OODT-761 Update PGE version in Radix.
+
+* OODT-749 Remove old XercesImpl jar from grid pom.
+
+* OODT-771 Fix push_pull_framework.properties path reference for config.external.properties.files in incorrect with a patch from Lewis
+
+* OODT-770 Fix the RADIX Issues with a patch from Lewis McGibbney
+
+Release 0.8 - 12/19/2014
+--------------------------------------------
+
+* OODT-751 OPSUI pages constantly expire (Tom Barber, mattmann)
+
+* OODT-748 Fix Description in agility/setup.py
+
+* OODT-787 PGE_ROOT env variable does not work in PGEConfig.xml and tasks.xml (Yi Sun, Sneha Deshmukh via mattmann)
+
+* OODT-562 Exposing parent-child & element addition/deletion functionality for product types using cas-curator (varunr)
+
+* OODT-563 Modifying Workflow Manager to allow adding new workflows (varunr)
+
+* OODT-786 Change shebang to bash for proper color output (Poojit Sharma via mattmann)
+
+* OODT-785 Update batch stub with legacyMode set to true (meghamsh4 via mattmann)
+
+* OODT-783 RADiX Crawler action bean config refers to WORKFLOWMGR_URL (Angela Wang via mattmann)
+
+* OODT-782 Resource manager port number must be compatible with the one used in workflow.properties (Imran Mammaldi via mattmann)
+
+* OODT-769 The wiki page command installs 0.6 version of the oodt radix installer (lewismc, Santosh SHankar)
+
+* OODT-777 Update fileconcatenator-pge.tar for building fileconcatenator-pge (threeears via mattmann)
+
+* OODT-776 Test fix: log messages can be different for different locales (Tom Tung via mattmann)
+
+* OODT-701 Adding in stream product structure for filemanager. Add in check for valid product structures, and fixed tests breaking from that check.
+
+* OODT-699 Adding in cluster managment scripts for mesos.
+
+* OODT-764 Adding in multiplexing resource manager backend.
+
+* OODT-763 Correcting path to OODT_HOME and FILEMGR_HOME (vishalhemnani via mattmann)
+
+* OODT-385, OODT-630, OODT-631, OODT-632. Upgraded Tika to version 1.6.
+
+* OODT-757 Fixed PGETaskInstance bug that prevented instantiation of AutoDetectProductCrawler (luca)
+
+* OODT-756 HttpClient NoClassDefFoundError For the url-downloader Script (Mengying Wang via mattmann)
+
+* OODT-754 Contribute ProdTypePatternMetExtractor (rickdn)
+
+* OODT-750 Issue with running mvn site:site due to old findbugs plugin (mattmann)
+
+
+Release 0.7 - 09/15/2014
+-------------------------------------------- 
+
+* OODT-669 Make Resource Manager work without Ganglia (mattmann, rajith)
+
+* OODT-644 Fix to FTP protocol tests, abstracting some of the logic to make the code better suited to mocking(magicaltrout)
+
+* OODT-644 Fix to stub FTP client protocol so it doesn't fail when the port is in use
+
+* OODT-644 Partial fix to stub SSH client so it doesn't fail when SSHD is running locally
+
+* OODT-745 Removed trailing spaces in the radix wmgr filex
+
+* OODT-473 Rremove "Deleted" Metadata Flag from Solr Indexer (magicaltrout)
+
+* OODT-736 Clean up test data left by TestTypeHandler test (rlaidlaw)
+
+* OODT-667 CAS-PGE no longer respects writers and file tags from 
+  earlier pgeConfig.xml files (mattmann)
+
+* OODT-741 Remove static modifier from elementMap, subToSuperMap and
+  productTypeElementMap fields in XMLValidationLayer (rlaidlaw)
+
+* OODT-737 Copied over latest cmd-line-options.xml with typeName (rlaidlaw)
+
+* OODT-739 Fix File Manager unit tests (step 12) - fix test classes in
+  'metadata' and 'versioning' packages (rlaidlaw)
+
+* OODT-738 Contribute workflow scripts to kill all by status or kill by instance ID (mattmann)
+
+* OODT-737 Fix File Manager unit tests (step 11) - fix test classes in 'cli'
+  package (rlaidlaw)
+
+* OODT-736 Fix File Manager unit tests (step 10) - fix test classes in 'structs'
+  package (rlaidlaw)
+
+* OODT-735 Fix File Manager unit tests (step 9) - fix test classes in 'tools'
+  package (rlaidlaw)
+
+* OODT-734 Fix File Manager unit tests (step 8) - fix test classes in 'system'
+  package (rlaidlaw)
+
+* OODT-733 Fix File Manager unit tests (step 7) - fix test classes in 'ingest'
+  package (rlaidlaw)
+
+* OODT-732 Fix File Manager unit tests (step 6) - fix test classes in
+  'datatransfer' package (rlaidlaw)
+
+* OODT-731 Fix File Manager unit tests (step 5) - fix test classes in 'catalog'
+  package (rlaidlaw)
+
+* OODT-728 Figure out why certain metadata fields get set twice during pipeline processing (mattmann)
+
+* OODT-729 Fix File Manager unit tests (step 4) - fix test classes in 'util'
+  package (rlaidlaw)
+
+* OODT-690 RADiX example policy for GenericFile missing "<metadata/>" node (rverma via mattmann)
+
+* OODT-718 RADiX Build Failure (tpalsulich, goodale via mattmann)
+
+* OODT-726 Create MetFilter Task Example (mattmann)
+
+* OODT-724 Crawler action bean config refers to WORKFLOWMGR_URL (mattmann)
+
+* OODT-723 Fix File Manager unit tests (step 3) - fix test class in 'validation'
+  package (rlaidlaw)
+
+* OODT-722 Fix File Manager unit tests (step 2) - fix test class in 'repository'
+  package (rlaidlaw)
+
+* OODT-672 OODT Start script does not notify user of status (asumarlidason via mattmann)
+
+* OODT-721 Fix File Manager unit tests (step 1) - remove duplicate XML from test
+  resources (rlaidlaw)
+  
+* OODT-719 Prevent httpclient from spawning closewait tcp connections (Konstantinos Mavrommatis via lewismc)
+
+* OODT-685 ix OODT 0.7-SNAPSHOT Jenkins nightly build on Ubuntu 1st Pass (lewismc)
+
+* OODT-716 Vagrant user on VM unable to start/stop oodt services (sherylj)
+
+* OODT-708 Add PGE RADiX Archetype (skhudiky)
+
+* OODT-593 Dyn workflows in Workflow Manager cause NPEs on restart (mattmann)
+
+* OODT-705 RADIX one step script (mattmann)
+
+* OODT-703 DataUtil.createProductZipFile throws exception when creating a zipfile of a hierarchical product (thomas)
+
+* OODT-674 MoveDatafileToFailureDir fails to move files off an NFS mounted directory (thomas)
+
+* OODT-662 Upgrade cog-jglobus dependency in protocol-ftp (lewismc)
+
+* OODT-668 ProductQueryServlet will now rename zip attachments with proper ".zip" extension,
+  and the conventions used can be overridden by subclasses (luca)
+
+* OODT-217 CAS-PGE User Manual (rverma, bfoster, mattmann)
+
+* OODT-665 CAS RDF and RSS product service doesn't allow product type met to have keys 
+  with spaces (mattmann)
+
+* OODT-664 RADIX deployment of opsui incorrectly uses /pcs-opsui as the web.xml context 
+  and omits 2 key PCS properties from context.xml (mattmann, pramirez, ahart)
+
+* OODT-663 Sample CAS-PGE workflow task should be included in cas-pge (mattmann)
+
+* OODT-661 Enabled case-insensitive specification of "OFSN" and "RT" parameter names
+  (not values!) when issuing a request to the Product Server (luca)
+
+* OODT-656 Provide alternative implementation of QueryServlet that is able 
+  to construct queries from non DIS-style parameters (luca)
+
+* OODT-613 CAS Product web application - configurable content writers for RDF
+  and RSS (rlaidlaw)
+
+* OODT-612 CAS Product web application - add content writers for XML, JSON, RDF,
+  RSS (rlaidlaw)
+
+* OODT-652 New TikaCmdLineMetExtractor (riverma)
+
+* OODT-651 Improve parameter initialization, validation and logging for
+  CAS-Product web application (rlaidlaw)
+
+* OODT-650 Remove duplicate context container example from CAS-Product web
+  application (rlaidlaw)
+
+* OODT-649 Add PathUtils.replaceEnvVariables wrapper around calls to
+  context.getInitParameter to process environment variables (rlaidlaw)
+
+* OODT-611 Implement JAX-RS services to access File Manager products as raw
+  data (rlaidlaw)
+
+* OODT-648 Add license headers to files so that mvn rat:check is successful 
+  (lewismc)
+
+* OODT-642 In CAS-Product, moved tests to src/test/java and test resources to
+  src/test/resources to match Maven standard directory layout (rlaidlaw)
+
+* OODT-619 Implement the capability to pull status of resource 
+  nodes from ganglia (Rajith Siriwardana via mattmann)
+
+
+Release 0.6 - 07/08/2013
+--------------------------------------------
+
+* OODT-633 Update PEAR package.xml files for upcoming release. (gmcdonald)
+
+* OODT-640 Added version number for maven-war-plugin to CAS-Product POM
+  (rlaidlaw)
+
+* OODT-639 Add a versioner based on Product Type Metadata (mattmann)
+
+* OODT-638 Include more provenance metadata in trace product type (mattmann)
+
+* OODT-637 Geo Product Type policy includes duplicative elements (mattmann)
+
+* OODT-635 Fix trivial errors in Basic CAS Curation Example (lewismc via mattmann, Bill Rideout)
+
+* OODT-636 Fixed Javadoc warnings in CAS-Product (rlaidlaw)
+
+* OODT-634 Fixed lingering bug in Solr Catalog implementation that would still not return null
+           if no matching product is found (luca)
+* OODT-628 Multiple cluster Ganglia xml parse error (Rajith Siriwardana via mattmann)
+
+* OODT-610 Added new empty package oodt.cas.product.service for future JAX-RS
+  classes (rlaidlaw)
+
+* OODT-609 Added Apache CXF dependency to the CAS-Product POM (rlaidlaw)
+
+* OODT-624 Updated maven-javadoc-plugin to v2.9 (rlaidlaw)
+
+* OODT-623 Addition of relativePath to the opendapps/pom.xml to help it locate
+  the oodt-core POM (rlaidlaw)
+
+* OODT-618 Implement ganglia XML parser (Rajith Siriwardana via mattmann)
+
+* OODT-625 Bake TraceableFile product policy into FM (mattmann)
+
+* OODT-622 Updated svn:ignore properties for several modules to add Eclipse
+  configuration files to the ignore lists (rlaidlaw)
+
+* OODT-572 Replace mkdir with more multi-thread resilient forceMkdir in 
+  LocalDataTransferer (Tom Barber via mattmann)
+
+* OODT-559 Unit test failure in testDoDynamicReplacement for "Europe/London" 
+  timezone (Tom Barber via mattmann)
+
+* OODT-615 Fixed bug in SolrCatalog that would throw exception if no matching products are found
+           (luca)
+
+* OODT-408 Drop Spring logging levels from WARNING to SEVERE (Lewis John McGibbney
+  via mattmann, bfoster)
+
+* OODT-607 SciPgeWriter that is a simple MetKey template replacer (mattmann)
+
+* OODT-606 SciPgeWriter that uses ApacheVelocity (mattmann, pramirez)
+
+* OODT-592 Provide Solr-based implementation of CAS File Manager (luca)
+
+* OODT-590 modify pcs references in opsui webapp (riverma)
+
+* OODT-589 add pcs_home env variable to env script (riverma)
+
+* OODT-586 RADiX archetype does not properly deploy crawler (riverma)
+
+* OODT-603 Enable Resource Manager start/stop within RADiX (riverma)
+
+* OODT-600 Modularize old OPSUI theme into a separate Wicket "skin" (riverma)
+
+* OODT-599 Modularize new theme into a stand-alone Wicket "skin" (riverma)
+
+* OODT-597 Improve theme/styling of OPSUI (riverma)
+
+* OODT-596 Fix opsui webapp's wicket 'Page Expired' error (riverma)
+
+* OODT-595 Improve styling of Workflow Viewer (webapp/components) (riverma)
+
+* OODT-602 Load not settable for WorkflowTaskJobs in 
+  IterativeWorkflowProcessThread (mattmann)
+
+* OODT-470 Enabled RSSProductTransferServlet to be configured using an XML
+  configuration file (rlaidlaw)
+
+* OODT-560 XmlStructFactory::getProductTypeMapXmlDocument does not 
+  output virtual product types (Varun Ratnakar via mattmann)
+
+* OODT-583 Added JUnit test class for RSSConfigReader (rlaidlaw)
+
+* OODT-582 Fix for oodt.cas.product.rss.RSSConfigReader.readTags() for detection
+  of source attribute for tags defined in the RSS configuration file (rlaidlaw)
+
+* OODT-576 Used try...finally in oodt.commons.database.SqlScript.loadScript() to
+  close BufferedReader (rlaidlaw)
+
+* OODT-537 Push/Pull NullPointerException on startup (mattmann, joyce)
+
+* OODT-578 Workflow Monitor experiences runtime exception (NPE) out 
+  of the box with RADIX (Arni Sumarlidason via mattmann)
+
+* OODT-575 Metadata extractor for parsing filename based on regex 
+  (Nga Chung via mattmann)
+
+* OODT-573 Refactored the return statement in the getTopNProducts method in the
+  LuceneCatalog class (rlaidlaw)
+
+* OODT-571 Updated assignments in setWorkflowInst and setWaitforConditionSatisfy
+  methods in IterativeWorkflowProcessorThread class (rlaidlaw)
+
+* OODT-574 RADiX POM Parent References (Arni Sumarlidason via mattmann)
+
+* OODT-491 Add nextState (determineNextState) function to 
+  WorkflowProcessor (mattmann)
+
+* OODT-558 Introducing optional web-grid servlet init parameter "org.apache.oodt.grid.GridServlet.config" 
+ that can be used to read/write its configuration to a location external to the web app directory (luca).
+
+* OODT-564 XMLPS should provided ordered results based on 
+  requested fields (mattmann, joyce)
+
+* OODT-369 Building with Maven3 (mattmann, Adam Estrada)
+
+* OODT-555, OODT-557 - Changed behavior of Lucene Catalog update methods to retrieve a product from the index to the cache, 
+  instead of failing if it is not found in the cache. At the same time, simplify the Curator updateMetadata() method to just update
+  the metadata, without removing and re-inserting the product before that.
+  (luca, rishi, mattmann)
+
+* OODT-553 Update org.apache.oodt.commons.exec.EnvUtilities to Use System.getEnvironment 
+  (Michael Starch via mattmann)
+
+* OODT-551 Insert primary key in metadata table for database-based File Manager, to always return metadata values in proper order
+           (luca, mattmann, bfoster)
+
+* OODT-548 Be more resilient to table definitions matching the master table 
+  in the Mapping config file (mattmann,joyce)
+
+* OODT-546 XMLPS inner-joins default table with itself (mattmann,joyce)
+
+* OODT-547 Switch newest Curator upudate metadata method to use XML-RPC FileManager client.
+           Switch other Curator update metadata method to use shared instance of Catalog Factory 
+           to minimize use of resources.
+           (luca)
+
+
+Release 0.5 
+--------------------------------------------
+Release Date: December 18, 2012
+
+* OODT-541 Implement a LenientDataSourceCatalog that allows for dynamically named
+  metadata fields, effectively bypassing the validation layer.
+  (luca)
+
+* OODT-544 Implement a DataSourceCatalog back-end to the CAS FileManager 
+  where the "product_id" column is of type string.
+  (luca)
+
+* OODT-545 Allow Curator web app to use a configurable CAS Catalog Factory 
+  as specified in curator.xml.
+  (luca)
+
+* OODT-543 Update ExpImpCatalog tool to allow core Metadata to pass through 
+  when Embedded Catalogs are used (mattmann, cinquini)
+
+* OODT-542 Need to update logging.properties for all servers post CAS CLI
+  (mattmann)
+
+* OODT-535 Provide capability to remove metadata tags through Curator 
+  web app. (luca)
+
+* OODT-533 Allow SolrIndexer to query and ingest products from the File 
+  Manager catalog by name. (luca)
+
+* OODT-534 Better error message when a product is looked by id in the catalog 
+  and not found. (luca)
+                      
+* OODT-529 Allow Lucene Catalog to be "lenient" with respect to XML validation,
+  i.e. allow handling of dynamically named fields if the "lenient" flag is set 
+  (defaults to false). (luca)
+
+* OODT-530 Bug fix: crawler MetExtractorProductCrawler had a list of precondition 
+  beans that was not initialized to an empty list, with would cause a Spring 
+  initialization exception when more than one preconditions was injected. (luca)
+           
+* OODT-531 Bug fix: the legacy Spring configuration file legacy-cmd-line-options.xml 
+  was missing some required configuration for the beans "noRecur" and "crawlForDirs".
+  (luca)
+           
+* OODT-532 Bug fix: pre-condition bean "AprioriUniquessCheckWithFilemgr" was missing 
+ the filemgrUrl property, which was therefore never set at initialization. (luca)
+
+* OODT-522 Provided additional Curator REST endpoint: "/metadata/update" that updated 
+  product metadata in place, i.e. preserves all existing catalog metadata (luca)
+
+* OODT-523 Updated Curator CXF dependency to 2.6.0 and removed FIXME patch to 
+  extract "id" from "/metadata/catalog" request (luca)
+
+* OODT-528 Merged back changed to CuratorLuceneCatalog into File Manager LuceneCatalog,
+  and change Curator to use mainstream File Manager Lucene Catalog (luca)
+
+* OODT-272 Enable Removal of Finished Ingestion Tasks
+  (Michael Joyce via mattmann)
+
+* OODT-328 Remove jpl.nasa.gov references from code 
+  (Michael Joyce via mattmann)
+
+* OODT-520 Default Crawler launcher doesn't reference FileManager 
+  properties (Mike Joyce via mattmann)
+
+* OODT-502: Support pre and post conditions in Workflow Processor 
+  Queue (mattmann)
+
+* OODT-516: Add WorkflowLifecycle tests that check pctComplete for 
+  wengine and w1 style lifecycles (mattmann)
+
+* OODT-501: Method to create Processor from Instance should be based on 
+  Graph structure (mattmann)
+
+* OODT-511: Solr Indexer Date Formatting Removes Metadata (pramirez)
+
+* OODT-510: SolrIndexer fails on ProductTypes that have 0 products (pramirez, ahart)
+
+* OODT-496: Convert EngineRunner interface to take TaskProcessor (mattmann)
+
+* OODT-505: Remove synchronous Runner (mattmann)
+
+* OODT-498: Overwrite and bring back 0.3 ThreadPoolWorkflowEngine plus 
+  patches (mattmann)
+
+* OODT-500: Rename property for max threads in AsyncLocalEngineRunner (mattmann)
+
+* OODT-497: Make WorkflowProcessor PrioritySorters thread-safe (mattmann)
+
+* OODT-492: Refactor Workflow Instance repo to store/retrieve that state 
+  information (mattmann)
+
+* OODT-490: Refactor WorkflowProcessor to push all of its state information 
+  into Workflow Instance (mattmann)
+
+* OODT-432: Add FileSize to know metadata fields set by CAS-Crawler 
+  (bfoster via mattmann)
+
+* OODT-381: Create Runner framework to allow flexible WorkflowTask 
+  execution on different runtimes (mattmann, bfoster)
+
+* OODT-215: Workflow2 Architecture (mattmann, bfoster, sherylj)
+
+* OODT-310: Port WEngine to trunk (mattmann, bfoster, sherylj)
+
+* OODT-487: MetadataBasedProductMover to handle when the source and destinations match (cgoodale)
+
+* OODT-488: Enhanced Solr Indexer capabilities (pramirez)
+
+* OODT-390: Removal of optimize call in Lucene Workflow Instance Repository (pramirez)
+
+* OODT-486: WorkflowInstance tries to cast null strings into dates causing
+  exceptions on getters/setters (mattmann)
+
+* OODT-483: Fix to prevent NumberFormatException in XmlRpcStructFactory (rlaidlaw)
+
+* OODT-471: Added namespace definitions to RSS config files for CAS REST API (rlaidlaw)
+
+* OODT-469: Modified RSSProductServlet to remove hard-coded namespaces and allow 
+configurable namespaces via RSS config files (rlaidlaw)
+
+* OODT-485: Factory out EngineRunner and WorkflowProcessor classes into their own packages (bfoster)
+
+* OODT-481: CAS-Pushpull uniqueMetadataElement is being loaded as empty String instead of null (bfoster)
+
+* OODT-474: Fixed the probem having in a configuration file 
+  ProtocolFactoryInfo.xml (mattmann, YunHee Kang)
+
+* OODT-478: Balance: New hook for hooks.php (nhidalgo via ahart)
+
+* OODT-476: RemoteSiteFile doesn't always set RemoteSite (bfoster)
+
+* OODT-477: CommonsNetFtpProtocol throws exception on successful download (bfoster)
+
+* OODT-329: OODT PEAR Channel (Gavin McDonald via mattmann)
+
+* OODT-472: Puny Module - Return to previous page once the user is done editing  (skhudiky)
+
+* OODT-467: Change SciPgeConfigFileWriter to DynamicConfigFileWriter (bfoster)
+
+* OODT-464: Add file staging support to CAS-PGE's XmlFilePgeConfigBuilder (bfoster)
+
+Release 0.4
+--------------------------------------------
+Release Date: June 13th, 2012
+
+* OODT-446 Addition of Puny module to Balance (skhudiky)
+
+* OODT-462: Include support for special processing instructions in opendap-ps configuration file (luca)
+
+* OODT-463: Ignore container-type DAS attributes when parsing metadata in opendap-ps module (luca)
+
+* OODT-402: Provided default File Manager policy and RSS configuration for 
+products with location metadata.  See subtasks OODT-449, 452 and 453. (rlaidlaw)
+
+* OODT-449: Added default GeoRSS configuration file for the CAS REST RSS service. (rlaidlaw)
+
+* OODT-452: Moved File Manager policy files for GenericFile type (elements.xml, product-types.xml, 
+product-type-element-map.xml) to filemgr/src/main/resources/examples/core subdirectory.  
+Updated six unit tests to use the new subdirectory. (rlaidlaw)
+
+* OODT-453: Added File Manager policy files for simple LocationAwareProduct
+(elements.xml, product-types.xml, product-type-element-map.xml). (rlaidlaw)
+
+* OODT-457: add missing Datatables images for paging through Cas-browser index page (skhudiky)
+
+* OODT-455 Small CSS tweak for the OPSUI Wicket App (cgoodale)
+
+* OODT-451 Implemented CAS metadata precondition class to check for existence of given marker file
+           within product directory (luca)
+
+* OODT-445 Extend env replace support introduced in OODT-343 (ahart)
+
+* OODT-444 Utils class missing from Balance cas-browser module (ahart)
+
+* OODT-448 added missing character (skhudiky)
+
+* OODT-447 changed reference to CAS-SSO directory in the profile management module README.txt (skhudiky)
+
+* OODT-450 Crawler being able to delete a non-empty product directory
+  upon successful product ingestion (luca)
+
+* OODT-421 Make WorkflowLifecycleManager understand 
+  WorkflowStates (mattmann)
+
+* OODT-440 Replace legacy getMetadata("UUID") calls with 
+  getMetadata("CAS." + CoreMetKeys.PRODUCT_ID) (thomas)
+
+* OODT-442 Apache project branding requirements: DOAP
+  file [PATCH] (Shane Curcuru via mattmann)
+
+* OODT-438 Make CAS-Crawlers MimeExtractorConfigReader relative file paths be relative to its
+   XML file (bfoster)
+ 
+* OODT-426 Introduce a CAS-Metadata based renaming interface (bfoster)
+
+* OODT-435 Refactor Graph and ParentChildWorkflow class 
+  outside of PackagedWorkflowRepository (mattmann)
+
+* OODT-157 PCS Operator User Interface web application  
+  (mattmann, ahart, cgoodale, pramirez)
+
+* OODT-401 added profile management module to balance modules directory (skhudiky)
+  
+* OODT-433 Fix retrieveFileByName and retrieveFileById buy setting the product's references 
+  before calling retrieveProduct (thomas)
+
+* OODT-430 Update all boolean type bean properties in the crawler cmd-line-options.xml (thomas)
+
+* OODT-412 Bugfix: MoveMetadataToFailureDir should read MoveMetadataFileToFailureDir (thomas)
+
+* OODT-428 CrawlerBeansPropHandler doesn't set list properties for Spring
+  PropertyOverrideConfigurer correctly (bfoster)
+
+* OODT-429 filemgr query throws NPE when sorting by key with possibly empty values (rickdn)
+
+* OODT-427 filemgr query throws "String index out of range: -1" when some products have undefined
+  metadata values (rickdn)
+
+* OODT-405 Introduced new syntax for environment variable replacement in opendapps module (luca)
+
+* OODT-425 CAS-PGE logger unit-test fails because it sometimes finds logger lock file (bfoster)
+
+* OODT-422 Allow for CAS-PGE PgeMetadata to be dumped to an xml
+  file after initialization (bfoster)
+
+* OODT-410 DataSourceCatalog compatible with HypersonicSQL 
+  (mattmann,rickdn,bfoster,pramirez)
+
+* OODT-413 filemgr query throws NPE when some products have undefined metadata values (rickdn)
+
+* OODT-420 CAS-PGE should fail when product ingests fail (bfoster)
+
+* OODT-419 Make PgeConfigBuilder configuration via PgeTaskMetKeys (bfoster)
+
+* OODT-418 Improve CAS-PGE logging (bfoster)
+
+* OODT-71 Add lifecycle model supporting transition, done 
+  and waiting states (mattmann)
+
+* OODT-414 Create Java Logger Handler for CAS-PGE (bfoster)
+
+* OODT-415 Add support to ExecUtils callProgram to take OutputStreams
+  for forwarding stdout and stderr (bfoster)
+
+* OODT-249 When a node is removed from the Resource Management 
+  system, it should be removed from all of its queues as well (gabe via mattmann)
+
+* OODT-376 Addition of Cas-Browser Balance module (gabe)
+
+* OODT-411 Port CAS-PGE's PcsMetadataKeys to PgeTaskMetKeys (bfoster)
+
+* OODT-409 Convert CAS-PGE metadata keys and workflow statuses from static Strings to enums (bfoster)
+
+* OODT-406 Add CAS-PGE support for multiple Property Adders (bfoster)
+
+* OODT-403 Port wengine ControlMetadata to cas-pge's PgeMetadata (bfoster)
+
+* OODT-34 Ability for File Manager to stage an ingested Product to one of its clients (bfoster)
+
+* OODT-400 Add options for CAS-Crawler's SendNotification CrawlerAction (bfoster)
+
+* OODT-399 cas-cli sub-option requirements for help printing was not
+  using p:required for GroupSubOption (bfoster)
+ 
+* OODT-395 SerializableMetadata.toXml() doesn't set namespace on root element (rickdn)
+
+* OODT-361 Workflow test event fails with NPE (rickdn)
+
+* OODT-373 PCS OpsUI component not listed in OODT pom.xml (ahart)
+
+* OODT-397 XmlRpcWorkflowManager tests don't properly clean up 
+  LuceneCatalog remnants (mattmann)
+
+* OODT-396 Add shutdown hook to the Workflow Manager server (mattmann)
+
+* OODT-394 Integrate CAS-Protocol with CAS-CLI (bfoster)
+
+* OODT-392 Remove old command line option code from commons (bfoster)
+
+* OODT-391 Integrate CAS-Catalog with CAS-CLI (bfoster)
+
+* OODT-352 Integrate CAS-CLI into CAS-Crawler (bfoster)
+
+* OODT-389 Lucene Workflow Instance Repository index now intialized on startup (pramirez)
+
+* OODT-382 Lucene FileManager index now intialized on startup (pramirez)
+
+* OODT-388 Clearly indicate location for loading globally-required Balance modules (ahart)
+
+* OODT-379 Fix CAS-CLI help option sorting and added handler initialization (bfoster)
+
+* OODT-362 Change CmdLineAction names to match their CmdLineOption long option (bfoster)
+
+* OODT-371 Improvements in the richness and consistency of metadata extracted from the THREDDS catalogs
+  in the opendap-ps module (luca)
+
+* OODT-341 XMLPS should be able to stream large results (rickdn)
+
+* OODT-375 Improve ApplicationResponse::includeJavascript to support including 
+  JavaScript snippets in addition to static files (ahart)
+
+* OODT-356 Tool to clean Workflow Instance repositories (mattmann, bfoster)
+
+* OODT-372 Correct LDAPAuthenticationProvider class name (Shakeh Khudikyan via ahart)
+
+* OODT-256 updateMetadata needed in XmlRpcFileManager (mattmann)
+
+* OODT-368 Refactoring of metadata extraction functionality for 
+  opendapps module (Luca Cinquini, mattmann)
+
+* OODT-366 Extension to opendapps module to extract ALL variables 
+  in DDS stream (Luca Cinquini, mattmann)
+
+* OODT-365 Main class to drive opendapps profile generation 
+  (Luca Cinquini, mattmann)
+
+* OODT-367 Integrate CAS protocol into PushPull (mattmann, bfoster)
+
+* OODT-364 Prevent XSS attacks via malformed query string (ahart)
+
+* OODT-363 Add support for LuceneQuery action to optionally return more than
+  just Product ID results (bfoster)
+
+* OODT-351 Integrate CAS-CLI into CAS-Filemgr (bfoster)
+
+* OODT-360 Add supported for CmdLineAction detailed help description (bfoster)
+
+* OODT-359 CmdLineUtility is not printing action messages (bfoster)
+
+* OODT-358 Change CmdLineUtility's run(String[]) method to throw
+  RuntimeException when in debug mode (bfoster)
+
+* OODT-357 Change Print Supported Actions StdCmdLinePrinter output
+  format to more "readable" (bfoster)
+
+* OODT-338 XMLPS unit tests should change constructor to 
+  setUp() (Ricky Nguyen via mattmann)
+
+* OODT-337 XMLPSProductHandler methods should return empty lists instead 
+  of null (Ricky Nguyen via mattmann)
+
+* OODT-336 xmlps should omit joining of tables that are unnecessary to 
+  fulfill query (Ricky Nguyen via mattmann)
+
+* OODT-339 MappingReader should add default join table to DatabaseTable 
+  if none defined (Ricky Nguyen via mattmann)
+
+* OODT-354 CAS-CLI StdCmdLinePrinter doesn't print optional sub-options
+  for group options (bfoster)
+
+* OODT-343 Add support for basic 'environment replacement' in 
+  module config.ini files (ahart)
+
+* OODT-353 getXmlRpcProduct and getProductFromXmlRpc should allow 
+  null values for product member variables since Product is just 
+  a carrier object (bfoster)
+
+* OODT-349 CAS-CLI CmdLineAction should take a printer which
+  it is required to print it's messages to (bfoster)
+
+* OODT-350 File Manager query_tool bug fix for Lucene
+  style queries (goodale, mattmann, bfoster)
+
+* OODT-348 Integration CAS-Resource with CAS-CLI (bfoster)
+
+* OODT-345 Integration CAS-Workflow with CAS-CLI (bfoster)
+
+* OODT-344 Workflow Conditions and Timeout Seconds causes 
+  backwards incompat SerDe issues with XML-RPC (mattmann)
+
+* OODT-330 Factor out command line utility from oodt-commons
+    - check in CAS-CLI... integration to other components to come (bfoster)
+
+* OODT-333 XMLPS query doesn't quote literal string 
+  (Ricky Nguyen, mattmann, Sheryl John)
+
+* OODT-323 Add new command line option in Workflow manager 
+  client to get Workflow Instance Metadata (Sheryl John via mattmann)
+
+* OODT-52 Update the CAS File Manager User Guide (thomas, goodale)
+
+* OODT-322 LDAPAuthenticationProvider class requires 
+  a non-existent path (Shakeh Khudikyan via mattmann)
+
+* OODT-321 PEAR install of PHP filemgr is not working 
+  properly (Shakeh Khudikyan via mattmann)
+
+* OODT-327 Open SSO plug-in client for CAS-SSO (mattmann, pramirez)
+
+* OODT-326 A tool to dump the File Manager catalog metadata 
+  into Solr (mattmann, pramirez)
+
+* OODT-37 Create an Action to Group other Actions Together (pramirez, mattmann)
+
+* OODT-36 Create an Action to Support Simple Branching (pramirez, mattmann)
+
+* OODT-150 ToggleAction addition to crawler (bfoster, mattmann)
+
+* OODT-34 Create an Action to Ingest an Ancillary File (mattmann, pramirez)
+
+* OODT-33 Refactor and Improve File Based Actions (mattmann, pramirez)
+
+* OODT-35 Create an Action to Send an Email (mattmann, pramirez)
+
+* OODT-320 Update OODT About Page (Adam Estrada via mattmann)
+
+* OODT-61 appendTableName property for mapping files in XMLPS 
+  breaks columnname-based lookup in 
+  ResultSet (Ricky Nguyen, mattmann, davekale) 
+
+* OODT-318 Fix for Jenkins builds and java.io.tmpdir issue 
+  for testing via Maven (mattann, Olivier Lamy)
+  
+* OODT-317 Workflow Priority Sorting (mattmann, bfoster)
+
+* OODT-172 Update CAS Curator Tutorial (thomas via mattmann)
+
+* OODT-316 Add the WorkflowManager Use Case Diagram back into 
+  the User Guide (mattmann)
+
+* OODT-43 Add the FileManager Use Case Diagram back into the 
+  User Guide (mattmann, cgoodale)
+
+* OODT-313 Copyright statement on OODT website needs updating 
+  (mattmann, Greg Stein)
+
+* OODT-311 Test failing related to workflow tasks and the 
+  DataSourceWorkflowRepository (mattmann, riverma, Cecilia S. Cheng)
+
+* OODT-299 Rename all Authentication properties to use
+  Authentication instead of Auth (ahart, Shakeh Khudikyan)
+
+* OODT-309 SequentialWorkflowProcessor doesn't need to be a 
+  Thread (mattmann)
+
+* OODT-205 WorkflowInstances should have pre-conditions as 
+  well (mattmann)
+
+* OODT-306 Added FileManager Tool Aliases (mattmann, goodale)
+
+* OODT-208 WorkflowConditions should be identifiable as optional 
+  or required (mattmann)
+
+* OODT-209 Workflow Manager Dynamic Workflows (mattmann)
+
+* OODT-211 Sub Workflows (mattmann, per OODT-70)
+
+* OODT-207 WorkflowConditions should have a timeout (mattmann)
+
+* OODT-308 WEditor save feature is not implemented (bfoster via mattmann)
+
+* OODT-296 Workflow Editor GUI (mattmann, bfoster, ahart)
+
+* OODT-294 Updated website to announce release of 0.3 (goodale)
+
+* OODT-180 Fixed small typo on website (goodale)
+
+* OODT-70 Add ability for sequential and parallel task 
+  specifications for Workflows (mattmann, bfoster)
+
+* OODT-295 BasicVersioner doesn't work with Hierarchical 
+  Products (mattmann, Tim Stough)
+
+
+Release 0.3
+--------------------------------------------
+Release Date: June 20th, 2011
+
+* OODT-292 Updated webapp poms to inherit from core parent pom to allow maven
+  release plugin to function properly (pramirez)
+
+* OODT-279 Make Resource Manager FAILURE and SUCCESS aware instead of just 
+  COMPLETE aware (mattmann, bfoster)
+
+* OODT-278 CAS-PGE returns success even if product file(s) fail to 
+  ingest (mattmann, bfoster)
+
+* OODT-243 Add method is called on already existing jobspec (mattmann, bfoster)
+
+* OODT-199 CoreMetExtractor chokes on Hierarchical Products (mattmann)
+
+* OODT-200 FinalFileLocationExtractor chokes on Hierarchical products
+  (mattmann)
+
+* OODT-186 Common base library for modular OODT web applications built 
+  using PHP (ahart, Gabe Resneck, Shakeh Khudikyan)
+
+* OODT-191 PushPull FileRetrievalSystem NPE if mime comment for type doesn't 
+  include ampersand (mattmann, bfoster) 
+
+* OODT-184 Add ability to poll the load of any particular node (Gabe Resneck via mattmann)
+
+* OODT-60 Annoying build warnings about dependencies that 
+  have "been relocated" (mattmann, davidkale)
+
+* OODT-198 Add test harness for XmlRpcResourceManager (mattmann)
+
+* OODT-182 Add ability to change node capacity during execution (Gabe Resneck via mattmann)
+
+* OODT-162 Parametric Data Model File Manager Catalog (mattmann, ahart, cgoodale)
+
+* OODT-195 XMLValidationLayer: Elements Map and ProductType to Element 
+  Map can become inconsistent (Michael Starch, mattmann, bfoster)
+
+* OODT-197 Null descriptions from elements.xml cause NPE when writing 
+  elements.xml file back out (mattmann, Michael Starch)
+
+* OODT-193 PGE ExternMetExtractor MetWriter (bfoster, mattmann)
+
+* OODT-192 PGE FilenameExtractorMetWriter (mattmann)
+
+* OODT-156 Rewrite Workflow Monitor webapp using Apache Wicket (mattmann)
+
+* OODT-190 WorkflowCondition configuration isn't read (mattmann)
+
+* OODT-189 Refactor and clean up WorkflowCondition 
+  Configuration (mattmann)
+
+* OODT-188 Precondition Comparator to ignore files with 
+  a particular extension (mattmann)
+
+* OODT-129 Integrate with Maven Central (mattmann)
+
+* OODT-177 PHP implementation of sso (ahart)
+
+* OODT-175 cas-wm-webapp javadoc fails maven build (mattmann, bfoster)
+
+* OODT-54 Xml Rpc Serde Casts Longs to Ints (pramirez via mattmann)
+
+* OODT-155 Rewrite File Manager Browser webapp using Apache Wicket (mattmann)
+
+* OODT-185 The File Manager's query_tool script does not handle quoted parameter values. (shardman)
+
+* OODT-181 Add documentation for metFileExt attribute on exec element in
+  ExternMetExtractor (mattmann)
+
+* OODT-179 Upgrade PushPull FTP protocol to use Commons Net 2.2 (mattmann)
+
+* OODT-176 Baked in file manager policy should include example of product type metadata (mattmann)
+
+* OODT-174 TestAcquisitionDateVersioner fails in the later evening (bfoster via mattmann)
+
+* OODT-172 Improvements to the Filename Extractor (mattmann)
+
+* OODT-170 cas-catalog shuffles query results . . . order is lost (bfoster)
+
+* OODT-169 Pushpull dirstruct xml files fail to replace global 
+  variables in name attribute for dir and file elements (bfoster)
+
+* OODT-166 Ability for puspull to dynamically generate ProductName for a given mime-type (bfoster)
+
+* OODT-167 ProcessedPageInfo isLastPage fails for case PageNum = 1 and totalPages = 0 (bfoster)
+
+* OODT-164 AcqusitionDate Versioner (mattmann)
+
+* OODT-163 DirectoryProduct Versioner for the File Manager (mattmann)
+
+* OODT-160 Allow number of session protocol connections in 
+  pushpull to be configurable for each site (bfoster)
+
+* OODT-161 PushPull getTextContext()/getNode() value still poses an issue for some JVMs (bfoster)
+
+* OODT-159 Dynamic Queue/Node was not added to command-line usage printout (bfoster)
+
+* OODT-152 Fixed website and download page.  Reverted back
+  to the same site from OODT-134. (goodale)
+
+* OODT-153 PathUtils [DATE.MONTH] and [DATE.DAY] should pad with 
+  a "0" not a " " (empty space) (bfoster via mattmann)
+
+* OODT-139 PCS JAX-RS services (mattmann, pramirez, bfoster, ahart)
+
+* OODT-151 Add xdocs for modules from old JPL site (mattmann)
+
+* OODT-110 TestExternMetExtractor fails if temp directory is mounted noexec (pramirez, smclees)
+
+* OODT-145 StreamGobbler stopGobblingAndDie() method doesn't wait 
+  until gobble is really dead (bfoster)
+
+* OODT-112 Link to slides on web-grid Maven page is broke (mattmann, goodale)
+
+* OODT-130 Fixed mailing list links for CAS Components maven generated site pages (goodale)
+
+* OODT-144 ExternAction for crawler which executes some external command (bfoster)
+
+* OODT-143 addition of fmprod context.xml and maven servlet-api 
+  dependency scope change to provider (Shakeh Khudikyan, mattmann)
+
+* OODT-142 TypeHandler SerDe is backwards incompat with older 
+  versions of FM (mattmann)
+
+* OODT-141 LuceneCatalog pagination throws LuceneException when 
+  grabbing out of bounds hitNum
+
+* OODT-140 commons-lang version should be standardized on 
+  (2.1. vs 2.3) (bfoster)
+
+* OODT-135 Process Control System Package (mattmann)
+
+* OODT-138 Reference class prints stack trace when mime type repo 
+  cannot be found (mattmann)
+
+* OODT-136 Lack of cataloged mime type causes recoverable exception in 
+  Reference core class (mattmann)
+
+* OODT-134 Update website with News of Release 0.2 and links for download (goodale)
+
+* OODT-128 CAS workflow monitor webapp fails to load due to unparsable 
+  character in JSP file (Rishi Verma via mattmann)
+
+Release 0.2 
+--------------------------------------------
+Release Date: January 17th, 2011
+
+* OODT-119 Curator REST API documentation (mattmann, joshuaga)
+
+* OODT-118 CAS Curator doesn't allow metadata update on file 
+  manager browser (mattmann)
+
+* OODT-116 Cas Curator doesn't ingest (mattmann)
+
+* OODT-117 Final File Location Extractor fails in actual use despite unit test 
+  passing (mattmann)
+
+* OODT-115 TestCatalogServiceLocal fails (mattmann, pramirez)
+
+* OODT-65 Release process now includes publishing to PyPi (pramirez, kelly, mattmann)
+
+* OODT-112 Link to slides on web-grid Maven page is broke (mattmann, kelly)
+
+* OODT-111 Allow a file to be specified in the URL for the 
+  opendapp configurator (Victor Hwang via mattmann)
+
+* OODT-108 Ability for the file manager to ingest a file in 
+  place (Faranak Davoodi via mattmann)
+
+* OODT-73 Update the OODT website @ http://oodt.apache.org  (goodale, kelly) 
+
+* OODT-86 The Product Server Should Have the Capability to Turn Off 
+  Reporting of File Sizes For File Listing Functions (Michael Cayanan via mattmann)
+
+* OODT-41 Product Constructor does not set transfer status (mattmann)
+
+* OODT-72 Unable to set Metadata based off Product Versioning during 
+  Product ingestion (mattmann, bfoster)
+
+* OODT-68 Add Number of Product Hits to ProductPage (bfoster, mattmann)
+
+* OODT-107 Typo identified in filemgr-client usage statement (Rishi Verma via mattmann)
+
+* OODT-58 update file manager to use Tika for mime-type detection (mattmann)
+
+* OODT-106 Logging properties for OPeNDAP ps doesn't include required .level suffix (mattmann)
+
+* OODT-105 Fix GeospatialCoverage in OPeNDAP ps (mattmann)
+
+* OODT-104 Allow const section to plumb information into profile elements (mattmann)
+
+* OODT-90 (cas-crawler patch) CAS-PGE returns success even if product file(s) failed to ingest (bfoster)
+
+* OODT-103 modify cas-common's cmd-line parser to throw a special OptionHelpException if no args 
+  are specified so it can be specifically trapped (bfoster)
+
+* OODT-192 cas-catalog should require a metadata flag for allowing Transaction updates (bfoster)
+
+* OODT-95 cas-catalog throws exceptions with blank messages (bfoster)
+
+* OODT-98 Resource Manager TestXmlQueueRepository test fails on some computers because 
+ <hashmap>.keySet() order is undefined (bfoster, mattmann)
+
+* OODT-97 Allow for unsorted paging across all catalogs to give option of lowering heap footprint (bfoster)
+
+* OODT-96 cas-catalog query parser fails when bucketNames are given (bfoster)
+
+* OODT-94 DataSourceIndex doesn't filter it's query to the bucket names provided in the QueryExpression (bfoster)
+
+* OODT-93 Spring XML resource classpaths are still set to /gov/nasa/jpl... and commons classpaths were not updates to remove 'cas' (bfoster)
+
+* OODT-91 cas-commons command-line help for a given arguments (bfoster)
+
+* OODT-89 (cas-pushpull patch) Synchonize OODT-CAS components, which depend on one another, on a particular Apache-Tika version (bfoster)
+
+* OODT-88 mvn eclipse:eclipse fails to merge resources (bfoster)
+
+* OODT-89 (cas-metadata patch) Synchonize OODT-CAS components, which depend on one another, on a particular Apache-Tika version (bfoster)
+
+* OODT-87 Add date roll dynamic replacement fuction to PathUtils (bfoster)
+
+* OODT-63 Use hard coded version numbers in shared component POMs so that MVN install works (bfoster)
+
+* OODT-85 XML configurator for opendap crashes on null pointer exceptions for some datasets (Victor Hwang via mattmann)
+
+* OODT-78 Ability for Resource Manager to dynamically add/remove nodes and modify queues at run-time (bfoster)
+
+* OODT-79 LRUScheduler removes a job from the JobQueue, but adds it back if can't schedule it, which (in the 
+  JobStack impl) causes JobRepo to create duplicate copies of the same JobSpec with different JobIds (bfoster)
+
+* OODT-77 Make resource manager Queue aware (bfoster)
+
+* OODT-80 Create Cached JobRepository for cas-resource (bfoster)
+
+* OODT-82 Make resource manager's node ip addresses envReplace-able (bfoster)
+
+* OODT-83 the artifactid for pushpull should change to cas-pushpull (Faranak Davoodi via mattmann)
+
+* OODT-76 Commons TimeEventWeightedHash throws a StackOverthrow Error when epsilon is large and event duration is small (bfoster)
+
+* OODT-9 Create a generic OpenDAP profile server (mattmann, Victor Hwang, smcclees)
+
+* OODT-67 All Loggers should be "static final" (bfoster)
+
+* OODT-66 Allow Filemgr XML-RPC connection retries and retry interval to be configurable (bfoster)
+
+* OODT-62 DateTimeVersioner fails on + side of the GMT line (Thomas Bennett via mattmann)
+
+* OODT-57 LuceneCatalog.getNumHits() doesn't properly translate the 
+  given Query object into the equivalent Lucene query (Gabe Resneck via mattmann)
+
+
+
+Release 0.1-incubating 
+--------------------------------------------
+Release Date: 0ct 31, 2010
+
+* OODT-56 Web Grid config contains old caltech licensing header (woollard)
+
+* OODT-55 Latest Tika 0.8-SNAPSHOT broke pushpull (mattmann)
+
+* OODT-51 Workflow unit test fails due to hsql not parsing the apache header correctly (woollard)
+
+* OODT-49 Basic user guide for PushPull (mattmann)
+
+* OODT-22 remove pushpull's jvftp dependency (mattmann)
+
+* OODT-47 Web-grid admin pages reference jpl.eda instead of org.apache.oodt (mattmann)
+
+* OODT-46 XMLPS mapping file doesn't declare XML at top of conf file (mattmann)
+
+* OODT-48 WildcardExpression in XMLPS generates incorrect SQL (mattmann)
+
+* OODT-44 doesn't build in JDK5 because of stringPropertyNames() in 
+  cas/workflow/structs/TaskJobInput.java (mattmann, davekale)
+
+* OODT-42 Fix CAS-Catalog to work in apache and pull over new changes 
+  since apache port (bfoster)
+
+* OODT-3 Update source code to use ASL headers and add NOTICE, etc. 
+  (smcclees, ahart, woollard, mattmann, kelly, pramirez)
+
+* OODT-38 Removed RAT from default build process. Use "mvn -P audit ..." to run RAT (pramirez)
+
+* OODT-40 DataSourceCatalogFactory default validation layer class is incorrect (pramirez via mattmann)
+
+* OODT-39 LuceneCatalogFactory default validation layer class is incorrect (pramirez via mattmann)
+
+* OODT-15 One trunk for all OODT components with top level build (mattmann, bfoster, kelly, woollard)
+
+* OODT-30 updated "grid" svn:ignore property to ignore target folder (David Kale via mattmann)
+
+* OODT-29 Import (ERNE) XMLPS component (David Kale, ahart, woollard, mattmann)
+
+* OODT-27 Import Web Grid component (David Kale, mattmann)
+
+* OODT-26 Typo identified in basic user's guide (Rishi Verma via mattmann)
+
+* OODT-16 OODT website (kelly)
+
+* OODT-24 Fmprod webapp currently depended on the toXML method of the Metadata object 
+  in cas-metadata, which has been removed... (woollard)
+
+* OODT-23 remove pushpull's javamail dependency (mattmann)
+
+* OODT-20 Upgrade oodt-profile's jena dependency to more recent version (kelly)
+
+* OODT-19 Metadata should not use a GPL-licensed XML parser (mattmann)
+
+* OODT-7 Curator adds escaped sequenced spaces when reading extractorBinPath 
+  tag from extractor config files (joshuaga via mattmann)
+
+* OODT-21 Remove jersey dependency from curator and replace with Apache CXF (mattmann)
+
+* OODT-18 Convert curator to use Apache commons rather than jTidy (mattmann)
+
+* OODT-16 - Remove JPL look+feel and web references, as well as dependency on private JPL M2 
+  repository; add relocation tags and disclaimer of Apache Incubation status (kelly)
+
+* OODT-14 User guide pre-requisite to checkout and install oodt-core before attempting to 
+   build cas-filemgr (Cameron Goodale via mattmann)
+
+* OODT-4 - Merge cas-commons and edm-commons (mattmann)
+
+* OODT-8 - grid-product/pom.xml references tika ver. 0.2-SNAPSHOT, which no longer exists (David Kale via mattmann)
+
+* OODT-2 - Import CAS components into OODT SVN (mattmann, joes)
+
+
diff --git a/0.10/HEADER.txt b/0.10/HEADER.txt
new file mode 100644
index 0000000..8c563ab
--- /dev/null
+++ b/0.10/HEADER.txt
@@ -0,0 +1,16 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
diff --git a/0.10/KEYS b/0.10/KEYS
new file mode 100644
index 0000000..d3856f9
--- /dev/null
+++ b/0.10/KEYS
@@ -0,0 +1,345 @@
+This file contains the PGP keys of various developers.
+Please don't use them for email unless you have to. Their main
+purpose is code signing.
+
+Users:
+    pgp < KEYS
+    gpg --import KEYS
+
+Developers: 
+    pgp -kxa <your name>   # and append to KEYS (this file)
+    (pgpk -ll <your name> && pgpk -xa <your name>) >> KEYS
+    (gpg --list-sigs <your name> && gpg --armor --export <your name>) >> KEYS
+
+----
+
+
+pub   1024D/B876884A 2007-12-24
+uid                  Chris Mattmann (CODE SIGNING KEY) <mattmann@apache.org>
+sig 3        B876884A 2007-12-24  Chris Mattmann (CODE SIGNING KEY) <mattmann@apache.org>
+sub   2048g/D3B4F350 2007-12-24
+sig          B876884A 2007-12-24  Chris Mattmann (CODE SIGNING KEY) <mattmann@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.8 (Darwin)
+
+mQGiBEdvL9QRBACuaV06by+pxZHXIxBsfAFYJk7XJgsqR23m5ClCDPusMeaI4XGB
+eU8Nw4iVwgG3p5VLWLXeMIm/KPz3pmxiNyEP/dHoDxOPR+hAqlP5v03D1iK19H7q
+46BIecIwo8q0ei70fBLvMQN+apIFlvYDqVCTm1lxoCQafagqd9p2JtTf+wCg70yM
+nGtrejB+ZTTcb08f7SAHsLED/11vIdcxViN3u+3klhbb99bd/g9KvCU/I/7+MDx1
+3zrSvJV2b2wrxabUJ1Oxsb4/4BXq8A1FyhC1h/d2PsawqiY0GZ02cucbzEmdXH51
+UnrRLM9/txtZ2b7V6YkDmPf0k6rD0SjqAAy1ERekEVUOxnY4sPGmJoyac4j9+pO9
+1vH/A/9LRoJlPTfv/mFYty6/Egckhv48YoRUBo1dNh6IPQY0oVpAFbcXc3GiTyCu
+5iQp7utxP7hoJTUM2Hn5tF9D7IniRC9wsrcW8Gi/f82O4HlmyV4+Tt75nWx018oI
+ObGmwitT27EkOnFcQc9F+Q53nKr+a22SBbpfffF9Xdbkw7V73bQ3Q2hyaXMgTWF0
+dG1hbm4gKENPREUgU0lHTklORyBLRVkpIDxtYXR0bWFubkBhcGFjaGUub3JnPohg
+BBMRAgAgBQJHby/UAhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQcPCcxrh2
+iEr8KwCffMIKMu3TBrGZVu1BPLbMBhjsrl8AoI15rg+tzYZZmZJD6tDS40klTsVA
+uQINBEdvL9QQCAClHjwXMu38iDR3nvbYkWmcz5rfBFvDm/KVQGLnnY96C1r890Ir
+cHxAlSpbGb6qPi5n27v87LoS2bYEitqCUUwB7AQLOgqmLvqMJ4qp5HUfTQ/wH9Br
+wK2LX1oGFJXH14lbZ7xW36n9A/JtXHY8vGz3GuDvKYqbdOCFo8fBLwotdFOHhNYy
+bBYS1G4gtmemXwzH8kcuoIW6LuoRNxluHi1tJGFC1F1uBoxKir7F7BC38DDNvhak
+dSJpm3WxFkEEkIUyIERVGVRoFzLlk72W0R3kZVvnXbtgPklTg/2Sy13Gb+MzTBYt
+5TF841neM/kHdgt45EgBhchHN3Ys3ljabihbAAMFB/4ke4Xe573V78UR/WTMUzfw
+pIysMUzEjNKqOfnAoNnR4WDDca4MwIUl62QqGTRrWZxTD8fAGYxc+m0qmygGKtYq
+LUYB5N/pLGu1sg2j23G8aBKthiCCE+jOr3uebU/j0BTzN/BwXCqIGogELFlPC5Tj
+Hr6c8LpkRFIOjVfuYB2TV4o2RfSFzrSFHCbrU82ojxhYSwyqDGAdD6EGtbbqaEMX
+tGZzHaMVm2gDeV9W2veurxOulgndNg2+FXvgUlOa+KZ2J2DxNBcJv1uBtDAWDyR9
+dTgTbK62ZnSjsnRYbgf0HdA+kW9n9XBMEHwgYk0q+doOWUOQFqC84TgrrhyDd1XZ
+iEkEGBECAAkFAkdvL9QCGwwACgkQcPCcxrh2iEplXwCgraY3ELlDStqpJDSUzVsN
+rGuNiwsAoKz92ycEjcMnoLnX8AaPADdo1m/P
+=zEfO
+-----END PGP PUBLIC KEY BLOCK-----
+
+
+pub   4096R/F9D98BFA 2010-04-16
+uid                  Sean Colin-Patrick Kelly (CODE SIGNING KEY) <kelly@apache.org>
+sig 3        F9D98BFA 2010-04-16  Sean Colin-Patrick Kelly (CODE SIGNING KEY) <kelly@apache.org>
+sub   4096R/AC17FFD8 2010-04-16
+sig          F9D98BFA 2010-04-16  Sean Colin-Patrick Kelly (CODE SIGNING KEY) <kelly@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG/MacGPG2 v2.0.14 (Darwin)
+
+mQINBEvImTIBEADICdyOLBPd1Wttdh9+hhX8DSbHHWOz3gtSfxnIWw+4BWqAvPo+
+oRFSd563gLJdJ/bpjfrhj4wQUMLAwmam1KpOJBbI2yBsgkyRkUalDMkq8QqxMJ7b
+AMCwdVoG507B2c+gzcMCWhIfXXMVId+Cc7c+K+ZGnG0F+SH87hLGD15E0tby2blm
+XHKRgiSjmqCPWjPdZpMrgcjjIpCIGaJ/vAr9CUErB3oi/BD+2WzzS1y4fy6kimuR
+3U3HQ1OmGhoNjTh0+eIH/dDsxeLxzYv5+xRAJXYPQRrG1QzWJleGBqAUYaJV+ctY
+L63jEjY4U8AIl5wzEuhq8qzS0hBT7BjK4ffuLsS6QbYRjmFJUAqxYbYOVmoAfsSw
+y8D38Wja1QM4QOvs9RxRQfn1NahpqAFQwI8L7cxGgLXSXX81OzWYabsjy7E/2pn/
+KxJlG9faShLelO2IBdUd/mUmxyUHc9rmYevStGhfKxeFaMXuGXB32n5WlrpizeTU
+SyALoYhSn6uNfg5DD1YwtoPEPgff5YvONy1os/kU+HXNY++9SfEhZW4kp505Q432
+TrEvs5XtdEyJmAFWdqgaWUpHstL3K9kDtmx4BdKVSguFA+NlkaBmIKFl8JZeopcx
+yfn+sB0SEaMIhCSvWMzI7LBF4Mk/3ZxP0iY0S9i8K1Dm+K+rg0dXpNYfvwARAQAB
+tD5TZWFuIENvbGluLVBhdHJpY2sgS2VsbHkgKENPREUgU0lHTklORyBLRVkpIDxr
+ZWxseUBhcGFjaGUub3JnPokCNwQTAQoAIQUCS8iZMgIbAwULCQgHAwUVCgkICwUW
+AgMBAAIeAQIXgAAKCRAXC2V++dmL+te7D/9Atq2uIWOYL5bLQG8zE18Y4Tw4vBPu
+7zFTqRTrIF+GKnRBi0yS//p5suC/8L7nYEeDit4EX69munzKKuDwYZr6aBfS8VH7
+aspN1HZwcq+R7oxuCOfl/GXzvhWwlSy66nPtI2VbwF/Csag/oS1/YRaGawDoilb/
++BSP69Lpw6m07qlKr79/zUlDpNryU3VyioTyAnI8tpKh4jV71LnZolVdbgWTBQyE
+xBCt7YnK0hcmo/efEd+4ziVQAZb8wSK6JzLxI6Q+K796XbvqSN2P6+vmnX5QjjLo
+qXK2C/WJQmpJWOQVi8NLPYTAnlq2rdmwwsdqXCWQVM7QQgQnf91vma8/mH++fGIC
+6clu9B2zLP9ugaMu0H9xgb3uAURuxisGpOuM8HA/cAfH39rGf5dlIjT2/1K77HZk
+rcrgW+kySAKTz3c7bEP+BNK8yEYKoSr+IavxY+SHHsPsVwvZEt7Hpa2GkvJMTIUw
+lTVU+IbRO3xz3iygc5SLGacdbR2V+iUIBCJwZs7Llwt9dqoBts758FwjGqCkBbKH
+0zRiv/tdd4MgMunjlDhtqoLa1zckWw32H7uOg99ZkA2UnijdCL5fFmewPgDf5l/I
+Ck7qGJmPSWZJGdUklzZFfx4X8401TqeAf5l2cNyScy0Tx21P1D7tV7fK0Egl0oZr
+RpzXbnphviLEXbkCDQRLyJkyARAA7/AWqxv38QORuU6hCem9VsGKw9jfkxzeW92h
++fmgfJOwfTsi1gLnA4tGUnPPWeQ4kpPfT1s1vJmdQbH8R0x+oqV+h4w5erUND22Y
+3n/cR6TckurGe/NEYRXrXo2M3CElEp/Fut2v2tqKannG2eGdH9tUaWpN3PBUY86z
+ma3ZLIMuJ2BJ0jtj8LuKV1ZDmrs5llsMpbK1GKYY3Jkc7G6JQgplPEBKVqdNwhoU
+Kp1SmlP90Bp58YetWFtA5VvXIhoWIH3SPCU2NyEF7eBnLwATkiIwqFUD2jwRIqyB
+SQKu//E7jQkMrlTSZfNWVt3qNTqfB42ovDJ/sw5xrrfTe7wFxTse6Xm3JnasXMKw
+yL6RyYz6+vmP5f/OFil3rY7+QUC2ykkGtu448MOQIQsDe+CilyuWY6FiTJVFGvE7
+F8tRb8ESBy7Kq/r9iqZdx/Qf8CTc+4m8Mpj+OP08O79PAmcUcrmWM9NRryOygHx5
+pVJY2PPkKb979RWco8StZK+DmCCNUb75vZ/SYooK6J0tZkYvBaGxQKZzRpjYNOZf
+wMo7lD+hvXOulaDTb6M3kKsrpnfZ6JG3UU2amfHf9fo7brxBy04vwUolpxj0/OEK
+6CJnX23XktPqVSMBd2WDVudh/m+jOZOF9wcWxYT3WQwXUyUQjexEZsM7eo90gQ5f
+BtkKLDUAEQEAAYkCHwQYAQoACQUCS8iZMgIbDAAKCRAXC2V++dmL+l5+EAC/pQXE
+JdKWYM5q9y8piYctWCCQO6DHNj1iY3oZ0MDpd7ZX9K5Y59VeA77JulkEokPMVwXk
+P0nfIoheORXGMd7psHcOcJUSPGSgXxKztx8gzIA0SVnDnCpwdqh2hCtmR8rUW8Ve
+qnSLoPAODA1e9wP8xcUPnKu65+l9FOmO6/Fi1Caeh3uRSJkObGPj3L9V3I4CjZ2W
+7bttRBdHM3UKhJfCpGntiF2A9lf9rMe79dnQhOz0+yX9wU8f58ZBPydgihgDY2Ky
+mp0ENZsG/PbTwfUPNyR4VKEh31Qs1vdbqrUYoBYB/ikARy1Qw3+Xqji/yA5vkBTH
+qthwYQxTAtLslrl3lP5KtNX+8kJg1ok0O/SuSTKoO9RkaKUtuMmU87JTnEZPpsw9
+ZHgWLAIHhGlOe4LunhcGNJNDZCaDI0i/H8lA3eWwZFJpes6l17cWz+88r32T5cOW
+tjZmC73Rpo5+6al5Eae67X7ilT0iFIyJy1W0caZZL785e72Tee415rpsnPp7OpU/
+6CYwZ3WpA9EjsSqHsZRwQNV+qSob59HJ8J5x0Rcs8bn+uI9lnE++vFFU0PfcM9uY
+wubRjONWRytauv9VTLHSFcYG0WTFt8wFNY585WyvZXtV6zwZ1EIt6L2WgLGJPx2i
+sRO6MhTu8oxGJTwZcP0kcpKlw75OybZxKLj+NA==
+=5uzi
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub   4096R/FF5B0BD5 2010-10-18
+uid                  David Woollard (CODE SIGNING KEY) <woollard@apache.org>
+sig 3        FF5B0BD5 2010-10-18  David Woollard (CODE SIGNING KEY) <woollard@apache.org>
+sub   4096R/0996746E 2010-10-18
+sig          FF5B0BD5 2010-10-18  David Woollard (CODE SIGNING KEY) <woollard@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.11 (Darwin)
+
+mQINBEy8kp8BEAC7aWBPvaoAbSy6boQpfbaBCzEZEZLAHPyLTR8M29Z42tdDi4Hn
+WKDb8a6IBccH1vYxM4y6S8amXWXlBk+ncUyEIKlA1+f0m3vPPVtdVAkSZhK5EcJ1
+qJ8Y4uxWjArwnZ/MgP76yyRNlHk9eKw23KL5d0ayTL+aI/P6TBHZ+VrwAXidNZv1
+ZUkvUcwFd7rDuDt3HAP2pir/wP7M8rfw4To1FlLDWVAts+pNTa+RgvMhWm5B20+q
+/w+BJSA93zEOoTYHEgne5CwWPUZ2qpa//uPVnJpL8o8n8rgBXbs/qsFh3DUcnohY
+jMVeoFcrWrdkTEvf4oq8uH54jYqZ/S3TMyWm4Ixi0p9Y2FlM9QcDuv4vd9c59bBa
+okfX95SSjlvC2miFZUDqlmlYEAktE7UIdbo9xseLoU3PfHw45D6qX0LDHgNvCs8h
+7SBGN7z+qw9rlpYnNIq2uvwRS5aLX+IUShwhUYfvl7bV+UYiknfPMvEXfJnHKgZD
+5w43Z7zoloJukkjtTS00/frela7cQdK9M3YJAbPeyj1eq63uKqn/Qhep7r3ed/CL
+yFjWJrn7uhl427bDyo+TTEqHIiEqSTURm7caVrx4L6q9OI9xH7Z+Kt+JTwYT33SS
+XxHyDZ49GEntDRBRR7QY8kPtJO2ZNmWs0+T2hS3UOO0GxJLa4LL+qx6e1wARAQAB
+tDdEYXZpZCBXb29sbGFyZCAoQ09ERSBTSUdOSU5HIEtFWSkgPHdvb2xsYXJkQGFw
+YWNoZS5vcmc+iQI0BBMBCgAeBQJMvJKfAhsDBQsJCAcDBRUKCQgLAhYCAh4BAheA
+AAoJEBGRFnn/WwvVhLYQAJ7GAmv7yyJVPTvq1yuhzPcwS+vzixXvnIFdvMpItL/o
+nNgZoI0anVcJAzQLcc8MpxxrvT+wulvJjvtwkW/UxK0Rky3nEa1NdVvScQ1NVVxM
+CKTHzDF9TEYe3ZxGnoOxJl6XKWPg9qNPP44Jyo1iPCp2CeHej55WUS0XGZwNI/X8
+P6y46CL9bGoY7kCgjY4Ic3pfhKgQfB10EdunoDUzVq9U2cX3AVLhE1p3Pw0S66wI
+XH21tYuJim/CPqL/9gxp6kyDy15E+1w75KV9QAZQyrgRSstITPhjVm8aR6mfuAsq
+1OwNWso2sQDlXORMI7ZHUwD8EbPNvrPytWV4CJ/7lSXBEgOPyvLbHJGL5gTwmCnx
+Nxyc4pjTIuCxp09YF1JkO6jDbCAK3MbV8KguK2N+n0Tu8BDU2o8yvbSqTwTSWahc
+X8DmatzdUBM2jnRmDfc29Sw3KCTVE6e/cG4qAurjcSD4eDG+OnHcHkXrR1txeIa1
+js9CnkC05ntKIC242SiW1DFSuZLEmjdf0DXg/pokyZC4uL9QS6C1lqSEil/VN97c
+bLqOB91qSO6ag5x6MbMprIEi2gjcsaKWS+I5k+pyiOXv8QamQLTpVbVzjxBtLIB+
+bjN/T2dCOBL6jHeIBJgmAzV00qxiqC/GDn2ReECYM5lGSp6buKLqsgutkspaoZs9
+uQINBEy8kp8BEADwX1KYoW71OA94CG7auevZX9Cj+nLKVuYDE+eaN34NCWkc3+n5
+aONgq/bbRkxzcDdZ3QzJFeV3j9q4F9BL1Zpo52eDu2emMzXaAWGOrZuv5tekMVCy
+Ilthnn/06qojSyqpm9UPPlj5mTsJpQWtWQffzTpj30YNgzH8S9C7wUzF0MKKNfa0
+PuU+K9em8JUI8/fUSp3FO7U0GE/uRd3g4nAP2I5tmG6p2yaXX7ey7kvief/Vyhv5
+GkMXEHze45URmfQxD6t/CFOGdaKQCNpV+sINyb3ZdQO1B1qyIaVA9YuSxWABfRRo
+hPU8/LhNVdViNBo6Vzz6cZQir9U/i4NWxtsvo9FyrAfBKn2hE5gD6PP+VZyKhTus
+fW5bcp8KnIM1PBG598Iz+XtMJzIKnivLNNKMvhTg/mPXUx+vCCen82k6IUNIpXWj
+biaZs3dQqSDK7L4LDBHuNxEl1NHffExyQMTx+4YU5fhiqnEgfsmM8n4cTbDL9sQA
+0fviUhrMeFOyKGhcizAAC/yvG7f7yZ8Go8/YHLEBn0V6pWpuCvsqH4U/HE/dHEKx
+0ns/ePgVnJ79QIea7eXZhKN3G3hmJsik0DMsxZStLtk4Pj1clwES+uzm0PttnWPE
+QDTkUlVgIEAyXlY0VGBdFnUjUEihZ9kVWlz7FkHlNanp2LRl8LNR8ycJwwARAQAB
+iQIfBBgBCgAJBQJMvJKfAhsMAAoJEBGRFnn/WwvV+XEP/A2tYJN9u+PEe5OeHU0u
+0+Bpje4E7lmUSj+HzEwZsoVZVw/9gaBAOwIgXy8M8Yr81GAKDfOZk8VncgXewSu1
+oap4tjkykH15lsQVAkOOfwYGvraeHt3unuouhhJIZb7Cb30h0XCneM3/JYabLTIZ
+sFcE+L2b1yNBK6SjlipTx+G5nljTssTczk12Gy9u+GPz2ySX6YXJy+Ywpkyh0IE0
+UWYsnlU9n2NLxmxF0yEf/wk93iuUmguDAaHRiSPrYsWDkXFhDAy7SA1zl1aE+k2O
+8fGDPL6I9bR0a7HL6s5dlUPcFF5NZPfQ6Xd/LngsctVXZZDz0q3e72h6n68FC8VQ
+THGXADwMooOYH55it52TkGX7zLj5HY3QRTFxOdXk/D2f02xncqB3q3E8lkNtfdEa
+HP+LlPDJYqeDBXXT+k7BEMqo0e0uleZl3caADC4YMSIsWXPNEhK5AfyG8TCvLIui
+mvVjf/yeaDFfcX+mvQbSY41ZEnjUxlYlJFY4YX6GnjwvApK1RZFLWCzWIP/Kiica
+jzlDnFVMDRQY2kiqz2r/gOOWe8+c4Ztqy28BxDhPB0UZCqeGQnmlewKRiW4WuBnT
+YAhPjzaghVUv7B0fANq6XxvJCSQ5w+MLmh1fGFYtBHDO/HtMW7TekryUH+4YO6to
+O0L6LH00gHOgmcXsM1UUlPdr
+=hxbO
+-----END PGP PUBLIC KEY BLOCK-----
+pub   4096R/2C47D568 2011-01-18
+uid                  Paul Michael Ramirez (CODE SIGNING KEY) <pramirez@apache.org>
+sig 3        2C47D568 2011-01-18  Paul Michael Ramirez (CODE SIGNING KEY) <pramirez@apache.org>
+sub   4096R/62C7C4E7 2011-01-18
+sig          2C47D568 2011-01-18  Paul Michael Ramirez (CODE SIGNING KEY) <pramirez@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.16 (FreeBSD)
+
+mQINBE01H/QBEAC9dDTLoaK/m1x9yKB+GC8iD2kdWKl8eSsn5jaNZYNpO/FTzaEC
+qtq1nu8FyWI5SMmIact6evEeEC5RqEHf/QgcoDOV8oum2OpcX0pKSNoAKUqm+4aB
+vWLkLS5X4R8eLWcrkDOpPUeCsDgeRaZw3clmh4vt9er5/YkCAJYdthmuEBkgaEM2
+u+Uf5e0glyWIbvX+BxdGxDGDyzM/QJzIOqwGI6fHxGFZoVCtw+PK/gLqDsdg2c7J
+vvaRSzyPPKsExAS9VcbgBt+XotFMNWQu65CWz/CuK6YCdfDxi3KiqBSBHbi8nwZm
+NJrKJKoahsfY55dflMrcDAWCgoB3eN/ra/pBFBMQNYMG5usd6ZeleYZBLe2jUbx7
+W4YWj3rnh+8trnVodAaepugQGuHXIyCpUJ2I+W0EdWTkKf/vgwuBJSCypJ6eraBD
+M7gYHAyJ9f+Q3SCslQGYoINmdkugOaV7vOGeFLURVkBJWVTCVri4Pq4kMy+E/BXD
+7Pu0xocbaWmTdbWqiQVXXWodl7fgQrQQ+iSEOOSswNwsITbfHqNDyQzhhGoI81uk
+t4YukxjUF2qawnCmSLJGnBiX+N3xLKmAWA8wAkP6YMhCgre0jeoCW9KVp3Y6r09S
+7omqb169XVz80SDqZ2PA7LyeEdYPE3MTg3P421QveFzMnISdE/kHUtHkSQARAQAB
+tD1QYXVsIE1pY2hhZWwgUmFtaXJleiAoQ09ERSBTSUdOSU5HIEtFWSkgPHByYW1p
+cmV6QGFwYWNoZS5vcmc+iQI3BBMBCgAhBQJNNR/0AhsDBQsJCAcDBRUKCQgLBRYC
+AwEAAh4BAheAAAoJEKXbwBgsR9VooCoQALKwFrjXNQANsDjVsb0FydTcEbqd2fAQ
+tByCTSBAfFNvvsTMvaJHTBfd6F6eU+dZQIUPlWJ5Bx/Vw55bzQ2GZ+igaPpJFG/z
+jlNbqmkIacxpeSfBqZ3CGtsKP3uxbAfRC7bt9HG1G15tFzp45bgAAC4ISlq66jIu
+NBuPjeYbEX+uZzMXoVqzvOPdSJis0qteAe7FuZLay+3JhGOGxm8iU1/9UZkP0FQK
+fneqsL/thCYj1KIIr74y+r7vjvNnoHULD/+zaF0DBZY1QpldONtJn7g4Yhonq4om
+gtlU6mOAgpvhZ9HnjySpcl8XtasWebx4ZUZdqQqeBrCLZet30xFnJhqt1AQwC6PY
+/3TQ0xQkNhV1YJI6V0Xrw9zJ8EosYRiz1bZDCqzLUj9Kvzh9gLJ8fYNVDPwUF4U7
+Mw7WdaK73rlCOrqcdGf13P7vQJTYHB6mCkpY5Qu9QejE2eE3Vrd5k6ktwWFkFqu9
+6i40zya2vs2++CM+R7VuEGdXpESvefVKpb0k5coZYZ5rGSye+BkbM7Qo/IvvUtxr
+ftyscUVuQ70/YL71UB2wk83dcYWtj1pz9fg8oqRGrFwiPWmFewpg7kWCUKomAqn4
+0XK1wG4W23AzKNar4Pn0wqQtKkqTTOmnglGCevl3etpx4VCm1NUnngsutC73Dz5E
+kHmuEKJTZfh6uQINBE01H/QBEAC03btD8DXIIEYCxDJG3G9G5FDi6fYh1wzxmyjV
+GTv6yFRnWHnS+amfWPW7F8zbXhf0OUure+2jj5TeGMGVLkNb1ys88NWwBczS8zeF
+4l8Kx/Jc3SSY/J71oGA2TJaxLs5ya/J9qzhk/9vJyetaGUSTTs+shkQqh4Hqv4WR
+XTpumbnc0rLvj7edm343NgJvItAlPjH2b4bCFtUjgI9BRSwe5yPJaEmUZI4C2C7O
+vk5SMLJC9WW9+7gs17Nh4af5wLNldUJPaLYmerKmA1mXWTAoagELWQufelqwtYVn
+NHdaU9XbUDFrMMr+O0PcJhyDQ2TDBlUNteeMR+O7LLDPc6A7O5tjNW1EZpjlQZxI
+izC+nSkPYjr7szwz5ImXywwsBGN3KTejLkPlOAWdUjt0kUoxhsb1pmJ0dsHlWu04
+yWaixl+c8sICFnKeCAFrU2sqMyxMyUOxFi/Gf4uafKEHklWWc/RD4e3hQkeF9TMW
+tV18nIjszNGWNXjUIPHmArZTwQoJl+vXe2E8smALnjmWmkNJukaYzvX0yr0YOO8l
+C0RmZhf5mb40ZE8nhSoVXvQJOsdTC47lI1FggYXCiIXa9guUt9FJhz3yaGUbAQfn
+jKhAa/kvZ1t31bNBlnl5MzV6/4P7ioEaxH1EHdrnH8vJ6HJsk0a850cJa9GErX8m
+WxUdrQARAQABiQIfBBgBCgAJBQJNNR/0AhsMAAoJEKXbwBgsR9Vo7h0QALfXu13c
+UvWP+g7uV9fEc/923FKsUDJqDXx++mVEphmiOG1d17HSP7l3naA3+WtzCBjobHpE
+vlKex4lsAefGnZt+3GnpCAwAXxPS56Z0D9gOyxNkqSOA8BL/cJ7clcdLFzivCzQw
+fpouBxn8Yd3SR9K8KNggsSwjPLiat0ZvPlml4YlGskGJVz8u7ZmYVpXKW9Hh3tlL
+Ze4Vpwlu1Bb02T50GdvTP3NDXKcjV7HXywz0LqGzI3BZhGX/VMnhbmzWjxHwkCFN
+aFD/D66pvakaMElSPeBJEzK8G6OWlhCfQzRFjUv3eDYt/7Uc0qkHpc59VAvi284v
+EMqjZm5m7IZ9hC5fboxUKkylMg1vKreL33qNpk0cy5zoRCcEselQ/gr7dN4/kmp0
+lDqIDqaVyCJBo+97eS2J1FeW5gCAXpAiWNSgq+LWPbgWQ5kva1gtbECvnXNxeqc2
+2mxjM5FHyqNEaOrIUxE4VapQFSXQXPLnnTr1SHL700rW+RFKVWW/8o/m0D6zOn8K
+QDnKDXRHvjq5l7ClpKRgsg2YWKMcsoHXgmB4QhwLZ5FyQzU9gXgCaUZnJ1dRbUzM
+7i4z6bYGWC517oJPPJz+3NbghByn3KM5lvNO261GMGrMcxEa3KNB4S8NES0DcRGq
+CizdyPOVKDT7Y7Gx7UhKzK2MNG6RvB0UZWl6
+=D1qo
+-----END PGP PUBLIC KEY BLOCK-----
+pub   4096R/80EF652D 2011-06-08
+uid                  Andrew Hart (CODE SIGNING KEY) <ahart@apache.org>
+sig 3        80EF652D 2011-06-08  Andrew Hart (CODE SIGNING KEY) <ahart@apache.org>
+sub   4096R/D4B0286D 2011-06-08
+sig          80EF652D 2011-06-08  Andrew Hart (CODE SIGNING KEY) <ahart@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.17 (FreeBSD)
+
+mQINBE3u3zUBEADDkrLBvE1NQZ/0jrracXWXNcaDwNVhDLtLseB/0Y8PUEsj/HyC
+DoVyKpDjNseCVOE8A8JvuWJMZDRxDlpsLFIWX4TZpapnwUy3iRuehZBN+iSG184k
+iyv1nomFOyBsF7PcJlkgwvEX7GkTAvRFfYx1PNHZBWghirM22GMihMauH2y4E8Fo
+s6gJ+hsFeV96xzJATWmhWEO4CJDTFiqdwlGBu85UOeDSkHu0qegrmCj7WuJpHD6t
+IIvmvLJH48+jF374u2Y9QZWGWzkWh91QBYHy8SA+UEcjkeZf7WEuGbrd1roVO+1q
+Wp0adCkzcqDTyRZZuSC3hmh+Rw6miRjcWjquKtET5v6HPMcvvnvcbbEVkpyrGgAR
+JMQMbZxIvJwP5i8e9T6kRzTyh4VAXoh2uidR2xSyF7pFoZu9KpDH99vv0R1h67R9
+uvfsjJuvnjIX5Gbg0T1haF4YseFLDPW3gA0sj366JvMQhVfqo1nvbfQaaVoAMExX
+4TO0j4++rvZKhovHRK1YdCbdxyP1iNmQqLGJjRMOLM1i88DG+/gWPDuPzOc0pyO5
+gbpTa3/Bc49QBJmqwroBnXquoBBbK+0fwhBa4KcIdmthiYpAzw21+F08hlU6R7ik
+G9jK3iaDxO/Wqx4Q25OytXGLsROEbg/kBJcDncSymAdpPq0ImO8pgtlA5wARAQAB
+tDFBbmRyZXcgSGFydCAoQ09ERSBTSUdOSU5HIEtFWSkgPGFoYXJ0QGFwYWNoZS5v
+cmc+iQI3BBMBCgAhBQJN7t81AhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJ
+EHGJcAiA72UtLFkP+wZioDcUoXuZAFbVdgoNI+oR6z/7OGq5Hj5WY/LMQaPJztM3
+kBtY4M7oEBIgz3N7EdTBwmCGLjZD0uMBXQYJxL9eL8exhzafHbMgzmcZvUIRt0H2
+nehzcAbZDHSxG9HM7VXCF5FuEOjVnqUgKeQZI1e3/RWZRjSeZ3vW0rh+DQf0pHQk
+7igBuEGORJH+YBRoLxp3obC0x3sMwv03djLB9oNDBhK0YZn3n7gNeGeYUrJ/7scx
+tLJpPlYMxBaUJIZRF3nEfk0M2UN4XF6Ayj1Ay7v6Hlb25UKJlgeuUz8BxrfW3X9Q
+3wJWNXuO1Q0JH5g5mHoT9AAk+aGAt62KUq885pLuKdigp4lEsYIm8jw9cQ6gRuAI
+8Nt4MwDn4Vura9Rdq8jZWtVeiWFDxD8HzoS0rDflnhc9QnF5GGr6YBU0dyc7akPT
+XGXVAZSShsXWUA82GGsBP+TuxdZblHZKFN53kOPXh7nmnTTbKz64SnNfO5ucgkGQ
+e9pyMcPGIk0/giX3aqW7TV1Dw2ZKdIjnXmF/CB3Yq2feifg+VtI0MaArx0gGLZiC
+9nHYKQz/YuyT+FgGXvj4zgn+l5K0ybDAtSJkfRXBdDrXsIG2joalmxxRIg1NdbbM
+yAp5UJIr67yBMt2c6dOzYycTiS5fWLeFrXSNXjwdKzWHPXvd2xT70Y1HaeZQuQIN
+BE3u3zUBEACw9COP560TW7zvRY9wmaZjk3wqobH+mXE+CT26VaDwoOpr9GT0JiHe
+sKc+/MbVyNJcIjupG++UoWIGu7LHtruPtsgDFBEIKJDPduaMiLiezAqoWOxnsth/
+/nvqcL6zBRvWWOxgjoqkCdIn4XivYpY9F1jXU5BEeEHUYZMlHn86oewfaGxX5htG
+eb8F4txxxxeGAcyn3AeFXT4UvgSIvtdf2jX4u28stjwTm4wvXKlGRy6bBOrfnfu0
+j7Wolhp5KRdJ0yHnFbaUbTeZ78nz0YXmj1vXVoFBpr/gZB1kuWiGyXGEltQkZw7+
+8App9KVEPSTOBJpyLAqMzu7it70oOZLEfMaa/vWhoCOnizs4DW7aJ8ik6gZ5pPaC
+kjpV8S3HFZeRwxslFCBrF47cL4BmleK4bUgNUP6NZpQqV90dt2e267nHBxhXJ0x+
+i/K9p2sfEp0FupptNi0xVMgJJctDSXpPpbr+FyWCwmzGuDf7wp5ixxfi3Vw0yunt
+XAIs+Di5O/nFDALV8TMoDbRLEgAtMI+geZVAYng9RXCf1xc2X0kO4QhJ/6YttV2D
+a5T9yPuBn1maOWY+WwbSKEOYC8dk0+HLE4Hyg6CI+g3AtqR7vN02m3oavgd4hwxT
+KaC75J95Vmky3WY7tlGcm5blJ68Wh8ZotlKaigKchThikCJdpExKtQARAQABiQIf
+BBgBCgAJBQJN7t81AhsMAAoJEHGJcAiA72Ut0OYP/AmhiJvQXH6mJSVQk2pQrfjV
+m/VK/wLcdKPK/gGq//NZzpNVeUOnz2KIzZhQ1VB/vjwkQiPS6lEMpiY6RKNusUEu
+maXWZaFuqq/BiOO6U4S6WUDehT+JxmtdcQ08FqUnWhGT4LtWSBSJ6y3PofKNwMJG
+gpuPrSgMuWKX8/LLMUdY6pr/gvlj1WgOu+6OrR8RaLGvFIbJqYN3cMMx6UbD4dlt
+0unBmewppj8ALF3zDSqxhfks24gvOS43ybMWeeapEy7BtyrNy+TObmbxqvP64QDh
+ZQDHx1PEGpJaO2og3/vdwWsFetVPION/EpJ/IkzJ3mI+UgkPcb/lJ5VvGyi0w+Rt
+vMvfvG/EMd6NzAfEEsR1hFCmhTaF+d5sp6xeKzFIpuc4NQMsQoU/fnDo72EEC7KD
+KC4lu3Gu+zSpTNWIxLP/3Ds4t7rBKXTRXEgnkZbiWc42h5DV8xOqF5gQQh79vEdG
+5zkgD3fQtUSQ/r8e1wgTqTZKZzcV6w3VB9UAskC6zUlo4QumE2VXVcxZOjbLhzHj
+wr2bWN6Yv2P8WfU8GvLcRrfnGumPoPXstHvVU/8oN82HkOl4JWWCZv14yO3G3tJS
+WFNwrLA6AujhiJgEHrqYbuq/2rJyW52C4/y8TwZlWiMfr1oGBEef8p6KEugRjxiG
+YDdbxIVQv3HKn/B7keII
+=ZNn9
+-----END PGP PUBLIC KEY BLOCK-----
+pub   4096R/783CE7BB 2014-12-19
+uid                  Tom Barber (CODE SIGNING KEY) <magicaltrout@apache.org>
+sig 3        783CE7BB 2014-12-19  Tom Barber (CODE SIGNING KEY) <magicaltrout@apache.org>
+sub   4096R/E0D420E1 2014-12-19
+sig          783CE7BB 2014-12-19  Tom Barber (CODE SIGNING KEY) <magicaltrout@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1
+
+mQINBFSUCJUBEADdQ+oIicAHr7W+2hGJR/dVt0Yh6TuK8i6k4JQ3vU0aWbVDC/By
+jTERCvdDGM0xglgwLj06nM1+RVtR4zk+fki49asZInocJG2bWnFz5oe3QCnsKuao
+UeTETVGEM47HKaxAQq4w8v4Kcs/ZftaxV1L0F3yOof5HwvctSrVtfjS5fbi6qVzP
+u9NH1s6Bvj6TxUGzu5LzeS1xRQJWXkxLCtMovKh57uZ2FI6nyoRyVFEKMxw4cFzj
+b4Q/2iJUI6+5QlPRHTeP5NAaocGjTogt7Rkbk/CY5PUZnYT84sxLvKOhFOUnhLMt
+sHBX34Be0fSfhaNLCkIYANV25R+WwFSrgyGfaQCOs/SkybywhAVPRLQxwlIfyZnO
+RvY1w/4EuqpjAyC/K83Fg+4Mlx3mvjSTXtVyVlxQZXxKH2khunwMNWIkEnapBwg+
+jADYLrs3FvxdHASCEZfezpnlEKvlIhg6Week/K9LU2vr9BrYznTT2AuPQxOXIYh8
+Bn2KLIX6PHcV4FnEBo0Y/PUnDHRHmrZpP0LEFuABTtj7RCx+/rFeRMH3g+VJ3bKs
+lKqkI1cqs7fjNRSw+KSb8Ljt7HJNeT+n5a2V9eYgx5hG0GSPw3utXKXGYof+pbeG
+d3CBv1yOD0FqyLrv9TTN+d60ShDjIZPsfy2An2+2trwUAobPqT/+rN+OOwARAQAB
+tDdUb20gQmFyYmVyIChDT0RFIFNJR05JTkcgS0VZKSA8bWFnaWNhbHRyb3V0QGFw
+YWNoZS5vcmc+iQI4BBMBAgAiBQJUlAiVAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIe
+AQIXgAAKCRDYFnFxeDznuxfEEACrAZBU21yrWvPozj16iq1wXYkMMYtpWuaYWnA0
+CWUjYqux690UaczmA/52YRN+1MquI9cjGEOGi6ygThmlUeU5yIfB1p8+jiRXZhbv
+8vsYcW6VpyE8jotG/24WG1p7QMGntUhjYA/uhK9/uhsMF/eG3A2PGfJTJ7+g4Ziu
+8SA1KLKCs/bg3n0uvrysbQ6LQRP0Tp8h+ZSbGI7lSnBytcYEAzHbVidYTUDIMhdz
+fNSwNha4aCfd4MNtbJ1H5gK8kEry31Cv9bzGQ+5TqX/0+Uh23r2hzgnhRNO5ZEBO
+VS8MKceJ6EHxrkzm0/Uk54oe2BkMY3sN0YzjPgOQQdltIE4JHT3wePs0oM1cZ/Ih
+S2agn3Tsq9ZdW7IdJTy3ct4EPpTrYQZoD4q/ZRfGJfP6bQNMnqRYxDgoUZQAMw5r
+QauRz7mtx6aXvAgxIVeDpW0Uj+JW990MvSKaoq/Tit7x69IFj0LCvQ+1lqRN1+CC
+28Cjam2WQcoBbu5fF+RU4FfJA2IPe9xIF/ZrZiMgaBVvArgRj5iheigIN5QQmo23
+/cTNueidsqR0zTuge5nIz6fxBjsrMJAyAOKn5FCw9+gcACDIvHa2aLgs+cWQ0X61
+6ZSjYyLJTOjYOkaHN7+gb0w3j/7HywaZ7WHQPvBx/mgnncaz0gMJj+MKhSxxsKN4
+3N7Au7kCDQRUlAiVARAArZ9t4bWjrI+uYzlNWCYmQOT5Etq0cByrZRJjBianhakW
+r0E1cBSqtynume2bVtCHtOQzoT7vAlDm2IVjZasMD39U2OpaH2RjUguUUsTrzwyN
+3OA+cV/wpbHSoJZPFWzD+V3PpwLGK1FKGQLuegQ9byOMAjReOlqQXvLqU6EHB1nU
+bftkhNcifDCecNrfhLrCcmmY+hLnNZmYqRR5RLyzXF8KMPimysOdrzPMBQnFyq4o
+GPpqubn3TM56faYtRfB4O31Fc1NxIg64xZ9Dq39ROmpsr7ZAQxKol4Tc+CIshMxv
+w+Hc+Qyxj6ZpjAEy2bwgH7czp4MkRi6fwETaQqmYjWyglndVlOCqaR83EMuY6wzg
+SgXYSyI7lHXxRA3P+R9uOR9JseF/NxC2o1TXDKQ9Eyd2pMdZ9txd1182Y6sCD+Ji
+KxELdMZRXT+UhLP6SKjhYQjtTdi8Q9IF9LAl2beF3uTtzjqCsTgtCQ8r+QPY7obW
+z5ZI1qxTIVPaSeBayi22I6mU02QUMTMCmH1piXxsESqyMQ221nuDik0pUVC2exsk
+HnqC4/aVvJSnxLk1H/9dopzyeWp37IFQRH9WzyXc4tcqExx+fCaetPAe7oKOlwzj
+arzAwsOM5GG/bs+rpMB7g9wGaz8fl9onpCcuGReSUheethY1DakW8W5JEp6deZkA
+EQEAAYkCHwQYAQIACQUCVJQIlQIbDAAKCRDYFnFxeDznu6uRD/sFXFwP6PSEt0v8
+Y5h2VFPhcN2nt42+jSyzfY5XEzasuuYYsTPxKEPbZHl5By86i0vLJ1wvJnvoEfEy
+XwMyLLnw87RgsnkHL0NHGDJF7drQpbhIgSZfQV2hY1J3Az1PMWs7rI2tbKeQUmC1
+k7iyyzEyQnphTbRd7FEKYAFPynrmrlRPnG1zNRFYGC2S5VkxCOV/fwN2Vp8PXfo9
+P9Az4Q0tEuowNiW7Pp4KaHundIQtSMdZAM0bj5wRkw4Y96UYGD/huxvAi7MCgOgv
+NB8e656rSxigNiEJ9gOYOwH/6kM44g8BPtcLzeU4tDKzZdfHCxrXzxcBIVa3QZ69
+c7JUJTU89LScJm8rbHBUwQrrf47vSxs8pzUTpFTEoPaBnLD/WOXlPwiJyyOqSh+w
+kZqpC3Jn0aXx4hcmVFtmoaNsNehiJwAl2bX7gwcia6zey0Z1Kljr7N+ikHMhZLjA
+PS3BpMfEc/hcL9+vr74sa32zm9S4bFrd1xCGMv2hcoVWyI38OrFBvvK6uKTxVCPl
+mlgDkcdEboOoQNg5TpVXGnyop0dXICTfx2W/v+7Pq97efFlo7h3ODI7GtltHxqbs
+IGuODo8x0hnGrvAOZYb5BUL7/CRo9PMt91DLPOREFSMHVEsA12OnaTQ2hPZrVOcV
+HSTj1SAfoZ5erxSk3/eMd3H52/6jBg==
+=yBb7
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/0.10/LICENSE.txt b/0.10/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/0.10/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/0.10/NOTICE.txt b/0.10/NOTICE.txt
new file mode 100644
index 0000000..e7bfeff
--- /dev/null
+++ b/0.10/NOTICE.txt
@@ -0,0 +1,103 @@
+Apache OODT
+Copyright 2010-2015 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+This product includes the WebAppers Progress Bar, version 0.2, written by
+Ray Cheung. WebAppers Progress Bar is freely distributable under the terms 
+of the Creative Commons license. For details, see the WebAppers web site: 
+    http://www.webappers.com/progressBar/
+
+This product includes the Prototype JavaScript framework, version 1.5.0,
+written by Sam Stephenson. Prototype is freely distributable under the terms 
+of an MIT-style license. For details, see the Prototype web site: 
+    http://prototype.conio.net/
+
+This product includes the jQuery JavaScript Library, version 1.3.1, written
+by John Resig. jQuery is dually licensed under the MIT and GPL licenses, and
+is included in this software distribution under the MIT license. For details,
+see the jQuery web site: 
+    http://jquery.com/
+
+This product includes the jQuery UI, versions 1.2.2b2 and 1.5.3, written by 
+Paul Bakaus. The jQuery UI is dually licensed under the MIT and GPL licenses, 
+and is included in this software distribution under the MIT license. For 
+details, see the jQuery UI web site:
+    http://docs.jquery.com/UI
+
+This product includes the jQuery UI CSS Framework. The jQuery UI CSS 
+Framework is dually licensed under the MIT and GPL licenses, and is included 
+in this software distribution under the MIT license. For details, see the 
+jQuery UI web site:
+    http://jqueryui.com/about
+
+This product includes the jQuery Alert Dialogs Plugin, version 1.1, written
+by Cory S.N. LaViska for A Beautiful Site, LLC. The jQuery Alert Dialogs 
+Plugin is dually licensed under the MIT and GPL licenses, and is included in 
+this software distribution under the MIT license. For details, see the A
+Beautiful Site web site:
+    http://abeautifulsite.net/
+
+This product includes the jQuery PeriodicalUpdater Plugin, version 2.0, 
+written by Robert Fischer for Smokejumper IT and 360innovate. The jQuery
+PeriodicalUpdater Plugin is dually licensed under the MIT and GPL licenses, 
+and is included in this software distribution under the MIT license. For 
+details, see the following websites:
+     Robert Fischer (http://smokejumperit.com)
+     360innovate (http://www.360innovate.co.uk)
+
+This product includes the jQuery blockUI Plugin, version 2.15, written by
+M. Alsup. The jQuery blockUI Plugin is dually licensed under the MIT and 
+GPL licenses, and is included in this software distribution under the MIT 
+license. For details, see:
+     http://malsup.com/jquery/block/
+
+This product includes the jQuery Cookie Plugin, written by Klaus Hartl.
+The jQuery Cookie Plugin is dually licensed under the MIT and GPL licenses, 
+and is included in this software distribution under the MIT license.
+
+This product includes the jQuery Treeview Plugin, version 1.4, and the
+Async Treeview extension, version 0.1, written by Jörn Zaefferer. The 
+jQuery Treeview Plugin and Async Treeview are dually licensed under the MIT 
+and GPL licenses, and is included in this software distribution under the 
+MIT license. For details, see:
+     http://bassistance.de/jquery-plugins/jquery-plugin-treeview/
+     http://docs.jquery.com/Plugins/Treeview
+ 
+This product includes the Dynamic Tree View Control, version 0.4.0, written
+by Martin Wendt. The Dynamic Tree View COntrol is licensed under the MIT
+license. For details, see:
+     http://dynatree.googlecode.com/
+
+This product utilizes using v1.3.4 of Doug Lea's java.util.concurrent.
+
+This product utilizes jsch version 0.1.42.
+
+------------------------------------------------------------------------------
+Copyright (c) 2002-2010 Atsuhiko Yamanaka, JCraft,Inc. 
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/0.10/README.md b/0.10/README.md
new file mode 100644
index 0000000..de11028
--- /dev/null
+++ b/0.10/README.md
@@ -0,0 +1,153 @@
+=============================================================
+ Welcome to Apache OODT  <http://oodt.apache.org/>
+=============================================================
+
+OODT is a grid middleware framework used on a number of successful projects at
+NASA's Jet Propulsion Laboratory/California Institute of Technology, and many
+other research institutions and universities, specifically those part of the:
+
+* National Cancer Institute's (NCI's) Early Detection Research Network (EDRN)
+  project - over 40+ institutions all performing research into discovering
+  biomarkers which are early indicators of disease.
+* NASA's Planetary Data System (PDS) - NASA's planetary data archive, a
+  repository and registry for all planetary data collected over the past 30+
+  years.
+* Various Earth Science data processing missions, including
+  Seawinds/QuickSCAT, the Orbiting Carbon Observatory, the NPP Sounder PEATE
+  project, and the Soil Moisture Active Passive (SMAP) mission.
+
+OODT is a Top Level project of the Apache Software Foundation
+<http://www.apache.org/>.
+
+Getting Started
+===============
+
+OODT is primarily written in Java, with some components available in Python.
+It requires Java 5 and uses the Maven 2 <http://maven.apache.org/> build
+system.  To build the Java components of OODT, use the following command in
+this directory:
+
+    mvn clean install
+
+For the Python components, see the "agility" subdirectory.
+
+Contributing
+============
+To contribute a patch, follow these instructions (note that installing
+[Hub](http://hub.github.com) is not strictly required, but is recommended).
+
+```
+0. Download and install hub.github.com
+1. File JIRA issue for your fix at https://issues.apache.org/jira/browse/OODT
+- you will get issue id OODT-xxx where xxx is the issue ID.
+2. git clone http://github.com/apache/oodt.git
+3. cd oodt
+4. git checkout -b OODT-xxx
+5. edit files
+6. git status (make sure it shows what files you expected to edit)
+7. git add <files>
+8. git commit -m “fix for OODT-xxx contributed by <your username>”
+9. git fork
+10. git push -u <your git username> OODT-xxx
+11. git pull-request
+```
+
+
+License (see also LICENSE.txt)
+==============================
+
+Collective work: Copyright 2010-2012 The Apache Software Foundation.
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Apache OODT includes a number of subcomponents with separate copyright
+notices and license terms. Your use of these subcomponents is subject to
+the terms and conditions of the licenses listed in the LICENSE.txt file.
+
+Export control
+==============
+
+This distribution includes cryptographic software.  The country in which you
+currently reside may have restrictions on the import, possession, use, and/or
+re-export to another country, of encryption software.  BEFORE using any
+encryption software, please check your country's laws, regulations and
+policies concerning the import, possession, or use, and re-export of
+encryption software, to see if this is permitted.  See
+<http://www.wassenaar.org/> for more information.
+
+The U.S.  Government Department of Commerce, Bureau of Industry and Security
+(BIS), has classified this software as Export Commodity Control Number (ECCN)
+5D002.C.1, which includes information security software using or performing
+cryptographic functions with asymmetric algorithms.  The form and manner of
+this Apache Software Foundation distribution makes it eligible for export
+under the License Exception ENC Technology Software Unrestricted (TSU)
+exception (see the BIS Export Administration Regulations, Section 740.13) for
+both object code and source code.
+
+The following provides more details on the included cryptographic software:
+
+    Apache OODT uses Apache Tika which uses the Bouncy Castle generic
+    encryption libraries for extracting text content and metadata from
+    encrypted PDF files.  See http://www.bouncycastle.org/ for more details on
+    Bouncy Castle.
+
+Documentation
+=============
+
+You can build a local copy of the OODT documentation including JavaDocs using
+the following Maven 2 command in the OODT source directory:
+
+    mvn site
+
+You can then open the OODT Documentation in a web browser:
+
+    ./target/site/index.html
+
+Note: all OODT source files are encoded with UTF-8.  You must set your
+MAVEN_OPTS environment variable to include "-Dfile.encoding=UTF-8" in order to
+properly generate the web site and other artifacts from source.
+
+Note: generating the documentation requires enormous amounts of memory.  More
+than likely you'll need to add to the MAVEN_OPTS environment variable in order
+to set the Java heap maximum size with "-Xmx512m" or larger before attempting
+to run "mvn site".
+
+Mailing Lists
+=============
+
+Discussion about OODT takes place on the following mailing lists:
+
+    dev@oodt.apache.org    - About using OODT and developing OODT
+
+Notification on all code changes are sent to the following mailing list:
+
+    commits@oodt.apache.org
+
+The mailing lists are open to anyone and publicly archived.
+
+You can subscribe the mailing lists by sending a message to
+<LIST>-subscribe@oodt.apache.org (for example
+dev-subscribe@oodt...).  To unsubscribe, send a message to
+<LIST>-unsubscribe@oodt.apache.org.  For more instructions, send a
+message to <LIST>-help@oodt.apache.org.
+
+Issue Tracker
+=============
+
+If you encounter errors in OODT or want to suggest an improvement or a new
+feature, please visit the OODT issue tracker at
+https://issues.apache.org/jira/browse/OODT.  There you can also find the
+latest information on known issues and recent bug fixes and enhancements.
diff --git a/0.10/agility/MANIFEST.in b/0.10/agility/MANIFEST.in
new file mode 100644
index 0000000..aba871f
--- /dev/null
+++ b/0.10/agility/MANIFEST.in
@@ -0,0 +1,2 @@
+include distribute_setup.py
+recursive-include docs *.txt
diff --git a/0.10/agility/README.txt b/0.10/agility/README.txt
new file mode 100644
index 0000000..c8c45a3
--- /dev/null
+++ b/0.10/agility/README.txt
@@ -0,0 +1,17 @@
+Agile OODT is a new version of `Object Oriented Data Technology`_.  It
+improves upon the previous version by being easier to develop, maintain, and
+extend; simpler to integrate by; and faster to use.
+
+.. This is licensed software; see the files NOTICE.txt and docs/LICENSE.txt.
+
+.. For installation instructions, see docs/INSTALL.txt.
+
+.. References:
+.. _`Object Oriented Data Technology`: http://oodt.apache.org/
+
+.. meta::
+    :keywords: OODT, data, object, OO, discovery, metadata,
+        transfer, transformation, query, search, retrieval
+    :description lang=en: Agile OODT, the nimble version of
+        Object Oriented Data Technology
+
diff --git a/0.10/agility/docs/HISTORY.txt b/0.10/agility/docs/HISTORY.txt
new file mode 100644
index 0000000..094256b
--- /dev/null
+++ b/0.10/agility/docs/HISTORY.txt
@@ -0,0 +1,21 @@
+Changelog
+=========
+
+What follows is a history of changes to this software.
+
+0.2 - Current
+-------------
+
+This is the current release of Agile OODT, representing its migration from the
+Apache Incubator to a full-fledged top-level Apache project.
+
+
+0.1-incubating - Initial Apache Release
+---------------------------------------
+
+This release marks the induction of Agile OODT into the Apache Software
+Foundation's Incubator_.
+
+
+.. References:
+.. _Incubator: http://incubator.apache.org/
\ No newline at end of file
diff --git a/0.10/agility/docs/INSTALL.txt b/0.10/agility/docs/INSTALL.txt
new file mode 100644
index 0000000..eaa074f
--- /dev/null
+++ b/0.10/agility/docs/INSTALL.txt
@@ -0,0 +1,91 @@
+Installation
+============
+
+What follows are the installation instructions for Agile OODT.
+
+
+Quick Instructions
+------------------
+
+As a user with administrative privileges, run either::
+
+    pip oodt
+
+or::
+
+    easy_install oodt
+
+depending on what's available on your system.  You're done!
+
+
+Full Instructions
+-----------------
+
+Agile OODT requires the Python_ programming language.  We recommend version 2.4
+or later.  As of this writing, 2.7 is the latest stable version.  If Python is
+not yet installed on your system, you can find binary and and source
+distributions from the Python website.
+
+To test if a correct version of Python is available on your system, run::
+
+    python -V
+    
+You should see output similar to::
+
+    Python 2.7
+    
+indicating the version of Python installed.  You may then proceed to install
+Agile OODT.
+
+By far the easiest, recommended, and encouraged way to install Agile OODT is
+either with Pip_ or EasyInstall_.  If your Python installation has either of
+these installers available to it, then one command is all you need to run in
+order to download, build, install, and generate command-line tools all in one
+go for all users on your system.  For Pip users, it's::
+
+    pip oodt
+
+And for EasyInstall users, it's::
+
+    easy_install oodt
+    
+Be sure to run that command as an administrative user.  For example, on Mac OS
+X and other Unix systems, you might need to run either of::
+
+    sudo pip oodt
+    sudo easy_install oodt
+
+
+Installing From Source
+~~~~~~~~~~~~~~~~~~~~~~
+
+If neither Pip nor EasyInstall are available, you can still make a proper
+installation of Agile OODT by building it from its source code.  Just follow
+these instructions:
+
+1.  Download the Agile OODT source distribution and extract the source
+    archive.  The source distribution is packaged as a gzip'd tar archive.
+2.  Change the current working directory to the newly extracted directory.
+3.  As an administrative user, run: ``python setup.py install``
+
+
+For More Information
+--------------------
+
+Visit any of the following links for additional information, to ask questions,
+report bugs, and so forth:
+
+OODT Home Page
+    http://oodt.apache.org/
+Mailing List for OODT Development
+    mailto:dev@oodt.apache.org
+Package Page (Cheese Shop)
+    http://pypi.python.org/pypi/oodt/
+Issue Tracker (submit bug reports here)
+    https://issues.apache.org/jira/browse/OODT
+
+
+.. References:
+.. _EasyInstall: http://packages.python.org/distribute/easy_install.html
+.. _Pip: http://pip.openplans.org/
+.. _Python: http://python.org/
diff --git a/0.10/agility/docs/LICENSE.txt b/0.10/agility/docs/LICENSE.txt
new file mode 100644
index 0000000..1a954d6
--- /dev/null
+++ b/0.10/agility/docs/LICENSE.txt
@@ -0,0 +1,179 @@
+License, Terms, and Conditions
+==============================
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/0.10/agility/ez_setup.py b/0.10/agility/ez_setup.py
new file mode 100644
index 0000000..025419b
--- /dev/null
+++ b/0.10/agility/ez_setup.py
@@ -0,0 +1,347 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Bootstrap setuptools installation
+
+To use setuptools in your package's setup.py, include this
+file in the same directory and add this to the top of your setup.py::
+
+    from ez_setup import use_setuptools
+    use_setuptools()
+
+To require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, simply supply
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import os
+import shutil
+import sys
+import tempfile
+import zipfile
+import optparse
+import subprocess
+import platform
+import textwrap
+import contextlib
+
+from distutils import log
+
+try:
+    from urllib.request import urlopen
+except ImportError:
+    from urllib2 import urlopen
+
+try:
+    from site import USER_SITE
+except ImportError:
+    USER_SITE = None
+
+DEFAULT_VERSION = "8.2.1"
+DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/"
+
+def _python_cmd(*args):
+    """
+    Return True if the command succeeded.
+    """
+    args = (sys.executable,) + args
+    return subprocess.call(args) == 0
+
+
+def _install(archive_filename, install_args=()):
+    with archive_context(archive_filename):
+        # installing
+        log.warn('Installing Setuptools')
+        if not _python_cmd('setup.py', 'install', *install_args):
+            log.warn('Something went wrong during the installation.')
+            log.warn('See the error message above.')
+            # exitcode will be 2
+            return 2
+
+
+def _build_egg(egg, archive_filename, to_dir):
+    with archive_context(archive_filename):
+        # building an egg
+        log.warn('Building a Setuptools egg in %s', to_dir)
+        _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
+    # returning the result
+    log.warn(egg)
+    if not os.path.exists(egg):
+        raise IOError('Could not build the egg.')
+
+
+class ContextualZipFile(zipfile.ZipFile):
+    """
+    Supplement ZipFile class to support context manager for Python 2.6
+    """
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, value, traceback):
+        self.close()
+
+    def __new__(cls, *args, **kwargs):
+        """
+        Construct a ZipFile or ContextualZipFile as appropriate
+        """
+        if hasattr(zipfile.ZipFile, '__exit__'):
+            return zipfile.ZipFile(*args, **kwargs)
+        return super(ContextualZipFile, cls).__new__(cls)
+
+
+@contextlib.contextmanager
+def archive_context(filename):
+    # extracting the archive
+    tmpdir = tempfile.mkdtemp()
+    log.warn('Extracting in %s', tmpdir)
+    old_wd = os.getcwd()
+    try:
+        os.chdir(tmpdir)
+        with ContextualZipFile(filename) as archive:
+            archive.extractall()
+
+        # going in the directory
+        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+        os.chdir(subdir)
+        log.warn('Now working in %s', subdir)
+        yield
+
+    finally:
+        os.chdir(old_wd)
+        shutil.rmtree(tmpdir)
+
+
+def _do_download(version, download_base, to_dir, download_delay):
+    egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg'
+                       % (version, sys.version_info[0], sys.version_info[1]))
+    if not os.path.exists(egg):
+        archive = download_setuptools(version, download_base,
+                                      to_dir, download_delay)
+        _build_egg(egg, archive, to_dir)
+    sys.path.insert(0, egg)
+
+    # Remove previously-imported pkg_resources if present (see
+    # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details).
+    if 'pkg_resources' in sys.modules:
+        del sys.modules['pkg_resources']
+
+    import setuptools
+    setuptools.bootstrap_install_from = egg
+
+
+def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+        to_dir=os.curdir, download_delay=15):
+    to_dir = os.path.abspath(to_dir)
+    rep_modules = 'pkg_resources', 'setuptools'
+    imported = set(sys.modules).intersection(rep_modules)
+    try:
+        import pkg_resources
+    except ImportError:
+        return _do_download(version, download_base, to_dir, download_delay)
+    try:
+        pkg_resources.require("setuptools>=" + version)
+        return
+    except pkg_resources.DistributionNotFound:
+        return _do_download(version, download_base, to_dir, download_delay)
+    except pkg_resources.VersionConflict as VC_err:
+        if imported:
+            msg = textwrap.dedent("""
+                The required version of setuptools (>={version}) is not available,
+                and can't be installed while this script is running. Please
+                install a more recent version first, using
+                'easy_install -U setuptools'.
+
+                (Currently using {VC_err.args[0]!r})
+                """).format(VC_err=VC_err, version=version)
+            sys.stderr.write(msg)
+            sys.exit(2)
+
+        # otherwise, reload ok
+        del pkg_resources, sys.modules['pkg_resources']
+        return _do_download(version, download_base, to_dir, download_delay)
+
+def _clean_check(cmd, target):
+    """
+    Run the command to download target. If the command fails, clean up before
+    re-raising the error.
+    """
+    try:
+        subprocess.check_call(cmd)
+    except subprocess.CalledProcessError:
+        if os.access(target, os.F_OK):
+            os.unlink(target)
+        raise
+
+def download_file_powershell(url, target):
+    """
+    Download the file at url to target using Powershell (which will validate
+    trust). Raise an exception if the command cannot complete.
+    """
+    target = os.path.abspath(target)
+    ps_cmd = (
+        "[System.Net.WebRequest]::DefaultWebProxy.Credentials = "
+        "[System.Net.CredentialCache]::DefaultCredentials; "
+        "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)"
+        % vars()
+    )
+    cmd = [
+        'powershell',
+        '-Command',
+        ps_cmd,
+    ]
+    _clean_check(cmd, target)
+
+def has_powershell():
+    if platform.system() != 'Windows':
+        return False
+    cmd = ['powershell', '-Command', 'echo test']
+    with open(os.path.devnull, 'wb') as devnull:
+        try:
+            subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
+        except Exception:
+            return False
+    return True
+
+download_file_powershell.viable = has_powershell
+
+def download_file_curl(url, target):
+    cmd = ['curl', url, '--silent', '--output', target]
+    _clean_check(cmd, target)
+
+def has_curl():
+    cmd = ['curl', '--version']
+    with open(os.path.devnull, 'wb') as devnull:
+        try:
+            subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
+        except Exception:
+            return False
+    return True
+
+download_file_curl.viable = has_curl
+
+def download_file_wget(url, target):
+    cmd = ['wget', url, '--quiet', '--output-document', target]
+    _clean_check(cmd, target)
+
+def has_wget():
+    cmd = ['wget', '--version']
+    with open(os.path.devnull, 'wb') as devnull:
+        try:
+            subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
+        except Exception:
+            return False
+    return True
+
+download_file_wget.viable = has_wget
+
+def download_file_insecure(url, target):
+    """
+    Use Python to download the file, even though it cannot authenticate the
+    connection.
+    """
+    src = urlopen(url)
+    try:
+        # Read all the data in one block.
+        data = src.read()
+    finally:
+        src.close()
+
+    # Write all the data in one block to avoid creating a partial file.
+    with open(target, "wb") as dst:
+        dst.write(data)
+
+download_file_insecure.viable = lambda: True
+
+def get_best_downloader():
+    downloaders = (
+        download_file_powershell,
+        download_file_curl,
+        download_file_wget,
+        download_file_insecure,
+    )
+    viable_downloaders = (dl for dl in downloaders if dl.viable())
+    return next(viable_downloaders, None)
+
+def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+        to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader):
+    """
+    Download setuptools from a specified location and return its filename
+
+    `version` should be a valid setuptools version number that is available
+    as an sdist for download under the `download_base` URL (which should end
+    with a '/'). `to_dir` is the directory where the egg will be downloaded.
+    `delay` is the number of seconds to pause before an actual download
+    attempt.
+
+    ``downloader_factory`` should be a function taking no arguments and
+    returning a function for downloading a URL to a target.
+    """
+    # making sure we use the absolute path
+    to_dir = os.path.abspath(to_dir)
+    zip_name = "setuptools-%s.zip" % version
+    url = download_base + zip_name
+    saveto = os.path.join(to_dir, zip_name)
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        log.warn("Downloading %s", url)
+        downloader = downloader_factory()
+        downloader(url, saveto)
+    return os.path.realpath(saveto)
+
+def _build_install_args(options):
+    """
+    Build the arguments to 'python setup.py install' on the setuptools package
+    """
+    return ['--user'] if options.user_install else []
+
+def _parse_args():
+    """
+    Parse the command line for options
+    """
+    parser = optparse.OptionParser()
+    parser.add_option(
+        '--user', dest='user_install', action='store_true', default=False,
+        help='install in user site package (requires Python 2.6 or later)')
+    parser.add_option(
+        '--download-base', dest='download_base', metavar="URL",
+        default=DEFAULT_URL,
+        help='alternative URL from where to download the setuptools package')
+    parser.add_option(
+        '--insecure', dest='downloader_factory', action='store_const',
+        const=lambda: download_file_insecure, default=get_best_downloader,
+        help='Use internal, non-validating downloader'
+    )
+    parser.add_option(
+        '--version', help="Specify which version to download",
+        default=DEFAULT_VERSION,
+    )
+    options, args = parser.parse_args()
+    # positional arguments are ignored
+    return options
+
+def main():
+    """Install or upgrade setuptools and EasyInstall"""
+    options = _parse_args()
+    archive = download_setuptools(
+        version=options.version,
+        download_base=options.download_base,
+        downloader_factory=options.downloader_factory,
+    )
+    return _install(archive, _build_install_args(options))
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/0.10/agility/oodt/__init__.py b/0.10/agility/oodt/__init__.py
new file mode 100644
index 0000000..5cc91fa
--- /dev/null
+++ b/0.10/agility/oodt/__init__.py
@@ -0,0 +1,27 @@
+# encoding: utf-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+
+'''Agile Object Oriented Data Technology (OODT).
+
+OODT is a set of software components that permit the discovery, correlation,
+search, and retrieval of data and metadata.  For more information, see the
+OODT web site_.
+
+.. _site: http://oodt.apache.org/
+'''
+
+__docformat__ = 'restructuredtext'
diff --git a/0.10/agility/oodt/oodterrors.py b/0.10/agility/oodt/oodterrors.py
new file mode 100644
index 0000000..46ee999
--- /dev/null
+++ b/0.10/agility/oodt/oodterrors.py
@@ -0,0 +1,26 @@
+# encoding: utf-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+
+'''Object Oriented Data Technology exceptions.
+'''
+
+__docformat__ = 'restructuredtext'
+
+class OODTException(Exception):
+    '''Common OODT exception base.
+    '''
+    pass
diff --git a/0.10/agility/oodt/profile.py b/0.10/agility/oodt/profile.py
new file mode 100644
index 0000000..aa6f99c
--- /dev/null
+++ b/0.10/agility/oodt/profile.py
@@ -0,0 +1,502 @@
+# encoding: utf-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+
+'''Profiles.  A profile is a metadata description of a resource.
+
+Profiles capture:
+
+* Inception metadata.  This includes items such as a resouce's title,
+  description, creators, contributors, publishers, language, and so on.  This
+  set of metdata is based on the Dublin Core.  Class `ResourceAttributes`
+  captures this metadata.
+
+* Composition metadata.  This includes data elements that describe what
+  the resource contains, such as ranges of data values (high and low
+  temperature, latitude/longitude) or enumerated data values (zoning code,
+  genus/species, etc.).  Subclasses of `ProfileElement` captures this
+  metadata.
+
+* Profile metadata.  This is metadata describing the profile itself, such
+  as revision notes, ID number, etc.  Class `ProfileAttributes` captures this
+  metadata.
+
+Objects of these classes are unified into a single `Profile` object.
+'''
+
+__docformat__ = 'restructuredtext'
+
+import xmlutils
+import xml.dom
+from xmlutils import DocumentableField
+
+class ProfileAttributes(xmlutils.Documentable):
+    '''Attributes of a profile.  These are attributes not related to the
+    resource that a profile profiles, but rather the profile itself.  In
+    most cases, simply constructing this object with no initializer
+    arguments will suffice.
+    '''
+    def __init__(self, id='UNKNOWN', version='1.0.0', type='profile', statusID='active', securityType='unclassified',
+        parentID='UNKNOWN', childIDs=None, regAuthority='UNKNOWN', revNotes=None, node=None):
+        '''Initialize profile attributes.
+        
+        - `id` unique ID for the profile
+        - `version` number
+        - `type` should always be the string "profile"
+        - `statusID` should always be the string "active"
+        - `securityType` tells whether the profile is secret
+        - `parentID` gives the unique ID for any "parent" profile
+        - `childIDs` should be a sequnece of any "children" profiles
+        - `regAuthority` is a unique ID identifying the registration
+           authority responsible for the profile
+        - `revNotes` is a sequence of comments detailing historical
+           changes to the profile
+        - `node` is an XML DOM node.  If the `node` argument is given,
+           it's used to initialize the object.
+        '''
+        self.id = id
+        self.version = version
+        self.type = type
+        self.statusID = statusID
+        self.securityType = securityType
+        self.parentID = parentID
+        self.childIDs = childIDs
+        self.regAuthority = regAuthority
+        self.revNotes = revNotes
+        if self.childIDs is None:
+            self.childIDs = []
+        if self.revNotes is None:
+            self.revNotes = []
+        if node is not None:
+            self.parse(node)
+    
+    def getDocumentElementName(self):
+        '''Get the XML tag for objects of this class: `profAttributes`.
+        '''
+        return 'profAttributes'
+    
+    def getDocumentableFields(self):
+        '''Get the attributes that are put into XML.
+        '''
+        return (DocumentableField('id', u'profId', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('version', u'profVersion', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('type', u'profType', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('statusID', u'profStatusId', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('securityType', u'profSecurityType', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('parentID', u'profParentId', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('childIDs', u'profChildId', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('regAuthority', u'profRegAuthority', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('revNotes', u'profRevisionNote', DocumentableField.MULTI_VALUED_KIND))
+
+    def __repr__(self):
+        return ('ProfileAttributes(id=%s,version=%s,type=%s,statusID=%s,securityType=%s,parentID=%s,childIDs=%s,' \
+            'regAuthority=%s,revNotes=%s)') % (self.id, self.version, self.type, self.statusID, self.securityType,
+            self.parentID, self.childIDs, self.regAuthority, self.revNotes)
+            
+    def __cmp__(self, other):
+        return cmp(self.id, other.id)
+    
+    def __hash__(self):
+        return hash(self.id)
+    
+
+class ResourceAttributes(xmlutils.Documentable):
+    '''Attributes of the resource.  Objects of this class collect data about the resource's
+    inception and are based on Dublin Core.
+    '''
+    def __init__(self, identifier='UNKNOWN', title='UNKNOWN', formats=None, description='UNKNOWN', creators=None, subjects=None,
+        publishers=None, contributors=None, dates=None, types=None, sources=None, languages=None, relations=None,
+        coverages=None, rights=None, contexts=None, aggregation='UNKNOWN', resClass='UNKNOWN', locations=None, node=None):
+        '''Initialize ResourceAttributes.
+        
+        The following arguments are required:
+        
+        - `identifier` is a URI uniquely identifying the resource
+        - `title` names the resource
+        - `description` gives a summary or abstract of it
+        - `aggregation` tells the gross structure of the resource and
+           should be one of the following values:
+            - data.granule
+            - data.dataSet
+            - data.dataSetCollection
+        - `resClass` gives the kind of the resource (what to expect
+           when connecting to one of its locations)
+        
+        All of the others should be initialized with sequences as the
+        resource attributes follows the Dublin Core recommendation on
+        multiplicity.
+        
+        If `node` is given, it's treated as an XML DOM node and is used to
+        initialize the resource attributes.
+        '''
+        self.identifier = identifier
+        self.title = title
+        self.formats = formats
+        self.description = description
+        self.creators = creators
+        self.subjects = subjects
+        self.publishers = publishers
+        self.contributors = contributors
+        self.dates = dates
+        self.types = types
+        self.sources = sources
+        self.languages = languages
+        self.relations = relations
+        self.coverages = coverages
+        self.rights = rights
+        self.contexts = contexts
+        self.aggregation = aggregation
+        self.resClass = resClass
+        self.locations = locations
+        for attr in ('formats', 'creators', 'subjects', 'publishers', 'contributors', 'dates', 'sources', 'languages',
+            'relations', 'coverages', 'rights', 'contexts', 'locations', 'types'):
+            if getattr(self, attr) is None:
+                setattr(self, attr, [])
+        if node is not None:
+            self.parse(node)
+    
+    def getDocumentElementName(self):
+        '''Give the XML tag name: `resAttributes`.
+        '''
+        return 'resAttributes'
+        
+    def __hash__(self):
+        return hash(self.identifier)
+    
+    def __cmp__(self, other):
+        return cmp(self.identifier, other.identifier)
+    
+    def getDocumentableFields(self):
+        '''Get the attributes that go into XML.
+        '''
+        return (DocumentableField('identifier', u'Identifier', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('title', u'Title', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('formats', u'Format', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('description', u'Description', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('creators', u'Creator', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('subjects', u'Subject', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('publishers', u'Publisher', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('contributors', u'Contributor', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('dates', u'Date', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('types', u'Type', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('sources', u'Source', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('languages', u'Language', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('relations', u'Relation', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('coverages', u'Coverage', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('rights', u'Rights', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('contexts', u'resContext', DocumentableField.MULTI_VALUED_KIND),
+            DocumentableField('aggregation', u'resAggregation', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('resClass', u'resClass', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('locations', u'resLocation', DocumentableField.MULTI_VALUED_KIND))
+    
+
+class Profile(object):
+    '''A profile "profiles" a resource by describing it with metadata.
+    '''
+    def __init__(self, profAttr=None, resAttr=None, profElements=None, node=None):
+        '''Initialize a profile.  The profElements should be a dicitonary that maps
+        from element name to instance of a `ProfileElement`.
+        '''
+        self.profAttr, self.resAttr, self.profElements = profAttr, resAttr, profElements
+        if self.profAttr is None:
+            self.profAttr = ProfileAttributes()
+        if self.resAttr is None:
+            self.resAttr = ResourceAttributes()
+        if self.profElements is None:
+            self.profElements = {}
+        if node is not None:
+            self.parse(node)
+    
+    def parse(self, node):
+        '''Initialize this object from the given XML DOM `node`.
+        '''
+        if node.nodeName != 'profile':
+            raise ValueError('Expected profile element but got "%s"' % node.nodeName)
+        for child in filter(lambda node: node.nodeType == xml.dom.Node.ELEMENT_NODE, node.childNodes):
+            name = child.nodeName
+            if name == u'profAttributes':
+                self.profAttr = ProfileAttributes()
+                self.profAttr.parse(child)
+            elif name == u'resAttributes':
+                self.resAttr = ResourceAttributes()
+                self.resAttr.parse(child)
+            elif name == u'profElement':
+                elem = _parseProfileElement(child)
+                self.profElements[elem.name] = elem
+                
+    def toXML(self, owner):
+        '''Convert this object into XML owned by the given `owner` document.
+        '''
+        root = owner.createElement(u'profile')
+        root.appendChild(self.profAttr.toXML(owner))
+        root.appendChild(self.resAttr.toXML(owner))
+        for elem in self.profElements.itervalues():
+            root.appendChild(elem.toXML(owner))
+        return root
+    
+    def __cmp__(self, other):
+        profAttr = cmp(self.profAttr, other.profAttr)
+        if profAttr < 0:
+            return -1
+        elif profAttr == 0:
+            resAttr = cmp(self.resAttr, other.resAttr)
+            if resAttr < 0:
+                return -1
+            elif resAttr == 0:
+                return cmp(self.profElements, other.profElements)
+        return 1
+    
+    def __hash__(self):
+        return hash(self.profAttr) ^ hash(self.resAttr)
+    
+    def __repr__(self):
+        return 'Profile(profAttr=%s,resAttr=%s,profElements=%s)' % (self.profAttr, self.resAttr, self.profElements)
+    
+
+class ProfileElement(object):
+    '''Abstract profile element.
+    '''
+    def __init__(self, name, description, type, units, synonyms, comment):
+        '''Initialize profile element.
+        '''
+        self.name = name
+        self.description = description
+        self.type = type
+        self.units = units
+        self.synonyms = synonyms
+        self.comment = comment
+    
+    def __repr__(self):
+        return 'ProfileElement(name=%r,description=%r,type=%r,units=%r,synonyms=%r,comment=%r)' % (self.name,
+            self.description, self.type, self.units, self.synonyms, self.comment)
+    
+    def __cmp__(self, other):
+        rc = cmp(self.name, other.name)
+        if rc < 0:
+            return -1
+        elif rc == 0:
+            rc = cmp(self.description, other.description)
+            if rc < 0:
+                return -1
+            elif rc == 0:
+                rc = cmp(self.type, other.type)
+                if rc < 0:
+                    return -1
+                elif rc == 0:
+                    rc = cmp(self.units, other.units)
+                    if rc < 0:
+                        return -1
+                    elif rc == 0:
+                        rc = cmp(self.synonyms, other.synonyms)
+                        if rc < 0:
+                            return -1
+                        elif rc == 0:
+                            return cmp(self.comment, other.comment)
+        return 1
+    
+    def __hash__(self):
+        return reduce(lambda x, y: hash(x) ^ hash(y), [getattr(self, attr) for attr in ('name', 'description', 'type',
+            'units', 'synonyms', 'comment')])
+    
+    def isEnumerated(self):
+        '''Is this an enumerated element?  Enumerated elements include those with
+        discrete values as well unspecified elements.
+        '''
+        return False;
+    
+    def getValues(self):
+        '''Get the discrete values of this element, which may be empty.
+        '''
+        return []
+    
+    def getMinValue(self):
+        '''Get the minimum value of this element, which may be zero.
+        '''
+        return 0.0
+    
+    def getMaxValue(self):
+        '''Get the maximum value of this element, which may be zero.
+        '''
+        return 0.0
+    
+    def toXML(self, owner):
+        '''Convert this object into XML owned by the given `owner` document.
+        '''
+        root = owner.createElement('profElement')
+        xmlutils.add(root, u'elemId', self.name)
+        xmlutils.add(root, u'elemName', self.name)
+        xmlutils.add(root, u'elemDesc', self.description)
+        xmlutils.add(root, u'elemType', self.type)
+        xmlutils.add(root, u'elemUnit', self.units)
+        if self.isEnumerated():
+            flag = 'T'
+        else:
+            flag = 'F'
+        xmlutils.add(root, u'elemEnumFlag', flag)
+        for value in self.getValues():
+            elem = owner.createElement('elemValue')
+            root.appendChild(elem)
+            elem.appendChild(owner.createCDATASection(value))
+        if not self.isEnumerated():
+            xmlutils.add(root, u'elemMinValue', str(self.getMinValue()))
+            xmlutils.add(root, u'elemMaxValue', str(self.getMaxValue()))
+        xmlutils.add(root, u'elemSynonym', self.synonyms)
+        xmlutils.add(root, u'elemComment', self.comment)
+        return root
+    
+
+class UnspecifiedProfileElement(ProfileElement):
+    '''An unspecified profile element merely documents the existence of a element within a
+    dataset but says nothing about its actual values.
+    '''
+    def __init__(self, name, description, type, units, synonyms, comment):
+        '''Initialize an "unspecified" profile element.
+        '''
+        super(UnspecifiedProfileElement, self).__init__(name, description, type, units, synonyms, comment)
+    
+    def isEnumerated(self):
+        '''An unspecified profile element is indeed enumerated.  It just has no enumerated values.
+        '''
+        return True
+    
+
+class EnumeratedProfileElement(ProfileElement):
+    '''An enumerated profile element describes set of discrete values.
+    '''
+    def __init__(self, name, description, type, units, synonyms, comment, values):
+        '''Initialize an enumerated profile element.
+        '''
+        super(EnumeratedProfileElement, self).__init__(name, description, type, units, synonyms, comment)
+        self.values = values
+        
+    def isEnumerated(self):
+        '''An enumerated profile element is indeed enumerated.
+        '''
+        return True
+    
+    def getValues(self):
+        '''Return the sequence of values.
+        '''
+        return self.values
+        
+    def __cmp__(self, other):
+        rc = super(EnumeratedProfileElement, self).__cmp__(other)
+        if rc < 0:
+            return -1
+        elif rc == 0:
+            return cmp(self.values, other.values)
+        return 1
+    
+
+class RangedProfileElement(ProfileElement):
+    '''A ranged profile element describes a value between two numeric ranges.
+    '''
+    def __init__(self, name, description, type, units, synonyms, comment, minValue, maxValue):
+        '''Initialize a ranged profile element.
+        '''
+        super(RangedProfileElement, self).__init__(name, description, type, units, synonyms, comment)
+        self.minValue = minValue
+        self.maxValue = maxValue
+    
+    def getMinValue(self):
+        '''Get the minimum value.
+        '''
+        return self.minValue
+    
+    def getMaxValue(self):
+        '''Get the maximum value.
+        '''
+        return self.maxValue
+    
+    def __repr__(self):
+        return 'RangedProfileElement(%r,minValue=%r,maxValue=%r)' % (super(RangedProfileElement, self).__repr__(),
+            self.minValue, self.maxValue)
+    
+    def __cmp__(self, other):
+        rc = super(RangedProfileElement, self).__cmp__(other)
+        if rc < 0:
+            return -1
+        elif rc == 0:
+            rc = self.minValue - other.minValue
+            if rc < 0:
+                return -1
+            elif rc == 0:
+                return self.maxValue - other.maxValue
+        return 1
+    
+
+def _parseProfileElement(node):
+    '''Construct an appropriate profile element from the given DOM node.
+    '''
+    if node.nodeName != u'profElement':
+        raise ValueError('Expected profElement element but got "%s"' % node.nodeName)
+    settings = dict(elemId=u'UNKNOWN', elemDesc=u'UNKNOWN', elemType=u'UNKNOWN', elemUnit=u'UNKNOWN', elemEnumFlag=u'F',
+        elemComment=u'UNKNOWN')
+    values, syns = [], []
+    for child in filter(lambda node: node.nodeType == xml.dom.Node.ELEMENT_NODE, node.childNodes):
+        text = xmlutils.text(child)
+        if child.nodeName == u'elemValue':
+            values.append(text)
+        elif child.nodeName == u'elemSynonym':
+            syns.append(text)
+        else:
+            settings[str(child.nodeName)] = text
+    if 'elemName' not in settings:
+        raise ValueError('profElement requires elemName but none specified')
+    if 'elemEnumFlag' not in settings:
+        raise ValueError('profElement requires elemEnumFlag but none specified')
+        
+    # Normally I'd treat only those XML elements where elemEnumFlag as T as possibly producing
+    # unspecified or enumerated, and F producing *only* ranged elements.  But PDS profile
+    # servers are producing profile elements with F for elemEnumFlag and yet NO elemMinValue
+    # and elemMaxValue.  If they're using the Java profile code, I'd call that a bug in that
+    # code.  If they're not, then I'd say they're producing profiles incorrectly.
+    if settings['elemEnumFlag'] == 'T':
+        if len(values) == 0:
+            return UnspecifiedProfileElement(settings['elemName'], settings['elemDesc'], settings['elemType'],
+                settings['elemUnit'], syns, settings['elemComment'])
+        else:
+            return EnumeratedProfileElement(settings['elemName'], settings['elemDesc'], settings['elemType'],
+                settings['elemUnit'], syns, settings['elemComment'], values)
+    else:
+        if 'elemMinValue' not in settings or 'elemMaxValue' not in settings:
+            return UnspecifiedProfileElement(settings['elemName'], settings['elemDesc'], settings['elemType'],
+                settings['elemUnit'], syns, settings['elemComment'])
+        else:
+            return RangedProfileElement(settings['elemName'], settings['elemDesc'], settings['elemType'],
+            settings['elemUnit'], syns, settings['elemComment'], float(settings['elemMinValue']),
+            float(settings['elemMaxValue']))
+    
+
+# Some sample code:
+# if __name__ == '__main__':
+#   import urllib2, xml.dom.minidom
+#   x = urllib2.urlopen('http://starbrite.jpl.nasa.gov/q?object=urn%3Aeda%3Armi%3AJPL.PDS.MasterProd&type=profile&keywordQuery=TARGET_NAME+%3D+MARS')
+#   d = xml.dom.minidom.parse(x)
+#   x.close()
+#   profiles = []
+#   for i in d.documentElement.getElementsByTagName(u'profile'):
+#       profiles.append(Profile(node=i))
+#       
+#   doc = xml.dom.minidom.getDOMImplementation().createDocument(None, None, None)
+#   print '<?xml version="1.0" encoding="UTF-8"?>'
+#   print '<!DOCTYPE profiles PUBLIC "-//JPL//DTD Profile 1.1//EN"'
+#   print '  "http://oodt.jpl.nasa.gov/grid-profile/dtd/prof.dtd">'
+#   print '<profiles>'
+#   for profile in profiles:
+#       print profile.toXML(doc).toxml()
+#   print '</profiles>'
+#   
diff --git a/0.10/agility/oodt/query.py b/0.10/agility/oodt/query.py
new file mode 100644
index 0000000..7bd97c0
--- /dev/null
+++ b/0.10/agility/oodt/query.py
@@ -0,0 +1,456 @@
+# encoding: utf-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+
+'''
+Agile OODT Query Expressions.
+
+Query expressions in OODT are based on the "DIS" style expressions originally
+developed for the Planetary Data System.  They consist of
+keyword/operator/literal-value triples, such as `targetName = Mars`, each
+linked with logical operators (and, or, not) and grouped with parentheses.
+For more information, see OODT_.
+
+This module defines classes that model the aspects of a query.  In general,
+use the `Query` class, passing a string containing your keyword expression as
+the first constructor argument.  From there, you have a `Query` object you can
+pass around to profile servers, product servers, and so forth.
+
+.. _OODT: http://oodt.apache.org/
+'''
+
+__docformat__ = 'restructuredtext'
+
+import oodterrors, shlex, xmlutils, xml.dom
+from xmlutils import DocumentableField
+
+class _QueryExpressionScanner(shlex.shlex):
+    '''Extend the shlex scanner but for the DIS-style query expressions we expect.
+    This means adding a dot to the characters that comprise a word so we can easily parse
+    floating-point numbers.  Also, there's no comment character.
+    '''
+    def __init__(self, str):
+        '''Create scanner.  `str` is the string to scan.
+        '''
+        shlex.shlex.__init__(self, str)
+        self.commenters = ''
+        self.wordchars = self.wordchars + '.-/:'
+        
+
+    def get_token(self):
+        '''Get the next token.  We strip quotes from strings, attach negative signs
+        to the numbers they're negating, and attach the = to <, >, and ! where needed.
+        '''
+        token = shlex.shlex.get_token(self)
+        if token == self.eof or token == None:
+            return None
+        if token[0] in self.quotes:
+            token = token[1:-1]
+        elif token in ('<', '>', '!'):
+            next = shlex.shlex.get_token(self)
+            if next == self.eof or next == None:
+                return None
+            elif next == '=':
+                token = token + next
+            else:
+                self.push_token(next)
+        return token
+    
+
+class QueryException(oodterrors.OODTException):
+    '''Exceptions related to query expression or query services
+    '''
+    pass
+    
+
+class ExpressionParseError(QueryException):
+    '''Error in parsing a query expression.
+    '''
+    pass
+    
+
+class QueryElement(xmlutils.Documentable):
+    '''An element of a query.
+    '''
+    def __init__(self, role='UNKNOWN', value='UNKNOWN', node=None):
+        '''Create a QueryElement.  You can provide role and value settings, or provide an XML
+        DOM node which will be parsed for role/value.
+        '''
+        self.role, self.value = role, value
+        if node != None:
+            self.parse(node)
+
+    def getDocumentElementName(self):
+        '''Give the XML tag name: `queryElement`.
+        '''
+        return 'queryElement'
+    
+    def getDocumentableFields(self):
+        '''Get the attributes that go into XML.
+        '''
+        return (DocumentableField('role', u'tokenRole', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('value', u'tokenValue', DocumentableField.SINGLE_VALUE_KIND))
+    
+    def __repr__(self):
+        return 'QueryElement(role="%s",value="%s")' % (self.role, self.value)
+    
+
+class QueryHeader(xmlutils.Documentable):
+    '''Header of a query.  Captures metadata like data dictionary in use, etc.
+    '''
+    def __init__(self, id='UNKNOWN', title='UNKNOWN', desc='UNKNOWN', type='QUERY', status='ACTIVE', security='UNKNOWN',
+        rev='2005-10-01 SCK v0.0.0 Under Development', dataDict='UNKNOWN', node=None):
+        '''Initialize a QueryHeader.  Provide id, title, desc, type, status, security, rev,
+        and dataDict settings.  Or, provide just an XML DOM node to be parsed.
+        '''
+        self.id, self.title, self.desc, self.type, self.status, self.security, self.rev, self.dataDict = \
+            id, title, desc, type, status, security, rev, dataDict
+        if node != None:
+            self.parse(node)
+    
+    def getDocumentElementName(self):
+        '''Give the XML tag name: `queryAttributes`.
+        '''
+        return 'queryAttributes'
+    
+    def getDocumentableFields(self):
+        '''Get the attributes that go into XML.
+        '''
+        return (DocumentableField('id', u'queryId', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('title', u'queryTitle', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('desc', u'queryDesc', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('type', u'queryType', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('status', u'queryStatusId', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('security', u'querySecurityType', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('rev', u'queryRevisionNote', DocumentableField.SINGLE_VALUE_KIND),
+            DocumentableField('dataDict', u'queryDataDictId', DocumentableField.SINGLE_VALUE_KIND))
+    
+    def __repr__(self):
+        return 'QueryHeader(id="%s",title="%s",desc="%s",type="%s",status="%s",security="%s",rev="%s",dataDict="%s")' % (
+            self.id, self.title, self.desc, self.type, self.status, self.security, self.rev, self.dataDict
+        )
+    
+    def __cmp__(self, other):
+        return cmp((self.id, self.title, self.desc, self.type, self.status, self.security, self.rev, self.dataDict),
+               (other.id, other.title, other.desc, other.type, other.status, other.security, other.rev, other.dataDict))
+    
+
+class QueryResult(object):
+    '''Result of a query.
+    '''
+    def __init__(self, results=[], node=None):
+        '''Results of a query are captured as a sequence of generic objects.
+        '''
+        self.results = results
+        if node != None: self.parse(node)
+    
+    def parse(self, node):
+        '''Initialize this object from the given XML DOM `node`.
+        '''
+        if 'queryResultSet' != node.nodeName:
+            raise ValueError('Expected queryResultSet but got "%s"' % node.nodeName)
+        for child in node.childNodes:
+            if child.nodeType == xml.dom.Node.ELEMENT_NODE and 'resultElement' == child.nodeName:
+                self.results.append(Result(node=child))
+    
+    def toXML(self, owner):
+        '''Convert this object into XML owned by the given `owner` document.
+        '''
+        root = owner.createElement('queryResultSet')
+        for result in self.results:
+            root.appendChild(result.toXML(owner))
+        return root
+    
+    def clear(self):
+        '''Remove all results.
+        '''
+        self.results = []
+    
+    def __getitem__(self, i):
+        return self.results[i]
+    
+    def __len__(self):
+        return len(self.results)
+    
+    def __cmp__(self, other):
+        return cmp(self.results, other.results)
+    
+
+_RELOPS = {
+    'LT':      'LT', 
+    'lt':      'LT', 
+    '<':       'LT',
+    'LE':      'LE',
+    'le':      'LE',
+    '<=':      'LE',
+    'EQ':      'EQ', 
+    'eq':      'EQ', 
+    '=':       'EQ',
+    'GE':      'GE',
+    'ge':      'GE',
+    '>=':      'GE',
+    'GT':      'GT',
+    'gt':      'GT',
+    '>':       'GT',
+    'NE':      'NE',
+    'ne':      'NE',
+    '!=':      'NE',
+    'LIKE':    'LIKE',
+    'like':    'LIKE',
+    'NOTLIKE': 'NOTLIKE',
+    'notlike': 'NOTLIKE',
+    'notLike': 'NOTLIKE',
+    'IS':      'IS',
+    'is':      'is',
+    'ISNOT':   'ISNOT',
+    'isnot':   'isnot',
+    'isNot':   'isnot'
+}
+
+_LOGOPS = {
+    'AND': 'AND',
+    'and': 'AND',
+    '&':   'AND',
+    'OR':  'OR',
+    'or':  'OR',
+    '|':   'OR',
+    'NOT': 'NOT',
+    'not': 'NOT',
+    '!':   'NOT'
+}
+
+_PRECEDENCE = {
+    'NOT': 2,
+    'AND': 1,
+    'OR': 0,
+}
+
+class Query(object):
+    '''Query.  In old OODT, this was called XMLQuery, even though XML was tagential to it.
+    Captures aspects of a query, including header, results, and so forth.  Most importantly, it
+    captures the query expression, which contains the constraints on user's desiderata and
+    range on what to return.
+    
+    As with other classes in this module, you can provide an XML DOM node to be parsed
+    for the query's settings, or provide each of them individually.
+    '''
+    def __init__(self, keywordQuery=None, header=QueryHeader(), resultModeID='ATTRIBUTE', propType='BROADCAST',
+        propLevels='N/A', maxResults=1, mimeAccept=[], parseQuery=True, node=None):
+        '''Initialize a query.  Usually you provide just the `keywordQuery` argument
+        which should be a keyword/query expression in the DIS-style; or you provide
+        the `node` which is an XML DOM node describing a query.
+        '''
+        self.header, self.resultModeID, self.propType, self.propLevels, self.maxResults, self.mimeAccept = \
+            header, resultModeID, propType, propLevels, maxResults, mimeAccept
+        self.wheres, self.selects, self.froms, self.resultSet = [], [], [], QueryResult()
+        if keywordQuery != None:
+            self.keywordQuery = keywordQuery
+            if parseQuery:
+                self.wheres, self.selects = _parseQuery(keywordQuery)
+        else:
+            self.keywordQuery = ''
+        if node != None:
+            self.parse(node)
+        
+    def toXML(self, owner):
+        '''Yield this query as an XML DOM.
+        '''
+        query = owner.createElement('query')
+        query.appendChild(self.header.toXML(owner))
+        xmlutils.add(query, u'queryResultModeId', self.resultModeID)
+        xmlutils.add(query, u'queryPropogationType', self.propType)
+        xmlutils.add(query, u'queryPropogationLevels', self.propLevels)
+        for mimeType in self.mimeAccept:
+            xmlutils.add(query, u'queryMimeAccept', mimeType)
+        xmlutils.add(query, u'queryMaxResults', str(self.maxResults))
+        xmlutils.add(query, u'queryKWQString', self.keywordQuery)
+        selects = owner.createElement(u'querySelectSet')
+        query.appendChild(selects)
+        for select in self.selects:
+            selects.appendChild(select.toXML(owner))
+        fromElement = owner.createElement(u'queryFromSet')
+        query.appendChild(fromElement)
+        for i in self.froms:
+            fromElement.appendChild(i.toXML(owner))
+        wheres = owner.createElement(u'queryWhereSet')
+        query.appendChild(wheres)
+        for where in self.wheres:
+            wheres.appendChild(where.toXML(owner))
+        query.appendChild(self.resultSet.toXML(owner))
+        return query
+        
+    def parse(self, node):
+        '''Parse the XML DOM node as a query document.
+        '''
+        if 'query' != node.nodeName:
+            raise ValueError('Expected query but got "%s"' % node.nodeName)
+        self.mimeAccept, self.results = [], []
+        for child in node.childNodes:
+            if child.nodeType == xml.dom.Node.ELEMENT_NODE:
+                if child.nodeName == u'queryAttributes':
+                    self.header = QueryHeader(node=child)
+                elif child.nodeName == u'resultModeID':
+                    self.resultModeID = xmlutils.text(child)
+                elif child.nodeName == u'queryPropogationType':
+                    self.propType = xmlutils.text(child)
+                elif child.nodeName == u'queryPropogationLevels':
+                    self.propLevels = xmlutils.text(child)
+                elif child.nodeName == u'queryMimeAccept':
+                    self.mimeAccept.append(xmlutils.text(child))
+                elif child.nodeName == u'queryMaxResults':
+                    self.maxResults = int(xmlutils.text(child))
+                elif child.nodeName == u'queryKWQString':
+                    self.keywordQuery = xmlutils.text(child)
+                elif child.nodeName == u'querySelectSet':
+                    self.selects = _parseQueryElements(child)
+                elif child.nodeName == u'queryFromSet':
+                    self.froms = _parseQueryElements(child)
+                elif child.nodeName == u'queryWhereSet':
+                    self.wheres = _parseQueryElements(child)
+                elif child.nodeName == u'queryResultSet':
+                    self.resultSet = QueryResult(node=child)
+    
+    def __cmp__(self, other):
+        header = cmp(self.header, other.header)
+        if header < 0:
+            return -1
+        elif header == 0:
+            resultModeID = cmp(self.resultModeID, other.resultModeID)
+            if resultModeID < 0:
+                return -1
+            elif resultModeID == 0:
+                propType = cmp(self.propType, other.propType)
+                if propType < 0:
+                    return -1
+                elif propType == 0:
+                    propLevels = cmp(self.propLevels, other.propLevels)
+                    if propLevels < 0:
+                        return -1
+                    elif propLevels == 0:
+                        maxResults = self.maxResults - other.maxResults
+                        if maxResults < 0:
+                            return -1
+                        elif maxResults == 0:
+                            mimeAccept = cmp(self.mimeAccept, other.mimeAccept)
+                            if mimeAccept < 0:
+                                return -1
+                            elif mimeAccept == 0:
+                                selects = cmp(self.selects, other.selects)
+                                if selects < 0:
+                                    return -1
+                                elif selects == 0:
+                                    froms = cmp(self.froms, other.froms)
+                                    if froms < 0:
+                                        return -1
+                                    elif froms == 0:
+                                        wheres = cmp(self.wheres, other.wheres)
+                                        if wheres < 0:
+                                            return -1
+                                        elif wheres == 0:
+                                            return cmp(self.resultSet, other.resultSet)
+        return 1
+    
+
+def _parseQueryElements(node):
+    '''The children of the given XML DOM node are a sequence of queryElements.  Parse them
+    and return a list of QueryElement objects.
+    '''
+    a = []
+    for child in node.childNodes:
+        if child.nodeType == xml.dom.Node.ELEMENT_NODE:
+            a.append(QueryElement(node=child))
+    return a
+    
+
+def _parseQuery(s):
+    '''Parse the query expression in `s`.
+    '''
+    if s is None:
+        return [], []
+    if len(s.strip()) == 0:
+        return [], []
+    if s.count('(') != s.count(')'):
+        raise ExpressionParseError('Unbalanced parentheses')
+    scanner = _QueryExpressionScanner(s)
+    return _buildQuery(scanner)
+    
+
+def _buildQuery(scanner):
+    '''Build the query stacks using the given `scanner`.
+    '''
+    operators, expression, selectors = [], [], []
+    while True:
+        token = scanner.get_token()
+        if token is None: break
+        if token in _LOGOPS:
+            op = QueryElement('LOGOP', _LOGOPS[token])
+            if len(operators) == 0:
+                operators.append(op)
+            else:
+                while len(operators) > 0 and _PRECEDENCE[operators[-1].value] > _PRECEDENCE[op.value]:
+                    expression.append(operators.pop())
+                operators.append(op)
+        elif token == '(':
+            subExpr, subSelectors = _buildQuery(scanner)
+            expression.extend(subExpr)
+            selectors.extend(subSelectors)
+        elif token == ')':
+            break
+        else:
+            _addTerm(token, scanner, expression, operators, selectors)
+    if len(operators) > 0 and len(expression) == 0:
+        raise ExpressionParseError('Query contains only logical operators')
+    operators.reverse()
+    expression.extend(operators)
+    return expression, selectors
+    
+
+def _addTerm(elemName, scanner, expression, operators, selectors):
+    '''Add a term to the correct stack.
+    '''
+    relop = scanner.get_token()
+    if relop is None:
+        raise ExpressionParseError('Expected relational operator after element name "%s"' % elemName)
+    if relop not in _RELOPS:
+        raise ExpressionParseError('Unknown relational operator "%s"' % relop)
+    literal = scanner.get_token()
+    if literal is None:
+        raise ExpressionParseError('Expected literal value for "%s %s" comparison' % (elemName, relop))
+    if elemName == 'RETURN':
+        selectors.append(QueryElement('elemName', literal))
+        if len(operators) > 0:
+            operators.pop()
+        else:
+            scanner.get_token()
+    else:
+        expression.append(QueryElement('elemName', elemName))
+        expression.append(QueryElement('LITERAL', literal))
+        expression.append(QueryElement('RELOP', _RELOPS[relop]))
+    
+
+# Sample code:
+# if __name__ == '__main__':
+#   import urllib, xml.dom.minidom
+# 
+#   impl = xml.dom.minidom.getDOMImplementation()
+#   doc = impl.createDocument(None, None, None) # nsURI, qName, docType
+# 
+#   q = Query('track = Innocente')
+#   node = q.toXML(doc)
+#   doc.appendChild(node)
+#   q = doc.toxml()
+#   f = urllib.urlopen('http://localhost:8080/pds/prof', urllib.urlencode(dict(xmlq=q)), {}) # url, postdata, proxies (none)
+#   print f.read()
diff --git a/0.10/agility/oodt/tests/__init__.py b/0.10/agility/oodt/tests/__init__.py
new file mode 100644
index 0000000..de351e1
--- /dev/null
+++ b/0.10/agility/oodt/tests/__init__.py
@@ -0,0 +1,38 @@
+# encoding: utf-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+
+'''Agile OODT Tests.
+'''
+
+__docformat__ = 'restructuredtext'
+
+import unittest
+import profileTest, queryTest, xmlutilsTest
+
+def test_suite():
+    '''Create the suite of tests.
+    '''
+    suite = unittest.TestSuite()
+    suite.addTest(profileTest.test_suite())
+    suite.addTest(queryTest.test_suite())
+    suite.addTest(xmlutilsTest.test_suite())
+    return suite
+    
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+    
diff --git a/0.10/agility/oodt/tests/profileTest.py b/0.10/agility/oodt/tests/profileTest.py
new file mode 100644
index 0000000..9fbe92a
--- /dev/null
+++ b/0.10/agility/oodt/tests/profileTest.py
@@ -0,0 +1,239 @@
+# encoding: utf-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+
+'''Unit tests for the `profile` module.
+'''
+
+__docformat__ = 'restructuredtext'
+
+import unittest, xml.dom.minidom
+from oodt.profile import ProfileAttributes, ResourceAttributes, Profile, UnspecifiedProfileElement, RangedProfileElement,\
+    EnumeratedProfileElement
+
+class ProfileAttributesTest(unittest.TestCase):
+    '''Unit test for the ProfileAttributes class.
+    '''
+    def testDefaults(self):
+        '''Test to see if default values are reasonable.
+        '''
+        pa = ProfileAttributes()
+        self.assertEquals('UNKNOWN', pa.id)
+        self.assertEquals('1.0.0', pa.version)
+        self.assertEquals('profile', pa.type)
+        self.assertEquals('active', pa.statusID)
+        self.assertEquals('unclassified', pa.securityType)
+        self.assertEquals('UNKNOWN', pa.parentID)
+        self.assertEquals(0, len(pa.childIDs))
+        self.assertEquals('UNKNOWN', pa.regAuthority)
+        self.assertEquals(0, len(pa.revNotes))
+
+    def testCmp(self):
+        '''Test comparison operators.
+        '''
+        a = ProfileAttributes('1')
+        b = ProfileAttributes('1')
+        c = ProfileAttributes('2')
+        self.assertEquals(a, a)
+        self.assertEquals(a, b)
+        self.assertNotEquals(a, c)
+        self.assert_(a <= a)
+        self.assert_(a <= b)
+        self.assert_(a <= c)
+        self.assert_(a < c)
+        
+    def testXML(self):
+        '''Test XML serialization and re-composition from XML.
+        '''
+        a = ProfileAttributes('1.3.1.9', '2.0.0', 'profile', 'inactive', 'classified', '1.3.1', ['1.3.1.9.1', '1.3.1.9.2'],
+            'NASA', ['Updated', 'Created'])
+        doc = xml.dom.minidom.getDOMImplementation().createDocument(None, None, None)
+        node = a.toXML(doc)
+        b = ProfileAttributes(node=node)
+        self.assertEquals(a, b)
+        
+    def testXMLValidity(self):
+        '''Test to see if all required XML elements are in there.
+        '''
+        a = ProfileAttributes('1.3.1.9', '2.0.0', 'profile', 'inactive', 'classified', '1.3.1', ['1.3.1.9.1', '1.3.1.9.2'],
+            'NASA', ['Updated', 'Created'])
+        doc = xml.dom.minidom.getDOMImplementation().createDocument(None, None, None)
+        node = a.toXML(doc)
+        self.assertEquals('profAttributes', node.nodeName)
+        childElements = [n.nodeName for n in node.childNodes]
+        self.assertEquals([u'profId', u'profVersion', u'profType', u'profStatusId', u'profSecurityType', u'profParentId',
+            u'profChildId', u'profChildId', u'profRegAuthority', u'profRevisionNote', u'profRevisionNote'],
+            childElements)
+    
+    def testInstances(self):
+        '''Test to ensure instances don't share instance data.
+        '''
+        a = ProfileAttributes(id='1')
+        b = ProfileAttributes(id='2')
+        self.assertNotEquals(a, b)
+        a.childIDs.append('3')
+        self.assertNotEquals(a.childIDs, b.childIDs)
+        a.revNotes.append('Uhhhhh, spam?')
+        self.assertNotEquals(a.revNotes, b.revNotes)
+    
+    
+class ResourceAttributesTest(unittest.TestCase):
+    '''Unit test for the ResourceAttributes class.
+    '''
+    def testDefaults(self):
+        '''Test if default values are reasonable.
+        '''
+        ra = ResourceAttributes()
+        self.assertEquals('UNKNOWN', ra.identifier)
+        self.assertEquals('UNKNOWN', ra.title)
+        self.assertEquals(0, len(ra.formats))
+        self.assertEquals('UNKNOWN', ra.description)
+        self.assertEquals(0, len(ra.creators))
+        self.assertEquals(0, len(ra.subjects))
+        self.assertEquals(0, len(ra.publishers))
+        self.assertEquals(0, len(ra.contributors))
+        self.assertEquals(0, len(ra.dates))
+        self.assertEquals(0, len(ra.types))
+        self.assertEquals(0, len(ra.sources))
+        self.assertEquals(0, len(ra.languages))
+        self.assertEquals(0, len(ra.relations))
+        self.assertEquals(0, len(ra.coverages))
+        self.assertEquals(0, len(ra.rights))
+        self.assertEquals(0, len(ra.contexts))
+        self.assertEquals('UNKNOWN', ra.aggregation)
+        self.assertEquals('UNKNOWN', ra.resClass)
+        self.assertEquals(0, len(ra.locations))
+    
+    def testCmp(self):
+        '''Test comparison operations.
+        '''
+        a = ResourceAttributes('uri:fish', 'Fish', ['text/html'], 'A book about fish.')
+        b = ResourceAttributes('uri:fish', 'Fish', ['text/html'], 'A book about fish.')
+        c = ResourceAttributes('uri:clams', 'Clams', ['text/html'], 'A book about clams.')
+        self.assertEquals(a, a)
+        self.assertEquals(a, b)
+        self.assertNotEquals(a, c)
+        self.assert_(a <= b)
+        self.assert_(a >= c)
+        self.assert_(a > c)
+        self.assert_(a <= a)
+    
+    def testXML(self):
+        '''Test XML serialization and recomposition from XML.
+        '''
+        a = ResourceAttributes('uri:fish', 'Fish', ['text/html'], 'A book about fish.')
+        doc = xml.dom.minidom.getDOMImplementation().createDocument(None, None, None)
+        node = a.toXML(doc)
+        b = ResourceAttributes(node=node)
+        self.assertEquals(a, b)
+    
+    def testXMLValidity(self):
+        '''Test to see if all required XML elements are in there.
+        '''
+        a = ResourceAttributes('uri:anus', 'Anus', ['text/html'], 'The anus, rectum, and other parts of the bum.',
+            ['Buttman'], ['butts', 'henies', 'booties'], ['Butts and Co Publishing'], ['Dr Eugene Bottomman, III'],
+            [], ['reference'], ['The Big Book of Booty'], ['en'], ['Buttholes and other oddities'],
+            ['anatomy'], ['Cannot touch this'], ['system.buttServer'], 'granule', 'system.buttServer',
+            ['http://butt.info/butt'])
+        doc = xml.dom.minidom.getDOMImplementation().createDocument(None, None, None)
+        node = a.toXML(doc)
+        childElements = [n.nodeName for n in node.childNodes]       
+        self.assertEquals([u'Identifier', u'Title', u'Format', u'Description', u'Creator', u'Subject', u'Subject',
+            u'Subject', u'Publisher', u'Contributor', u'Type', u'Source', u'Language', u'Relation', u'Coverage',
+            u'Rights', u'resContext', u'resAggregation', u'resClass', u'resLocation'], childElements)
+    
+    def testInstances(self):
+        '''Test to ensure instances don't share instance data.
+        '''
+        a = ResourceAttributes()
+        b = ResourceAttributes()
+        a.formats.append('text/xml')
+        a.creators.append('Dennis Moore')
+        a.subjects.append('Silliness')
+        a.publishers.append('BBC')
+        a.contributors.append('Nigel')
+        a.types.append('Video')
+        a.languages.append('en')
+        a.relations.append('Fawlty Towers')
+        a.coverages.append('1970')
+        a.rights.append('Abused')
+        a.contexts.append('humor')
+        a.locations.append('http://bbc.co.uk/')
+        self.assertNotEquals(a.formats, b.formats)
+        self.assertNotEquals(a.creators, b.creators)
+        self.assertNotEquals(a.subjects, b.subjects)
+        self.assertNotEquals(a.publishers, b.publishers)
+        self.assertNotEquals(a.contributors, b.contributors)
+        self.assertNotEquals(a.types, b.types)
+        self.assertNotEquals(a.languages, b.languages)
+        self.assertNotEquals(a.relations, b.relations)
+        self.assertNotEquals(a.coverages, b.coverages)
+        self.assertNotEquals(a.rights, b.rights)
+        self.assertNotEquals(a.contexts, b.contexts)
+        self.assertNotEquals(a.locations, b.locations)
+    
+
+class ProfileTest(unittest.TestCase):
+    '''Unit test for class Profile.
+    '''
+    def testCmp(self):
+        a = Profile()
+        b = Profile()
+        self.assertEquals(a, b)
+        
+    def testXML(self):
+        '''Test XML serialization and recomposition from XML.
+        '''
+        a = Profile()
+        doc = xml.dom.minidom.getDOMImplementation().createDocument(None, None, None)
+        node = a.toXML(doc)
+        b = Profile(node=node)
+        self.assertEquals(a, b)
+
+        x = UnspecifiedProfileElement('tastiness', 'How tasty it was', 'char', 'subjective', ['yumminess'],
+            'This is highly subjective.')
+        y = EnumeratedProfileElement('meal', 'What meal was eaten', 'char', 'meal', ['serving'], 'Typical values',
+            ['Breakfast', 'Brunch', 'Lunch', 'Dinner'])
+        z = RangedProfileElement('spicyness', 'How spicy it was', 'float', 'scovilles', ['piquancy'],
+            'Hotter the better, I say', 0.0, 1000000.0)
+        a.profElements['tastiness'], a.profElements['meal'], a.profElements['spicyness'] = x, y, z
+        node = a.toXML(doc)
+        b = Profile(node=node)
+        self.assertEquals(a, b)
+
+    def testInstances(self):
+        '''Test to ensure isntances don't share isntance data.
+        '''
+        a = Profile()
+        b = Profile()
+        self.assertEquals(a, b)
+        a.profElements['a'] = 'b'
+        self.assertNotEquals(a, b)
+    
+
+def test_suite():
+    '''Make the test suite.
+    '''
+    import doctest
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(ProfileAttributesTest))
+    suite.addTest(unittest.makeSuite(ResourceAttributesTest))
+    suite.addTest(unittest.makeSuite(ProfileTest))
+    return suite
+    
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/0.10/agility/oodt/tests/queryTest.py b/0.10/agility/oodt/tests/queryTest.py
new file mode 100644
index 0000000..20d6f32
--- /dev/null
+++ b/0.10/agility/oodt/tests/queryTest.py
@@ -0,0 +1,341 @@
+# encoding: utf-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+
+'''Unit tests for the query module.
+
+This module tests various query classes and the query expression parser.
+'''
+
+__docformat__ = 'restructuredtext'
+
+import unittest, xml.dom, oodt.xmlutils
+from oodt.query import QueryElement, QueryHeader, QueryResult, Query, _parseQuery, ExpressionParseError
+
+class QueryElementTest(unittest.TestCase):
+    '''Test the `QueryElement` class.
+    '''
+    def testDefaults(self):
+        '''Test to see if defaults are reasonable.
+        '''
+        qe = QueryElement()
+        self.assertEqual('UNKNOWN', qe.role)
+        self.assertEqual('UNKNOWN', qe.value)
+
+    def testArgs(self):
+        '''Test to see if initializer arguments are used.
+        '''
+        qe = QueryElement(role='role', value='value')
+        self.assertEqual('role', qe.role)
+        self.assertEqual('value', qe.value)
+    
+    def testComparisons(self):
+        '''Test comparison operators.
+        '''
+        a = QueryElement('a', '1')
+        b = QueryElement('a', '1')
+        c = QueryElement('b', '1')
+        d = QueryElement('a', '2')
+        self.assertEqual(a, a)
+        self.assertEqual(a, b)
+        self.assertEqual(b, a)
+        self.assertNotEqual(a, c)
+        self.assertNotEqual(c, a)
+        self.assertNotEqual(a, d)
+        self.assertNotEqual(d, a)
+        self.assertNotEqual(c, d)
+        self.assert_(a <= a)
+        self.assert_(a <= c)
+        self.assert_(a < c)
+        self.assert_(a <= d)
+        self.assert_(a < d)
+    
+    def testBadArgs(self):
+        '''Test reactions to bad arugments.
+        '''
+        domImpl = xml.dom.getDOMImplementation()
+        doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype
+        self.assertRaises(ValueError, QueryElement, node=doc.createElement('notAQueryElement'))
+    
+    def testXML(self):
+        '''Test XML serialization.
+        '''
+        domImpl = xml.dom.getDOMImplementation()
+        doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype
+        q1 = QueryElement(role='a', value='1')
+        root = q1.toXML(doc)
+        self.assertEqual('queryElement', root.nodeName)
+        for child in root.childNodes:
+            if 'tokenRole' == child.nodeName:
+                self.assertEquals('a', oodt.xmlutils.text(child))
+            elif 'tokenValue' == child.nodeName:
+                self.assertEquals('1', oodt.xmlutils.text(child))
+            else:
+                self.fail('Unknown node "' + child.nodeName + '" in XML result')
+
+        q2 = QueryElement(node=root)
+        self.assertEqual(q1, q2)
+
+        root = doc.createElement('queryElement')
+        doc.appendChild(root)
+        elem = doc.createElement('tokenRole')
+        root.appendChild(elem)
+        elem.appendChild(doc.createTextNode('a'))
+        elem = doc.createElement('tokenValue')
+        root.appendChild(elem)
+        elem.appendChild(doc.createTextNode('2'))
+        q3 = QueryElement(node=root)
+        self.assertNotEqual(q1, q3)
+    
+
+_QUERY_HEADER_ATTRS = {
+    'queryId': 'id',
+    'queryTitle': 'title',
+    'queryDesc': 'desc',
+    'queryType': 'type',
+    'queryStatusId': 'status',
+    'querySecurityType': 'security',
+    'queryRevisionNote': 'rev',
+    'queryDataDictId': 'dataDict'
+}
+
+class QueryHeaderTest(unittest.TestCase):
+    '''Unit test for the `QueryHeader` class.
+    '''
+    def testDefaults(self):
+        '''Test if defaults are reasonable.
+        '''
+        qh = QueryHeader()
+        self.assertEqual('UNKNOWN', qh.id)
+        self.assertEqual('UNKNOWN', qh.title)
+        self.assertEqual('UNKNOWN', qh.desc)
+        self.assertEqual('QUERY', qh.type)
+        self.assertEqual('ACTIVE', qh.status)
+        self.assertEqual('UNKNOWN', qh.security)
+        self.assertEqual('2005-10-01 SCK v0.0.0 Under Development', qh.rev)
+        self.assertEqual('UNKNOWN', qh.dataDict)
+
+    def testArgs(self):
+        '''Test if initializer arguments are used.
+        '''
+        qh = QueryHeader('id', 'title', 'desc', 'type', 'status', 'security', 'rev', 'dataDict')
+        self.assertEqual(qh.id, 'id')
+        self.assertEqual(qh.title, 'title')
+        self.assertEqual(qh.desc, 'desc')
+        self.assertEqual(qh.type, 'type')
+        self.assertEqual(qh.status, 'status')
+        self.assertEqual(qh.security, 'security')
+        self.assertEqual(qh.rev, 'rev')
+        self.assertEqual(qh.dataDict, 'dataDict')
+
+    def testBadArgs(self):
+        '''Test reaction to bad arguments.
+        '''
+        domImpl = xml.dom.getDOMImplementation()
+        doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype
+        self.assertRaises(ValueError, QueryHeader, node=doc.createElement('notAQueryHeader'))
+
+    def testXML(self):
+        '''Test XML serialization.
+        '''
+        domImpl = xml.dom.getDOMImplementation()
+        doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype
+        q1 = QueryHeader('id', 'title', 'desc', 'type', 'status', 'security', 'rev', 'dataDict')
+        root = q1.toXML(doc)
+        self.assertEqual('queryAttributes', root.nodeName)
+        for child in root.childNodes:
+            self.check(child.nodeName, oodt.xmlutils.text(child))
+        q2 = QueryHeader(node=root)
+        self.assertEqual(q1, q2)
+
+    def check(self, name, value):
+        '''Check if the given tag name is valid.
+        '''
+        if name in _QUERY_HEADER_ATTRS:
+            self.assertEqual(value, _QUERY_HEADER_ATTRS[name])
+        else:
+            fail('Unknown element ' + name + ' in query header')
+    
+
+class QueryResultTest(unittest.TestCase):
+    '''Unit test for the `QueryResults` class.
+    '''
+    def testDefaults(self):
+        '''Test if defaults are reasonable.
+        '''
+        qr = QueryResult()
+        self.assertEquals(0, len(qr.results))
+        self.assertEquals(0, len(qr))
+        self.assertRaises(IndexError, qr.__getitem__, 0)
+
+    def testBadArgs(self):
+        '''Test reaction to bad arguments.
+        '''
+        domImpl = xml.dom.getDOMImplementation()
+        doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype
+        self.assertRaises(ValueError, QueryResult, node=doc.createElement('notAQueryResult'))
+
+    def testXML(self):
+        '''Test XML serialization.
+        '''
+        domImpl = xml.dom.getDOMImplementation()
+        doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype
+        q1 = QueryResult()
+        root = q1.toXML(doc)
+        self.assertEqual('queryResultSet', root.nodeName)
+        q2 = QueryResult(node=root)
+        self.assertEqual(q1, q2)
+    
+
+class QueryTest(unittest.TestCase):
+    '''Unit test for the `Query` class.
+    '''
+    def testDefaults(self):
+        '''Test if defaults are reasonable.
+        '''
+        q = Query()
+        self.assertEqual(QueryHeader(), q.header)
+        self.assertEqual('ATTRIBUTE', q.resultModeID)
+        self.assertEqual('BROADCAST', q.propType)
+        self.assertEqual('N/A', q.propLevels)
+        self.assertEqual(1, q.maxResults)
+        self.assertEqual(0, len(q.mimeAccept))
+
+    def testParser(self):
+        '''Test the query expresion parser.
+        '''
+        # Empty
+        self.assertEqual(([], []), _parseQuery(''))
+        
+        # Simple
+        self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='1'),
+            QueryElement(role='RELOP', value='EQ') ], []), _parseQuery('x = 1'))
+
+        # Logical or
+        self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='1'),      
+            QueryElement(role='RELOP', value='LT'), QueryElement(role='elemName', value='y'),
+            QueryElement(role='LITERAL', value='-2'), QueryElement(role='RELOP', value='GT'),
+            QueryElement(role='LOGOP', value='OR') ], []), _parseQuery('x < 1 or y > -2'))
+
+        # Logical and has higher precendence
+        self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='1'),
+            QueryElement(role='RELOP', value='LE'), QueryElement(role='elemName', value='y'),
+            QueryElement(role='LITERAL', value='2'), QueryElement(role='RELOP', value='GE'),
+            QueryElement(role='elemName', value='z'), QueryElement(role='LITERAL', value='-3.96'),
+            QueryElement(role='RELOP', value='NE'), QueryElement(role='LOGOP', value='AND'),
+            QueryElement(role='LOGOP', value='OR') ], []), _parseQuery('x <= 1 | y >= 2 and z != -3.96'))
+        
+        # Logical or has lower precedence
+        self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='1'),
+            QueryElement(role='RELOP', value='LT'), QueryElement(role='elemName', value='y'),
+            QueryElement(role='LITERAL', value='2'), QueryElement(role='RELOP', value='GT'),
+            QueryElement(role='LOGOP', value='AND'), QueryElement(role='elemName', value='z'),
+            QueryElement(role='LITERAL', value='3'), QueryElement(role='RELOP', value='NE'), 
+            QueryElement(role='LOGOP', value='OR') ], []), _parseQuery('x LT 1 & y GT 2 or z NE 3'))
+
+        # Parenthesis
+        self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='1'),
+            QueryElement(role='RELOP', value='LE'), QueryElement(role='elemName', value='y'), \
+            QueryElement(role='LITERAL', value='2'), QueryElement(role='RELOP', value='GE'),
+            QueryElement(role='LOGOP', value='OR'), QueryElement(role='elemName', value='z'),
+            QueryElement(role='LITERAL', value='3'), QueryElement(role='RELOP', value='EQ'), 
+            QueryElement(role='LOGOP', value='AND') ], []), _parseQuery('(x LE 1 or y GE 2) and z EQ 3'))
+
+        # Logical not
+        self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='1'),
+            QueryElement(role='RELOP', value='LE'), QueryElement(role='elemName', value='y'),
+            QueryElement(role='LITERAL', value='2'), QueryElement(role='RELOP', value='GE'),
+            QueryElement(role='LOGOP', value='OR'), QueryElement(role='LOGOP', value='NOT'),
+            QueryElement(role='elemName', value='z'), QueryElement(role='LITERAL', value='3'),
+            QueryElement(role='RELOP', value='EQ'), QueryElement(role='LOGOP', value='NOT'),
+            QueryElement(role='LOGOP', value='AND') ], []), _parseQuery('not (x LE 1 or y GE 2) and ! z EQ 3'))
+
+        # Quoted strings
+        self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='Fish Poop'),
+            QueryElement(role='RELOP', value='EQ'), QueryElement(role='elemName', value='y'),
+            QueryElement(role='LITERAL', value='Monkey Poop'), QueryElement(role='RELOP', value='EQ'),
+            QueryElement(role='LOGOP', value='AND') ], []),
+            _parseQuery("""x = "Fish Poop" and y = 'Monkey Poop'"""))
+                
+        # RETURN elements
+        self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='Fish Poop'),
+            QueryElement(role='RELOP', value='EQ'), QueryElement(role='elemName', value='y'),
+            QueryElement(role='LITERAL', value='Monkey Poop'), QueryElement(role='RELOP', value='EQ'),
+            QueryElement(role='LOGOP', value='AND') ], [QueryElement(role='elemName', value='fish')]),
+            _parseQuery("""x = "Fish Poop" and RETURN > fish and y = 'Monkey Poop'"""))
+        
+        # RETURN at beginning and middle
+        self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='Fish Poop'),
+            QueryElement(role='RELOP', value='EQ'), QueryElement(role='elemName', value='y'),
+            QueryElement(role='LITERAL', value='Monkey Poop'), QueryElement(role='RELOP', value='EQ'),
+            QueryElement(role='LOGOP', value='AND') ], [QueryElement(role='elemName', value='fish'),
+            QueryElement(role='elemName', value='poop')]),
+            _parseQuery("""RETURN < fish and x = "Fish Poop" and RETURN > poop and y = 'Monkey Poop'"""))
+            
+        # Bad expressions
+        for expr in ('heya', 'RETURN =', 'AND', 'LAT ~ fish', 'NOT', '(', ')'):
+            self.assertRaises(ExpressionParseError, _parseQuery, expr)
+            
+        # Unusual symbols
+        self.assertEqual(([QueryElement('elemName', 'DATA_SET_ID'), QueryElement('LITERAL', 'ARCB-L-RTLS-3-70CM-V1.0'),
+            QueryElement('RELOP', 'EQ')], []), _parseQuery('''DATA_SET_ID = ARCB-L-RTLS-3-70CM-V1.0'''))
+        self.assertEqual(([QueryElement('elemName', 'file'), QueryElement('LITERAL', '/usr/local/bin/poop'),
+            QueryElement('RELOP', 'EQ')], []), _parseQuery('''file = /usr/local/bin/poop'''))
+        self.assertEqual(([QueryElement('elemName', 'start_time'), QueryElement('LITERAL', '2006-02-06T13:12:13'),
+            QueryElement('RELOP', 'EQ')], []), _parseQuery('''start_time = 2006-02-06T13:12:13'''))
+    
+    def testCmp(self):
+        '''Test comparison operators.
+        '''
+        a = Query('lat > 3 and lon < -92.6')
+        b = Query('lat > 3 and lon < -92.6')
+        c = Query('lat < 3 and lon > -92.6')
+        self.assert_(a == a)
+        self.assert_(a == b)
+        self.assert_(a < c)
+        self.assert_(c > a)
+        self.assert_(a <= b)
+        self.assert_(a >= b)
+        self.assert_(a <= c)
+        self.assert_(c >= a)
+        self.assert_(c != a)
+
+    def testXML(self):
+        '''Test XML serialization.
+        '''
+        domImpl = xml.dom.getDOMImplementation()
+        doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype
+        q1 = Query('lat > 3 and lon < -92.6')
+        root = q1.toXML(doc)
+        self.assertEqual('query', root.nodeName)
+        q2 = Query(node=root)
+        self.assertEqual(q1, q2)
+    
+
+def test_suite():
+    '''Create the suite of tests.
+    '''
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(QueryElementTest))
+    suite.addTest(unittest.makeSuite(QueryHeaderTest))
+    suite.addTest(unittest.makeSuite(QueryResultTest))
+    suite.addTest(unittest.makeSuite(QueryTest))
+    return suite
+    
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+    
diff --git a/0.10/agility/oodt/tests/xmlutilsTest.py b/0.10/agility/oodt/tests/xmlutilsTest.py
new file mode 100644
index 0000000..e8f64a4
--- /dev/null
+++ b/0.10/agility/oodt/tests/xmlutilsTest.py
@@ -0,0 +1,109 @@
+# encoding: utf-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+
+'''Unit tests for the `xmlutils` module.
+'''
+
+__docformat__ = 'restructuredtext'
+
+import unittest, xml.dom, oodt.xmlutils
+from oodt.xmlutils import DocumentableField
+
+class XMLTest(unittest.TestCase):
+    '''Unit test the XML utilities.
+    '''
+    def testText(self):
+        '''Test getting text from nodes.
+        '''
+        domImpl = xml.dom.getDOMImplementation()
+        doc = domImpl.createDocument(None, None, None) # ns URI, qual name, doctype
+        root = doc.createElement('root')
+        root.appendChild(doc.createTextNode('Hello'))
+        child = doc.createElement('child')
+        root.appendChild(child)
+        root.appendChild(doc.createCDATASection('world'))
+        child.appendChild(doc.createTextNode('cruel'))
+        empty = doc.createElement('empty')
+        root.appendChild(empty)
+
+        self.assertEquals('cruel', oodt.xmlutils.text(child))
+        self.assertEquals(0, len(oodt.xmlutils.text(empty)))
+        self.assertEquals('Hellocruelworld', oodt.xmlutils.text(root))
+    
+    def testAdds(self):
+        '''Test adding things to nodes.
+        '''
+        domImpl = xml.dom.getDOMImplementation()
+        doc = domImpl.createDocument(None, None, None) # ns URI, qual name, doctype
+        root = doc.createElement('root')
+
+        oodt.xmlutils.add(root, 'first')
+        self.assertEquals(0, len(oodt.xmlutils.text(root)))
+        oodt.xmlutils.add(root, 'second', 'with text')
+        self.assertEquals('with text', oodt.xmlutils.text(root))
+        
+        oodt.xmlutils.add(root, 'number', [str(i) for i in range(1, 4)])
+        self.assertEquals('with text123', oodt.xmlutils.text(root))
+    
+
+class DocumentableTest(unittest.TestCase):
+    '''Unit test the `Documentable` and `DocumentableField` classes.
+    '''
+    def testIt(self):
+        '''Test the `Documentable` and `DocumentableField` classes.
+        '''
+        class X(oodt.xmlutils.Documentable):
+            def __init__(self, mission='UNK', target='UNK', measurements=[], node=None):
+                self.mission, self.target, self.measurements = mission, target, measurements
+                if node is not None:
+                    self.parse(node)
+            def computeValueFromDocument(self, attrName, text):
+                if attrName == 'measurements':
+                    return int(text)
+                else:
+                    return text
+            def getDocumentElementName(self):
+                return 'X'
+            def getDocumentableFields(self):
+                return (DocumentableField('mission', u'MISSION', DocumentableField.SINGLE_VALUE_KIND),
+                DocumentableField('target', u'TARGET', DocumentableField.SINGLE_VALUE_KIND),
+                DocumentableField('measurements', u'MZR', DocumentableField.MULTI_VALUED_KIND))
+            def __eq__(self, other):
+                return (self.mission == other.mission and self.target == other.target
+                    and cmp(self.measurements, other.measurements) == 0)
+            def __str__(self):
+                return '%s,%s:%s' % (self.mission, self.target, self.measurements)
+        a = X('Explorer', 'Moon', [1, 2, 3])
+        doc = xml.dom.getDOMImplementation().createDocument(None, None, None)
+        node = a.toXML(doc)
+        b = X(node=node)
+        self.assertEquals(a, b)
+    
+
+def test_suite():
+    '''Create the suite of tests.
+    '''
+    import doctest
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(XMLTest))
+    suite.addTest(unittest.makeSuite(DocumentableTest))
+    suite.addTest(doctest.DocTestSuite(oodt.xmlutils))
+    return suite
+    
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/0.10/agility/oodt/webgrid.py b/0.10/agility/oodt/webgrid.py
new file mode 100644
index 0000000..8f56fe1
--- /dev/null
+++ b/0.10/agility/oodt/webgrid.py
@@ -0,0 +1,226 @@
+# encoding: utf-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+
+'''Agile Web Grid.  HTTP-based profile and product servers.
+'''
+
+__docformat__ = 'restructuredtext'
+
+from oodt.query import Query
+from xml.dom.minidom import parseString, getDOMImplementation
+from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
+import cgi, os, shutil, stat
+
+_validKinds = ('profile', 'product')
+_doc = getDOMImplementation().createDocument(None, None, None) # qname, nsuri, doctype
+    
+class WebGridRequestHandler(BaseHTTPRequestHandler):
+    '''HTTP request handler for Web-Grid requests.  This request handler accepts GET
+    or POST requests and directs them to profile or product handlers.  Additionally,
+    requests to install new handlers, to list currently installed handlers, and to
+    remove handlers by ID are supported.
+    '''
+    def do_POST(self):
+        '''Handle a POST.
+        '''
+        try:
+            length = int(self.headers['content-length'])
+            kind = self.headers['content-type']
+            if kind.endswith('www-form-urlencoded'):
+                if length > 0:
+                    params = cgi.parse_qs(self.rfile.read(length), True, True) # Keep blanks, strict parse
+                else:
+                    params = {}
+                self.__execute(self.path, params)
+            else:
+                raise ValueError('Unknown encoding "%s"' % kind)
+        except Exception, e:
+            self.send_error(500, str(e))
+    
+    def do_GET(self):
+        '''Handle a GET.
+        '''
+        try:
+            index = self.path.find('?')
+            if index >= 0:
+                params = cgi.parse_qs(self.path[index+1:], True, True) # Keep blanks, strict parse
+                path = self.path[0:index]
+            else:
+                params, path = {}, self.path
+            self.__execute(path, params)
+        except Exception, e:
+            self.send_error(500, str(e))
+    
+    def __execute(self, path, params):
+        '''Execute an HTTP request.
+        '''
+        components = path.split('/')
+        if len(components) == 3:
+            context, command = components[1], components[2]
+            if context != self.server.serverID:
+                raise ValueError('Unknown server ID "%s"' % context)
+            func = getattr(self, command)
+            if callable(func):
+                func(params)
+                return
+        raise KeyError('Unknown command')
+    
+    def echo(self, params):
+        '''Debugging method that echoes back the request parameters.
+        '''
+        u = unicode(params)
+        self.send_response(200)
+        self.send_header('Content-type', 'text/plain;charset=utf-8')
+        self.send_header('Content-length', str(len(u)))
+        self.end_headers()
+        self.wfile.write(u)
+    
+    def sendEmptyResponse(self):
+        '''Send an empty response to the HTTP client.
+        '''
+        self.send_response(200)
+        self.send_header('Content-type', 'text/plain;charset=utf-8')
+        self.send_header('Content-length', '0')
+        self.end_headers()
+    
+    def install(self, params):
+        '''Install a new handler.  This will overwrite existing handlers with the
+        same ID.
+        '''
+        handlers = self.server.getHandlers(params['kind'][0])
+        globs = dict(globals())
+        del globs['__name__']
+        # TODO: use rexec or otherwise limit the code than can be uploaded.
+        exec params['code'][0] in globs, globs
+        handlers[params['id'][0]] = globs['handler']
+        self.sendEmptyResponse()
+    
+    def remove(self, params):
+        '''Remove an existing handler.
+        '''
+        handlers = self.server.getHandlers(params['kind'][0])
+        del handlers[params['id'][0]]
+        self.sendEmptyResponse()
+    
+    def list(self, params):
+        '''List installed handlers.
+        '''
+        handlers = {}
+        for kind in _validKinds:
+            handlers[kind] = self.server.getHandlers(kind).keys()
+        handlers = unicode(handlers)
+        self.send_response(200)
+        self.send_header('Content-type', 'text/plain;charset=utf-8')
+        self.send_header('Content-length', str(len(handlers)))
+        self.end_headers()
+        self.wfile.write(handlers)
+    
+    def __createQuery(self, params):
+        '''Create a Query from the request parameters.  This method prefers the
+        xmlq parameter and parses it as an XML document and into a Query object.
+        However, if it's not provided, or fails to parse, it'll use the q parameter,
+        which is expected to be just a query expression.
+        '''
+        try:
+            doc = parseString(params['xmlq'][0])
+            return Query(node=doc.documentElement)
+        except KeyError:
+            return Query(params['q'][0])
+    
+    def sendProduct(self, match):
+        '''Send a matching product.
+        '''
+        self.send_response(200)
+        self.send_header('Content-type', match.contentType)
+        self.send_header('Content-length', str(match.length))
+        self.end_headers()
+        shutil.copyfileobj(match.data, self.wfile)
+        self.log_request(200, match.length)
+    
+    def prod(self, params):
+        '''Handle a product query.
+        '''
+        query = self.__createQuery(params)
+        for handler in self.server.getHandlers('product'):
+            matches = handler.query(query)
+            if len(matches) > 0:
+                self.sendProduct(matches[0])
+        self.send_error(404, 'No matching products')
+    
+    def prof(self, params):
+        '''Handle a profile query.
+        '''
+        query = self.__createQuery(params)
+        tmp = os.tmpfile()
+        tmp.writelines((u'<?xml version="1.0" encoding="UTF-8"?>\n',
+            u'<!DOCTYPE profiles PUBLIC "-//JPL//DTD Profile 1.1//EN"\n',
+            u'  "http://oodt.jpl.nasa.gov/grid-profile/dtd/prof.dtd">\n',
+            u'<profiles>\n'))
+        for handler in self.server.getHandlers('profile').itervalues():
+            for profile in handler.query(query):
+                node = profile.toXML(_doc)
+                tmp.write(node.toxml())
+        tmp.write(u'</profiles>')
+        tmp.flush()
+        tmp.seek(0L)
+        self.send_response(200)
+        self.send_header('Content-type', 'text/xml;charset=utf-8')
+        size = os.fstat(tmp.fileno())[stat.ST_SIZE]
+        self.send_header('Content-length', str(size))
+        self.end_headers()
+        shutil.copyfileobj(tmp, self.wfile)
+        self.log_request(200, size)
+    
+
+class WebGridServer(HTTPServer):
+    '''Web grid HTTP server.  This server handles incoming HTTP requests and directs them to a
+    WebGridRequestHandler.  It also contains the server's ID, and the sequences of profile and
+    product handlers.
+    '''
+    def __init__(self, addr, serverID):
+        '''Initialize by saving the server ID and creating empty sequences of profile
+        and product handlers.
+        '''
+        HTTPServer.__init__(self, addr, WebGridRequestHandler)
+        self.serverID = serverID
+        self.__handlers = {}
+        for kind in _validKinds:
+            self.__handlers[kind] = {}
+    
+    def getHandlers(self, kind):
+        '''Get the map of handlers for the given kind, which is either "product" or "profile".
+        '''
+        if kind not in _validKinds:
+            raise ValueError('Invalid handler kind "%s"' % kind)
+        return self.__handlers[kind]
+    
+
+def _main():
+    '''Run the web grid server.
+    '''
+    import sys
+    try:
+        serverID = sys.argv[1]
+    except IndexError:
+        serverID = 'oodt'
+    listenAddr = ('', 7576)
+    httpd = WebGridServer(listenAddr, serverID)
+    httpd.serve_forever()
+    
+
+if __name__ == '__main__':
+    _main()
diff --git a/0.10/agility/oodt/xmlutils.py b/0.10/agility/oodt/xmlutils.py
new file mode 100644
index 0000000..44e7ec1
--- /dev/null
+++ b/0.10/agility/oodt/xmlutils.py
@@ -0,0 +1,209 @@
+# encoding: utf-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+
+'''Object Oriented Data Technology XML utilities. These are some simple utilities
+that make working with rather obtuse DOM API less painful.
+'''
+
+__docformat__ = 'restructuredtext'
+
+import string
+import xml.dom
+    
+def text(node):
+    '''Return the text under the given node.  Text and CDATA nodes simply return
+    their content.  Otherwise, text is gathered in a depth-first left-to-right
+    traversal of the node, building up text as we go along.
+    
+    >>> import xml.dom
+    >>> domImpl = xml.dom.getDOMImplementation()
+    >>> doc = domImpl.createDocument(None, None, None)
+    >>> root = doc.createElement('root')
+    >>> root.appendChild(doc.createTextNode('alpha')) # doctest: +ELLIPSIS
+    <DOM Text node...>
+    >>> text(root)
+    'alpha'
+    >>> child = root.appendChild(doc.createElement('child'))
+    >>> text(child.appendChild(doc.createCDATASection('beta')))
+    'beta'
+    >>> text(root)
+    'alphabeta'
+    '''
+    strings = []
+    _text0(node, strings)
+    return string.join(strings, '') # no separator
+    
+
+def _text0(node, strings):
+    '''Add the text under the given node to the list of strings.
+    '''
+    if node.nodeType == xml.dom.Node.CDATA_SECTION_NODE:
+        strings.append(node.nodeValue)
+    elif node.nodeType == xml.dom.Node.TEXT_NODE:
+        strings.append(node.nodeValue)
+    for child in node.childNodes:
+        _text0(child, strings)
+    
+
+def add(node, elementName, value=None):
+    '''Add an element under the given node.  Optionally, the value text is added to the element.
+    Returns the original node, handy for chaining calls together.  If the value is a sequence,
+    then the element is added multiple times for each item in the sequence, using the item
+    as the text.
+
+    >>> from xml.dom.minidom import getDOMImplementation
+    >>> doc = getDOMImplementation().createDocument(None, None, None)
+    >>> root = doc.createElement('root')
+    >>> root.toxml()
+    '<root/>'
+    >>> add(root, 'empty').toxml()
+    '<root><empty/></root>'
+    >>> add(root, 'full', 'of text').toxml()
+    '<root><empty/><full>of text</full></root>'
+    >>> add(root, 'item', [str(i) for i in range(1, 4)]).toxml()
+    '<root><empty/><full>of text</full><item>1</item><item>2</item><item>3</item></root>'
+    '''
+    owner = node.ownerDocument
+    if isinstance(value, basestring):
+        elem = owner.createElement(elementName)
+        node.appendChild(elem)
+        elem.appendChild(owner.createTextNode(value))
+    elif value is not None:
+        for i in value:
+            elem = owner.createElement(elementName)
+            node.appendChild(elem)
+            elem.appendChild(owner.createTextNode(i))
+    else:
+        node.appendChild(owner.createElement(elementName))
+    return node
+    
+
+class DocumentableField(object):
+    '''A documentable field is an attribute of an object that we can serialize into XML.
+    '''
+    SINGLE_VALUE_KIND = 1
+    MULTI_VALUED_KIND = 2
+    DOCUMENTABLE_KIND = 3
+
+    def __init__(self, attrName, elemName, kind):
+        '''Initialize a documentable field with attrName (name of attribute in object),
+        elemName (name to use for XML element) and kind, which is one of the DOCUMENTABLE_*
+        constants.
+        '''
+        self.attrName = attrName
+        self.elemName = elemName
+        if kind not in (DocumentableField.SINGLE_VALUE_KIND, DocumentableField.MULTI_VALUED_KIND,
+            DocumentableField.DOCUMENTABLE_KIND):
+            raise ValueError('Invalid kind %d' % kind)
+        self.kind = kind
+    
+    def __cmp__(self, other):
+        attrName = cmp(self.attrName, other.attrName)
+        if attrName < 0:
+            return -1
+        elif attrName == 0:
+            elemName = cmp(self.elemName, other.elemName)
+            if elemName < 0:
+                return -1
+            elif elemName == 0:
+                return cmp(self.kind, other.kind)
+        return 1
+    
+    def __hash__(self):
+        return hash(self.attrName) ^ hash(self.elemName) ^ self.kind
+    
+
+class Documentable(object):
+    '''An object that can be documented into XML and parsed from an XML document.
+    '''
+    def getDocumentElementName(self):
+        '''Get the XML tag name.  Subclasses must override this
+        otherwise they get the tag name `UNKNOWN`.
+        '''
+        return 'UNKNOWN'
+        
+    def getDocumentableFields(self):
+        '''Get the sequence of documentable attributes.  Subclasses should override this
+        with a sequence of `DocumentableField` objects.  By default, we return an empty
+        sequence.
+        '''
+        return []
+    
+    def toXML(self, owner):
+        '''Convert this object into XML owned by the given `owner` document.
+        '''
+        root = owner.createElement(self.getDocumentElementName())
+        for df in self.getDocumentableFields():
+            if df.kind == DocumentableField.SINGLE_VALUE_KIND:
+                add(root, df.elemName, str(getattr(self, df.attrName)))
+            elif df.kind == DocumentableField.MULTI_VALUED_KIND:
+                add(root, df.elemName, [str(value) for value in getattr(self, df.attrName)])
+            else:
+                root.appendChild(getattr(self, df.attrName).toXML(owner))
+        return root
+    
+    def computeValueFromDocument(self, attrName, text):
+        '''Compute a value for the attribute named `attrName` from the
+        XML text representation `text`.  Subclasses may wish to
+        override this in order to provide custom typing for certain
+        attributes, such as returning an integer or datetime value by
+        parsing the text.  By default, the `text` is returned
+        unmodified as a string.
+        '''
+        return text
+    
+    def parse(self, node):
+        '''Initialize this object from the given XML DOM `node`.
+        '''
+        if node.nodeName != self.getDocumentElementName():
+            raise ValueError('Expected %s element but got %s' % (self.getDocumentElementName(), node.nodeName))
+        fieldMap = dict(zip([i.elemName for i in self.getDocumentableFields()], self.getDocumentableFields()))
+        for child in filter(lambda n: n.nodeType == xml.dom.Node.ELEMENT_NODE, node.childNodes):
+            name = child.nodeName
+            if name in fieldMap:
+                field = fieldMap[name]
+                if field.kind == DocumentableField.SINGLE_VALUE_KIND:
+                    setattr(self, field.attrName, self.computeValueFromDocument(field.attrName, text(child)))
+                elif field.kind == DocumentableField.MULTI_VALUED_KIND:
+                    if not hasattr(self, field.attrName):
+                        setattr(self, field.attrName, [])
+                    getattr(self, field.attrName).append(self.computeValueFromDocument(field.attrName,
+                        text(child)))
+                else:
+                    getattr(self, field.attrName).parse(child)
+    
+    def __cmp__(self, other):
+        '''The documentable fields provide a bonus: we can use them to do comparisons.
+        '''
+        for field in self.getDocumentableFields():
+            attrName = field.attrName
+            mine = getattr(self, attrName)
+            others = getattr(other, attrName)
+            rc = cmp(mine, others)
+            if rc < 0:
+                return -1
+            elif rc > 0:
+                return 1
+        return 0
+    
+    def __hash__(self):
+        '''The documentable fields provide another bonus: we can use them
+        to do hashing.
+        '''
+        return reduce(lambda x, y: hash(x) ^ hash(y), [getattr(self, i.attrName) for i in self.getDocumentableFields()],
+            0x55555555)
+    
\ No newline at end of file
diff --git a/0.10/agility/setup.cfg b/0.10/agility/setup.cfg
new file mode 100644
index 0000000..51418d4
--- /dev/null
+++ b/0.10/agility/setup.cfg
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+
+[egg_info]
+tag_build = .dev
+tag_svn_revision = 1
diff --git a/0.10/agility/setup.py b/0.10/agility/setup.py
new file mode 100644
index 0000000..7d2b57c
--- /dev/null
+++ b/0.10/agility/setup.py
@@ -0,0 +1,98 @@
+# encoding: utf-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+
+import os.path
+from ez_setup import use_setuptools
+use_setuptools()
+from setuptools import find_packages, setup
+
+
+# Package data
+# ------------
+
+_name         = 'oodt'
+_version      = '0.10'
+_description  = 'Apache OODT'
+_url          = 'http://oodt.apache.org/'
+_downloadURL  = 'http://pypi.python.org/pypi/oodt/'
+_author       = 'Sean Kelly'
+_authorEmail  = 'kelly@apache.org'
+_license      = 'Apache 2.0'
+_namespaces   = []
+_testSuite    = 'oodt.tests.test_suite'
+_zipSafe      = True
+_keywords     = 'data grid discovery query optimization object middleware archive catalog index'
+_requirements = []
+_entryPoints  = {
+    'console_scripts': ['webgrid = oodt.webgrid:_main'],
+}
+_classifiers  = [
+    'Environment :: Console',
+    'Environment :: No Input/Output (Daemon)',
+    'Intended Audience :: Developers',
+    'Intended Audience :: Information Technology',
+    'Intended Audience :: Science/Research',
+    'Topic :: Database :: Front-Ends',
+    'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
+    'Topic :: Internet :: WWW/HTTP :: HTTP Servers',
+    'Topic :: Internet :: Z39.50',
+    'Topic :: Scientific/Engineering',
+    'Development Status :: 5 - Production/Stable',
+    'Environment :: Web Environment',
+    'License :: OSI Approved :: Apache Software License',
+    'Operating System :: OS Independent',
+    'Programming Language :: Python',
+    'Topic :: Internet :: WWW/HTTP',
+    'Topic :: Software Development :: Libraries :: Python Modules',
+]
+
+
+# Setup Metadata
+# --------------
+
+def _read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+_header = '*' * len(_name) + '\n' + _name + '\n' + '*' * len(_name)
+_longDescription = '\n\n'.join([
+    _header,
+    _read('README.txt'),
+    _read('docs', 'INSTALL.txt'),
+    _read('docs', 'HISTORY.txt')
+])
+open('doc.txt', 'w').write(_longDescription)
+
+setup(
+    author=_author,
+    author_email=_authorEmail,
+    classifiers=_classifiers,
+    description=_description,
+    download_url=_downloadURL,
+    entry_points=_entryPoints,
+    include_package_data=True,
+    install_requires=_requirements,
+    keywords=_keywords,
+    license=_license,
+    long_description=_longDescription,
+    name=_name,
+    namespace_packages=_namespaces,
+    packages=find_packages(exclude=['ez_setup']),
+    test_suite=_testSuite,
+    url=_url,
+    version=_version,
+    zip_safe=_zipSafe,
+)
diff --git a/0.10/app/fmbrowser/pom.xml b/0.10/app/fmbrowser/pom.xml
new file mode 100644
index 0000000..e469dd0
--- /dev/null
+++ b/0.10/app/fmbrowser/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements.  See the NOTICE.txt file distributed with this work for
+additional information regarding copyright ownership.  The ASF licenses this
+file to you under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License.  You may obtain a copy of
+the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+License for the specific language governing permissions and limitations under
+the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.oodt</groupId>
+    <artifactId>oodt-core</artifactId>
+    <version>0.10</version>
+    <relativePath>../../core/pom.xml</relativePath>
+  </parent>
+  <artifactId>cas-fm-browser</artifactId>
+  <name>Catalog and Archive File Management Browser</name>
+  <description>The graphical front-end interface to the Catalog and Archive Service. This component
+     provides the user of the CAS File Manager with a graphical environment in which they
+     can view archived products' metadata, query for products with particular metadata, 
+     and export results of queries to the MS Excel(c) file format.</description>
+  <scm>
+   	<connection>scm:svn:https://svn.apache.org/repos/asf/oodt/tags/0.10-rc2/app/fmbrowser</connection>
+   	<developerConnection>scm:svn:https://svn.apache.org/repos/asf/oodt/tags/0.10-rc2/app/fmbrowser</developerConnection>
+   	<url>http://svn.apache.org/viewvc/oodt/tags/0.10-rc2/app/fmbrowser</url>
+  </scm>
+  <build>
+    <plugins />
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.oodt</groupId>
+      <artifactId>cas-metadata</artifactId>
+      <version>${project.parent.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.oodt</groupId>
+      <artifactId>cas-filemgr</artifactId>
+      <version>${project.parent.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.poi</groupId>
+      <artifactId>poi</artifactId>
+      <version>3.2-FINAL</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>3.8.2</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/0.10/app/fmbrowser/src/main/bin/filemgr-browser b/0.10/app/fmbrowser/src/main/bin/filemgr-browser
new file mode 100644
index 0000000..f6d3881
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/bin/filemgr-browser
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# init script for XmlRpcFileManager
+#
+# chkconfig: 345 88 22
+# description: CAS File Manager Browser
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE.txt file distributed with
+# this work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.    
+#
+# $Id$
+
+[ -f /etc/sysconfig/java ] && . /etc/sysconfig/java
+#[ -f /etc/sysconfig/cas-filemgr] && . /etc/sysconfig/cas-filemgr
+
+JAVA_HOME=/path/to/java/home
+export JAVA_HOME
+FILEMGR_BROWSER_HOME=..
+export FILEMGR_BROWSER_HOME
+
+PATH=${JAVA_HOME}/bin:${FILEMGR_BROWSER_HOME}/bin:/usr/bin:/bin:/usr/sbin:/sbin
+export PATH
+
+$JAVA_HOME/bin/java -Djava.ext.dirs=${FILEMGR_BROWSER_HOME}/lib \
+  org.apache.oodt.cas.filemgr.browser.system.XmlRpcFilemgrBrowser &
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/controller/TableListener.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/controller/TableListener.java
new file mode 100644
index 0000000..9aeb5d7
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/controller/TableListener.java
@@ -0,0 +1,168 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.controller;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.io.FileOutputStream;
+
+import javax.swing.JFileChooser;
+
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+import org.apache.oodt.cas.filemgr.browser.view.menus.RightClickMenu;
+import org.apache.oodt.cas.filemgr.browser.view.panels.HeaderCell;
+import org.apache.oodt.cas.filemgr.browser.view.panels.HeaderSpacer;
+import org.apache.oodt.cas.filemgr.browser.view.panels.Row;
+import org.apache.oodt.cas.filemgr.browser.view.panels.TablePane;
+
+public class TableListener implements MouseListener, ActionListener {
+
+  private Component caller;
+  private TablePane table;
+  private int mousePos;
+  private RightClickMenu rcMenu;
+
+  public TableListener(TablePane t) {
+    table = t;
+    mousePos = -1;
+    caller = null;
+    rcMenu = new RightClickMenu(this);
+
+  }
+
+  public void mouseClicked(MouseEvent arg0) {
+  }
+
+  public void mouseEntered(MouseEvent arg0) {
+  }
+
+  public void mouseExited(MouseEvent e) {
+  }
+
+  public void mousePressed(MouseEvent e) {
+    caller = e.getComponent();
+    mousePos = e.getX() + caller.getX();
+  }
+
+  public void mouseReleased(MouseEvent e) {
+    if (caller != null) {
+      if (caller instanceof HeaderSpacer) {
+        HeaderSpacer hsCaller = (HeaderSpacer) caller;
+        if (e.getModifiers() == 18
+            && table.hiddenCols.contains(new Integer(hsCaller.getColNum() + 1))) {
+          rcMenu.setUnhideMode();
+          rcMenu.show(caller, e.getX(), e.getY());
+
+        } else {
+          int change = e.getX() + caller.getX() - mousePos;
+          int curWidth = table.header.getWidth(hsCaller.getColNum());
+          table.header.changeWidth(hsCaller.getColNum(), curWidth + change);
+
+          for (int i = 1; i < table.getComponentCount(); i++) {
+            ((Row) table.getComponent(i)).changeWidth(((HeaderSpacer) caller)
+                .getColNum(), curWidth + change + 2);
+          }
+          caller = null;
+        }
+      } else {
+        if (caller instanceof HeaderCell && e.getModifiers() == 18) {
+          rcMenu.setHideMode();
+          rcMenu.show(caller, e.getX(), e.getY());
+
+        }
+      }
+    }
+
+  }
+
+  public void actionPerformed(ActionEvent arg0) {
+    if (arg0.getActionCommand().equals("Hide")) {
+      if (caller != null) {
+        int colNum = ((HeaderCell) caller).getColNum();
+        table.hideColumn(colNum);
+        caller = null;
+      }
+    } else if (arg0.getActionCommand().equals("Unhide")) {
+      if (caller != null) {
+        int colNum = ((HeaderSpacer) caller).getColNum() + 1;
+        table.unhideColumn(colNum);
+        caller = null;
+      }
+    } else if (arg0.getActionCommand().equals("Unhide Columns")) {
+      while (!table.hiddenCols.isEmpty()) {
+        table.unhideColumn((table.hiddenCols.firstElement()).intValue());
+      }
+    } else if (arg0.getActionCommand().equals("Export Table")) {
+
+      final JFileChooser fc = new JFileChooser();
+      int returnVal = fc.showSaveDialog(table);
+      if (returnVal == JFileChooser.APPROVE_OPTION) {
+
+        // write out excel file
+        String fullFileName = (fc.getSelectedFile()).getAbsolutePath();
+        if (!fullFileName.endsWith(".xls"))
+          fullFileName += ".xls";
+
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet sheet = wb.createSheet("results");
+        HSSFRow headerRow = sheet.createRow((short) 0);
+
+        int i = 0;
+        for (int j = 0; j < table.getRow(0).getComponentCount(); j++) {
+          if (!table.hiddenCols.contains(new Integer(j))) {
+            headerRow.createCell((short) i).setCellValue(
+                table.header.getText(j));
+            i++;
+          }
+        }
+
+        for (int k = 0; k < table.getComponentCount() - 1; k++) {
+          HSSFRow row = sheet.createRow((short) k + 1);
+          i = 0;
+          for (int j = 0; j < table.getRow(0).getComponentCount(); j++) {
+            if (!table.hiddenCols.contains(new Integer(j))) {
+              row.createCell((short) i).setCellValue(
+                  (table.getRow(k)).getText(j));
+              i++;
+            }
+          }
+        }
+
+        FileOutputStream fileOut;
+        try {
+
+          fileOut = new FileOutputStream(fullFileName);
+          wb.write(fileOut);
+          fileOut.close();
+
+        } catch (Exception e) {
+          e.printStackTrace();
+        }
+      }
+
+    }
+
+  }
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/controller/WindowListener.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/controller/WindowListener.java
new file mode 100644
index 0000000..81fd721
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/controller/WindowListener.java
@@ -0,0 +1,166 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.controller;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JOptionPane;
+
+import org.apache.oodt.cas.filemgr.browser.model.CasDB;
+import org.apache.oodt.cas.filemgr.browser.view.MainWindow;
+import org.apache.oodt.cas.filemgr.browser.view.prompts.ConnectPrompt;
+import org.apache.oodt.cas.filemgr.browser.view.prompts.QueryBuilderPrompt;
+import org.apache.oodt.cas.filemgr.browser.view.prompts.SortPrompt;
+
+public class WindowListener implements ActionListener {
+
+  private MainWindow window;
+  private ConnectPrompt prompt;
+  private SortPrompt sort;
+  private QueryBuilderPrompt query;
+
+  private CasDB db;
+
+  public WindowListener(MainWindow m) {
+    window = m;
+    db = new CasDB();
+  }
+
+  public void actionPerformed(ActionEvent arg0) {
+    if (arg0.getActionCommand().equals("About")) {
+      String aboutInfo = "OODT Catalog and Archive Server File Manger Browser.\n"
+          + "Copyright (c) 2010, Apache Software Foundation.";
+      JOptionPane.showMessageDialog(window, aboutInfo);
+    } else if (arg0.getActionCommand().equals("Query Language")) {
+      String aboutQueryLanguage = "The CAS File Manager Browser uses the Lucene Query Language.\n"
+          + "More Information can be found at:\n"
+          + "http://lucene.apache.org/java/docs/queryparsersyntax.html";
+      JOptionPane.showMessageDialog(window, aboutQueryLanguage);
+    } else if (arg0.getActionCommand().equals("Exit")) {
+      String exitWarning = "Are you sure you want to exit the CAS File Manager Browser?\n"
+          + "All unexported queries will be lost.";
+      int returnVal = JOptionPane.showConfirmDialog(window, exitWarning,
+          "Exit", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
+      if (returnVal == JOptionPane.OK_OPTION) {
+        System.exit(0);
+      }
+    } else if (arg0.getActionCommand().equals("Connect...")) {
+      // this is from the main menu
+      window.bar.changeConnectStatus();
+      prompt = new ConnectPrompt(this);
+      prompt.pack();
+      prompt.setVisible(true);
+    } else if (arg0.getActionCommand().equals("Disconnect")) {
+      String disconnectWarning = "Are you sure you want to disconnect from this File Manager?\n"
+          + "All unexported queries will be lost.";
+      int returnVal = JOptionPane.showConfirmDialog(window, disconnectWarning,
+          "Disconnect", JOptionPane.OK_CANCEL_OPTION,
+          JOptionPane.WARNING_MESSAGE);
+      if (returnVal == JOptionPane.OK_OPTION) {
+        window.bar.changeConnectStatus();
+        db.disconnect();
+        window.mPane.tPane.setBlank();
+        window.qPane.updateTypes(new String[] { "" });
+        window.bPane.changeStatus("Disconnected");
+      }
+    } else if (arg0.getActionCommand().equals("Cancel")) {
+      if (((JButton) arg0.getSource()).getName().equals("ConnectCancel")) {
+        prompt.dispose();
+        window.bar.changeConnectStatus();
+      } else if (((JButton) arg0.getSource()).getName().equals("SortCancel")) {
+        sort.dispose();
+      }
+    } else if (arg0.getActionCommand().equals("Connect")) {
+      // this is from the connect prompt
+      boolean connected = db.connect(prompt.getCASUrl());
+      if (connected) {
+        window.qPane.updateTypes(db.getAvailableTypes());
+        window.bPane.changeStatus("Connected to " + prompt.getCASUrl());
+      } else {
+        window.bar.changeConnectStatus();
+        String errorConnectingString = "Error Connecting to CAS File Manager at this following address:"
+            + prompt.getCASUrl();
+        JOptionPane.showMessageDialog(window, errorConnectingString, "Error",
+            JOptionPane.ERROR_MESSAGE);
+      }
+      prompt.dispose();
+    } else if (arg0.getActionCommand().equals("Sort")) {
+      sort = new SortPrompt(window, this);
+      sort.pack();
+      sort.setVisible(true);
+    } else if (arg0.getActionCommand().equals("Query Builder")) {
+      if (db.isConnected()) {
+        query = new QueryBuilderPrompt(db, this);
+        query.pack();
+        query.setVisible(true);
+      } else {
+        String errorQueryString = "You must connect to a CAS File Manager before using the Query Builder.";
+        JOptionPane.showMessageDialog(window, errorQueryString, "Disconnected",
+            JOptionPane.WARNING_MESSAGE);
+      }
+    } else if (arg0.getActionCommand().equals("OK")) {
+      window.mPane.tPane.sortRows(sort.getSortIndex(), sort.getSortType());
+      sort.dispose();
+    } else if (arg0.getActionCommand().equals("Search")) {
+      if (((JButton) arg0.getSource()).getName().equals("AdvancedQuery")) {
+        if (db.isConnected()) {
+          window.bPane.changeStatus("Querying the CAS...");
+          boolean result = db.issueQuery(query.getQuery(), query
+              .getProductType());
+          if (result) {
+            window.mPane.tPane.newTable(db.results.getData());
+            window.bPane.changeStatus("Query: " + query.getQueryString()
+                + " returned " + db.results.getNumRecords() + " records.");
+          } else {
+            window.mPane.tPane.setBlank();
+            window.bPane.changeStatus("Query failed.");
+            window.qPane.clearQuery();
+          }
+        }
+        query.dispose();
+      } else {
+        if (db.isConnected()) {
+          window.bPane.changeStatus("Querying the CAS...");
+          String query = window.qPane.getQuery();
+          boolean result = db.createQuery(query, window.qPane.getType());
+          if (result) {
+            window.mPane.tPane.newTable(db.results.getData());
+            window.bPane.changeStatus("Query: " + query + " returned "
+                + db.results.getNumRecords() + " records.");
+          } else {
+            window.mPane.tPane.setBlank();
+            window.bPane.changeStatus("Query failed.");
+            window.qPane.clearQuery();
+          }
+        }
+      }
+    } else if (arg0.getActionCommand().equals("Clear Query")) {
+      String clearWarning = "Are you sure you want to clear the current Query?\n"
+          + "All unexported queries will be lost.";
+      int returnVal = JOptionPane.showConfirmDialog(window, clearWarning,
+          "Clear", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
+      if (returnVal == JOptionPane.OK_OPTION) {
+        window.mPane.tPane.setBlank();
+        window.bPane.changeStatus("Query cleared.");
+      }
+    }
+  }
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/model/CasDB.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/model/CasDB.java
new file mode 100644
index 0000000..dcd5346
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/model/CasDB.java
@@ -0,0 +1,215 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.model;
+
+import org.apache.oodt.cas.filemgr.structs.Element;
+import org.apache.oodt.cas.filemgr.structs.Product;
+import org.apache.oodt.cas.filemgr.structs.ProductType;
+import org.apache.oodt.cas.filemgr.structs.exceptions.RepositoryManagerException;
+import org.apache.oodt.cas.filemgr.structs.exceptions.ValidationLayerException;
+import org.apache.oodt.cas.filemgr.system.XmlRpcFileManagerClient;
+import org.apache.oodt.cas.metadata.Metadata;
+
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.apache.lucene.analysis.standard.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.Query;
+
+public class CasDB {
+
+  URL filemgrUrl;
+  XmlRpcFileManagerClient client;
+  public Results results;
+
+  private static String freeTextBlock = "__FREE__";
+
+  public CasDB() {
+    filemgrUrl = null;
+  }
+
+  public boolean connect(String cas) {
+    try {
+      filemgrUrl = new URL(cas);
+      client = new XmlRpcFileManagerClient(filemgrUrl);
+    } catch (Exception e) {
+      filemgrUrl = null;
+      return false;
+    }
+    return true;
+  }
+
+  public boolean disconnect() {
+    filemgrUrl = null;
+    return true;
+  }
+
+  public boolean isConnected() {
+    if (filemgrUrl == null) {
+      return false;
+    } else
+      return true;
+  }
+
+  public String[] getAvailableTypes() {
+    String[] types = null;
+    if (filemgrUrl != null) {
+      try {
+        Vector<ProductType> v = (Vector<ProductType>) client.getProductTypes();
+        Vector<String> names = new Vector<String>();
+        types = new String[v.size()];
+
+        for (int i = 0; i < v.size(); i++)
+          names.add(v.get(i).getName());
+
+        Collections.sort(names);
+        names.toArray(types);
+
+      } catch (RepositoryManagerException e) {
+        // e.printStackTrace();
+      }
+
+    } else {
+      types = new String[1];
+      types[0] = "";
+    }
+
+    return types;
+  }
+
+  public String[] getAvailableElements(String productTypeName) {
+    String[] elements = null;
+    if (filemgrUrl != null) {
+      try {
+        ProductType pt = client.getProductTypeByName(productTypeName);
+        Vector<Element> v = (Vector<Element>) client
+            .getElementsByProductType(pt);
+        Vector<String> names = new Vector<String>();
+        elements = new String[v.size()];
+
+        for (int i = 0; i < v.size(); i++)
+          names.add(v.get(i).getElementName());
+
+        Collections.sort(names);
+        names.toArray(elements);
+
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    } else {
+      elements = new String[1];
+      elements[0] = "";
+    }
+    return elements;
+  }
+
+  public String getElementID(String elementName) {
+    String elementID = new String();
+    Element e;
+    try {
+      e = client.getElementByName(elementName);
+      elementID = e.getElementId();
+    } catch (ValidationLayerException e1) {
+    }
+    return elementID;
+  }
+
+  public boolean issueQuery(org.apache.oodt.cas.filemgr.structs.Query query,
+      String productType) {
+    results = new Results();
+    try {
+      ProductType type = client.getProductTypeByName(productType);
+      Vector<Product> products = (Vector<Product>) client.query(query, type);
+      int maxVal = 20;
+      if (products.size() < maxVal)
+        maxVal = products.size();
+      for (int i = 0; i < maxVal; i++) {
+        Vector<Element> elements = (Vector<Element>) client
+            .getElementsByProductType(type);
+        Metadata m = client.getMetadata(products.get(i));
+        Hashtable hash = m.getHashtable();
+        results.addProduct(m);
+      }
+    } catch (Exception e) {
+      return false;
+    }
+
+    return true;
+  }
+
+  public boolean createQuery(String queryText, String productType) {
+
+    results = new Results();
+    if (queryText.trim().equals("*")) {
+      ProductType type;
+      try {
+        type = client.getProductTypeByName(productType);
+        Vector<Product> products = (Vector<Product>) client
+            .getProductsByProductType(type);
+        // for(int i=0;i<products.size();i++){
+        int maxVal = 20;
+        if (products.size() < maxVal)
+          maxVal = products.size();
+        for (int i = 0; i < maxVal; i++) {
+          Vector<Element> elements = (Vector<Element>) client
+              .getElementsByProductType(type);
+          Metadata m = client.getMetadata(products.get(i));
+          Hashtable hash = m.getHashtable();
+          results.addProduct(m);
+        }
+      } catch (Exception e) {
+        return false;
+      }
+
+    } else {
+
+      QueryBuilder qb = new QueryBuilder(this);
+      org.apache.oodt.cas.filemgr.structs.Query casQ = qb.ParseQuery(queryText);
+      ProductType type = null;
+      try {
+        type = client.getProductTypeByName(productType);
+        Vector<Product> products = (Vector<Product>) client.query(casQ, type);
+        int maxVal = 20;
+        if (products.size() < maxVal)
+          maxVal = products.size();
+        for (int i = 0; i < maxVal; i++) {
+          Vector<Element> elements = (Vector<Element>) client
+              .getElementsByProductType(type);
+          Metadata m = client.getMetadata(products.get(i));
+          Hashtable hash = m.getHashtable();
+          results.addProduct(m);
+        }
+      } catch (Exception e) {
+        return false;
+      }
+
+    }
+
+    return true;
+  }
+
+  public boolean clearQuery() {
+    results = null;
+    return true;
+  }
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/model/QueryBuilder.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/model/QueryBuilder.java
new file mode 100644
index 0000000..f22df71
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/model/QueryBuilder.java
@@ -0,0 +1,112 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.model;
+
+import org.apache.lucene.analysis.standard.ParseException;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.PhraseQuery;
+import org.apache.lucene.search.RangeQuery;
+import org.apache.lucene.search.TermQuery;
+
+import org.apache.oodt.cas.filemgr.structs.Query;
+import org.apache.oodt.cas.filemgr.structs.RangeQueryCriteria;
+import org.apache.oodt.cas.filemgr.structs.TermQueryCriteria;
+import org.apache.oodt.cas.filemgr.tools.CASAnalyzer;
+
+public class QueryBuilder {
+
+  private CasDB database;
+
+  public QueryBuilder(CasDB db) {
+    database = db;
+  }
+
+  public Query ParseQuery(String query) {
+    // note that "__FREE__" is a control work for free text searching
+    QueryParser parser = new QueryParser("__FREE__", new CASAnalyzer());
+
+    org.apache.lucene.search.Query luceneQ = null;
+    org.apache.oodt.cas.filemgr.structs.Query casQ = new org.apache.oodt.cas.filemgr.structs.Query();
+
+    try {
+      luceneQ = parser.parse(query);
+    } catch (org.apache.lucene.queryParser.ParseException e) {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }
+
+    System.out.println(luceneQ.toString());
+    GenerateCASQuery(casQ, luceneQ);
+
+    return casQ;
+  }
+
+  public void GenerateCASQuery(org.apache.oodt.cas.filemgr.structs.Query casQ,
+      org.apache.lucene.search.Query luceneQ) {
+    if (luceneQ instanceof TermQuery) {
+      Term t = ((TermQuery) luceneQ).getTerm();
+      if (t.field().equals("__FREE__")) {
+        // if(casQuery.getCriteria().isEmpty()) casQuery.addCriterion(new
+        // FreeTextQueryCriteria());
+        // ((FreeTextQueryCriteria)casQuery.getCriteria().get(0)).addValue(t.text());
+      } else {
+        String element = database.getElementID(t.field());
+        if (!element.equals("") && !t.text().equals("")) {
+
+          casQ.addCriterion(new TermQueryCriteria(element, t.text()));
+        }
+      }
+    } else if (luceneQ instanceof PhraseQuery) {
+      Term[] t = ((PhraseQuery) luceneQ).getTerms();
+      if (t[0].field().equals("__FREE__")) {
+        // if(casQuery.getCriteria().isEmpty()) casQuery.addCriterion(new
+        // FreeTextQueryCriteria());
+        // for(int i=0;i<t.length;i++)
+        // ((FreeTextQueryCriteria)casQuery.getCriteria().get(0)).addValue(t[i].text());
+      } else {
+        for (int i = 0; i < t.length; i++) {
+          String element = database.getElementID(t[i].field());
+          if (!element.equals("") && !t[i].text().equals("")) {
+            casQ.addCriterion(new TermQueryCriteria(element, t[i].text()));
+          }
+        }
+      }
+    } else if (luceneQ instanceof RangeQuery) {
+      Term startT = ((RangeQuery) luceneQ).getLowerTerm();
+      Term endT = ((RangeQuery) luceneQ).getUpperTerm();
+      String element = database.getElementID(startT.field());
+      if (!element.equals("") && !startT.text().equals("")
+          && !endT.text().equals("")) {
+        casQ.addCriterion(new RangeQueryCriteria(element, startT.text(), endT
+            .text()));
+      }
+    } else if (luceneQ instanceof BooleanQuery) {
+      BooleanClause[] clauses = ((BooleanQuery) luceneQ).getClauses();
+      for (int i = 0; i < clauses.length; i++) {
+        GenerateCASQuery(casQ, (clauses[i]).getQuery());
+      }
+    } else {
+      System.out.println("Error Parsing Query");
+      System.exit(-1);
+    }
+  }
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/model/Results.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/model/Results.java
new file mode 100644
index 0000000..1baf0be
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/model/Results.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.model;
+
+import org.apache.oodt.cas.metadata.Metadata;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+public class Results {
+
+  public Vector<Metadata> products;
+
+  public Results() {
+    products = new Vector<Metadata>();
+  }
+
+  public void addProduct(Metadata m) {
+    products.add(m);
+  }
+
+  public int getNumRecords() {
+    return products.size();
+  }
+
+  public String[][] getData() {
+
+    String[][] data = null;
+    if (products.size() > 0) {
+      data = new String[products.size() + 1][];
+
+      Hashtable hash = products.firstElement().getHashtable();
+      int numCols = hash.size();
+      data[0] = new String[numCols];
+      int i = 0;
+      for (Enumeration e = hash.keys(); e.hasMoreElements();) {
+        data[0][i] = e.nextElement().toString();
+        System.out.println(data[0][i]);
+        i++;
+      }
+
+      for (int j = 0; j < products.size(); j++) {
+        data[j + 1] = new String[i];
+        for (int k = 0; k < i; k++) {
+          data[j + 1][k] = products.get(j).getMetadata(data[0][k]);
+        }
+      }
+
+    }
+    return data;
+  }
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/system/XmlRpcFilemgrBrowser.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/system/XmlRpcFilemgrBrowser.java
new file mode 100644
index 0000000..0ac9caf
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/system/XmlRpcFilemgrBrowser.java
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.system;
+
+import org.apache.oodt.cas.filemgr.browser.view.MainWindow;
+
+public class XmlRpcFilemgrBrowser {
+
+  private static MainWindow window;
+
+  public static void main(String args[]) {
+    javax.swing.SwingUtilities.invokeLater(new Runnable() {
+      public void run() {
+        window = new MainWindow();
+        window.pack();
+        window.setVisible(true);
+      }
+    });
+  }
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/GuiParams.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/GuiParams.java
new file mode 100644
index 0000000..7f12da5
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/GuiParams.java
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view;
+
+public class GuiParams {
+
+  public static final int WINDOW_WIDTH = 600;
+  public static final int WINDOW_HEIGHT = 400;
+
+  public static final int DEFAULT_CELL_WIDTH = 70;
+  public static final int DEFAULT_CELL_HEIGHT = 20;
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/MainWindow.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/MainWindow.java
new file mode 100644
index 0000000..2034277
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/MainWindow.java
@@ -0,0 +1,84 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JScrollPane;
+
+import org.apache.oodt.cas.filemgr.browser.controller.TableListener;
+import org.apache.oodt.cas.filemgr.browser.controller.WindowListener;
+import org.apache.oodt.cas.filemgr.browser.model.CasDB;
+import org.apache.oodt.cas.filemgr.browser.view.panels.BottomPane;
+import org.apache.oodt.cas.filemgr.browser.view.panels.HeaderRow;
+import org.apache.oodt.cas.filemgr.browser.view.panels.MiddlePane;
+import org.apache.oodt.cas.filemgr.browser.view.panels.QueryPane;
+import org.apache.oodt.cas.filemgr.browser.view.prompts.ConnectPrompt;
+import org.apache.oodt.cas.filemgr.browser.view.prompts.SortPrompt;
+import org.apache.oodt.cas.filemgr.browser.view.menus.MenuBar;
+
+public class MainWindow extends JFrame {
+
+  public QueryPane qPane;
+  public MiddlePane mPane;
+  public BottomPane bPane;
+  public MenuBar bar;
+
+  private WindowListener wListener;
+
+  public MainWindow() {
+    wListener = new WindowListener(this);
+    // db = new CasDB();
+    setName("CAS File Manager Browser");
+    setDefaultCloseOperation(EXIT_ON_CLOSE);
+    qPane = new QueryPane(wListener);
+    mPane = new MiddlePane();
+    bPane = new BottomPane();
+    Dimension d = new Dimension(GuiParams.WINDOW_WIDTH, GuiParams.WINDOW_HEIGHT);
+    Container p = this.getContentPane();
+    p.setPreferredSize(d);
+    p.setMinimumSize(d);
+    p.setMaximumSize(d);
+    p.setBackground(Color.WHITE);
+    p.setLayout(new BorderLayout());
+    p.add(qPane, BorderLayout.NORTH);
+    p.add(mPane, BorderLayout.CENTER);
+    p.add(bPane, BorderLayout.SOUTH);
+
+    bar = new MenuBar(wListener, mPane.getListener());
+    this.setJMenuBar(bar);
+  }
+
+  public String[] getColHeaders() {
+    HeaderRow h = mPane.tPane.getHeader();
+    String[] heading = new String[h.getNumCols()];
+    for (int i = 0; i < h.getNumCols(); i++) {
+      heading[i] = h.getText(i);
+    }
+    return heading;
+  }
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/menus/MenuBar.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/menus/MenuBar.java
new file mode 100644
index 0000000..2351ec1
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/menus/MenuBar.java
@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.menus;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.KeyStroke;
+
+public class MenuBar extends JMenuBar {
+
+  private JMenu fileMenu;
+  private JMenu queryMenu;
+  private JMenu helpMenu;
+
+  private JMenuItem queryItem;
+  private JMenuItem aboutItem;
+  private JMenuItem clearItem;
+  private JMenuItem unhideItem;
+  private JMenuItem exportItem;
+  private JMenuItem exitItem;
+  private JMenuItem sortItem;
+  private JMenuItem advancedItem;
+  private JMenuItem connectItem;
+
+  public MenuBar(ActionListener windowListener, ActionListener tableListener) {
+    fileMenu = new JMenu("File");
+    fileMenu.setMnemonic(KeyEvent.VK_F);
+    queryMenu = new JMenu("Query");
+    queryMenu.setMnemonic(KeyEvent.VK_Q);
+    helpMenu = new JMenu("Help");
+
+    // build help menu
+    queryItem = new JMenuItem("Query Language");
+    queryItem.addActionListener(windowListener);
+    aboutItem = new JMenuItem("About");
+    aboutItem.addActionListener(windowListener);
+    helpMenu.add(queryItem);
+    helpMenu.addSeparator();
+    helpMenu.add(aboutItem);
+
+    // build query menu
+    clearItem = new JMenuItem("Clear Query");
+    clearItem.addActionListener(windowListener);
+    advancedItem = new JMenuItem("Query Builder");
+    advancedItem.addActionListener(windowListener);
+    sortItem = new JMenuItem("Sort");
+    sortItem.addActionListener(windowListener);
+    unhideItem = new JMenuItem("Unhide Columns");
+    unhideItem.addActionListener(tableListener);
+    queryMenu.add(unhideItem);
+    queryMenu.add(advancedItem);
+    queryMenu.add(sortItem);
+    queryMenu.addSeparator();
+    queryMenu.add(clearItem);
+
+    // build file menu
+    connectItem = new JMenuItem("Connect...");
+    connectItem.addActionListener(windowListener);
+    exportItem = new JMenuItem("Export Table");
+    exportItem.addActionListener(tableListener);
+    exitItem = new JMenuItem("Exit");
+    exitItem.addActionListener(windowListener);
+    fileMenu.add(connectItem);
+    fileMenu.add(exportItem);
+    fileMenu.addSeparator();
+    fileMenu.add(exitItem);
+
+    add(fileMenu);
+    add(queryMenu);
+    add(helpMenu);
+  }
+
+  public void changeConnectStatus() {
+    if (connectItem.getActionCommand().equals("Connect...")) {
+      connectItem.setActionCommand("Disconnect");
+      connectItem.setText("Disconnect");
+    } else {
+      connectItem.setActionCommand("Connect...");
+      connectItem.setText("Connect...");
+    }
+  }
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/menus/RightClickMenu.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/menus/RightClickMenu.java
new file mode 100644
index 0000000..854cfb3
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/menus/RightClickMenu.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.menus;
+
+import java.awt.event.ActionListener;
+
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+
+public class RightClickMenu extends JPopupMenu {
+
+  private JMenuItem hideItem;
+  private JMenuItem unhideItem;
+
+  // private JMenuItem resizeItem;
+
+  public RightClickMenu(ActionListener listener) {
+
+    hideItem = new JMenuItem("Hide");
+    hideItem.addActionListener(listener);
+    unhideItem = new JMenuItem("Unhide");
+    unhideItem.addActionListener(listener);
+    // resizeItem = new JMenuItem("Resize");
+    // resizeItem.addActionListener(listener);
+
+    this.add(hideItem);
+    this.add(unhideItem);
+    // this.addSeparator();
+    // this.add(resizeItem);
+  }
+
+  public void setUnhideMode() {
+    hideItem.setEnabled(false);
+    unhideItem.setEnabled(true);
+    // resizeItem.setEnabled(false);
+  }
+
+  public void setHideMode() {
+    hideItem.setEnabled(true);
+    unhideItem.setEnabled(false);
+    // resizeItem.setEnabled(true);
+  }
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/BottomPane.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/BottomPane.java
new file mode 100644
index 0000000..414fa02
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/BottomPane.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.panels;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+
+public class BottomPane extends JPanel {
+
+  public JLabel statusMessage;
+
+  public BottomPane() {
+
+    // set background and panel size
+    setBackground(Color.WHITE);
+    EmptyBorder line1 = new EmptyBorder(4, 10, 4, 2);
+    LineBorder line2 = new LineBorder(Color.BLACK, 1);
+    EmptyBorder line3 = new EmptyBorder(4, 10, 4, 10);
+    CompoundBorder cb1 = new CompoundBorder(line1, line2);
+    CompoundBorder cb2 = new CompoundBorder(cb1, line3);
+    setBorder(cb2);
+
+    JLabel statusLabel = new JLabel(" Status: ");
+    statusLabel.setFont(new Font("san-serif", Font.PLAIN, 10));
+
+    statusMessage = new JLabel(" Disconnected");
+    statusMessage.setFont(new Font("san-serif", Font.PLAIN, 10));
+    statusMessage.setForeground(Color.RED);
+    statusMessage.setBackground(Color.WHITE);
+
+    // set layout
+    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
+    add(statusLabel);
+    add(statusMessage);
+  }
+
+  public void changeStatus(String status) {
+    statusMessage.setText(status);
+    this.repaint();
+  }
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/Cell.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/Cell.java
new file mode 100644
index 0000000..e5a29ea
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/Cell.java
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.panels;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.LineBorder;
+
+import org.apache.oodt.cas.filemgr.browser.view.GuiParams;
+
+public class Cell extends JPanel {
+
+  protected JLabel text;
+
+  public Cell() {
+    setLayout(new BorderLayout());
+    setBackground(Color.WHITE);
+    setForeground(Color.BLACK);
+    Dimension size = new Dimension(GuiParams.DEFAULT_CELL_WIDTH,
+        GuiParams.DEFAULT_CELL_HEIGHT);
+    setMinimumSize(size);
+    setMaximumSize(size);
+    setPreferredSize(size);
+    setBorder(new LineBorder(Color.LIGHT_GRAY, 1));
+
+    text = new JLabel("");
+    Font f = new Font("san-sarif", Font.PLAIN, 10);
+    text.setFont(f);
+    this.add(text, BorderLayout.CENTER);
+  }
+
+  public void setText(String newText) {
+    text.setText(newText);
+  }
+
+  public String getText() {
+    return text.getText();
+  }
+
+  public void setWidth(int newWidth) {
+    Dimension size = this.getSize();
+    size.width = newWidth;
+    this.setSize(size);
+    text.setSize(size);
+  }
+
+  public void setHeight(int newHeight) {
+    Dimension size = this.getSize();
+    size.height = newHeight;
+    this.setSize(size);
+    text.setSize(size);
+  }
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/HeaderCell.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/HeaderCell.java
new file mode 100644
index 0000000..6ac5464
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/HeaderCell.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.panels;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.event.MouseListener;
+
+import javax.swing.border.LineBorder;
+
+import org.apache.oodt.cas.filemgr.browser.view.GuiParams;
+
+public class HeaderCell extends Cell {
+
+  private int colNum;
+
+  public HeaderCell(MouseListener listener, int colNum) {
+    super();
+    this.setBackground(Color.LIGHT_GRAY);
+    this.setBorder(null);
+    Dimension size = new Dimension(GuiParams.DEFAULT_CELL_WIDTH - 2,
+        GuiParams.DEFAULT_CELL_HEIGHT);
+    setMinimumSize(size);
+    setMaximumSize(size);
+    setPreferredSize(size);
+    Font f = new Font("san-serif", Font.BOLD, 11);
+    text.setFont(f);
+
+    this.colNum = colNum;
+    this.addMouseListener(listener);
+  }
+
+  public int getColNum() {
+    return colNum;
+  }
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/HeaderRow.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/HeaderRow.java
new file mode 100644
index 0000000..6980edf
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/HeaderRow.java
@@ -0,0 +1,99 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.panels;
+
+import java.awt.Color;
+import java.awt.event.MouseListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JPanel;
+
+public class HeaderRow extends JPanel {
+
+  private HeaderCell cells[];
+  private int numCells;
+
+  public HeaderRow(MouseListener listener, int numCells) {
+    // create cells
+    this.numCells = numCells;
+    cells = new HeaderCell[numCells];
+    for (int i = 0; i < numCells; i++) {
+      cells[i] = new HeaderCell(listener, i);
+    }
+
+    // set background, etc.
+    this.setBackground(Color.WHITE);
+    this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
+
+    for (int i = 0; i < numCells; i++) {
+      this.add(cells[i]);
+      this.add(new HeaderSpacer(listener, i));
+    }
+  }
+
+  public int getNumCols() {
+    return numCells;
+  }
+
+  public void hideCol(int colNum) {
+    int trueNum = colNum * 2;
+    HeaderCell c = (HeaderCell) this.getComponent(trueNum);
+    c.setVisible(false);
+    HeaderSpacer sp = (HeaderSpacer) this.getComponent(trueNum + 1);
+    sp.setVisible(false);
+  }
+
+  public void unhideCol(int colNum) {
+    int trueNum = colNum * 2;
+    HeaderCell c = (HeaderCell) this.getComponent(trueNum);
+    c.setVisible(true);
+    HeaderSpacer sp = (HeaderSpacer) this.getComponent(trueNum + 1);
+    sp.setVisible(true);
+  }
+
+  public void setText(int colNum, String text) {
+    int trueNum = colNum * 2;
+    HeaderCell c = (HeaderCell) this.getComponent(trueNum);
+    c.setText(text);
+    c.repaint();
+  }
+
+  public String getText(int colNum) {
+    int trueNum = colNum * 2;
+    HeaderCell c = (HeaderCell) this.getComponent(trueNum);
+    return c.getText();
+  }
+
+  public int getWidth(int colNum) {
+    int trueNum = colNum * 2;
+    HeaderCell c = (HeaderCell) this.getComponent(trueNum);
+    return c.getWidth();
+  }
+
+  public void changeWidth(int colNum, int newWidth) {
+    int trueNum = colNum * 2;
+    HeaderCell c = (HeaderCell) this.getComponent(trueNum);
+    int change = c.getWidth() - newWidth;
+    c.setWidth(newWidth);
+    for (int i = trueNum + 1; i < this.getComponentCount(); i++) {
+      this.getComponent(i).setLocation(this.getComponent(i).getX() - change,
+          this.getComponent(i).getY());
+    }
+    this.repaint();
+  }
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/HeaderSpacer.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/HeaderSpacer.java
new file mode 100644
index 0000000..e8f8f19
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/HeaderSpacer.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.panels;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.MouseListener;
+
+import javax.swing.JPanel;
+
+import org.apache.oodt.cas.filemgr.browser.view.GuiParams;
+
+public class HeaderSpacer extends JPanel {
+
+  private int col;
+
+  public HeaderSpacer(MouseListener listener, int pos) {
+    // set background, size
+    this.setBackground(Color.WHITE);
+    Dimension d = new Dimension(2, GuiParams.DEFAULT_CELL_HEIGHT);
+    this.setMinimumSize(d);
+    this.setMaximumSize(d);
+    this.setPreferredSize(d);
+
+    col = pos;
+    this.addMouseListener(listener);
+  }
+
+  public int getColNum() {
+    return col;
+  }
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/MiddlePane.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/MiddlePane.java
new file mode 100644
index 0000000..35f4ed2
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/MiddlePane.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.panels;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import org.apache.oodt.cas.filemgr.browser.controller.TableListener;
+
+public class MiddlePane extends JPanel {
+
+  public TablePane tPane;
+
+  public MiddlePane() {
+    this.setBackground(Color.WHITE);
+    this.setLayout(new BorderLayout());
+
+    tPane = new TablePane();
+
+    JPanel inset = new JPanel();
+    inset.setBackground(Color.WHITE);
+    inset.setLayout(new BorderLayout());
+    inset.add(tPane, BorderLayout.WEST);
+
+    JScrollPane scrollPane = new JScrollPane(inset);
+
+    this.add(scrollPane, BorderLayout.CENTER);
+  }
+
+  public TableListener getListener() {
+    return tPane.getListener();
+  }
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/QueryField.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/QueryField.java
new file mode 100644
index 0000000..7f1b4ac
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/QueryField.java
@@ -0,0 +1,109 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.panels;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+public class QueryField extends JPanel implements ActionListener {
+
+  private JTextField text;
+  private JButton button;
+  private JComboBox types;
+
+  public QueryField(ActionListener listener) {
+
+    // setbackground an size for panel
+    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
+    setBackground(Color.WHITE);
+    setMinimumSize(new Dimension(500, 50));
+    setPreferredSize(new Dimension(500, 50));
+
+    text = new JTextField(30);
+    button = new JButton("Search");
+    String[] blankType = { "" };
+    types = new JComboBox(blankType);
+
+    // set background and size for textbox and combo
+    text.setForeground(Color.BLACK);
+    text.setBackground(Color.WHITE);
+    text.setMinimumSize(new Dimension(100, 25));
+    text.setPreferredSize(new Dimension(100, 25));
+    text.setMaximumSize(new Dimension(100, 25));
+    text.addActionListener(this);
+    Dimension typeDim = new Dimension(100, 25);
+    types.setMaximumSize(typeDim);
+    types.setMinimumSize(typeDim);
+    types.setPreferredSize(typeDim);
+    types.setBackground(Color.WHITE);
+
+    // set button
+    button.setBackground(Color.WHITE);
+    button.addActionListener(listener);
+    button.setName("Query");
+
+    add(new JLabel("ProductType:  "));
+    add(types);
+    add(new JLabel("    Query:  "));
+    add(text);
+    add(button);
+  }
+
+  public String getQueryString() {
+    return text.getText();
+  }
+
+  public void clearQuery() {
+    text.setText("");
+  }
+
+  public String getProductType() {
+    return types.getSelectedItem().toString();
+  }
+
+  public void updateTypes(String[] typeNames) {
+    this.remove(types);
+    types = new JComboBox(typeNames);
+    types.setBackground(Color.WHITE);
+    Dimension typeDim = new Dimension(100, 25);
+    types.setMaximumSize(typeDim);
+    types.setMinimumSize(typeDim);
+    types.setPreferredSize(typeDim);
+    add(types, 1);
+    this.repaint();
+  }
+
+  public void actionPerformed(ActionEvent arg0) {
+    button.doClick();
+  }
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/QueryPane.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/QueryPane.java
new file mode 100644
index 0000000..7211ac7
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/QueryPane.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.panels;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.ActionListener;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+
+import org.apache.oodt.cas.filemgr.browser.view.GuiParams;
+
+public class QueryPane extends JPanel {
+
+  private QueryField field;
+
+  public QueryPane(ActionListener listener) {
+    super(new BorderLayout());
+
+    // set background and panel size
+    setBackground(Color.WHITE);
+    Dimension paneSize = new Dimension();
+    paneSize.width = GuiParams.WINDOW_WIDTH;
+    paneSize.height = (int) (GuiParams.WINDOW_HEIGHT * (0.1));
+
+    // set border
+    EmptyBorder line1 = new EmptyBorder(2, 10, 2, 2);
+    LineBorder line2 = new LineBorder(Color.BLACK, 1);
+    CompoundBorder cp = new CompoundBorder(line1, line2);
+    this.setBorder(cp);
+
+    // add query field to pane
+    field = new QueryField(listener);
+    add(field, BorderLayout.EAST);
+  }
+
+  public String getQuery() {
+    return field.getQueryString();
+  }
+
+  public void clearQuery() {
+    field.clearQuery();
+  }
+
+  public String getType() {
+    return field.getProductType();
+  }
+
+  public void updateTypes(String[] types) {
+    field.updateTypes(types);
+  }
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/Row.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/Row.java
new file mode 100644
index 0000000..d127180
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/Row.java
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.panels;
+
+import java.awt.Color;
+
+import javax.swing.BoxLayout;
+import javax.swing.JPanel;
+
+public class Row extends JPanel {
+
+  private Cell cells[];
+
+  public Row(int numCells) {
+    // create cells
+    cells = new Cell[numCells];
+    for (int i = 0; i < numCells; i++) {
+      cells[i] = new Cell();
+      cells[i].setText("");
+    }
+
+    // set background, etc.
+    this.setBackground(Color.WHITE);
+    this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
+
+    for (int i = 0; i < numCells; i++) {
+      this.add(cells[i]);
+    }
+  }
+
+  public void hideCol(int colNum) {
+    Cell c = (Cell) this.getComponent(colNum);
+    c.setVisible(false);
+  }
+
+  public void unhideCol(int colNum) {
+    Cell c = (Cell) this.getComponent(colNum);
+    c.setVisible(true);
+  }
+
+  public void setText(int colNum, String text) {
+    Cell c = (Cell) this.getComponent(colNum);
+    c.setText(text);
+    c.repaint();
+  }
+
+  public String getText(int colNum) {
+    Cell c = (Cell) this.getComponent(colNum);
+    return c.getText();
+  }
+
+  public int getWidth(int colNum) {
+    Cell c = (Cell) this.getComponent(colNum);
+    return c.getWidth();
+  }
+
+  public void changeWidth(int colNum, int newWidth) {
+    Cell c = (Cell) this.getComponent(colNum);
+    int change = c.getWidth() - newWidth;
+    c.setWidth(newWidth);
+    for (int i = colNum + 1; i < this.getComponentCount(); i++) {
+      this.getComponent(i).setLocation(this.getComponent(i).getX() - change,
+          this.getComponent(i).getY());
+    }
+    this.repaint();
+  }
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/TablePane.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/TablePane.java
new file mode 100644
index 0000000..fcc8f5c
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/panels/TablePane.java
@@ -0,0 +1,169 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.cas.filemgr.browser.view.panels;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.util.Vector;
+
+import javax.swing.BoxLayout;
+import javax.swing.JFileChooser;
+import javax.swing.JPanel;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.LineBorder;
+import javax.swing.filechooser.FileFilter;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+import org.apache.oodt.cas.filemgr.browser.controller.TableListener;
+import org.apache.oodt.cas.filemgr.browser.view.menus.RightClickMenu;
+
+public class TablePane extends JPanel{ 
+	
+	public Vector<Integer> hiddenCols;
+	public HeaderRow header;
+	
+	private TableListener listener;
+	
+	public TablePane(){
+		listener = new TableListener(this);
+		
+		hiddenCols = new Vector<Integer>();
+		setBackground(Color.WHITE);
+		LineBorder line1 = new LineBorder(Color.WHITE,10);
+		setBorder(line1);
+		
+		setLayout(new BoxLayout(this,BoxLayout.PAGE_AXIS));
+		
+		this.setBlank();
+	}
+	
+	public TableListener getListener(){
+		return listener;
+	}
+	
+	public void setBlank(){
+		this.removeAll();
+		int numColumns = 10;
+		int numRows = 20;
+		header = new HeaderRow(listener,numColumns);
+		add(header);
+		
+		for(int i=0;i<numRows;i++){
+			Row r = new Row(numColumns);
+			add(r);
+		}
+		
+		this.validate();
+		this.repaint();
+	}
+	
+	public void newTable(String[][] data){
+		this.removeAll();
+		if(data.length>1){
+			header = new HeaderRow(listener,data[0].length);
+			for(int j=0;j<data[0].length;j++){
+				header.setText(j, data[0][j]);
+			}
+			this.add(header);
+			
+			for(int j=1;j<data.length;j++){
+				Row r = new Row(data[0].length);
+				for(int k=0;k<data[0].length;k++){
+					r.setText(k, data[j][k]);
+				}
+				this.add(r);
+			}
+		}
+		this.validate();
+		this.repaint();
+	}
+	
+	public HeaderRow getHeader(){
+		return header;
+	}
+
+	public void sortRows(int col, String sortType){
+		int length = this.getComponentCount();
+		int i,j;
+
+	    for (i=length; --i >=1;) {
+	       for (j=1; j<i;j++) {
+	    	   Row jThRow = (Row)this.getComponent(j);
+	    	   Row iThRow = (Row)this.getComponent(i);
+	    	   if(sortType.equals("Accending")){
+	    		   if(jThRow.getText(col).compareTo(iThRow.getText(col))>0){
+	    			   swapRows(j,j+1);
+	    		   }
+	    	   } else {
+	    		   if(jThRow.getText(col).compareTo(iThRow.getText(col))<0){
+	    			   swapRows(j,j+1);
+	    		   }
+	    	   }
+	       }
+	    }
+	    this.validate();
+	    this.repaint();
+	    
+	}
+	
+	public void swapRows(int index1, int index2){
+		Component c1 = this.getComponent(index1);
+		Component c2 = this.getComponent(index2);
+		this.add(c1, index2);
+		this.add(c2, index1);
+	}
+	
+	public Row getRow(int num){
+		Row r = null;
+		if(num<this.getComponentCount()-1){
+			r = (Row)this.getComponent(num+1);
+		}
+		return r;
+	}
+	
+	public void hideColumn(int colNum){
+		header.hideCol(colNum);
+		for(int i=1;i<this.getComponentCount();i++){
+			((Row)this.getComponent(i)).hideCol(colNum);
+		}
+		hiddenCols.add(new Integer(colNum));
+	}
+	
+	public void unhideColumn(int colNum){
+		header.unhideCol(colNum);
+		for(int i=1;i<this.getComponentCount();i++){
+			((Row)this.getComponent(i)).unhideCol(colNum);
+		}
+		hiddenCols.remove(new Integer(colNum));
+		
+	}
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/prompts/ConnectPrompt.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/prompts/ConnectPrompt.java
new file mode 100644
index 0000000..cd08d4e
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/prompts/ConnectPrompt.java
@@ -0,0 +1,115 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.prompts;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+
+public class ConnectPrompt extends JFrame implements ActionListener {
+
+  private CASField topPanel;
+  private ConnectButton bottomPanel;
+
+  public ConnectPrompt(ActionListener listener) {
+    this.setName("New Connection");
+    this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+    Dimension d = new Dimension(400, 100);
+    this.setMinimumSize(d);
+    this.setMaximumSize(d);
+    this.setPreferredSize(d);
+
+    this.getContentPane().setLayout(
+        new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS));
+    this.getContentPane().setBackground(Color.WHITE);
+
+    topPanel = new CASField(this);
+    bottomPanel = new ConnectButton(listener);
+    this.add(topPanel);
+    this.add(bottomPanel);
+  }
+
+  public String getCASUrl() {
+    return topPanel.casRef.getText();
+  }
+
+  private class CASField extends JPanel {
+
+    protected JTextField casRef;
+
+    protected CASField(ActionListener listener) {
+      this.setBackground(Color.WHITE);
+      this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+      Dimension d = new Dimension(400, 30);
+      this.setMaximumSize(d);
+      this.setMinimumSize(d);
+      this.setPreferredSize(d);
+
+      EmptyBorder line1 = new EmptyBorder(5, 10, 5, 5);
+      this.setBorder(line1);
+
+      this.add(new JLabel("CAS File Manager URL:  "));
+      casRef = new JTextField();
+      casRef.addActionListener(listener);
+      this.add(casRef);
+    }
+  }
+
+  private class ConnectButton extends JPanel {
+
+    protected JButton connect;
+    protected JButton cancel;
+
+    protected ConnectButton(ActionListener listener) {
+      this.setBackground(Color.WHITE);
+      this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+
+      EmptyBorder line1 = new EmptyBorder(5, 20, 5, 5);
+      this.setBorder(line1);
+
+      this.add(new JLabel("                          "));
+      connect = new JButton("Connect");
+      connect.setBackground(Color.WHITE);
+      connect.addActionListener(listener);
+      cancel = new JButton("Cancel");
+      cancel.setBackground(Color.WHITE);
+      cancel.addActionListener(listener);
+      cancel.setName("ConnectCancel");
+      this.add(cancel);
+      this.add(connect);
+    }
+  }
+
+  public void actionPerformed(ActionEvent arg0) {
+    bottomPanel.connect.doClick();
+  }
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/prompts/QueryBuilderPrompt.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/prompts/QueryBuilderPrompt.java
new file mode 100644
index 0000000..5204db5
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/prompts/QueryBuilderPrompt.java
@@ -0,0 +1,453 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.prompts;
+
+import org.apache.oodt.cas.filemgr.structs.RangeQueryCriteria;
+import org.apache.oodt.cas.filemgr.structs.TermQueryCriteria;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+
+import org.apache.oodt.cas.filemgr.browser.controller.WindowListener;
+import org.apache.oodt.cas.filemgr.browser.model.CasDB;
+
+public class QueryBuilderPrompt extends JFrame {
+
+  protected CasDB database;
+  protected WindowListener listener;
+
+  protected TypePanel tPanel;
+  protected QuerySelectionPanel qPanel;
+  protected JScrollPane scrollPane;
+  protected BuiltQueryPane builtPanel;
+  protected SearchPanel sPanel;
+
+  public QueryBuilderPrompt(CasDB db, WindowListener l) {
+
+    database = db;
+    listener = l;
+
+    this.setName("Query Builder");
+    this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+    Dimension d = new Dimension(500, 400);
+    this.setMinimumSize(d);
+    this.setMaximumSize(d);
+    this.setPreferredSize(d);
+
+    this.getContentPane().setLayout(
+        new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS));
+    this.getContentPane().setBackground(Color.WHITE);
+
+    tPanel = new TypePanel(this);
+    qPanel = new QuerySelectionPanel(this);
+    sPanel = new SearchPanel(this);
+    builtPanel = new BuiltQueryPane(this);
+
+    scrollPane = new JScrollPane(qPanel);
+    Dimension scrollDim = new Dimension(500, 220);
+    scrollPane.setMaximumSize(scrollDim);
+    scrollPane.setMinimumSize(scrollDim);
+    scrollPane.setPreferredSize(scrollDim);
+
+    this.getContentPane().add(tPanel);
+    this.getContentPane().add(scrollPane);
+    this.getContentPane().add(builtPanel);
+    this.getContentPane().add(sPanel);
+  }
+
+  public org.apache.oodt.cas.filemgr.structs.Query getQuery() {
+    return qPanel.getCasQuery();
+  }
+
+  public String getQueryString() {
+    return qPanel.getQuery();
+  }
+
+  public String getProductType() {
+    return tPanel.getType();
+  }
+
+  private class QueryPanel extends JPanel {
+
+    private JComboBox elements;
+    private JComboBox ops;
+    private JPanel placeholder;
+    private boolean showOp;
+
+    public QueryPanel(QueryBuilderPrompt prompt) {
+      Dimension d = new Dimension(460, 35);
+      this.setMaximumSize(d);
+      this.setMinimumSize(d);
+      this.setPreferredSize(d);
+      this.setBackground(Color.WHITE);
+      this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+
+      showOp = false;
+      ops = new JComboBox(new String[] { "AND", "OR", "NOT" });
+      Dimension opsDim = new Dimension(75, 25);
+      ops.setMaximumSize(opsDim);
+      ops.setMinimumSize(opsDim);
+      ops.setPreferredSize(opsDim);
+      ops.setBackground(Color.WHITE);
+      ops.setVisible(false);
+
+      placeholder = new JPanel();
+      placeholder.setBackground(Color.WHITE);
+      placeholder.setMaximumSize(opsDim);
+      placeholder.setMinimumSize(opsDim);
+      placeholder.setPreferredSize(opsDim);
+
+      elements = new JComboBox(prompt.database
+          .getAvailableElements(prompt.tPanel.getType()));
+      elements.setBackground(Color.WHITE);
+      Dimension dElem = new Dimension(150, 25);
+      elements.setMaximumSize(dElem);
+      elements.setMinimumSize(dElem);
+      elements.setPreferredSize(dElem);
+
+      this.add(ops);
+      this.add(placeholder);
+      this.add(elements);
+    }
+
+    public void addOp() {
+      showOp = true;
+      ops.setVisible(true);
+      placeholder.setVisible(false);
+    }
+
+    public String getElement() {
+      return elements.getSelectedItem().toString();
+    }
+
+    public String getOp() {
+      String op = "";
+      if (showOp) {
+        op = ops.getSelectedItem().toString();
+      }
+      return op;
+    }
+  }
+
+  private class TermQueryPanel extends QueryPanel {
+
+    private JTextField text;
+
+    public TermQueryPanel(QueryBuilderPrompt prompt) {
+      super(prompt);
+
+      text = new JTextField();
+      Dimension dText = new Dimension(150, 25);
+      text.setPreferredSize(dText);
+      text.setMaximumSize(dText);
+      text.setMinimumSize(dText);
+      text.setBackground(Color.WHITE);
+      text.addFocusListener(prompt.builtPanel);
+      text.addActionListener(prompt.builtPanel);
+
+      this.add(new JLabel("  Matches  "));
+      this.add(text);
+    }
+
+    public String getText() {
+      return text.getText();
+    }
+
+  }
+
+  private class RangeQueryPanel extends QueryPanel {
+
+    private JTextField start;
+    private JTextField stop;
+
+    public RangeQueryPanel(QueryBuilderPrompt prompt) {
+      super(prompt);
+
+      start = new JTextField();
+      stop = new JTextField();
+
+      Dimension dText = new Dimension(60, 25);
+      start.setMaximumSize(dText);
+      start.setMinimumSize(dText);
+      start.setPreferredSize(dText);
+      start.addFocusListener(prompt.builtPanel);
+      start.addActionListener(prompt.builtPanel);
+
+      stop.setMaximumSize(dText);
+      stop.setMinimumSize(dText);
+      stop.setPreferredSize(dText);
+      stop.addFocusListener(prompt.builtPanel);
+      stop.addActionListener(prompt.builtPanel);
+
+      this.add(new JLabel("  Between  "));
+      this.add(start);
+      this.add(new JLabel(" And "));
+      this.add(stop);
+    }
+
+    public String getStart() {
+      return start.getText();
+    }
+
+    public String getStop() {
+      return stop.getText();
+    }
+
+  }
+
+  private class QuerySelectionPanel extends JPanel implements ActionListener {
+
+    private JButton addTerm;
+    private JButton addRange;
+    private QueryBuilderPrompt prompt;
+
+    public QuerySelectionPanel(QueryBuilderPrompt prompt) {
+      this.prompt = prompt;
+      this.setBackground(Color.WHITE);
+
+      EmptyBorder line1 = new EmptyBorder(2, 2, 2, 2);
+      LineBorder line2 = new LineBorder(Color.BLACK, 1);
+      CompoundBorder cp = new CompoundBorder(line1, line2);
+      this.setBorder(cp);
+      this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+      JPanel buttonPanel = new JPanel();
+      Dimension buttonDim = new Dimension(460, 30);
+      buttonPanel.setMaximumSize(buttonDim);
+      buttonPanel.setMinimumSize(buttonDim);
+      buttonPanel.setPreferredSize(buttonDim);
+      buttonPanel.setBackground(Color.WHITE);
+      buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
+
+      addTerm = new JButton("Add Term Criteria");
+      addTerm.setBackground(Color.WHITE);
+      addTerm.addActionListener(this);
+
+      addRange = new JButton("Add Range Criteria");
+      addRange.setBackground(Color.WHITE);
+      addRange.addActionListener(this);
+
+      buttonPanel.add(addTerm);
+      buttonPanel.add(addRange);
+      this.add(buttonPanel);
+    }
+
+    public void actionPerformed(ActionEvent arg0) {
+      if (arg0.getActionCommand().equals("Add Term Criteria")) {
+        TermQueryPanel tq = new TermQueryPanel(prompt);
+        if (this.getComponentCount() > 1)
+          tq.addOp();
+        int insertOrder = this.getComponentCount() - 1;
+        if (insertOrder < 0)
+          insertOrder = 0;
+        this.add(tq, insertOrder);
+        this.validate();
+        prompt.scrollPane.validate();
+      } else if (arg0.getActionCommand().equals("Add Range Criteria")) {
+        RangeQueryPanel rq = new RangeQueryPanel(prompt);
+        if (this.getComponentCount() > 1)
+          rq.addOp();
+        int insertOrder = this.getComponentCount() - 1;
+        if (insertOrder < 0)
+          insertOrder = 0;
+        this.add(rq, insertOrder);
+        this.validate();
+        prompt.scrollPane.validate();
+      }
+    }
+
+    public String getQuery() {
+      String q = new String();
+      for (int i = 0; i < this.getComponentCount(); i++) {
+        Component c = this.getComponent(i);
+        if (c instanceof TermQueryPanel) {
+          q += ((TermQueryPanel) c).getOp() + " ";
+          q += ((TermQueryPanel) c).getElement() + ":";
+          q += ((TermQueryPanel) c).getText() + " ";
+        } else if (c instanceof RangeQueryPanel) {
+          q += ((RangeQueryPanel) c).getOp() + " ";
+          q += ((RangeQueryPanel) c).getElement() + ":[";
+          q += ((RangeQueryPanel) c).getStart() + " TO ";
+          q += ((RangeQueryPanel) c).getStop() + "] ";
+        }
+      }
+
+      return q;
+    }
+
+    public org.apache.oodt.cas.filemgr.structs.Query getCasQuery() {
+      org.apache.oodt.cas.filemgr.structs.Query q = new org.apache.oodt.cas.filemgr.structs.Query();
+      for (int i = 0; i < this.getComponentCount(); i++) {
+        Component c = this.getComponent(i);
+        String element;
+        if (c instanceof TermQueryPanel) {
+          element = database.getElementID(((TermQueryPanel) c).getElement());
+          String criteria = ((TermQueryPanel) c).getText();
+          if (!element.equals("") && !criteria.equals("")) {
+            TermQueryCriteria tc = new TermQueryCriteria();
+            tc.setElementName(element);
+            tc.setValue(criteria);
+            q.addCriterion(tc);
+          }
+        } else if (c instanceof RangeQueryPanel) {
+          element = database.getElementID(((RangeQueryPanel) c).getElement());
+          String startCriteria = ((RangeQueryPanel) c).getStart();
+          String stopCriteria = ((RangeQueryPanel) c).getStop();
+          if (!element.equals("") && !startCriteria.equals("")
+              && !stopCriteria.equals("")) {
+            RangeQueryCriteria rt = new RangeQueryCriteria();
+            rt.setElementName(element);
+            rt.setStartValue(startCriteria);
+            rt.setEndValue(stopCriteria);
+            q.addCriterion(rt);
+          }
+        }
+      }
+
+      return q;
+    }
+
+  }
+
+  private class SearchPanel extends JPanel {
+    private JButton search;
+
+    public SearchPanel(QueryBuilderPrompt prompt) {
+      Dimension d = new Dimension(500, 40);
+      this.setMaximumSize(d);
+      this.setMinimumSize(d);
+      this.setPreferredSize(d);
+      EmptyBorder line1 = new EmptyBorder(2, 2, 2, 2);
+      LineBorder line2 = new LineBorder(Color.BLACK, 1);
+      CompoundBorder cp = new CompoundBorder(line1, line2);
+      this.setBorder(cp);
+      this.setLayout(new BorderLayout());
+      this.setBackground(Color.WHITE);
+
+      search = new JButton("Search");
+      search.setName("AdvancedQuery");
+      search.addActionListener(prompt.listener);
+      search.setBackground(Color.WHITE);
+
+      JPanel buttonPanel = new JPanel();
+      buttonPanel.setBackground(Color.WHITE);
+      buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
+      buttonPanel.add(search);
+      this.add(buttonPanel, BorderLayout.EAST);
+    }
+
+  }
+
+  private class TypePanel extends JPanel {
+
+    private JComboBox types;
+
+    public TypePanel(QueryBuilderPrompt prompt) {
+
+      Dimension d = new Dimension(500, 40);
+      this.setMaximumSize(d);
+      this.setMinimumSize(d);
+      this.setPreferredSize(d);
+      EmptyBorder line1 = new EmptyBorder(2, 2, 2, 2);
+      LineBorder line2 = new LineBorder(Color.BLACK, 1);
+      CompoundBorder cp = new CompoundBorder(line1, line2);
+      this.setBorder(cp);
+
+      types = new JComboBox(database.getAvailableTypes());
+      types.setBackground(Color.WHITE);
+      Dimension tDim = new Dimension(200, 30);
+      types.setMaximumSize(tDim);
+      types.setMinimumSize(tDim);
+      types.setPreferredSize(tDim);
+
+      this.setBackground(Color.WHITE);
+      this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+
+      JLabel label = new JLabel("        Product Type:   ");
+
+      this.add(label);
+      this.add(types);
+    }
+
+    public String getType() {
+      return types.getSelectedItem().toString();
+    }
+  }
+
+  private class BuiltQueryPane extends JPanel implements FocusListener,
+      ActionListener {
+
+    private QueryBuilderPrompt p;
+    private JTextArea field;
+
+    public BuiltQueryPane(QueryBuilderPrompt prompt) {
+
+      Dimension d = new Dimension(500, 75);
+      this.setMaximumSize(d);
+      this.setMinimumSize(d);
+      this.setPreferredSize(d);
+
+      EmptyBorder line1 = new EmptyBorder(2, 2, 2, 2);
+      LineBorder line2 = new LineBorder(Color.BLACK, 1);
+      CompoundBorder cp = new CompoundBorder(line1, line2);
+      this.setBorder(cp);
+      this.setLayout(new BorderLayout());
+
+      field = new JTextArea();
+      Font font = new Font("san-serif", Font.PLAIN, 10);
+      field.setFont(font);
+      field.setBackground(Color.WHITE);
+      field.setLineWrap(true);
+      field.setWrapStyleWord(true);
+
+      this.add(field, BorderLayout.CENTER);
+    }
+
+    public void focusGained(FocusEvent arg0) {
+    }
+
+    public void focusLost(FocusEvent arg0) {
+      field.setText(qPanel.getQuery());
+    }
+
+    public void actionPerformed(ActionEvent arg0) {
+      field.setText(qPanel.getQuery());
+    }
+  }
+
+}
diff --git a/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/prompts/SortPrompt.java b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/prompts/SortPrompt.java
new file mode 100644
index 0000000..5da550b
--- /dev/null
+++ b/0.10/app/fmbrowser/src/main/java/org/apache/oodt/cas/filemgr/browser/view/prompts/SortPrompt.java
@@ -0,0 +1,153 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.filemgr.browser.view.prompts;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+
+import org.apache.oodt.cas.filemgr.browser.view.MainWindow;
+
+public class SortPrompt extends JFrame {
+
+  private ColumnPanel cPanel;
+  private ConnectButton cButtons;
+
+  public SortPrompt(MainWindow window, ActionListener listener) {
+    this.setName("Sort");
+    this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+    Dimension d = new Dimension(350, 150);
+    this.setMinimumSize(d);
+    this.setMaximumSize(d);
+    this.setPreferredSize(d);
+
+    this.getContentPane().setLayout(
+        new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS));
+    this.getContentPane().setBackground(Color.WHITE);
+
+    cPanel = new ColumnPanel(window);
+    cButtons = new ConnectButton(listener);
+    this.add(cPanel);
+    this.add(cButtons);
+  }
+
+  public int getSortIndex() {
+    return cPanel.getSortIndex();
+  }
+
+  public String getSortType() {
+    return cPanel.getSortType();
+  }
+
+  protected class ColumnPanel extends JPanel {
+
+    protected JComboBox ColChoices;
+    protected JComboBox SortChoices;
+    protected JButton cancel;
+    protected JButton ok;
+
+    public ColumnPanel(MainWindow window) {
+      ColChoices = new JComboBox(window.getColHeaders());
+      ColChoices.setSelectedIndex(0);
+      ColChoices.setBackground(Color.WHITE);
+      String[] sortPatterns = { "Accending", "Descending" };
+      SortChoices = new JComboBox(sortPatterns);
+      SortChoices.setSelectedIndex(0);
+      SortChoices.setBackground(Color.WHITE);
+
+      Dimension choicesDim = new Dimension(200, 20);
+      ColChoices.setMinimumSize(choicesDim);
+      ColChoices.setMaximumSize(choicesDim);
+      ColChoices.setPreferredSize(choicesDim);
+      SortChoices.setMinimumSize(choicesDim);
+      SortChoices.setMaximumSize(choicesDim);
+      SortChoices.setPreferredSize(choicesDim);
+
+      this.setBackground(Color.WHITE);
+      this.setLayout(new GridBagLayout());
+      GridBagConstraints c = new GridBagConstraints();
+
+      EmptyBorder line1 = new EmptyBorder(5, 10, 5, 5);
+      this.setBorder(line1);
+
+      c.gridx = 0;
+      c.gridy = 0;
+      c.ipadx = 10;
+      c.ipady = 10;
+      this.add(new JLabel("Sort By:  "), c);
+
+      c.gridx = 1;
+      c.gridy = 0;
+      this.add(ColChoices, c);
+
+      c.gridx = 0;
+      c.gridy = 1;
+      this.add(new JLabel("In Order:  "), c);
+
+      c.gridx = 1;
+      c.gridy = 1;
+      this.add(SortChoices, c);
+
+    }
+
+    public int getSortIndex() {
+      return ColChoices.getSelectedIndex();
+    }
+
+    public String getSortType() {
+      return SortChoices.getSelectedItem().toString();
+    }
+
+  }
+
+  private class ConnectButton extends JPanel {
+
+    protected JButton connect;
+    protected JButton cancel;
+
+    protected ConnectButton(ActionListener listener) {
+      this.setBackground(Color.WHITE);
+      this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+
+      EmptyBorder line1 = new EmptyBorder(5, 20, 5, 5);
+      this.setBorder(line1);
+
+      this.add(new JLabel("                          "));
+      connect = new JButton("OK");
+      connect.setBackground(Color.WHITE);
+      connect.addActionListener(listener);
+      cancel = new JButton("Cancel");
+      cancel.setBackground(Color.WHITE);
+      cancel.addActionListener(listener);
+      cancel.setName("SortCancel");
+      this.add(cancel);
+      this.add(connect);
+    }
+  }
+
+}
diff --git a/0.10/app/weditor/pom.xml b/0.10/app/weditor/pom.xml
new file mode 100644
index 0000000..9b00d8c
--- /dev/null
+++ b/0.10/app/weditor/pom.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements.  See the NOTICE.txt file distributed with this work for
+additional information regarding copyright ownership.  The ASF licenses this
+file to you under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License.  You may obtain a copy of
+the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+License for the specific language governing permissions and limitations under
+the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.apache.oodt</groupId>
+		<artifactId>oodt-core</artifactId>
+		<version>0.10</version>
+		<relativePath>../../core/pom.xml</relativePath>
+	</parent>
+	<artifactId>weditor</artifactId>
+	<name>Catalog and Archive Workflow Management GUI Editor</name>
+	<description>Apache OODT Workflow Editor GUI</description>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<version>2.2-beta-2</version>
+				<configuration>
+					<descriptors>
+						<descriptor>src/main/assembly/assembly.xml</descriptor>
+					</descriptors>
+				</configuration>
+				<executions>
+					<execution>
+						<phase>package</phase>
+						<goals>
+							<goal>single</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+		<resources>
+			<resource>
+				<targetPath>org/apache/oodt/cas/workflow/gui/util</targetPath>
+				<directory>${basedir}/src/main/resources/icons</directory>
+				<includes>
+					<include>**</include>
+				</includes>
+			</resource>
+		</resources>
+	</build>
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.oodt</groupId>
+			<artifactId>cas-workflow</artifactId>
+			<version>0.10</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.jung</groupId>
+			<artifactId>jung-api</artifactId>
+			<version>2.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.jung</groupId>
+			<artifactId>jung-visualization</artifactId>
+			<version>2.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.jung</groupId>
+			<artifactId>jung-algorithms</artifactId>
+			<version>2.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.jung</groupId>
+			<artifactId>jung-graph-impl</artifactId>
+			<version>2.0.1</version>
+		</dependency>
+        <dependency>
+            <groupId>jgraph</groupId>        
+            <artifactId>jgraph</artifactId>
+            <version>5.13.0.0</version>
+        </dependency>		
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>3.8.2</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>
diff --git a/0.10/app/weditor/src/main/assembly/assembly.xml b/0.10/app/weditor/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..f64863d
--- /dev/null
+++ b/0.10/app/weditor/src/main/assembly/assembly.xml
@@ -0,0 +1,72 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<assembly>

+  <id>dist</id>

+  <formats>

+    <format>tar.gz</format>
+    <format>zip</format>

+  </formats>

+  <includeBaseDirectory>true</includeBaseDirectory>
+  <baseDirectory>${project.artifactId}-${project.version}</baseDirectory>  
+  <includeSiteDirectory>false</includeSiteDirectory>

+  <fileSets>

+    <fileSet>

+      <directory>${basedir}</directory>

+      <outputDirectory>.</outputDirectory>

+      <includes>

+        <include>LICENSE.txt</include>

+        <include>CHANGES.txt</include>

+      </includes>

+    </fileSet>
+    <fileSet>
+      <directory>${basedir}/src/main/bin</directory>
+      <outputDirectory>bin</outputDirectory>
+      <includes/>
+      <fileMode>755</fileMode>
+    </fileSet>
+    <fileSet>
+      <directory>${basedir}/src/main/resources</directory>
+      <outputDirectory>etc</outputDirectory>
+      <includes>
+        <include>**.properties</include>
+      </includes>
+    </fileSet>
+    <fileSet>
+      <directory>${basedir}/src/main/resources</directory>
+      <outputDirectory>logs</outputDirectory>
+      <includes>
+        <include>REMOVE.log</include>
+      </includes>
+    </fileSet>
+    <fileSet>
+      <directory>${basedir}/src/main/resources/examples</directory>
+      <outputDirectory>workspace</outputDirectory>
+      <includes>
+        <include>**</include>
+      </includes>
+    </fileSet>

+  </fileSets>

+  <dependencySets>

+    <dependencySet>

+      <outputDirectory>lib</outputDirectory>

+      <unpack>false</unpack>
+      <useProjectArtifact>true</useProjectArtifact>
+      <useTransitiveDependencies>true</useTransitiveDependencies>

+      <unpackOptions/>

+    </dependencySet>

+  </dependencySets>

+</assembly>

diff --git a/0.10/app/weditor/src/main/bin/weditor b/0.10/app/weditor/src/main/bin/weditor
new file mode 100644
index 0000000..c17df0e
--- /dev/null
+++ b/0.10/app/weditor/src/main/bin/weditor
@@ -0,0 +1,22 @@
+#!/bin/csh
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+${JAVA_HOME}/bin/java \
+	-Djava.ext.dirs=../lib \
+	-Djava.util.logging.config.file=../etc/logging.properties \
+	org.apache.oodt.cas.workflow.gui.WorkflowGUI
+
+
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/WorkflowGUI.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/WorkflowGUI.java
new file mode 100644
index 0000000..954eabe
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/WorkflowGUI.java
@@ -0,0 +1,314 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui;
+
+//JDK imports
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JMenuBar;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowFocusListener;
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Vector;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+
+//OODT imports
+import org.apache.oodt.cas.workflow.gui.menu.EditMenu;
+import org.apache.oodt.cas.workflow.gui.menu.FileMenu;
+import org.apache.oodt.cas.workflow.gui.model.ModelGraph;
+import org.apache.oodt.cas.workflow.gui.model.repo.XmlWorkflowModelRepository;
+import org.apache.oodt.cas.workflow.gui.model.repo.XmlWorkflowModelRepositoryFactory;
+import org.apache.oodt.cas.workflow.gui.perspective.MultiStatePerspective;
+import org.apache.oodt.cas.workflow.gui.perspective.build.BuildPerspective;
+import org.apache.oodt.cas.workflow.gui.perspective.view.View;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewState;
+import org.apache.oodt.cas.workflow.gui.toolbox.Tool;
+import org.apache.oodt.cas.workflow.gui.toolbox.ToolBox;
+import org.apache.oodt.cas.workflow.gui.util.IconLoader;
+
+//Commons import
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * 
+ * 
+ * Main driver shell and JFrame for the Workflow Editor GUI.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class WorkflowGUI extends JFrame {
+
+  private static final long serialVersionUID = -8217540440195126377L;
+
+  private ToolBox toolbox;
+
+  private MultiStatePerspective perspective;
+
+  private static AtomicInteger untitledIter = new AtomicInteger(0);
+
+  private JMenuBar menu;
+
+  private File workspace;
+
+  private XmlWorkflowModelRepository repo;
+
+  public WorkflowGUI() throws Exception {
+
+    this.addWindowFocusListener(new WindowFocusListener() {
+
+      public void windowGainedFocus(WindowEvent e) {
+        if (menu != null)
+          menu.revalidate();
+        if (toolbox != null)
+          toolbox.revalidate();
+        if (perspective != null)
+          perspective.refresh();
+      }
+
+      public void windowLostFocus(WindowEvent e) {
+      }
+
+    });
+
+    this.setLayout(new BorderLayout());
+    this.setPreferredSize(new Dimension(1000, 800));
+
+    Vector<Tool> tools = new Vector<Tool>();
+    Tool editTool = new Tool(IconLoader.getIcon(IconLoader.EDIT),
+        IconLoader.getIcon(IconLoader.EDIT_SELECTED)) {
+      private static final long serialVersionUID = 1682845263796161282L;
+
+      @Override
+      public void onClick() {
+        WorkflowGUI.this.perspective.setMode(View.Mode.EDIT);
+      }
+    };
+    tools.add(editTool);
+    Tool deleteTool = new Tool(IconLoader.getIcon(IconLoader.DELETE),
+        IconLoader.getIcon(IconLoader.DELETE_SELECTED)) {
+      private static final long serialVersionUID = 5050127713254634783L;
+
+      @Override
+      public void onClick() {
+        WorkflowGUI.this.perspective.setMode(View.Mode.DELETE);
+      }
+    };
+    tools.add(deleteTool);
+    Tool moveTool = new Tool(IconLoader.getIcon(IconLoader.MOVE),
+        IconLoader.getIcon(IconLoader.MOVE_SELECTED)) {
+      private static final long serialVersionUID = 1682845263796161282L;
+
+      @Override
+      public void onClick() {
+        WorkflowGUI.this.perspective.setMode(View.Mode.MOVE);
+      }
+    };
+    tools.add(moveTool);
+    Tool zoomInTool = new Tool(IconLoader.getIcon(IconLoader.ZOOM_IN),
+        IconLoader.getIcon(IconLoader.ZOOM_IN_SELECTED)) {
+      private static final long serialVersionUID = 1682845263796161282L;
+
+      @Override
+      public void onClick() {
+        WorkflowGUI.this.perspective.setMode(View.Mode.ZOOM_IN);
+      }
+    };
+    tools.add(zoomInTool);
+    Tool zoomOutTool = new Tool(IconLoader.getIcon(IconLoader.ZOOM_OUT),
+        IconLoader.getIcon(IconLoader.ZOOM_OUT_SELECTED)) {
+      private static final long serialVersionUID = 1682845263796161282L;
+
+      @Override
+      public void onClick() {
+        WorkflowGUI.this.perspective.setMode(View.Mode.ZOOM_OUT);
+      }
+    };
+    tools.add(zoomOutTool);
+    toolbox = new ToolBox(tools);
+    toolbox.setSelected(editTool);
+    this.add(toolbox, BorderLayout.NORTH);
+
+    this.setJMenuBar(menu = this.generateMenuBar());
+    perspective = new BuildPerspective();
+    perspective.refresh();
+    this.add(perspective, BorderLayout.CENTER);
+  }
+
+  private void updateWorkspaceText() {
+    if (this.workspace == null)
+      this.setTitle(null);
+    else
+      this.setTitle(StringUtils.leftPad("Workspace: " + this.workspace, 100));
+  }
+
+  private void loadProjects() {
+    try {
+      XmlWorkflowModelRepositoryFactory factory = new XmlWorkflowModelRepositoryFactory();
+      factory.setWorkspace(this.workspace.getAbsolutePath());
+      repo = factory.createModelRepository();
+      repo.loadGraphs(new HashSet<String>(Arrays.asList("sequential",
+          "parallel", "task", "condition")));
+      for (File file : repo.getFiles()) {
+        List<ModelGraph> graphs = new Vector<ModelGraph>();
+        for (ModelGraph graph : repo.getGraphs())
+          if (graph.getModel().getFile().equals(file))
+            graphs.add(graph);
+        System.out.println(graphs);
+        perspective.addState(new ViewState(file, null, graphs, repo
+            .getGlobalConfigGroups()));
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  public JMenuBar generateMenuBar() {
+    JMenuBar bar = new JMenuBar();
+    FileMenu fileMenu = new FileMenu();
+    bar.add(fileMenu);
+    fileMenu.getExit().addActionListener(new ActionListener() {
+      
+      @Override
+      public void actionPerformed(ActionEvent event) {
+        System.exit(1);        
+      }
+    });
+    
+    fileMenu.getOpenWorkspace().addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent event) {
+        try {
+          JFileChooser chooser = new JFileChooser(new File(".")) {
+            boolean acceptFile(File f) {
+              return f.isDirectory();
+            }
+          };
+          chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+          int value = chooser.showOpenDialog(WorkflowGUI.this);
+          if (value == JFileChooser.APPROVE_OPTION) {
+            workspace = chooser.getSelectedFile();
+            updateWorkspaceText();
+            perspective.reset();
+            loadProjects();
+          }
+        } catch (Exception e1) {
+          e1.printStackTrace();
+        }
+      }
+    });
+    fileMenu.getImport().addActionListener(new ActionListener() {
+
+      public void actionPerformed(ActionEvent event) {
+        try {
+          if (workspace == null)
+            return;
+          JFileChooser chooser = new JFileChooser(new File("."));
+          int value = chooser.showOpenDialog(WorkflowGUI.this);
+          if (value == JFileChooser.APPROVE_OPTION) {
+            File file = chooser.getSelectedFile();
+            XmlWorkflowModelRepositoryFactory factory = new XmlWorkflowModelRepositoryFactory();
+            factory.setWorkspace(workspace.getAbsolutePath());
+            View activeView = perspective.getActiveView();
+
+            if (activeView != null) {
+              // TODO: add code for import
+            }
+          }
+        } catch (Exception e) {
+          e.printStackTrace();
+        }
+      }
+
+    });
+    fileMenu.getNewWorkspace().addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent event) {
+        JFileChooser chooser = new JFileChooser(new File(".")) {
+          boolean acceptFile(File f) {
+            return f.isDirectory();
+          }
+        };
+        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+        int value = chooser.showOpenDialog(WorkflowGUI.this);
+        if (value == JFileChooser.APPROVE_OPTION) {
+          workspace = chooser.getSelectedFile();
+          updateWorkspaceText();
+          perspective.reset();
+          loadProjects();
+          perspective.refresh();
+        }
+      }
+    });
+
+    fileMenu.getNewProject().addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent event) {
+        // TODO: add new project code
+      }
+    });
+    fileMenu.getSave().addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent event) {
+      	try {
+      		repo.save();
+      	} catch (Exception e) {
+      		e.printStackTrace();
+      	}
+      }
+    });
+    EditMenu editMenu = new EditMenu();
+    bar.add(editMenu);
+    editMenu.getUndo().addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent event) {
+        try {
+          perspective.undo();
+        } catch (Exception e) {
+          e.printStackTrace();
+        }
+      }
+    });
+    bar.revalidate();
+    return bar;
+  }
+
+  public static void main(String[] args) {
+    UIManager.put("TabbedPane.selected", new Color(238, 238, 238));
+    SwingUtilities.invokeLater(new Runnable() {
+      public void run() {
+        WorkflowGUI gui;
+        try {
+          gui = new WorkflowGUI();
+          gui.pack();
+          gui.setVisible(true);
+        } catch (Exception e) {
+          e.printStackTrace();
+        }
+      }
+    });
+  }
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/menu/EditMenu.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/menu/EditMenu.java
new file mode 100644
index 0000000..db10dba
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/menu/EditMenu.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.menu;
+
+//JDK imports
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+/**
+ * 
+ * 
+ * Edit menu driver for the Workflow Editor GUI.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class EditMenu extends JMenu {
+
+  private static final long serialVersionUID = -8368744697132421274L;
+
+  private JMenuItem undoItem;
+
+  public EditMenu() {
+    super("Edit");
+    this.add(this.undoItem = new JMenuItem("Undo"));
+  }
+
+  public JMenuItem getUndo() {
+    return this.undoItem;
+  }
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/menu/FileMenu.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/menu/FileMenu.java
new file mode 100644
index 0000000..599a331
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/menu/FileMenu.java
@@ -0,0 +1,82 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.menu;
+
+//JDK imports
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+/**
+ * 
+ * 
+ * File menu driver for the Workflow Editor GUI.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class FileMenu extends JMenu {
+
+  private static final long serialVersionUID = -5702987396916441718L;
+
+  private JMenuItem newWorkspace;
+  private JMenuItem newProject;
+  private JMenuItem openWorkspace;
+  private JMenuItem importItem;
+  private JMenuItem saveItem;
+  private JMenuItem exitItem;
+  private JMenu newMenu, openMenu, importMenu;
+
+  public FileMenu() {
+    super("File");
+    this.add(newMenu = new JMenu("New"));
+    newMenu.add(this.newWorkspace = new JMenuItem("Workspace"));
+    newMenu.add(this.newProject = new JMenuItem("Project"));
+    this.add(openMenu = new JMenu("Open"));
+    openMenu.add(this.openWorkspace = new JMenuItem("Workspace"));
+    this.add(importMenu = new JMenu("Import"));
+    importMenu.add(this.importItem = new JMenuItem("Project"));
+    this.add(this.saveItem = new JMenuItem("Save Project"));
+    this.add(this.exitItem = new JMenuItem("Exit"));
+  }
+
+  public JMenuItem getOpenWorkspace() {
+    return this.openWorkspace;
+  }
+
+  public JMenuItem getImport() {
+    return this.importItem;
+  }
+
+  public JMenuItem getNewWorkspace() {
+    return this.newWorkspace;
+  }
+
+  public JMenuItem getNewProject() {
+    return this.newProject;
+  }
+
+  public JMenuItem getSave() {
+    return this.saveItem;
+  }
+  
+  public JMenuItem getExit(){
+    return this.exitItem;
+  }
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/model/ModelGraph.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/model/ModelGraph.java
new file mode 100644
index 0000000..6801340
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/model/ModelGraph.java
@@ -0,0 +1,281 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.model;
+
+//OODT imports
+import org.apache.oodt.cas.metadata.Metadata;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewState;
+import org.apache.oodt.cas.workflow.gui.util.GuiUtils;
+
+//JDK imports
+import java.util.List;
+import java.util.Stack;
+import java.util.Vector;
+
+/**
+ * 
+ * 
+ * The graph to display per workflow in the Workflow Editor GUI.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class ModelGraph {
+
+  private ModelGraph parent;
+  private ModelNode model;
+  private ModelGraph preConditions;
+  private Vector<ModelGraph> children;
+  private ModelGraph postConditions;
+  private boolean isPreCondition;
+  private boolean isPostCondition;
+
+  public ModelGraph(ModelNode model) {
+    this.isPreCondition = isPostCondition = false;
+    this.model = model;
+    this.children = new Vector<ModelGraph>();
+    if (this.model.isParentType())
+      this.addChild(new ModelGraph(GuiUtils.createDummyNode()));
+  }
+
+  public void setIsRef(boolean isRef) {
+    Stack<ModelGraph> stack = new Stack<ModelGraph>();
+    stack.add(this);
+    while (!stack.empty()) {
+      ModelGraph curGraph = stack.pop();
+      curGraph.getModel().setIsRef(isRef);
+      stack.addAll(curGraph.getChildren());
+      if (curGraph.getPreConditions() != null)
+        stack.add(curGraph.getPreConditions());
+      if (curGraph.getPostConditions() != null)
+        stack.add(curGraph.getPostConditions());
+    }
+  }
+
+  public boolean isCondition() {
+    return this.isPreCondition || this.isPostCondition;
+  }
+
+  public boolean isExcused() {
+    return this.parent != null
+        && this.parent.getModel().getExcusedSubProcessorIds()
+            .contains(this.getModel().getModelId());
+  }
+
+  public String getId() {
+    return model.getId();
+  }
+
+  public void setParent(ModelGraph parent) {
+    if (this.parent != null) {
+      if (this.isCondition() && !this.parent.isCondition()) {
+        if (this.isPreCondition)
+          this.parent.preConditions = null;
+        else
+          this.parent.postConditions = null;
+      } else {
+        this.parent.removeChild(this);
+      }
+    }
+    this.parent = parent;
+    if (!this.getModel().isRef() && parent != null && parent.getModel().isRef())
+      this.getModel().setIsRef(true);
+  }
+
+  public ModelGraph getParent() {
+    return this.parent;
+  }
+
+  public ModelGraph getRootParent() {
+    if (this.parent == null)
+      return this;
+    else
+      return this.parent.getRootParent();
+  }
+
+  public List<ModelGraph> getPathFromRootParent() {
+    Vector<ModelGraph> path = new Vector<ModelGraph>();
+    ModelGraph curGraph = this;
+    while (curGraph != null) {
+      path.add(0, curGraph);
+      curGraph = this.parent;
+    }
+    return path;
+  }
+
+  public void addChild(ModelGraph graph) {
+    if (this.children.size() == 1
+        && GuiUtils.isDummyNode(this.children.get(0).getModel()))
+      this.children.clear();
+    this.children.add(graph);
+    graph.setParent(this);
+  }
+
+  public void removeChild(ModelGraph graph) {
+    this.children.remove(graph);
+    this.getModel().getExcusedSubProcessorIds()
+        .remove(graph.getModel().getModelId());
+    graph.parent = null;
+    if (this.children.size() == 0)
+      this.addChild(new ModelGraph(GuiUtils.createDummyNode()));
+  }
+
+  public List<ModelGraph> getChildren() {
+    if (this.getModel().isParentType() && children.size() == 0)
+      this.addChild(new ModelGraph(GuiUtils.createDummyNode()));
+    return children;
+  }
+
+  public boolean hasChildren() {
+    if (this.children.size() == 1
+        && GuiUtils.isDummyNode(this.children.get(0).getModel()))
+      return false;
+    else
+      return this.children.size() > 0;
+  }
+
+  public ModelNode getModel() {
+    return model;
+  }
+
+  public void setModel(ModelNode model) {
+    this.model = model;
+  }
+
+  public Metadata getInheritedStaticMetadata(ViewState state) {
+    Metadata m = new Metadata();
+    if (this.parent != null) {
+      m.replaceMetadata(this.parent.getInheritedStaticMetadata(state));
+      if (this.parent.getModel().getStaticMetadata() != null)
+        m.replaceMetadata(this.parent.getModel().getStaticMetadata());
+      if (this.parent.getModel().getExtendsConfig() != null)
+        for (String configGroup : this.parent.getModel().getExtendsConfig())
+          m.replaceMetadata(state.getGlobalConfigGroups().get(configGroup)
+              .getMetadata());
+    }
+    return m;
+  }
+
+  public ModelGraph getPreConditions() {
+    return preConditions;
+  }
+
+  public void setPreConditions(ModelGraph preConditions) {
+    if (this.preConditions != null)
+      this.preConditions.setParent(null);
+    Stack<ModelGraph> stack = new Stack<ModelGraph>();
+    stack.add(preConditions);
+    while (!stack.empty()) {
+      ModelGraph graph = stack.pop();
+      graph.isPreCondition = true;
+      stack.addAll(graph.getChildren());
+    }
+    (this.preConditions = preConditions).setParent(this);
+  }
+
+  public ModelGraph getPostConditions() {
+    return postConditions;
+  }
+
+  public void setPostConditions(ModelGraph postConditions) {
+    if (this.postConditions != null)
+      this.postConditions.setParent(null);
+    Stack<ModelGraph> stack = new Stack<ModelGraph>();
+    stack.add(postConditions);
+    while (!stack.empty()) {
+      ModelGraph graph = stack.pop();
+      graph.isPostCondition = true;
+      stack.addAll(graph.getChildren());
+    }
+    (this.postConditions = postConditions).setParent(this);
+  }
+
+  public ModelGraph recursiveFind(String id) {
+    Stack<ModelGraph> stack = new Stack<ModelGraph>();
+    stack.add(this);
+    while (!stack.empty()) {
+      ModelGraph curGraph = stack.pop();
+      if (curGraph.getId().equals(id))
+        return curGraph;
+      stack.addAll(curGraph.getChildren());
+      if (curGraph.getPreConditions() != null)
+        stack.add(curGraph.getPreConditions());
+      if (curGraph.getPostConditions() != null)
+        stack.add(curGraph.getPostConditions());
+    }
+    return null;
+  }
+
+  public ModelGraph recursiveFindByModelId(String modelId) {
+    Stack<ModelGraph> stack = new Stack<ModelGraph>();
+    stack.add(this);
+    while (!stack.empty()) {
+      ModelGraph curGraph = stack.pop();
+      if (curGraph.getModel().getModelId().equals(modelId))
+        return curGraph;
+      stack.addAll(curGraph.getChildren());
+      if (curGraph.getPreConditions() != null)
+        stack.add(curGraph.getPreConditions());
+      if (curGraph.getPostConditions() != null)
+        stack.add(curGraph.getPostConditions());
+    }
+    return null;
+  }
+
+  public List<ModelGraph> getLeafNodes() {
+    Vector<ModelGraph> leafNodes = new Vector<ModelGraph>();
+    Stack<ModelGraph> stack = new Stack<ModelGraph>();
+    stack.add(this);
+    while (!stack.empty()) {
+      ModelGraph curGraph = stack.pop();
+      if (curGraph.getChildren().size() == 0)
+        leafNodes.add(curGraph);
+      else
+        stack.addAll(curGraph.getChildren());
+    }
+    return leafNodes;
+  }
+
+  public int hashCode() {
+    return this.getId().hashCode();
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof ModelGraph)
+      return this.getId().equals(((ModelGraph) obj).getId());
+    else
+      return false;
+  }
+
+  public String toString() {
+    return this.getModel().getModelId();
+  }
+
+  public ModelGraph clone() {
+    ModelNode cloneNode = this.model.clone();
+    ModelGraph clone = new ModelGraph(cloneNode);
+    for (ModelGraph child : this.children)
+      clone.addChild(child.clone());
+    if (this.preConditions != null)
+      clone.setPreConditions(this.preConditions.clone());
+    if (this.postConditions != null)
+      clone.setPostConditions(this.postConditions.clone());
+    return clone;
+  }
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/model/ModelNode.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/model/ModelNode.java
new file mode 100644
index 0000000..14283e8
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/model/ModelNode.java
@@ -0,0 +1,367 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.model;
+
+//JDK imports
+import java.awt.Color;
+import java.io.File;
+import java.util.List;
+import java.util.UUID;
+import java.util.Vector;
+
+//OODT imports
+import org.apache.oodt.cas.metadata.Metadata;
+
+/**
+ * 
+ * 
+ * Represents a node in the Workflow Model graph.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class ModelNode {
+
+  private String id;
+  private String alias;
+  private boolean isRef;
+  private File file;
+  private boolean textVisible;
+  private boolean entryPoint;
+  private List<String> configGroups;
+  private String executionType;
+  private String modelId;
+  private String modelName;
+  private String instanceClass;
+  private List<String> excusedSubProcessorIds;
+  private Metadata staticMetadata;
+  private long timeout;
+  private boolean optional;
+
+  public ModelNode(File file) {
+    super();
+    this.file = file;
+    this.id = UUID.randomUUID().toString();
+    this.isRef = false;
+    this.executionType = "task";
+    this.textVisible = true;
+    this.entryPoint = false;
+    this.configGroups = new Vector<String>();
+    this.modelId = null;
+    this.modelName = null;
+    this.instanceClass = null;
+    this.excusedSubProcessorIds = new Vector<String>();
+    this.staticMetadata = new Metadata();
+    this.timeout = -1;
+    this.optional = false;
+  }
+
+  public ModelNode(File file, String modelId) {
+    this(file);
+    this.setModelId(modelId);
+    this.setModelName(modelId);
+  }
+
+  public ModelNode(File file, String modelId, boolean isRef) {
+    this(file, modelId);
+    this.isRef = isRef;
+  }
+
+  public String getId() {
+    return this.id;
+  }
+
+  public void setAlias(String alias) {
+  	this.alias = alias;
+  }
+
+  public String getAlias() {
+  	return alias;
+  }
+
+  public void setIsRef(boolean isRef) {
+    this.isRef = isRef;
+  }
+
+  public boolean isRef() {
+    return this.isRef;
+  }
+
+  public File getFile() {
+    return this.file;
+  }
+
+  public void setExtendsConfig(List<String> configGroups) {
+    this.configGroups.clear();
+    this.configGroups.addAll(configGroups);
+  }
+
+  public List<String> getExtendsConfig() {
+    return this.configGroups;
+  }
+
+  public void setTextVisible(boolean textVisible) {
+    this.textVisible = textVisible;
+  }
+
+  public void setEntryPoint(boolean entryPoint) {
+    this.entryPoint = entryPoint;
+  }
+
+  public boolean isEntryPoint() {
+    return this.entryPoint;
+  }
+
+  public boolean isParentType() {
+    return !(this.getExecutionType().equals("task") || this.getExecutionType()
+        .equals("condition"));
+  }
+
+  public Color getColor() {
+    if (this.isParentType()) {
+      if (this.getExecutionType().equals("sequential"))
+        return new Color(100, 149, 237);
+      else if (this.getExecutionType().equals("parallel"))
+        return new Color(143, 188, 143);
+      else
+        return Color.darkGray;
+    } else {
+      if (this.getExecutionType().equals("task"))
+        return Color.orange;
+      else
+        return Color.cyan;
+    }
+  }
+
+  public Color getGradientColor() {
+    if (this.isParentType()) {
+      if (this.getExecutionType().equals("sequential"))
+        return new Color(200, 200, 200);
+      else if (this.getExecutionType().equals("parallel"))
+        return new Color(200, 200, 200);
+      else
+        return Color.white;
+    } else {
+      return Color.darkGray;
+    }
+  }
+
+  public String getModelId() {
+    return modelId;
+  }
+
+  public String getModelName() {
+    if (modelName == null) {
+      return modelId;
+    } else {
+      return modelName;
+    }
+  }
+
+  public String getExecutionType() {
+    return executionType;
+  }
+
+  public String getInstanceClass() {
+    return instanceClass;
+  }
+
+  public List<String> getExcusedSubProcessorIds() {
+    if (this.excusedSubProcessorIds == null)
+      this.excusedSubProcessorIds = new Vector<String>();
+    return this.excusedSubProcessorIds;
+  }
+
+  public Metadata getStaticMetadata() {
+    return staticMetadata != null ? this.staticMetadata
+        : (this.staticMetadata = new Metadata());
+  }
+
+  public int hashCode() {
+    return this.getId().hashCode();
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof ModelNode)
+      return this.getId().equals(((ModelNode) obj).getId());
+    else
+      return false;
+  }
+
+  public String toString() {
+    if (this.textVisible) {
+      if (this.isParentType())
+        return this.getModelName() + " : " + this.getExecutionType();
+      else
+        return this.getModelName();
+    } else {
+      return null;
+    }
+  }
+
+  public ModelNode clone() {
+    ModelNode clone = new ModelNode(this.file);
+    clone.id = this.id;
+    if (this.excusedSubProcessorIds != null)
+      clone.excusedSubProcessorIds = new Vector<String>(
+          this.excusedSubProcessorIds);
+    clone.executionType = this.executionType;
+    clone.instanceClass = this.instanceClass;
+    clone.modelId = this.modelId;
+    clone.modelName = this.modelName;
+    clone.staticMetadata = null;
+    clone.textVisible = this.textVisible;
+    if (this.staticMetadata != null)
+      clone.staticMetadata = new Metadata(this.staticMetadata);
+    return clone;
+  }
+
+  /**
+   * @param modelId
+   *          the modelId to set
+   */
+  public void setModelId(String modelId) {
+    this.modelId = modelId;
+  }
+
+  /**
+   * @param modelName
+   *          the modelName to set
+   */
+  public void setModelName(String modelName) {
+    this.modelName = modelName;
+  }
+
+  /**
+   * @param instanceClass
+   *          the instanceClass to set
+   */
+  public void setInstanceClass(String instanceClass) {
+    this.instanceClass = instanceClass;
+  }
+
+  /**
+   * @param excusedSubProcessorIds
+   *          the excusedSubProcessorIds to set
+   */
+  public void setExcusedSubProcessorIds(List<String> excusedSubProcessorIds) {
+    this.excusedSubProcessorIds = excusedSubProcessorIds;
+  }
+
+
+
+  /**
+   * @return the configGroups
+   */
+  public List<String> getConfigGroups() {
+    return configGroups;
+  }
+
+
+
+  /**
+   * @param configGroups the configGroups to set
+   */
+  public void setConfigGroups(List<String> configGroups) {
+    this.configGroups = configGroups;
+  }
+
+
+
+  /**
+   * @return the textVisible
+   */
+  public boolean isTextVisible() {
+    return textVisible;
+  }
+
+
+
+  /**
+   * @param id the id to set
+   */
+  public void setId(String id) {
+    this.id = id;
+  }
+
+
+
+  /**
+   * @param isRef the isRef to set
+   */
+  public void setRef(boolean isRef) {
+    this.isRef = isRef;
+  }
+
+
+
+  /**
+   * @param file the file to set
+   */
+  public void setFile(File file) {
+    this.file = file;
+  }
+
+
+
+  /**
+   * @param executionType the executionType to set
+   */
+  public void setExecutionType(String executionType) {
+    this.executionType = executionType;
+  }
+
+
+
+  /**
+   * @param staticMetadata the staticMetadata to set
+   */
+  public void setStaticMetadata(Metadata staticMetadata) {
+    this.staticMetadata = staticMetadata;
+  }
+
+  /**
+   * @return the timeout
+   */
+  public long getTimeout() {
+    return timeout;
+  }
+
+  /**
+   * @param timeout the timeout to set
+   */
+  public void setTimeout(long timeout) {
+    this.timeout = timeout;
+  }
+
+  /**
+   * @return the optional
+   */
+  public boolean isOptional() {
+    return optional;
+  }
+
+  /**
+   * @param optional the optional to set
+   */
+  public void setOptional(boolean optional) {
+    this.optional = optional;
+  }
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/model/repo/XmlWorkflowModelRepository.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/model/repo/XmlWorkflowModelRepository.java
new file mode 100644
index 0000000..2767d60
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/model/repo/XmlWorkflowModelRepository.java
@@ -0,0 +1,597 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.model.repo;
+
+//OODT imports
+import org.apache.oodt.cas.metadata.Metadata;
+import org.apache.oodt.cas.workflow.gui.model.ModelGraph;
+import org.apache.oodt.cas.workflow.gui.model.ModelNode;
+import org.apache.oodt.commons.xml.XMLUtils;
+
+//JDK imports
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.Vector;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathFactory;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * 
+ * Model Repository which stores models in xml files
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class XmlWorkflowModelRepository {
+
+  private File workspace;
+  private List<File> files;
+  private Set<ModelGraph> graphs;
+  private Map<String, ConfigGroup> globalConfigGroups;
+  private static final Logger LOG = Logger
+      .getLogger(XmlWorkflowModelRepository.class.getName());
+
+  public XmlWorkflowModelRepository(File workspace) {
+    this.files = new Vector<File>();
+    for (File file : (this.workspace = workspace).listFiles())
+      if (!file.isDirectory())
+        this.files.add(file);
+  }
+
+  public void loadGraphs(Set<String> supportedProcessorIds) throws Exception {
+    this.graphs = new HashSet<ModelGraph>();
+    HashMap<String, ConfigGroup> globalConfGroups = new HashMap<String, ConfigGroup>();
+    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+    factory.setNamespaceAware(true);
+    DocumentBuilder parser = factory.newDocumentBuilder();
+    List<FileBasedElement> rootElements = new Vector<FileBasedElement>();
+    for (File file : files) {
+      System.out.println("Loading: " + file);
+      rootElements.add(new FileBasedElement(file, parser.parse(file)
+          .getDocumentElement()));
+    }
+    for (FileBasedElement root : rootElements) {
+      loadConfiguration(rootElements, root, null, globalConfGroups);
+      NodeList rootChildren = root.getElement().getChildNodes();
+      for (int i = 0; i < rootChildren.getLength(); i++)
+        if (rootChildren.item(i).getNodeType() == Node.ELEMENT_NODE
+            && !rootChildren.item(i).getNodeName().equals("configuration")
+            && !rootChildren.item(i).getNodeName().equals("event")) {
+          System.out.println("node name: ["
+              + rootChildren.item(i).getNodeName() + "]");
+          ModelGraph graph = this.loadGraph(rootElements, new FileBasedElement(
+              root.getFile(), (Element) rootChildren.item(i)), new Metadata(),
+              globalConfGroups, supportedProcessorIds);
+          this.graphs.add(graph);
+        }
+    }
+    ensureUniqueIds(graphs);
+    this.globalConfigGroups = globalConfGroups;
+    System.out.println(this.globalConfigGroups.keySet());
+  }
+
+  public Set<ModelGraph> getGraphs() {
+    return this.graphs;
+  }
+
+  public Map<String, ConfigGroup> getGlobalConfigGroups() {
+    return this.globalConfigGroups;
+  }
+
+  public void setGlobalConfigGroups(Map<String, ConfigGroup> globalConfigGroups) {
+    this.globalConfigGroups = new HashMap<String, ConfigGroup>(
+        globalConfigGroups);
+  }
+
+  public List<File> getFiles() {
+    return this.files;
+  }
+
+  public void save() throws Exception {
+    this.backupCurrentFiles();
+    this.saveGraphs();
+  }
+
+  private void backupCurrentFiles() throws Exception {
+    File backupDir = new File(this.workspace, ".backup");
+    for (File file : this.files) {
+      FileUtils.copyFile(file, new File(backupDir, file.getName()));
+      file.delete();
+    }
+    this.files.clear();
+  }
+
+  private void saveGraphs() throws FileNotFoundException,
+      ParserConfigurationException {
+    Map<File, Document> documents = new HashMap<File, Document>();
+    for (ModelGraph graph : this.graphs) {
+      Document document = documents.get(graph.getModel().getFile());
+      if (document == null) {
+        document = createDocument();
+        document.appendChild(document.createElement("workflows"));
+        documents.put(graph.getModel().getFile(), document);
+      }
+      saveGraph(graph, document.getDocumentElement(), document);
+    }
+    saveGlobalConfigGroups(documents);
+    writeOutDocuments(documents);
+    this.files = new ArrayList<File>(documents.keySet());
+  }
+
+  private void writeOutDocuments(Map<File, Document> documents) {
+    for (File file : documents.keySet()) {
+      XMLUtils.writeXmlFile(documents.get(file), file.getAbsolutePath());
+    }
+  }
+
+  private void saveGlobalConfigGroups(Map<File, Document> documents)
+      throws ParserConfigurationException {
+    File globalConfigGroupsFile = new File(workspace,
+        "shared-configuration.xml");
+    Document document = documents.get(globalConfigGroupsFile);
+    if (document == null) {
+      document = createDocument();
+      document.appendChild(document.createElement("workflows"));
+      documents.put(globalConfigGroupsFile, document);
+    }
+    for (String configName : this.globalConfigGroups.keySet()) {
+      ConfigGroup globalConfig = this.globalConfigGroups.get(configName);
+      Element configElem = document.createElement("configuration");
+      document.getDocumentElement().appendChild(configElem);
+      configElem.setAttribute("name", globalConfig.getName());
+      if (!globalConfig.getExtends().isEmpty()) {
+        configElem.setAttribute("extends",
+            StringUtils.join(globalConfig.getExtends(), ", "));
+      }
+
+      String[] properties = globalConfig.getMetadata().getAllKeys()
+          .toArray(new String[globalConfig.getMetadata().getAllKeys().size()]);
+      Arrays.sort(properties);
+      for (String property : properties) {
+        Element propElem = document.createElement("property");
+        configElem.appendChild(propElem);
+        propElem.setAttribute("name", property);
+        propElem.setAttribute("value",
+            globalConfig.getMetadata().getMetadata(property));
+      }
+    }
+  }
+
+  private void saveGraph(ModelGraph graph, Element parentElem, Document document)
+      throws FileNotFoundException, ParserConfigurationException {
+    ModelNode node = graph.getModel();
+
+    Element workflowElem = document.createElement(node.getExecutionType());
+    parentElem.appendChild(workflowElem);
+
+    if (node.isRef()) {
+      workflowElem.setAttribute("id-ref", node.getModelId());
+      if (node.getAlias() != null) {
+        workflowElem.setAttribute("alias", node.getAlias());
+      }
+      saveConfiguration(node, workflowElem, document);
+    } else {
+      workflowElem.setAttribute("id", node.getModelId());
+      workflowElem.setAttribute("name", node.getModelName());
+      if (node.getInstanceClass() != null) {
+        workflowElem.setAttribute("class", node.getInstanceClass());
+      }
+
+      saveConfiguration(node, workflowElem, document);
+
+      // handle preconditions
+      if (graph.getPreConditions() != null) {
+        Element preConditions = document.createElement("conditions");
+        workflowElem.appendChild(preConditions);
+        preConditions.setAttribute("type", "pre");
+        preConditions.setAttribute("execution", graph.getPreConditions()
+            .getModel().getExecutionType());
+        preConditions.setAttribute("timeout", String.valueOf(graph.getModel().getTimeout()));
+        preConditions.setAttribute("optional", String.valueOf(graph.getModel().isOptional()));
+        for (ModelGraph preCondition : graph.getPreConditions().getChildren()) {
+          saveGraph(preCondition, preConditions, document);
+        }
+      }
+
+      // handle subprocessors
+      for (ModelGraph subProcessor : graph.getChildren()) {
+        saveGraph(subProcessor, workflowElem, document);
+      }
+
+      // handle postconditions
+      if (graph.getPostConditions() != null) {
+        Element postConditions = document.createElement("conditions");
+        workflowElem.appendChild(postConditions);
+        postConditions.setAttribute("type", "post");
+        postConditions.setAttribute("execution", graph.getPostConditions()
+            .getModel().getExecutionType());
+        postConditions.setAttribute("timeout", String.valueOf(graph.getModel().getTimeout()));
+        postConditions.setAttribute("optional", String.valueOf(graph.getModel().isOptional()));
+        for (ModelGraph postCondition : graph.getPostConditions().getChildren()) {
+          saveGraph(postCondition, postConditions, document);
+        }
+      }
+    }
+    if (!node.getExcusedSubProcessorIds().isEmpty()) {
+      workflowElem.setAttribute("excused",
+          StringUtils.join(node.getExcusedSubProcessorIds(), ","));
+    }
+    if (node.isEntryPoint()) {
+      workflowElem.setAttribute("entryPoint", "true");
+    }
+  }
+
+  private void saveConfiguration(ModelNode node, Element workflowElem,
+      Document document) {
+    if (!node.getStaticMetadata().getAllKeys().isEmpty()) {
+      Element configElem = document.createElement("configuration");
+      workflowElem.appendChild(configElem);
+      if (!node.getExtendsConfig().isEmpty()) {
+        configElem.setAttribute("extends",
+            StringUtils.join(node.getExtendsConfig(), ", "));
+      }
+      String[] properties = node.getStaticMetadata().getAllKeys()
+          .toArray(new String[node.getStaticMetadata().getAllKeys().size()]);
+      Arrays.sort(properties);
+      for (String property : properties) {
+        Element propElem = document.createElement("property");
+        configElem.appendChild(propElem);
+        propElem.setAttribute("name", property);
+        propElem.setAttribute("value",
+            node.getStaticMetadata().getMetadata(property));
+      }
+    }
+  }
+
+  private Document createDocument() throws ParserConfigurationException {
+    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+    factory.setNamespaceAware(true);
+    return factory.newDocumentBuilder().newDocument();
+  }
+
+  private void ensureUniqueIds(Set<ModelGraph> graphs) {
+    for (ModelGraph graph : graphs) {
+      HashSet<String> names = new HashSet<String>();
+      Vector<ModelGraph> stack = new Vector<ModelGraph>();
+      stack.add(graph);
+      while (!stack.isEmpty()) {
+        ModelGraph currentGraph = stack.remove(0);
+        String currentId = currentGraph.getId();
+        for (int i = 1; names.contains(currentId); i++)
+          currentId = currentGraph.getId() + "-" + i;
+        names.add(currentId);
+        if (!currentId.equals(currentGraph.getId()))
+          currentGraph.getModel().setModelId(currentId);
+        stack.addAll(currentGraph.getChildren());
+      }
+    }
+  }
+
+  private ModelGraph loadGraph(List<FileBasedElement> rootElements,
+      FileBasedElement workflowNode, Metadata staticMetadata,
+      HashMap<String, ConfigGroup> globalConfGroups,
+      Set<String> supportedProcessorIds) throws Exception {
+
+    String modelIdRef = null;
+    String modelId = null;
+    String modelName = null;
+    String alias = null;
+    String executionType = null;
+    List<String> excused = new Vector<String>();
+    String clazz = null;
+    long timeout = -1;
+    boolean optional = false;
+    boolean entryPoint = false;
+
+    NamedNodeMap attributes = workflowNode.getElement().getAttributes();
+    for (int i = 0; attributes != null && i < attributes.getLength(); i++) {
+      Node node = workflowNode.getElement().getAttributes().item(i);
+      if (node.getNodeName().equals("id")) {
+        modelId = node.getNodeValue();
+      } else if (node.getNodeName().equals("name")) {
+        modelName = node.getNodeValue();
+      } else if (node.getNodeName().equals("class")) {
+        clazz = node.getNodeValue();
+      } else if (node.getNodeName().equals("id-ref")) {
+        modelIdRef = node.getNodeValue();
+      } else if (node.getNodeName().equals("excused")) {
+        excused.addAll(Arrays.asList(node.getNodeValue().split(",")));
+      } else if (node.getNodeName().equals("entryPoint")) {
+        entryPoint = Boolean.parseBoolean(node.getNodeValue());
+      } else if (node.getNodeName().equals("alias")) {
+        alias = node.getNodeValue();
+      } else if (node.getNodeName().equals("execution")) {
+        executionType = node.getNodeValue();
+      } else if (node.getNodeName().equals("timeout")) {
+        timeout = node.getNodeValue() != null
+            && !node.getNodeValue().equals("") ? Long.valueOf(node
+            .getNodeValue()) : -1;
+      } 
+      else if(node.getNodeName().equals("optional")){
+        optional = Boolean.valueOf(node.getNodeValue());
+      }
+        else if (node.getNodeName().startsWith("p:")) {
+        staticMetadata.replaceMetadata(node.getNodeName().substring(2),
+            node.getNodeValue());
+      }
+    }
+
+    if (modelId == null && modelIdRef == null)
+      modelId = UUID.randomUUID().toString();
+
+    ModelGraph graph = null;
+    if (modelId != null) {
+
+      if (workflowNode.getElement().getNodeName().equals("workflow")
+          || workflowNode.getElement().getNodeName().equals("conditions")
+          || workflowNode.getElement().getNodeName().equals("tasks")) {
+        if (executionType == null) {
+          LOG.log(Level.WARNING, "workflow model '"
+              + workflowNode.getElement().getNodeName()
+              + "' missing execution type: assuming sequential");
+          executionType = "sequential";
+        }
+      } else {
+        executionType = workflowNode.getElement().getNodeName();
+      }
+
+      if (!supportedProcessorIds.contains(executionType))
+        LOG.log(Level.WARNING, "Unsupported execution type id '"
+            + executionType + "'");
+
+      ModelNode modelNode = new ModelNode(workflowNode.getFile());
+      modelNode.setModelId(modelId);
+      modelNode.setModelName(modelName);
+      modelNode.setExecutionType(executionType);
+      modelNode.setStaticMetadata(staticMetadata);
+      modelNode.setExcusedSubProcessorIds(excused);
+      modelNode.setInstanceClass(clazz);
+      modelNode.setEntryPoint(entryPoint);
+      modelNode.setTimeout(timeout);
+      modelNode.setOptional(optional);
+
+      loadConfiguration(rootElements, workflowNode, modelNode, globalConfGroups);
+
+      graph = new ModelGraph(modelNode);
+
+      boolean loadedPreConditions = false;
+      NodeList children = workflowNode.getElement().getChildNodes();
+      for (int i = 0; i < children.getLength(); i++) {
+        Node curChild = children.item(i);
+        if (curChild.getNodeType() == Node.ELEMENT_NODE) {
+          if (curChild.getNodeName().equals("conditions")) {
+            boolean isPreCondition = !loadedPreConditions;
+            String type = ((Element) curChild).getAttribute("type");
+            if (type.length() > 0)
+              isPreCondition = type.toLowerCase().equals("pre");
+            if (isPreCondition)
+              graph.setPreConditions(this.loadGraph(rootElements,
+                  new FileBasedElement(workflowNode.getFile(),
+                      (Element) curChild), new Metadata(staticMetadata),
+                  globalConfGroups, supportedProcessorIds));
+            else
+              graph.setPostConditions(this.loadGraph(rootElements,
+                  new FileBasedElement(workflowNode.getFile(),
+                      (Element) curChild), new Metadata(staticMetadata),
+                  globalConfGroups, supportedProcessorIds));
+            loadedPreConditions = true;
+          } else if (!curChild.getNodeName().equals("configuration")
+              && !curChild.getNodeName().equals("requiredMetFields")) {
+            graph.addChild(this.loadGraph(rootElements, new FileBasedElement(
+                workflowNode.getFile(), (Element) curChild), new Metadata(
+                staticMetadata), globalConfGroups, supportedProcessorIds));
+
+          }
+        }
+      }
+
+    } else if (modelIdRef != null) {
+      graph = this.findGraph(rootElements, modelIdRef, new Metadata(
+          staticMetadata), globalConfGroups, supportedProcessorIds);
+      if (graph == null)
+        throw new Exception("Workflow '" + modelIdRef
+            + "' has not been defined in this context");
+      graph.setIsRef(true);
+      graph.getModel().setStaticMetadata(new Metadata());
+      loadConfiguration(rootElements, workflowNode, graph.getModel(),
+          globalConfGroups);
+      graph.getModel().setAlias(alias);
+    }
+
+    if (entryPoint && graph.getParent() != null) {
+      this.graphs.add(graph);
+    }
+
+    return graph;
+  }
+
+  protected ModelGraph findGraph(List<FileBasedElement> rootElements,
+      String modelIdRef, Metadata staticMetadata,
+      HashMap<String, ConfigGroup> globalConfGroups,
+      Set<String> supportedProcessorIds) throws Exception {
+    XPath xpath = XPathFactory.newInstance().newXPath();
+    XPathExpression expr = xpath.compile("//*[@id = '" + modelIdRef + "']");
+    for (FileBasedElement rootElement : rootElements) {
+      Node node = (Node) expr.evaluate(rootElement.getElement(),
+          XPathConstants.NODE);
+      if (node != null) {
+        return this.loadGraph(rootElements,
+            new FileBasedElement(rootElement.getFile(), (Element) node),
+            staticMetadata, globalConfGroups, supportedProcessorIds);
+      }
+    }
+    return null;
+  }
+
+  private void loadConfiguration(List<FileBasedElement> rootElements,
+      FileBasedElement workflowNode, ModelNode modelNode,
+      HashMap<String, ConfigGroup> globalConfGroups) throws Exception {
+    NodeList children = workflowNode.getElement().getChildNodes();
+    for (int i = 0; i < children.getLength(); i++) {
+      Node curChild = children.item(i);
+      if (curChild.getNodeName().equals("configuration")) {
+        Metadata curMetadata = new Metadata();
+        if (modelNode != null
+            && !((Element) curChild).getAttribute("extends").equals(""))
+          modelNode.setExtendsConfig(Arrays.asList(((Element) curChild)
+              .getAttribute("extends").split(",")));
+        curMetadata.replaceMetadata(this.loadConfiguration(rootElements,
+            curChild, globalConfGroups));
+        if (!((Element) curChild).getAttribute("name").equals("")) {
+          ConfigGroup configGroup = new ConfigGroup(
+              ((Element) curChild).getAttribute("name"), curMetadata);
+          if (modelNode != null) {
+            List<String> extendsConfig = new Vector<String>(
+                modelNode.getExtendsConfig());
+            configGroup.addAllExtends(extendsConfig);
+            extendsConfig.add(configGroup.getName());
+            modelNode.setExtendsConfig(extendsConfig);
+          }
+          globalConfGroups.put(((Element) curChild).getAttribute("name"),
+              configGroup);
+        } else if (modelNode != null) {
+          modelNode.setStaticMetadata(curMetadata);
+        }
+      }
+    }
+  }
+
+  private Metadata loadConfiguration(List<FileBasedElement> rootElements,
+      Node configNode, HashMap<String, ConfigGroup> globalConfGroups)
+      throws Exception {
+    Metadata curMetadata = new Metadata();
+    NodeList curGrandChildren = configNode.getChildNodes();
+    for (int k = 0; k < curGrandChildren.getLength(); k++) {
+      if (curGrandChildren.item(k).getNodeName().equals("property")) {
+        Element property = (Element) curGrandChildren.item(k);
+        String delim = property.getAttribute("delim");
+        String envReplace = property.getAttribute("envReplace");
+        String name = property.getAttribute("name");
+        String value = property.getAttribute("value");
+        if (Boolean.parseBoolean(envReplace))
+          curMetadata.replaceMetadata(name + "/envReplace", "true");
+        List<String> values = new Vector<String>();
+        if (delim.length() > 0)
+          values.addAll(Arrays.asList(value.split("\\" + delim)));
+        else
+          values.add(value);
+        curMetadata.replaceMetadata(name, values);
+      }
+    }
+    return curMetadata;
+  }
+
+  private class FileBasedElement {
+
+    private File file;
+    private Element element;
+
+    public FileBasedElement(File file, Element element) {
+      this.file = file;
+      this.element = element;
+    }
+
+    public File getFile() {
+      return this.file;
+    }
+
+    public Element getElement() {
+      return this.element;
+    }
+
+  }
+
+  public class ConfigGroup {
+
+    private String name;
+    private Metadata metadata;
+    private List<String> extendsConfig;
+
+    public ConfigGroup(String name, Metadata metadata) {
+      this.name = name;
+      this.metadata = metadata;
+      this.extendsConfig = new Vector<String>();
+    }
+
+    public String getName() {
+      return this.name;
+    }
+
+    public Metadata getMetadata() {
+      return this.metadata;
+    }
+
+    public void addExtends(String child) {
+      this.extendsConfig.add(child);
+    }
+
+    public void addAllExtends(List<String> children) {
+      this.extendsConfig.addAll(children);
+    }
+
+    public void removeExtends(String child) {
+      this.extendsConfig.remove(child);
+    }
+
+    public List<String> getExtends() {
+      return this.extendsConfig;
+    }
+
+    public int hashCode() {
+      return this.name.hashCode();
+    }
+
+    public boolean equals(Object obj) {
+      if (obj instanceof ConfigGroup) {
+        ConfigGroup comp = (ConfigGroup) obj;
+        return comp.name.equals(this.name);
+      } else
+        return false;
+    }
+
+    public String toString() {
+      return this.name;
+    }
+
+  }
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/model/repo/XmlWorkflowModelRepositoryFactory.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/model/repo/XmlWorkflowModelRepositoryFactory.java
new file mode 100644
index 0000000..704b071
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/model/repo/XmlWorkflowModelRepositoryFactory.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.model.repo;
+
+//JDK imports
+import java.io.File;
+
+/**
+ * 
+ * Factory for creating xml model repositories
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class XmlWorkflowModelRepositoryFactory {
+
+  private String workspace;
+
+  public XmlWorkflowModelRepository createModelRepository() {
+    if (workspace == null)
+      return null;
+    if (!new File(workspace).exists())
+      new File(workspace).mkdirs();
+    return new XmlWorkflowModelRepository(new File(workspace));
+  }
+
+  public void setWorkspace(String workspace) {
+    this.workspace = workspace;
+  }
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/MultiStatePerspective.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/MultiStatePerspective.java
new file mode 100644
index 0000000..b55f7f5
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/MultiStatePerspective.java
@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective;
+
+//JDK imports
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+//OODT imports
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewState;
+import org.apache.oodt.cas.workflow.gui.perspective.view.View.Mode;
+
+/**
+ * 
+ * A multi-state display perspective.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public abstract class MultiStatePerspective extends Perspective {
+
+  private static final long serialVersionUID = -6410768713084872977L;
+
+  private final Map<String, ViewState> states = new HashMap<String, ViewState>();
+
+  private Mode mode;
+
+  public MultiStatePerspective(String name) {
+    super(name);
+    this.mode = Mode.EDIT;
+  }
+
+  public void addState(ViewState state) {
+    this.setState(state);
+  }
+
+  public void setState(ViewState state) {
+    super.setState(state);
+    state.setMode(this.mode);
+    if (!this.states.containsKey(state.getId())) {
+      this.states.put(state.getId(), state);
+      this.handleAddState(state);
+    }
+  }
+
+  public void removeState(ViewState state) {
+    this.states.remove(state);
+    this.handleRemoveState(state);
+    super.setState(this.getActiveState());
+  }
+
+  public ViewState getState(String stateId) {
+    return this.states.get(stateId);
+  }
+
+  public List<ViewState> getStates() {
+    return new Vector<ViewState>(this.states.values());
+  }
+
+  public Set<String> getStateIds() {
+    return this.states.keySet();
+  }
+
+  public void setMode(Mode mode) {
+    this.mode = mode;
+    for (ViewState state : states.values())
+      state.setMode(mode);
+    this.refresh();
+  }
+
+  public void reset() {
+    super.reset();
+    this.mode = Mode.EDIT;
+    states.clear();
+  }
+
+  public abstract void handleAddState(ViewState state);
+
+  public abstract void handleRemoveState(ViewState state);
+
+  public abstract ViewState getActiveState();
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/Perspective.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/Perspective.java
new file mode 100644
index 0000000..c19eda0
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/Perspective.java
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective;
+
+//JDK imports
+import javax.swing.JPanel;
+
+//OODT imports
+import org.apache.oodt.cas.workflow.gui.perspective.view.View;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewListener;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewState;
+import org.apache.oodt.cas.workflow.gui.perspective.view.View.Mode;
+
+/**
+ * 
+ * 
+ * A view listener and jpanel for keeping the Workflow perspective in sync.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public abstract class Perspective extends JPanel implements ViewListener {
+
+  private static final long serialVersionUID = -3343805159396435882L;
+
+  private ViewState state;
+  private String name;
+
+  public Perspective(String name) {
+    this.name = name;
+  }
+
+  public String getName() {
+    return this.name;
+  }
+
+  public ViewState getState() {
+    return this.state;
+  }
+
+  public void setState(ViewState state) {
+    this.state = state;
+  }
+
+  public void setMode(Mode mode) {
+    this.state.setMode(mode);
+    this.refresh();
+  }
+
+  public void save() {
+    if (this.state != null)
+      this.state.save();
+  }
+
+  public void undo() {
+    if (this.state != null) {
+      this.state.undo();
+      this.refresh();
+    }
+  }
+
+  public void reset() {
+    this.state = null;
+  }
+
+  public abstract void refresh();
+
+  public abstract View getActiveView();
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/build/BuildPerspective.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/build/BuildPerspective.java
new file mode 100644
index 0000000..2d7416b
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/build/BuildPerspective.java
@@ -0,0 +1,425 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.build;
+
+//JDK imports
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JSplitPane;
+import javax.swing.JTabbedPane;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+//OODT imports
+import org.apache.oodt.cas.workflow.gui.model.ModelGraph;
+import org.apache.oodt.cas.workflow.gui.perspective.MultiStatePerspective;
+import org.apache.oodt.cas.workflow.gui.perspective.view.MultiStateView;
+import org.apache.oodt.cas.workflow.gui.perspective.view.View;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewChange;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewListener;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewState;
+import org.apache.oodt.cas.workflow.gui.perspective.view.impl.DefaultPropView;
+import org.apache.oodt.cas.workflow.gui.perspective.view.impl.DefaultTreeView;
+import org.apache.oodt.cas.workflow.gui.perspective.view.impl.GlobalConfigView;
+import org.apache.oodt.cas.workflow.gui.perspective.view.impl.GraphView;
+import org.apache.oodt.cas.workflow.gui.perspective.view.impl.TreeProjectView;
+import org.apache.oodt.cas.workflow.gui.util.GuiUtils;
+
+/**
+ * 
+ * The default build perspective.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class BuildPerspective extends MultiStatePerspective {
+
+  private static final long serialVersionUID = 3387632819576057527L;
+
+  private View projectView;
+  private View globalConfigView;
+  private Class<? extends View> projectViewClass;
+  private Class<? extends View> mainViewClass;
+  private Class<? extends View> treeViewClass;
+  private Class<? extends View> propViewClass;
+  private Class<? extends View> globalViewClass;
+
+  private HashMap<ViewState, BuildPanel> stateViews;
+  private ViewState activeState;
+
+  public static final int MAIN_VIEW = 1;
+
+  private static final int WIDTH = 1000;
+  private static final int HEIGHT = 700;
+
+  private JSplitPane projectSplitPane;
+
+  private boolean findSelectedInTab = false;
+
+  public BuildPerspective() throws InstantiationException,
+      IllegalAccessException {
+    this(TreeProjectView.class, GraphView.class, DefaultTreeView.class,
+        DefaultPropView.class, GlobalConfigView.class);
+  }
+
+  public BuildPerspective(Class<? extends View> projectViewClass,
+      Class<? extends View> mainViewClass, Class<? extends View> treeViewClass,
+      Class<? extends View> propViewClass, Class<? extends View> globalViewClass)
+      throws InstantiationException, IllegalAccessException {
+    super("Build");
+    this.projectViewClass = projectViewClass;
+    this.mainViewClass = mainViewClass;
+    this.treeViewClass = treeViewClass;
+    this.propViewClass = propViewClass;
+    this.globalViewClass = globalViewClass;
+    this.stateViews = new HashMap<ViewState, BuildPanel>();
+    this.projectView = this.createProjectView();
+    this.projectView.setPreferredSize(new Dimension(WIDTH / 10, HEIGHT / 2));
+    this.globalConfigView = this.createGlobalConfigView();
+    this.globalConfigView
+        .setPreferredSize(new Dimension(WIDTH / 10, HEIGHT / 2));
+  }
+
+  public void reset() {
+    super.reset();
+    this.activeState = null;
+    this.stateViews.clear();
+    this.projectView = this.createProjectView();
+    this.globalConfigView = this.createGlobalConfigView();
+  }
+
+  public void stateChangeNotify(ViewChange<?> change) {
+    if (change instanceof ViewChange.NEW_ACTIVE_STATE) {
+      this.activeState = ((ViewChange.NEW_ACTIVE_STATE) change).getObject();
+      this.refresh();
+    } else if (change instanceof ViewChange.REFRESH_VIEW) {
+      this.refresh();
+    } else if (change instanceof ViewChange.STATE_NAME_CHANGE) {
+      ViewState state = ((ViewChange.STATE_NAME_CHANGE) change).getObject();
+      this.refresh();
+    } else if (change instanceof ViewChange.VIEW_MODEL) {
+      String modelId = ((ViewChange.VIEW_MODEL) change).getObject();
+      for (ViewState state : this.stateViews.keySet()) {
+        for (ModelGraph graph : state.getGraphs()) {
+          ModelGraph found = graph.recursiveFindByModelId(modelId);
+          if (found != null && !found.getModel().isRef()) {
+            this.activeState = state;
+            this.activeState.setSelected(found);
+            break;
+          }
+        }
+      }
+      this.findSelectedInTab = true;
+      this.refresh();
+      this.findSelectedInTab = false;
+    }
+  }
+
+  @Override
+  public ViewState getActiveState() {
+    return this.activeState;
+  }
+
+  public View getActiveView() {
+    if (this.getActiveState() != null)
+      return this.stateViews.get(this.getActiveState()).getActiveView();
+    else
+      return null;
+  }
+
+  @Override
+  public void handleAddState(final ViewState state) {
+    this.activeState = state;
+    BuildPanel buildPanel = new BuildPanel(state);
+    this.stateViews.put(state, buildPanel);
+    this.refresh();
+  }
+
+  @Override
+  public void handleRemoveState(ViewState state) {
+    this.stateViews.remove(state);
+    if (this.stateViews.size() > 0)
+      this.activeState = this.stateViews.keySet().iterator().next();
+    else
+      this.activeState = null;
+    this.refresh();
+  }
+
+  @Override
+  public void refresh() {
+    this.save();
+    this.removeAll();
+    this.setLayout(new BorderLayout());
+    JPanel panel = null;
+    if (this.activeState != null) {
+      BuildPanel buildPanel = this.stateViews.get(this.activeState);
+      buildPanel.refresh();
+      panel = buildPanel;
+    } else {
+      panel = new JPanel();
+    }
+
+    if (this.projectView instanceof MultiStateView)
+      ((MultiStateView) this.projectView).refreshView(this.activeState,
+          this.getStates());
+    else
+      this.projectView.refreshView(this.activeState);
+
+    this.globalConfigView.refreshView(this.activeState);
+
+    JPanel globalPanel = new JPanel();
+    globalPanel.setLayout(new BorderLayout());
+    globalPanel.add(this.projectView, BorderLayout.CENTER);
+    globalPanel.add(this.globalConfigView, BorderLayout.SOUTH);
+
+    int dividerLocation = -1;
+    if (projectSplitPane != null)
+      dividerLocation = projectSplitPane.getDividerLocation();
+    projectSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, globalPanel,
+        panel);
+    if (dividerLocation != -1)
+      projectSplitPane.setDividerLocation(dividerLocation);
+    this.add(projectSplitPane, BorderLayout.CENTER);
+
+    this.revalidate();
+
+  }
+
+  private View createMainView(String name) {
+    try {
+      return this.mainViewClass.getConstructor(String.class).newInstance(name);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  private View createTreeView() {
+    try {
+      return this.treeViewClass.getConstructor(String.class).newInstance(
+          this.treeViewClass.getSimpleName());
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  private View createGlobalConfigView() {
+    try {
+      return this.globalViewClass.getConstructor(String.class).newInstance(
+          this.globalViewClass.getSimpleName());
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  private View createProjectView() {
+    try {
+      View view = this.projectViewClass.getConstructor(String.class)
+          .newInstance(this.projectViewClass.getSimpleName());
+      view.registerListener(this);
+      return view;
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  private View createPropView() {
+    try {
+      return this.propViewClass.getConstructor(String.class).newInstance(
+          this.propViewClass.getSimpleName());
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  private class BuildPanel extends JPanel implements ViewListener {
+
+    private static final long serialVersionUID = -6120047959962567963L;
+
+    private JTabbedPane tabbedPane;
+    private Map<View, ViewState> mainViews;
+    private View propView;
+    private View treeView;
+
+    private View primaryMainView;
+
+    private JPopupMenu closeTabPopup;
+
+    private ViewState state;
+
+    public BuildPanel(ViewState state) {
+      this.state = state;
+
+      mainViews = new HashMap<View, ViewState>();
+
+      propView = createPropView();
+      propView.registerListener(this);
+
+      treeView = createTreeView();
+      treeView.registerListener(this);
+
+      tabbedPane = new JTabbedPane();
+
+      this.addMainView(createMainView(state.getFile().getName()), state);
+
+      closeTabPopup = new JPopupMenu();
+      JMenuItem closeItem = new JMenuItem("Close");
+      closeItem.addActionListener(new ActionListener() {
+
+        public void actionPerformed(ActionEvent e) {
+          View mainView = (View) BuildPanel.this.tabbedPane
+              .getSelectedComponent();
+          BuildPanel.this.removeMainView(mainView);
+        }
+
+      });
+      closeTabPopup.add(closeItem);
+
+      this.tabbedPane.addMouseListener(new MouseListener() {
+
+        public void mouseClicked(MouseEvent e) {
+          if (e.getButton() == MouseEvent.BUTTON3
+              && !BuildPanel.this.tabbedPane.getSelectedComponent().equals(
+                  BuildPanel.this.primaryMainView)) {
+            closeTabPopup.show(BuildPanel.this.tabbedPane, e.getX(), e.getY());
+          }
+        }
+
+        public void mouseEntered(MouseEvent e) {
+        }
+
+        public void mouseExited(MouseEvent e) {
+        }
+
+        public void mousePressed(MouseEvent e) {
+        }
+
+        public void mouseReleased(MouseEvent e) {
+        }
+
+      });
+
+      this.tabbedPane.addChangeListener(new ChangeListener() {
+
+        public void stateChanged(ChangeEvent e) {
+          View activeView = (View) BuildPanel.this.tabbedPane
+              .getSelectedComponent();
+          activeView.notifyListeners();
+        }
+
+      });
+
+      treeView.setPreferredSize(new Dimension(WIDTH / 10, HEIGHT / 2));
+      propView.setPreferredSize(new Dimension(WIDTH / 10, HEIGHT / 2));
+      JSplitPane treePropPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
+          treeView, propView);
+      treePropPane.setResizeWeight(.25);
+      tabbedPane.setPreferredSize(new Dimension(WIDTH, HEIGHT));
+      JSplitPane mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
+          tabbedPane, treePropPane);
+      mainSplitPane.setResizeWeight(.75);
+      this.setLayout(new BorderLayout());
+      this.add(mainSplitPane, BorderLayout.CENTER);
+    }
+
+    public void addMainView(View mainView, ViewState state) {
+      if (this.mainViews.size() == 0) {
+        this.primaryMainView = mainView;
+        mainView.setPrimary(true);
+      }
+      this.mainViews.put(mainView, state);
+      this.tabbedPane.addTab(mainView.getName(), mainView);
+      this.tabbedPane.setSelectedComponent(mainView);
+      mainView.registerListener(this);
+    }
+
+    public void removeMainView(View mainView) {
+      if (!this.primaryMainView.equals(mainView)) {
+        for (int i = 0; i < this.tabbedPane.getTabCount(); i++) {
+          if (mainView.getName().equals(this.tabbedPane.getTitleAt(i))) {
+            this.tabbedPane.removeTabAt(i);
+            this.mainViews.remove(mainView);
+            return;
+          }
+        }
+      }
+    }
+
+    public View getActiveView() {
+      return (View) this.tabbedPane.getSelectedComponent();
+    }
+
+    public void refresh() {
+      if (this.getActiveView() != null) {
+        ViewState viewState = null;
+        if (this.state.getSelected() != null && findSelectedInTab) {
+          TOP: for (Entry<View, ViewState> entry : this.mainViews.entrySet()) {
+            for (ModelGraph graph : entry.getValue().getGraphs()) {
+              ModelGraph found = graph.recursiveFindByModelId(this.state
+                  .getSelected().getModel().getModelId());
+              if (found != null && !found.getModel().isRef()) {
+                viewState = entry.getValue();
+                viewState.setSelected(found);
+                this.tabbedPane.setSelectedComponent(entry.getKey());
+                break TOP;
+              }
+            }
+          }
+        } else {
+          viewState = this.mainViews.get(this.getActiveView());
+        }
+        this.getActiveView().refreshView(viewState);
+        this.propView.refreshView(viewState);
+        this.treeView.refreshView(viewState);
+      }
+      this.revalidate();
+    }
+
+    public void stateChangeNotify(ViewChange<?> change) {
+      if (change instanceof ViewChange.NEW_VIEW) {
+        this.addMainView(
+            createMainView(((ViewChange.NEW_VIEW) change).getObject()
+                .getModel().getModelId()),
+            new ViewState(this.state.getFile(), null, Collections
+                .singletonList(GuiUtils.find(this.state.getGraphs(),
+                    ((ViewChange.NEW_VIEW) change).getObject().getModel()
+                        .getId())), this.state.getGlobalConfigGroups()));
+        this.refresh();
+      }
+      BuildPerspective.this.stateChangeNotify(change);
+    }
+
+  }
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/MultiStateView.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/MultiStateView.java
new file mode 100644
index 0000000..ae34285
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/MultiStateView.java
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.view;
+
+//JDK imports
+import java.util.List;
+
+/**
+ * 
+ * A multi-state view.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public abstract class MultiStateView extends View {
+
+  public MultiStateView(String name) {
+    super(name);
+  }
+
+  private static final long serialVersionUID = -6968627851727698540L;
+
+  public void refreshView(ViewState state) {
+    throw new RuntimeException(
+        "This is a mutli-state view -- call multi-state view refreshView method!");
+  }
+
+  public abstract void refreshView(ViewState activeState, List<ViewState> states);
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/View.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/View.java
new file mode 100644
index 0000000..b669267
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/View.java
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.view;
+
+//JDK imports
+import java.util.UUID;
+import java.util.Vector;
+import javax.swing.JPanel;
+
+/**
+ * 
+ * 
+ * View abstract base class.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public abstract class View extends JPanel {
+
+  private static final long serialVersionUID = -708692459667309413L;
+
+  public enum Mode {
+    DELETE, EDIT, MOVE, ZOOM_IN, ZOOM_OUT;
+  }
+
+  private Vector<ViewListener> listeners;
+  private String id;
+  private boolean isPrimary;
+
+  public static final String DISPLAY_GRAPH_IDS = "DisplayGraphIds";
+
+  public View(String name) {
+    super();
+    this.id = UUID.randomUUID().toString();
+    if (name != null)
+      this.setName(name);
+    this.listeners = new Vector<ViewListener>();
+  }
+
+  public void setPrimary(boolean isPrimary) {
+    this.isPrimary = isPrimary;
+  }
+
+  public boolean isPrimary() {
+    return this.isPrimary;
+  }
+
+  public String getId() {
+    return this.id;
+  }
+
+  public void registerListener(ViewListener listener) {
+    listeners.add(listener);
+  }
+
+  public void deregisterListener(ViewListener listener) {
+    this.listeners.remove(listener);
+  }
+
+  public void notifyListeners(ViewChange<?> change) {
+    for (ViewListener listener : listeners)
+      listener.stateChangeNotify(change);
+  }
+
+  public void notifyListeners() {
+    this.notifyListeners(new ViewChange.REFRESH_VIEW(this, this));
+  }
+
+  public int hashCode() {
+    return this.id.hashCode();
+  }
+
+  public boolean equals(Object obj) {
+    return obj instanceof View && ((View) obj).id.equals(this.id);
+  }
+
+  public abstract void refreshView(ViewState state);
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/ViewChange.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/ViewChange.java
new file mode 100644
index 0000000..5fd2a15
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/ViewChange.java
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.view;
+
+//OODT imports
+import org.apache.oodt.cas.workflow.gui.model.ModelGraph;
+
+/**
+ * 
+ * Represents a change in the Workflow GUI model view.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public abstract class ViewChange<T> {
+
+  private T object;
+  private View source;
+
+  protected ViewChange(T object, View source) {
+    this.object = object;
+    this.source = source;
+  }
+
+  public T getObject() {
+    return this.object;
+  }
+
+  public View getSource() {
+    return this.source;
+  }
+
+  public static final class NEW_VIEW extends ViewChange<ModelGraph> {
+    public NEW_VIEW(ModelGraph object, View source) {
+      super(object, source);
+    }
+  }
+
+  public static final class DELETE_VIEW extends ViewChange<View> {
+    public DELETE_VIEW(View object, View source) {
+      super(object, source);
+    }
+  }
+
+  public static final class REFRESH_VIEW extends ViewChange<View> {
+    public REFRESH_VIEW(View object, View source) {
+      super(object, source);
+    }
+  }
+
+  public static final class NEW_STATE extends ViewChange<ViewState> {
+    public NEW_STATE(ViewState object, View source) {
+      super(object, source);
+    }
+  }
+
+  public static final class REMOVE_STATE extends ViewChange<ViewState> {
+    public REMOVE_STATE(ViewState object, View source) {
+      super(object, source);
+    }
+  }
+
+  public static final class NEW_ACTIVE_STATE extends ViewChange<ViewState> {
+    public NEW_ACTIVE_STATE(ViewState object, View source) {
+      super(object, source);
+    }
+  }
+
+  public static final class STATE_NAME_CHANGE extends ViewChange<ViewState> {
+    public STATE_NAME_CHANGE(ViewState object, View source) {
+      super(object, source);
+    }
+  }
+
+  public static final class VIEW_MODEL extends ViewChange<String> {
+    public VIEW_MODEL(String modelId, View source) {
+      super(modelId, source);
+    }
+  }
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/ViewListener.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/ViewListener.java
new file mode 100644
index 0000000..da88b27
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/ViewListener.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.view;
+
+/**
+ * 
+ * Interface for listening/notifying about Workflow View state changes.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public interface ViewListener {
+
+  public void stateChangeNotify(ViewChange<?> change);
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/ViewState.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/ViewState.java
new file mode 100644
index 0000000..1513b7e
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/ViewState.java
@@ -0,0 +1,260 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.view;
+
+//OODT imports
+import org.apache.oodt.cas.metadata.Metadata;
+import org.apache.oodt.cas.workflow.gui.model.ModelGraph;
+import org.apache.oodt.cas.workflow.gui.model.repo.XmlWorkflowModelRepository.ConfigGroup;
+import org.apache.oodt.cas.workflow.gui.perspective.view.View.Mode;
+import org.apache.oodt.cas.workflow.gui.util.GuiUtils;
+
+//JDK imports
+import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+import java.util.UUID;
+import java.util.Vector;
+
+/**
+ * 
+ * The current state of a particular Workflow GUI editor view.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class ViewState {
+
+  private static final HashMap<String, Stack<ViewState>> undoHistory = new HashMap<String, Stack<ViewState>>();
+
+  private ModelGraph selected;
+  private List<ModelGraph> graphs;
+  private String id;
+  private String currentMetGroup;
+  private Metadata properties;
+  private Mode mode;
+  private File file;
+  private boolean change = false;
+  private Map<String, ConfigGroup> globalConfigGroups;
+
+  public ViewState(File file, ModelGraph selected, List<ModelGraph> graphs,
+      Map<String, ConfigGroup> globalConfigGroups) {
+    this.selected = selected;
+    this.graphs = new Vector<ModelGraph>(graphs);
+    this.id = UUID.randomUUID().toString();
+    this.currentMetGroup = null;
+    this.properties = new Metadata();
+    this.mode = Mode.EDIT;
+    this.file = file;
+    this.globalConfigGroups = globalConfigGroups;
+  }
+
+  public Map<String, ConfigGroup> getGlobalConfigGroups() {
+    return this.globalConfigGroups;
+  }
+
+  public void addGlobalConfigGroup(String groupName, ConfigGroup configGroup) {
+    this.globalConfigGroups.put(groupName, configGroup);
+  }
+
+  public void removeGlobalConfigGroup(String groupName) {
+    this.globalConfigGroups.remove(groupName);
+  }
+
+  public File getFile() {
+    return this.file;
+  }
+
+  public String getId() {
+    return this.id;
+  }
+
+  public boolean containsProperty(String key) {
+    return this.properties.containsGroup(key);
+  }
+
+  public void setProperty(String key, String value) {
+    Vector<String> values = new Vector<String>();
+    values.add(value);
+    this.setProperty(key, values);
+  }
+
+  public void setProperty(String key, List<String> values) {
+    this.properties.replaceMetadata(key, values);
+    this.change = true;
+  }
+
+  public String getFirstPropertyValue(String key) {
+    List<String> values = this.getProperty(key);
+    if (values == null || values.size() == 0)
+      return null;
+    return values.get(0);
+  }
+
+  public List<String> getProperty(String key) {
+    return this.properties.getAllMetadata(key);
+  }
+
+  public void removeProperty(String key) {
+    this.properties.removeMetadata(key);
+    this.change = true;
+  }
+
+  public List<String> getKeysRecur(String subGroup) {
+    Vector<String> keys = new Vector<String>();
+    for (String key : this.properties.getAllKeys())
+      if (key.contains(subGroup))
+        keys.add(key);
+    return keys;
+  }
+
+  public void setSelected(ModelGraph selected) {
+    if (this.selected == null || selected == null
+        || !this.selected.equals(selected)) {
+      this.currentMetGroup = null;
+      this.selected = selected;
+      this.change = true;
+    }
+  }
+
+  public ModelGraph getSelected() {
+    if (this.mode.equals(Mode.EDIT))
+      return this.selected;
+    else
+      return null;
+  }
+
+  public Set<String> getGraphIds() {
+    HashSet<String> graphIds = new HashSet<String>();
+    for (ModelGraph graph : this.getGraphs())
+      graphIds.add(graph.getModel().getModelId());
+    return graphIds;
+  }
+
+  public List<ModelGraph> getGraphs() {
+    return this.graphs;
+  }
+
+  public void removeGraph(ModelGraph graph) {
+    this.graphs.remove(graph);
+  }
+
+  public void addGraph(ModelGraph graph) {
+    this.graphs.add(graph);
+  }
+
+  public void setMode(Mode mode) {
+    this.mode = mode;
+    this.change = true;
+  }
+
+  public Mode getMode() {
+    return this.mode;
+  }
+
+  public void setCurrentMetGroup(String currentMetGroup) {
+    this.currentMetGroup = currentMetGroup;
+    this.change = true;
+  }
+
+  public String getCurrentMetGroup() {
+    return this.currentMetGroup;
+  }
+
+  public boolean hasChanged() {
+    return this.change;
+  }
+
+  public void save() {
+    if (this.change) {
+      Stack<ViewState> stack = undoHistory.get(this.id);
+      if (stack == null)
+        stack = new Stack<ViewState>();
+      if (stack.size() >= 100)
+        stack.remove(stack.size() - 1);
+      stack.push(this.clone());
+      undoHistory.put(this.id, stack);
+      this.change = false;
+    }
+  }
+
+  public void undo() {
+    Stack<ViewState> stack = undoHistory.get(this.id);
+    if (stack != null && !stack.empty()) {
+      this.clone(stack.pop());
+      System.out.println(this.getGraphIds());
+      this.change = false;
+    }
+  }
+
+  public void clone(ViewState state) {
+    this.graphs = null;
+    this.selected = null;
+    if (state.graphs != null) {
+      this.graphs = new Vector<ModelGraph>();
+      for (ModelGraph graph : state.graphs)
+        this.graphs.add(graph.clone());
+      if (state.selected != null)
+        this.selected = GuiUtils.find(this.graphs, state.selected.getModel()
+            .getModelId());
+    }
+    this.properties = new Metadata(state.properties);
+    this.id = state.id;
+    this.currentMetGroup = state.currentMetGroup;
+    this.mode = state.mode;
+  }
+
+  public ViewState clone() {
+    List<ModelGraph> cloneGraphs = null;
+    ModelGraph selected = null;
+    if (this.graphs != null) {
+      cloneGraphs = new Vector<ModelGraph>();
+      for (ModelGraph graph : this.graphs)
+        cloneGraphs.add(graph.clone());
+      if (this.selected != null)
+        selected = GuiUtils.find(cloneGraphs, this.selected.getModel()
+            .getModelId());
+    }
+    ViewState clone = new ViewState(this.file, selected, cloneGraphs,
+        this.globalConfigGroups);
+    clone.id = this.id;
+    clone.file = this.file;
+    clone.currentMetGroup = this.currentMetGroup;
+    clone.properties = new Metadata(this.properties);
+    clone.mode = this.mode;
+    return clone;
+  }
+
+  public int hashCode() {
+    return this.id.hashCode();
+  }
+
+  public boolean equals(Object obj) {
+    return obj instanceof ViewState && this.id.equals(((ViewState) obj).id);
+  }
+
+  public String toString() {
+    return this.getId();
+  }
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/DefaultPropView.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/DefaultPropView.java
new file mode 100644
index 0000000..cacfae9
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/DefaultPropView.java
@@ -0,0 +1,853 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.view.impl;
+
+//JDK imports
+import java.awt.BorderLayout;
+import java.awt.Checkbox;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.text.DecimalFormat;
+import java.text.Format;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Vector;
+import javax.swing.BoxLayout;
+import javax.swing.DefaultListModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JSlider;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.border.EtchedBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+
+//Apache imports
+import org.apache.commons.lang.StringUtils;
+
+//OODT imports
+import org.apache.oodt.cas.metadata.Metadata;
+import org.apache.oodt.cas.workflow.gui.model.ModelGraph;
+import org.apache.oodt.cas.workflow.gui.perspective.view.View;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewState;
+import org.apache.oodt.cas.workflow.gui.util.GuiUtils;
+
+/**
+ * 
+ * 
+ * The default view displaying a workflow property (for a task, or a condition,
+ * or set of workflows).
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class DefaultPropView extends View {
+
+  private static final long serialVersionUID = -5521047300551974898L;
+
+  private JTable table;
+  private JPopupMenu tableMenu;
+  private JMenuItem override;
+  private JMenuItem delete;
+  private static final String OVERRIDE = "Override";
+  private static final String DELETE = "Delete";
+  private int DEFAULT_PRIORITY = 5;
+
+  public DefaultPropView(String name) {
+    super(name);
+    this.setLayout(new BorderLayout());
+  }
+
+  private JTable createTable(final ViewState state) {
+    JTable table = null;
+    final ModelGraph selected = state.getSelected();
+    if (selected != null) {
+      final Vector<Vector<String>> rows = new Vector<Vector<String>>();
+      HashMap<String, String> keyToGroupMap = new HashMap<String, String>();
+      Metadata staticMet = selected.getModel().getStaticMetadata();
+      Metadata inheritedMet = selected.getInheritedStaticMetadata(state);
+      Metadata completeMet = new Metadata();
+      if (staticMet != null) {
+        completeMet.replaceMetadata(staticMet.getSubMetadata(state
+            .getCurrentMetGroup()));
+      }
+      if (selected.getModel().getExtendsConfig() != null) {
+        for (String configGroup : selected.getModel().getExtendsConfig()) {
+          Metadata extendsMetadata = state.getGlobalConfigGroups()
+              .get(configGroup).getMetadata()
+              .getSubMetadata(state.getCurrentMetGroup());
+          for (String key : extendsMetadata.getAllKeys()) {
+            if (!completeMet.containsKey(key)) {
+              keyToGroupMap.put(key, configGroup);
+              completeMet.replaceMetadata(key,
+                  extendsMetadata.getAllMetadata(key));
+            }
+          }
+        }
+      }
+      if (inheritedMet != null) {
+        Metadata inheritedMetadata = inheritedMet.getSubMetadata(state
+            .getCurrentMetGroup());
+        for (String key : inheritedMetadata.getAllKeys()) {
+          if (!completeMet.containsKey(key)) {
+            keyToGroupMap.put(key, "__inherited__");
+            completeMet.replaceMetadata(key,
+                inheritedMetadata.getAllMetadata(key));
+          }
+        }
+      }
+      List<String> keys = completeMet.getAllKeys();
+      Collections.sort(keys);
+      for (String key : keys) {
+        if (key.endsWith("/envReplace"))
+          continue;
+        String values = StringUtils.join(completeMet.getAllMetadata(key), ",");
+        Vector<String> row = new Vector<String>();
+        row.add(keyToGroupMap.get(key));
+        row.add(key);
+        row.add(values);
+        row.add(Boolean.toString(Boolean.parseBoolean(completeMet
+            .getMetadata(key + "/envReplace"))));
+        rows.add(row);
+      }
+      table = new JTable();// rows, new Vector<String>(Arrays.asList(new
+                           // String[] { "key", "values", "envReplace" })));
+      table.setModel(new AbstractTableModel() {
+        public String getColumnName(int col) {
+          switch (col) {
+          case 0:
+            return "group";
+          case 1:
+            return "key";
+          case 2:
+            return "values";
+          case 3:
+            return "envReplace";
+          default:
+            return null;
+          }
+        }
+
+        public int getRowCount() {
+          return rows.size() + 1;
+        }
+
+        public int getColumnCount() {
+          return 4;
+        }
+
+        public Object getValueAt(int row, int col) {
+          if (row >= rows.size())
+            return null;
+          String value = rows.get(row).get(col);
+          if (value == null && col == 3)
+            return "false";
+          if (value == null && col == 0)
+            return "__local__";
+          return value;
+        }
+
+        public boolean isCellEditable(int row, int col) {
+          if (row >= rows.size()) {
+            if (selected.getModel().getStaticMetadata()
+                .containsGroup(state.getCurrentMetGroup()))
+              return true;
+            else
+              return false;
+          }
+          if (col == 0)
+            return false;
+          String key = rows.get(row).get(1);
+          if (key == null
+              || (selected.getModel().getStaticMetadata() != null && selected
+                  .getModel().getStaticMetadata()
+                  .containsKey(getKey(key, state))))
+            return true;
+          return false;
+        }
+
+        public void setValueAt(Object value, int row, int col) {
+          if (row >= rows.size()) {
+            Vector<String> newRow = new Vector<String>(Arrays
+                .asList(new String[] { null, null, null, null }));
+            newRow.add(col, (String) value);
+            rows.add(newRow);
+          } else {
+            Vector<String> rowValues = rows.get(row);
+            rowValues.add(col, (String) value);
+            rowValues.remove(col + 1);
+          }
+          this.fireTableCellUpdated(row, col);
+        }
+
+      });
+      MyTableListener tableListener = new MyTableListener(state);
+      table.getModel().addTableModelListener(tableListener);
+      table.getSelectionModel().addListSelectionListener(tableListener);
+    } else {
+      table = new JTable(new Vector<Vector<String>>(), new Vector<String>(
+          Arrays.asList(new String[] { "key", "values", "envReplace" })));
+    }
+
+    // table.setFillsViewportHeight(true);
+    table.setSelectionBackground(Color.cyan);
+    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+    TableCellRenderer cellRenderer = new TableCellRenderer() {
+
+      public Component getTableCellRendererComponent(JTable table,
+          Object value, boolean isSelected, boolean hasFocus, int row,
+          int column) {
+        JLabel field = new JLabel((String) value);
+        if (column == 0) {
+          field.setForeground(Color.gray);
+        } else {
+          if (isSelected)
+            field.setBorder(new EtchedBorder(1));
+          if (table.isCellEditable(row, 1))
+            field.setForeground(Color.black);
+          else
+            field.setForeground(Color.gray);
+        }
+        return field;
+      }
+
+    };
+    TableColumn groupCol = table.getColumnModel().getColumn(0);
+    groupCol.setPreferredWidth(75);
+    groupCol.setCellRenderer(cellRenderer);
+    TableColumn keyCol = table.getColumnModel().getColumn(1);
+    keyCol.setPreferredWidth(200);
+    keyCol.setCellRenderer(cellRenderer);
+    TableColumn valuesCol = table.getColumnModel().getColumn(2);
+    valuesCol.setPreferredWidth(300);
+    valuesCol.setCellRenderer(cellRenderer);
+    TableColumn envReplaceCol = table.getColumnModel().getColumn(3);
+    envReplaceCol.setPreferredWidth(75);
+    envReplaceCol.setCellRenderer(cellRenderer);
+
+    table.addMouseListener(new MouseListener() {
+      public void mouseClicked(MouseEvent e) {
+        if (e.getButton() == MouseEvent.BUTTON3
+            && DefaultPropView.this.table.getSelectedRow() != -1) {
+          int row = DefaultPropView.this.table.getSelectedRow();// rowAtPoint(DefaultPropView.this.table.getMousePosition());
+          String key = getKey(
+              (String) DefaultPropView.this.table.getValueAt(row, 1), state);
+          Metadata staticMet = state.getSelected().getModel()
+              .getStaticMetadata();
+          override.setVisible(staticMet == null || !staticMet.containsKey(key));
+          delete.setVisible(staticMet != null && staticMet.containsKey(key));
+          tableMenu.show(DefaultPropView.this.table, e.getX(), e.getY());
+        }
+      }
+
+      public void mouseEntered(MouseEvent e) {
+      }
+
+      public void mouseExited(MouseEvent e) {
+      }
+
+      public void mousePressed(MouseEvent e) {
+      }
+
+      public void mouseReleased(MouseEvent e) {
+      }
+    });
+
+    return table;
+  }
+
+  @Override
+  public void refreshView(final ViewState state) {
+    this.removeAll();
+
+    tableMenu = new JPopupMenu("TableMenu");
+    this.add(tableMenu);
+    override = new JMenuItem(OVERRIDE);
+    override.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        int row = DefaultPropView.this.table.getSelectedRow();// rowAtPoint(DefaultPropView.this.table.getMousePosition());
+        String key = getKey(
+            (String) DefaultPropView.this.table.getValueAt(row, 1), state);
+        Metadata staticMet = state.getSelected().getModel().getStaticMetadata();
+        if (staticMet == null)
+          staticMet = new Metadata();
+        if (e.getActionCommand().equals(OVERRIDE)) {
+          if (!staticMet.containsKey(key)) {
+            staticMet.addMetadata(key,
+                (String) DefaultPropView.this.table.getValueAt(row, 2));
+            String envReplace = (String) DefaultPropView.this.table.getValueAt(
+                row, 3);
+            if (Boolean.valueOf(envReplace))
+              staticMet.addMetadata(key + "/envReplace", envReplace);
+            state.getSelected().getModel().setStaticMetadata(staticMet);
+            DefaultPropView.this.notifyListeners();
+          }
+        }
+      }
+    });
+    delete = new JMenuItem(DELETE);
+    delete.addActionListener(new ActionListener() {
+
+      public void actionPerformed(ActionEvent e) {
+        int row = DefaultPropView.this.table.getSelectedRow();// rowAtPoint(DefaultPropView.this.table.getMousePosition());
+        String key = getKey(
+            (String) DefaultPropView.this.table.getValueAt(row, 1), state);
+        Metadata staticMet = state.getSelected().getModel().getStaticMetadata();
+        if (staticMet == null)
+          staticMet = new Metadata();
+        staticMet.removeMetadata(key);
+        staticMet.removeMetadata(key + "/envReplace");
+        state.getSelected().getModel().setStaticMetadata(staticMet);
+        DefaultPropView.this.notifyListeners();
+      }
+
+    });
+    tableMenu.add(override);
+    tableMenu.add(delete);
+
+    if (state.getSelected() != null) {
+      JPanel masterPanel = new JPanel();
+      masterPanel.setLayout(new BoxLayout(masterPanel, BoxLayout.Y_AXIS));
+      masterPanel.add(this.getModelIdPanel(state.getSelected(), state));
+      masterPanel.add(this.getModelNamePanel(state.getSelected(), state));
+      if (!state.getSelected().getModel().isParentType())
+        masterPanel.add(this.getInstanceClassPanel(state.getSelected(), state));
+      masterPanel.add(this.getExecutionTypePanel(state.getSelected(), state));
+      masterPanel.add(this.getPriorityPanel(state));
+      masterPanel.add(this.getExecusedIds(state.getSelected()));
+      if (state.getSelected().getModel().getExecutionType().equals("condition")) {
+        masterPanel.add(this.getTimeout(state.getSelected(), state));
+        masterPanel.add(this.getOptional(state.getSelected(), state));
+      }
+      JScrollPane scrollPane = new JScrollPane(table = this.createTable(state),
+          JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+          JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+      scrollPane.getHorizontalScrollBar().setUnitIncrement(10);
+      scrollPane.getVerticalScrollBar().setUnitIncrement(10);
+      JPanel panel = new JPanel();
+      panel.setLayout(new BorderLayout());
+      panel.setBorder(new EtchedBorder());
+      final JLabel metLabel = new JLabel("Static Metadata");
+      metLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+      final JLabel extendsLabel = new JLabel("<extends>");
+      extendsLabel.setFont(new Font("Serif", Font.PLAIN, 10));
+      extendsLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+      extendsLabel.addMouseListener(new MouseListener() {
+
+        private JScrollPane availableScroller;
+        private JScrollPane mineScroller;
+        private JList mineList;
+        private JList availableList;
+        private DefaultListModel mineModel;
+        private DefaultListModel availableModel;
+
+        public void mouseClicked(MouseEvent e) {
+          final JPopupMenu popup = new JPopupMenu();
+          popup.setLayout(new BorderLayout());
+
+          JPanel main = new JPanel();
+          main.setLayout(new BoxLayout(main, BoxLayout.X_AXIS));
+
+          JPanel mine = new JPanel();
+          mine.setBorder(new EtchedBorder());
+          mine.setLayout(new BorderLayout());
+          JLabel mineLabel = new JLabel("Mine");
+          mineScroller = new JScrollPane(mineList = createJList(
+              mineModel = new DefaultListModel(), state.getSelected()
+                  .getModel().getExtendsConfig()));
+          mineScroller.setPreferredSize(new Dimension(250, 80));
+          mine.add(mineLabel, BorderLayout.NORTH);
+          mine.add(mineScroller, BorderLayout.CENTER);
+
+          JPanel available = new JPanel();
+          available.setBorder(new EtchedBorder());
+          available.setLayout(new BorderLayout());
+          JLabel availableLabel = new JLabel("Available");
+          Vector<String> availableGroups = new Vector<String>(state
+              .getGlobalConfigGroups().keySet());
+          availableGroups.removeAll(state.getSelected().getModel()
+              .getExtendsConfig());
+          availableScroller = new JScrollPane(availableList = this.createJList(
+              availableModel = new DefaultListModel(), availableGroups));
+          availableScroller.setPreferredSize(new Dimension(250, 80));
+          available.add(availableLabel, BorderLayout.NORTH);
+          available.add(availableScroller, BorderLayout.CENTER);
+
+          JPanel buttons = new JPanel();
+          buttons.setLayout(new BoxLayout(buttons, BoxLayout.Y_AXIS));
+          JButton addButton = new JButton("<---");
+          addButton.addMouseListener(new MouseListener() {
+
+            public void mouseClicked(MouseEvent e) {
+              String selected = availableList.getSelectedValue().toString();
+              Vector<String> extendsConfig = new Vector<String>(state
+                  .getSelected().getModel().getExtendsConfig());
+              extendsConfig.add(selected);
+              state.getSelected().getModel().setExtendsConfig(extendsConfig);
+              availableModel.remove(availableList.getSelectedIndex());
+              mineModel.addElement(selected);
+              popup.revalidate();
+              DefaultPropView.this.notifyListeners();
+            }
+
+            public void mouseEntered(MouseEvent e) {
+            }
+
+            public void mouseExited(MouseEvent e) {
+            }
+
+            public void mousePressed(MouseEvent e) {
+            }
+
+            public void mouseReleased(MouseEvent e) {
+            }
+
+          });
+          JButton removeButton = new JButton("--->");
+          removeButton.addMouseListener(new MouseListener() {
+
+            public void mouseClicked(MouseEvent e) {
+              String selected = mineList.getSelectedValue().toString();
+              Vector<String> extendsConfig = new Vector<String>(state
+                  .getSelected().getModel().getExtendsConfig());
+              extendsConfig.remove(selected);
+              state.getSelected().getModel().setExtendsConfig(extendsConfig);
+              mineModel.remove(mineList.getSelectedIndex());
+              availableModel.addElement(selected);
+              popup.revalidate();
+              DefaultPropView.this.notifyListeners();
+            }
+
+            public void mouseEntered(MouseEvent e) {
+            }
+
+            public void mouseExited(MouseEvent e) {
+            }
+
+            public void mousePressed(MouseEvent e) {
+            }
+
+            public void mouseReleased(MouseEvent e) {
+            }
+
+          });
+          buttons.add(addButton);
+          buttons.add(removeButton);
+
+          main.add(mine);
+          main.add(buttons);
+          main.add(available);
+          popup.add(main, BorderLayout.CENTER);
+          popup.show(extendsLabel, e.getX(), e.getY());
+        }
+
+        public void mouseEntered(MouseEvent e) {
+          extendsLabel.setForeground(Color.blue);
+        }
+
+        public void mouseExited(MouseEvent e) {
+          extendsLabel.setForeground(Color.black);
+        }
+
+        public void mousePressed(MouseEvent e) {
+        }
+
+        public void mouseReleased(MouseEvent e) {
+        }
+
+        private JList createJList(DefaultListModel model,
+            final List<String> list) {
+          for (String value : list)
+            model.addElement(value);
+          JList jList = new JList(model);
+          jList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+          jList.setLayoutOrientation(JList.VERTICAL);
+          return jList;
+        }
+      });
+      JLabel metGroupLabel = new JLabel("(Sub-Group: "
+          + (state.getCurrentMetGroup() != null ? state.getCurrentMetGroup()
+              : "<base>") + ")");
+      metGroupLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+      JPanel labelPanel = new JPanel();
+      labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.Y_AXIS));
+      JPanel top = new JPanel();
+      top.setLayout(new BoxLayout(top, BoxLayout.Y_AXIS));
+      top.add(extendsLabel);
+      top.add(metLabel);
+      labelPanel.add(top);
+      labelPanel.add(metGroupLabel);
+      panel.add(labelPanel, BorderLayout.NORTH);
+      panel.add(scrollPane, BorderLayout.CENTER);
+      masterPanel.add(panel);
+      this.add(masterPanel);
+    } else {
+      this.add(new JPanel());
+    }
+    this.revalidate();
+  }
+
+  private JPanel getTimeout(final ModelGraph graph, final ViewState state) {
+    JPanel panel = new JPanel();
+    panel.setLayout(new BorderLayout());
+    panel.setBorder(new EtchedBorder());
+    panel.add(new JLabel("Timeout:"), BorderLayout.NORTH);
+    JTextField field = new JTextField(String.valueOf(graph.getModel()
+        .getTimeout()), 50);
+    field.addActionListener(new ActionListener() {
+
+      public void actionPerformed(ActionEvent e) {
+        if (!graph.getModel().getModelId().equals(e.getActionCommand())) {
+          graph.getModel().setTimeout(Long.valueOf(
+              e.getActionCommand() != null && 
+              !e.getActionCommand().equals("") ? 
+                  e.getActionCommand():"-1"));
+          DefaultPropView.this.notifyListeners();
+          DefaultPropView.this.refreshView(state);
+        }
+      }
+
+    });
+    panel.add(field, BorderLayout.CENTER);
+    return panel;
+  }
+
+  private JPanel getModelIdPanel(final ModelGraph graph, final ViewState state) {
+    JPanel panel = new JPanel();
+    panel.setLayout(new BorderLayout());
+    panel.setBorder(new EtchedBorder());
+    panel.add(new JLabel("ModelId:"), BorderLayout.NORTH);
+    JTextField field = new JTextField(graph.getModel().getModelId(), 50);
+    field.addActionListener(new ActionListener() {
+
+      public void actionPerformed(ActionEvent e) {
+        if (!graph.getModel().getModelId().equals(e.getActionCommand())) {
+          GuiUtils.updateGraphModelId(state, graph.getModel().getId(),
+              e.getActionCommand());
+          DefaultPropView.this.notifyListeners();
+          DefaultPropView.this.refreshView(state);
+        }
+      }
+
+    });
+    panel.add(field, BorderLayout.CENTER);
+    return panel;
+  }
+
+  private JPanel getModelNamePanel(final ModelGraph graph, final ViewState state) {
+    JPanel panel = new JPanel();
+    panel.setLayout(new BorderLayout());
+    panel.setBorder(new EtchedBorder());
+    panel.add(new JLabel("ModelName:"), BorderLayout.NORTH);
+    JTextField field = new JTextField(graph.getModel().getModelName(), 50);
+    field.addActionListener(new ActionListener() {
+
+      public void actionPerformed(ActionEvent e) {
+        if (!graph.getModel().getModelName().equals(e.getActionCommand())) {
+          // GuiUtils.updateGraphModelName(getState(),
+          // graph.getModel().getModelId(), e.getActionCommand());
+          graph.getModel().setModelName(e.getActionCommand());
+          DefaultPropView.this.notifyListeners();
+          DefaultPropView.this.refreshView(state);
+        }
+      }
+
+    });
+    panel.add(field, BorderLayout.CENTER);
+    return panel;
+  }
+
+  private JPanel getInstanceClassPanel(final ModelGraph graph,
+      final ViewState state) {
+    JPanel panel = new JPanel();
+    panel.setLayout(new BorderLayout());
+    panel.setBorder(new EtchedBorder());
+    panel.add(new JLabel("InstanceClass:"), BorderLayout.NORTH);
+    JTextField field = new JTextField(graph.getModel().getInstanceClass(), 50);
+    field.addActionListener(new ActionListener() {
+
+      public void actionPerformed(ActionEvent e) {
+        if (graph.getModel().getInstanceClass() == null
+            || !graph.getModel().getInstanceClass()
+                .equals(e.getActionCommand())) {
+          graph.getModel().setInstanceClass(e.getActionCommand());
+          DefaultPropView.this.notifyListeners();
+          DefaultPropView.this.refreshView(state);
+        }
+      }
+
+    });
+    panel.add(field, BorderLayout.CENTER);
+    return panel;
+  }
+
+  private JPanel getPriorityPanel(final ViewState state) {
+    JPanel panel = new JPanel();
+    panel.setBorder(new EtchedBorder());
+    panel.setLayout(new BorderLayout());
+    panel.add(new JLabel("Priority:  "), BorderLayout.WEST);
+    final JLabel priorityLabel = new JLabel(String.valueOf(DEFAULT_PRIORITY));
+    panel.add(priorityLabel, BorderLayout.CENTER);
+    JSlider slider = new JSlider(0, 100, (int) 5 * 10);
+    slider.setMajorTickSpacing(10);
+    slider.setMinorTickSpacing(1);
+    slider.setPaintLabels(true);
+    slider.setPaintTicks(true);
+    slider.setSnapToTicks(false);
+    Format f = new DecimalFormat("0.0");
+    Hashtable<Integer, JComponent> labels = new Hashtable<Integer, JComponent>();
+    for (int i = 0; i <= 10; i += 2) {
+      JLabel label = new JLabel(f.format(i));
+      label.setFont(label.getFont().deriveFont(Font.PLAIN));
+      labels.put(i * 10, label);
+    }
+    slider.setLabelTable(labels);
+    slider.addChangeListener(new ChangeListener() {
+
+      public void stateChanged(ChangeEvent e) {
+        double value = ((JSlider) e.getSource()).getValue() / 10.0;
+        priorityLabel.setText(value + "");
+        priorityLabel.revalidate();
+        if (!((JSlider) e.getSource()).getValueIsAdjusting()) {
+          // FIXME: deal with priorities
+          DefaultPropView.this.notifyListeners();
+        }
+      }
+
+    });
+
+    panel.add(slider, BorderLayout.SOUTH);
+    return panel;
+  }
+
+  private JPanel getExecutionTypePanel(final ModelGraph graph,
+      final ViewState state) {
+    JPanel panel = new JPanel();
+    panel.setBorder(new EtchedBorder());
+    panel.setLayout(new BorderLayout());
+    panel.add(new JLabel("ExecutionType:"), BorderLayout.WEST);
+    JComboBox comboBox = new JComboBox();
+    if (graph.hasChildren()) {
+      comboBox.addItem("parallel");
+      comboBox.addItem("sequential");
+    } else if (graph.getModel().getExecutionType().equals("task")) {
+      comboBox.addItem("parallel");
+      comboBox.addItem("sequential");
+      comboBox.addItem("task");
+    } else if (graph.isCondition()
+        || graph.getModel().getExecutionType().equals("condition")) {
+      comboBox.addItem("parallel");
+      comboBox.addItem("sequential");
+      comboBox.addItem("condition");
+    } else {
+      comboBox.addItem("parallel");
+      comboBox.addItem("sequential");
+      comboBox.addItem("task");
+    }
+    comboBox.setSelectedItem(graph.getModel().getExecutionType());
+    comboBox.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        if (!graph.getModel().getExecutionType().equals(e.getItem())) {
+          graph.getModel().setExecutionType((String) e.getItem());
+          DefaultPropView.this.notifyListeners();
+          DefaultPropView.this.refreshView(state);
+        }
+      }
+    });
+    panel.add(comboBox, BorderLayout.CENTER);
+    return panel;
+  }
+
+  private JPanel getOptional(final ModelGraph graph, final ViewState state) {
+    JPanel panel = new JPanel();
+    panel.setLayout(new BorderLayout());
+    panel.setBorder(new EtchedBorder());
+    panel.add(new JLabel("Optional:"), BorderLayout.NORTH);
+    JPanel checkBoxes = new JPanel();
+    checkBoxes.setLayout(new GridLayout(1, 1));
+    Checkbox checkbox = new Checkbox("ignore", graph.getModel().isOptional());
+    checkBoxes.add(checkbox);
+        checkbox.addItemListener(new ItemListener() {
+
+          public void itemStateChanged(ItemEvent e) {
+            if (e.getStateChange() == ItemEvent.DESELECTED)
+              graph.getModel().setOptional(false);
+            else if (e.getStateChange() == ItemEvent.SELECTED)
+              graph.getModel().setOptional(true);
+            else
+              return;
+            DefaultPropView.this.notifyListeners();
+            DefaultPropView.this.refreshView(state);
+          }
+
+        });
+    panel.add(checkBoxes, BorderLayout.CENTER);
+    return panel;
+  }  
+  
+  
+  private JPanel getExecusedIds(final ModelGraph graph) {
+    JPanel panel = new JPanel();
+    panel.setLayout(new BorderLayout());
+    panel.setBorder(new EtchedBorder());
+    panel.add(new JLabel("ExcusedSubProcessorIds:"), BorderLayout.NORTH);
+    JPanel checkBoxes = new JPanel();
+    checkBoxes.setLayout(new GridLayout(graph.getChildren().size(), 1));
+    if (graph.hasChildren()) {
+      for (ModelGraph childGraph : graph.getChildren()) {
+        final String modelId = childGraph.getModel().getModelId();
+        Checkbox checkbox = new Checkbox(modelId, graph.getModel()
+            .getExcusedSubProcessorIds().contains(modelId));
+        checkBoxes.add(checkbox);
+        checkbox.addItemListener(new ItemListener() {
+
+          public void itemStateChanged(ItemEvent e) {
+            if (e.getStateChange() == ItemEvent.DESELECTED)
+              graph.getModel().getExcusedSubProcessorIds().remove(modelId);
+            else if (e.getStateChange() == ItemEvent.SELECTED)
+              graph.getModel().getExcusedSubProcessorIds().add(modelId);
+            else
+              return;
+            DefaultPropView.this.notifyListeners();
+          }
+
+        });
+      }
+    }
+    panel.add(checkBoxes, BorderLayout.CENTER);
+    return panel;
+  }
+
+  public class MyTableListener implements TableModelListener,
+      ListSelectionListener {
+
+    String oldKey, oldValue, oldEnvReplace;
+
+    private ViewState state;
+
+    public MyTableListener(ViewState state) {
+      this.state = state;
+    }
+
+    public void tableChanged(TableModelEvent e) {
+      System.out.println(oldKey + " " + oldValue + " " + oldEnvReplace);
+      if (e.getType() == TableModelEvent.UPDATE) {
+        Metadata staticMet = state.getSelected().getModel().getStaticMetadata();
+        if (staticMet == null)
+          staticMet = new Metadata();
+        if (e.getColumn() == 1) {
+          String newGrouplessKey = (String) table.getValueAt(e.getFirstRow(),
+              e.getColumn());
+          if (newGrouplessKey.equals("")
+              || (newGrouplessKey.equals("envReplace") && !state.getSelected()
+                  .getModel().getStaticMetadata()
+                  .containsGroup(state.getCurrentMetGroup()))) {
+            notifyListeners();
+            return;
+          }
+          String newKey = getKey(newGrouplessKey, state);
+          System.out.println("newKey: " + newKey);
+          if (oldKey != null) {
+            staticMet.replaceMetadata(newKey, staticMet.getAllMetadata(oldKey));
+            if (staticMet.containsKey(oldKey + "/envReplace"))
+              staticMet.replaceMetadata(newKey,
+                  staticMet.getAllMetadata(oldKey + "/envReplace"));
+            if (!newKey.equals(oldKey))
+              staticMet.removeMetadata(oldKey);
+            notifyListeners();
+          } else {
+            staticMet.replaceMetadata(oldKey = newKey, (String) null);
+          }
+        } else if (e.getColumn() == 2) {
+          if (oldKey != null) {
+            String newValue = (String) table.getValueAt(e.getFirstRow(),
+                e.getColumn());
+            if (oldKey.endsWith("/envReplace")) {
+              newValue = newValue.toLowerCase();
+              if (newValue.equals("false"))
+                staticMet.removeMetadata(oldKey);
+              else
+                staticMet.replaceMetadata(oldKey,
+                    Arrays.asList(newValue.split(",")));
+            } else {
+              staticMet.replaceMetadata(oldKey,
+                  Arrays.asList(newValue.split(",")));
+            }
+            notifyListeners();
+          }
+        } else if (e.getColumn() == 3) {
+          if (oldKey != null) {
+            String newEnvReplace = ((String) table.getValueAt(e.getFirstRow(),
+                e.getColumn())).toLowerCase();
+            if (newEnvReplace.equals("true"))
+              staticMet.replaceMetadata(oldKey + "/envReplace", newEnvReplace);
+            else
+              staticMet.removeMetadata(oldKey + "/envReplace");
+            notifyListeners();
+          }
+        }
+        state.getSelected().getModel().setStaticMetadata(staticMet);
+      }
+
+    }
+
+    public void valueChanged(ListSelectionEvent e) {
+      oldKey = getKey((String) table.getValueAt(e.getFirstIndex(), 1), state);
+      oldValue = (String) table.getValueAt(e.getFirstIndex(), 2);
+      oldEnvReplace = (String) table.getValueAt(e.getFirstIndex(), 3);
+    }
+
+  }
+
+  private String getKey(String key, ViewState state) {
+    if (key != null && state.getCurrentMetGroup() != null)
+      return state.getCurrentMetGroup() + "/" + key;
+    else
+      return key;
+  }
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/DefaultTreeView.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/DefaultTreeView.java
new file mode 100644
index 0000000..b8ac2f5
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/DefaultTreeView.java
@@ -0,0 +1,483 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.view.impl;
+
+//JDK imports
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.MenuItem;
+import java.awt.PopupMenu;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Stack;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreePath;
+
+//Apache imports
+import org.apache.commons.lang.StringUtils;
+
+//OODT imports
+import org.apache.oodt.cas.metadata.Metadata;
+import org.apache.oodt.cas.workflow.gui.model.ModelGraph;
+import org.apache.oodt.cas.workflow.gui.perspective.view.View;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewChange;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewState;
+import org.apache.oodt.cas.workflow.gui.util.GuiUtils;
+
+/**
+ * 
+ * The default Workflow GUI editor view shell.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class DefaultTreeView extends View {
+
+  private static final long serialVersionUID = -8295070597651190576L;
+
+  private JTree tree;
+  private PopupMenu actionsMenu, orderSubMenu;
+  private JScrollPane scrollPane;
+
+  private static final String EXPAND_STATIC_METADATA = "DefaultTreeView/expand/static-metadata";
+  private static final String EXPAND_PRECONDITIONS = "DefaultTreeView/expand/pre-conditions";
+  private static final String EXPAND_POSTCONDITIONS = "DefaultTreeView/expand/post-conditions";
+
+  public DefaultTreeView(String name) {
+    super(name);
+    this.setLayout(new BorderLayout());
+  }
+
+  public TreePath getTreePath(DefaultMutableTreeNode node, ModelGraph graph) {
+    if (node.getUserObject().equals(graph)) {
+      return new TreePath(node.getPath());
+    } else {
+      for (int i = 0; i < node.getChildCount(); i++) {
+        // System.out.println("i: " + ((DefaultMutableTreeNode)
+        // node.getChildAt(i)).getUserObject());
+        TreePath treePath = this.getTreePath(
+            (DefaultMutableTreeNode) node.getChildAt(i), graph);
+        if (treePath != null)
+          return treePath;
+      }
+      return null;
+    }
+  }
+
+  private TreePath getTreePath(TreePath currentPath, ViewState state) {
+    String lookingForPath = state.getCurrentMetGroup();
+    Stack<DefaultMutableTreeNode> stack = new Stack<DefaultMutableTreeNode>();
+    DefaultMutableTreeNode baseNode = (DefaultMutableTreeNode) currentPath
+        .getLastPathComponent();
+    for (int i = 0; i < baseNode.getChildCount(); i++)
+      stack.push((DefaultMutableTreeNode) baseNode.getChildAt(i));
+    while (!stack.empty()) {
+      DefaultMutableTreeNode node = stack.pop();
+      if (node.getUserObject().equals("static-metadata")) {
+        for (int i = 0; i < node.getChildCount(); i++)
+          stack.push((DefaultMutableTreeNode) node.getChildAt(i));
+      } else if (node.getUserObject() instanceof HashMap) {
+        String key = (String) ((HashMap<String, String>) node.getUserObject())
+            .keySet().iterator().next();
+        if (lookingForPath.equals(key)) {
+          return new TreePath(node.getPath());
+        } else if (lookingForPath.startsWith(key + "/")) {
+          lookingForPath = lookingForPath
+              .substring(lookingForPath.indexOf("/") + 1);
+          stack.clear();
+          for (int i = 0; i < node.getChildCount(); i++)
+            stack.add((DefaultMutableTreeNode) node.getChildAt(i));
+        }
+      }
+    }
+    return currentPath;
+  }
+
+  private void resetProperties(ViewState state) {
+    state.setProperty(EXPAND_STATIC_METADATA, "false");
+    state.setProperty(EXPAND_PRECONDITIONS, "false");
+    state.setProperty(EXPAND_POSTCONDITIONS, "false");
+  }
+
+  @Override
+  public void refreshView(final ViewState state) {
+    Rectangle visibleRect = null;
+    if (this.tree != null)
+      visibleRect = this.tree.getVisibleRect();
+
+    this.removeAll();
+
+    this.actionsMenu = this.createPopupMenu(state);
+
+    DefaultMutableTreeNode root = new DefaultMutableTreeNode("WORKFLOWS");
+    for (ModelGraph graph : state.getGraphs())
+      root.add(this.buildTree(graph, state));
+    tree = new JTree(root);
+    tree.setShowsRootHandles(true);
+    tree.setRootVisible(false);
+    tree.add(this.actionsMenu);
+
+    if (state.getSelected() != null) {
+      // System.out.println("SELECTED: " + state.getSelected());
+      TreePath treePath = this.getTreePath(root, state.getSelected());
+      if (state.getCurrentMetGroup() != null) {
+        treePath = this.getTreePath(treePath, state);
+      } else if (Boolean.parseBoolean(state
+          .getFirstPropertyValue(EXPAND_STATIC_METADATA))) {
+        DefaultMutableTreeNode baseNode = (DefaultMutableTreeNode) treePath
+            .getLastPathComponent();
+        for (int i = 0; i < baseNode.getChildCount(); i++) {
+          if (((DefaultMutableTreeNode) baseNode.getChildAt(i)).getUserObject()
+              .equals("static-metadata")) {
+            treePath = new TreePath(
+                ((DefaultMutableTreeNode) baseNode.getChildAt(i)).getPath());
+            break;
+          }
+        }
+      } else if (Boolean.parseBoolean(state
+          .getFirstPropertyValue(EXPAND_PRECONDITIONS))) {
+        if (treePath == null)
+          treePath = this.getTreePath(root, state.getSelected()
+              .getPreConditions());
+        DefaultMutableTreeNode baseNode = (DefaultMutableTreeNode) treePath
+            .getLastPathComponent();
+        for (int i = 0; i < baseNode.getChildCount(); i++) {
+          if (((DefaultMutableTreeNode) baseNode.getChildAt(i)).getUserObject()
+              .equals("pre-conditions")) {
+            treePath = new TreePath(
+                ((DefaultMutableTreeNode) baseNode.getChildAt(i)).getPath());
+            break;
+          }
+        }
+      } else if (Boolean.parseBoolean(state
+          .getFirstPropertyValue(EXPAND_POSTCONDITIONS))) {
+        if (treePath == null)
+          treePath = this.getTreePath(root, state.getSelected()
+              .getPostConditions());
+        DefaultMutableTreeNode baseNode = (DefaultMutableTreeNode) treePath
+            .getLastPathComponent();
+        for (int i = 0; i < baseNode.getChildCount(); i++) {
+          if (((DefaultMutableTreeNode) baseNode.getChildAt(i)).getUserObject()
+              .equals("post-conditions")) {
+            treePath = new TreePath(
+                ((DefaultMutableTreeNode) baseNode.getChildAt(i)).getPath());
+            break;
+          }
+        }
+      }
+      this.tree.expandPath(treePath);
+      this.tree.setSelectionPath(treePath);
+    }
+
+    tree.addTreeSelectionListener(new TreeSelectionListener() {
+
+      public void valueChanged(TreeSelectionEvent e) {
+        if (e.getPath().getLastPathComponent() instanceof DefaultMutableTreeNode) {
+          DefaultTreeView.this.resetProperties(state);
+          DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.getPath()
+              .getLastPathComponent();
+          if (node.getUserObject() instanceof ModelGraph) {
+            state.setSelected((ModelGraph) node.getUserObject());
+            state.setCurrentMetGroup(null);
+            DefaultTreeView.this.notifyListeners();
+          } else if (node.getUserObject().equals("static-metadata")
+              || node.getUserObject().equals("pre-conditions")
+              || node.getUserObject().equals("post-conditions")) {
+            state.setSelected((ModelGraph) ((DefaultMutableTreeNode) node
+                .getParent()).getUserObject());
+            state.setCurrentMetGroup(null);
+            state.setProperty(EXPAND_STATIC_METADATA, Boolean.toString(node
+                .getUserObject().equals("static-metadata")));
+            state.setProperty(EXPAND_PRECONDITIONS,
+                Boolean.toString(node.getUserObject().equals("pre-conditions")));
+            state.setProperty(EXPAND_POSTCONDITIONS, Boolean.toString(node
+                .getUserObject().equals("post-conditions")));
+            DefaultTreeView.this.notifyListeners();
+          } else if (node.getUserObject() instanceof HashMap) {
+            DefaultMutableTreeNode metNode = null;
+            String group = null;
+            Object[] path = e.getPath().getPath();
+            for (int i = path.length - 1; i >= 0; i--) {
+              if (((DefaultMutableTreeNode) path[i]).getUserObject() instanceof ModelGraph) {
+                metNode = (DefaultMutableTreeNode) path[i];
+                break;
+              } else if (((DefaultMutableTreeNode) path[i]).getUserObject() instanceof HashMap) {
+                if (group == null)
+                  group = (String) ((HashMap<String, String>) ((DefaultMutableTreeNode) path[i])
+                      .getUserObject()).keySet().iterator().next();
+                else
+                  group = (String) ((HashMap<String, String>) ((DefaultMutableTreeNode) path[i])
+                      .getUserObject()).keySet().iterator().next()
+                      + "/"
+                      + group;
+              }
+            }
+            ModelGraph graph = (ModelGraph) metNode.getUserObject();
+            state.setSelected(graph);
+            state.setCurrentMetGroup(group);
+            DefaultTreeView.this.notifyListeners();
+          } else {
+            state.setSelected(null);
+            DefaultTreeView.this.notifyListeners();
+          }
+        }
+      }
+
+    });
+    tree.setCellRenderer(new TreeCellRenderer() {
+
+      public Component getTreeCellRendererComponent(JTree tree, Object value,
+          boolean selected, boolean expanded, boolean leaf, int row,
+          boolean hasFocus) {
+        DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
+        if (node.getUserObject() instanceof String) {
+          JPanel panel = new JPanel();
+          panel.setLayout(new BorderLayout());
+          JLabel label = new JLabel((String) node.getUserObject());
+          label.setForeground(Color.blue);
+          panel.add(label, BorderLayout.CENTER);
+          panel.setBackground(selected ? Color.lightGray : Color.white);
+          return panel;
+        } else if (node.getUserObject() instanceof ModelGraph) {
+          JPanel panel = new JPanel();
+          panel.setLayout(new BorderLayout());
+          JLabel iconLabel = new JLabel(((ModelGraph) node.getUserObject())
+              .getModel().getExecutionType() + ": ");
+          iconLabel.setForeground(((ModelGraph) node.getUserObject())
+              .getModel().getColor());
+          iconLabel.setBackground(Color.white);
+          JLabel idLabel = new JLabel(((ModelGraph) node.getUserObject())
+              .getModel().getModelName());
+          idLabel.setBackground(Color.white);
+          panel.add(iconLabel, BorderLayout.WEST);
+          panel.add(idLabel, BorderLayout.CENTER);
+          panel.setBackground(selected ? Color.lightGray : Color.white);
+          return panel;
+        } else if (node.getUserObject() instanceof HashMap) {
+          JPanel panel = new JPanel();
+          panel.setLayout(new BorderLayout());
+          String group = (String) ((HashMap<String, String>) node
+              .getUserObject()).keySet().iterator().next();
+          JLabel nameLabel = new JLabel(group + " : ");
+          nameLabel.setForeground(Color.blue);
+          nameLabel.setBackground(Color.white);
+          JLabel valueLabel = new JLabel(((HashMap<String, String>) node
+              .getUserObject()).get(group));
+          valueLabel.setForeground(Color.darkGray);
+          valueLabel.setBackground(Color.white);
+          panel.add(nameLabel, BorderLayout.WEST);
+          panel.add(valueLabel, BorderLayout.EAST);
+          panel.setBackground(selected ? Color.lightGray : Color.white);
+          return panel;
+        } else {
+          return new JLabel();
+        }
+      }
+
+    });
+    tree.addMouseListener(new MouseListener() {
+      public void mouseClicked(MouseEvent e) {
+        if (e.getButton() == MouseEvent.BUTTON3
+            && DefaultTreeView.this.tree.getSelectionPath() != null) {
+          DefaultMutableTreeNode node = (DefaultMutableTreeNode) DefaultTreeView.this.tree
+              .getSelectionPath().getLastPathComponent();
+          if (node.getUserObject() instanceof String
+              && !(node.getUserObject().equals("pre-conditions") || node
+                  .getUserObject().equals("post-conditions")))
+            return;
+          orderSubMenu.setEnabled(node.getUserObject() instanceof ModelGraph
+              && !((ModelGraph) node.getUserObject()).isCondition()
+              && ((ModelGraph) node.getUserObject()).getParent() != null);
+          DefaultTreeView.this.actionsMenu.show(DefaultTreeView.this.tree,
+              e.getX(), e.getY());
+        }
+      }
+
+      public void mouseEntered(MouseEvent e) {
+      }
+
+      public void mouseExited(MouseEvent e) {
+      }
+
+      public void mousePressed(MouseEvent e) {
+      }
+
+      public void mouseReleased(MouseEvent e) {
+      }
+    });
+    this.scrollPane = new JScrollPane(tree,
+        JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+        JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+    this.add(this.scrollPane, BorderLayout.CENTER);
+    if (visibleRect != null)
+      this.tree.scrollRectToVisible(visibleRect);
+
+    this.revalidate();
+  }
+
+  private DefaultMutableTreeNode buildTree(ModelGraph graph, ViewState state) {
+    DefaultMutableTreeNode node = new DefaultMutableTreeNode(graph);
+    DefaultMutableTreeNode metadataNode = new DefaultMutableTreeNode(
+        "static-metadata");
+    Metadata staticMetadata = new Metadata();
+    if (graph.getInheritedStaticMetadata(state) != null)
+      staticMetadata.replaceMetadata(graph.getInheritedStaticMetadata(state));
+    if (graph.getModel().getStaticMetadata() != null)
+      staticMetadata.replaceMetadata(graph.getModel().getStaticMetadata());
+    this.addMetadataNodes(metadataNode, staticMetadata);
+    if (!metadataNode.isLeaf())
+      node.add(metadataNode);
+
+    if (graph.getPreConditions() != null) {
+      DefaultMutableTreeNode preConditions = new DefaultMutableTreeNode(
+          "pre-conditions");
+      List<ModelGraph> leafNodes = graph.getPreConditions().getLeafNodes();
+      for (ModelGraph cond : leafNodes) {
+        DefaultMutableTreeNode condNode = new DefaultMutableTreeNode(cond);
+        preConditions.add(condNode);
+      }
+      node.add(preConditions);
+    }
+    if (graph.getPostConditions() != null) {
+      DefaultMutableTreeNode postConditions = new DefaultMutableTreeNode(
+          "post-conditions");
+      List<ModelGraph> leafNodes = graph.getPostConditions().getLeafNodes();
+      for (ModelGraph cond : leafNodes) {
+        DefaultMutableTreeNode condNode = new DefaultMutableTreeNode(cond);
+        postConditions.add(condNode);
+      }
+      node.add(postConditions);
+    }
+    for (ModelGraph child : graph.getChildren())
+      if (!GuiUtils.isDummyNode(child.getModel()))
+        node.add(this.buildTree(child, state));
+    return node;
+  }
+
+  private void addMetadataNodes(DefaultMutableTreeNode metadataNode,
+      Metadata staticMetadata) {
+    for (String group : staticMetadata.getGroups()) {
+      Object userObj = null;
+      if (staticMetadata.getMetadata(group) != null) {
+        HashMap<String, String> map = new HashMap<String, String>();
+        map.put(group,
+            StringUtils.join(staticMetadata.getAllMetadata(group), ","));
+        userObj = map;
+      } else {
+        HashMap<String, String> map = new HashMap<String, String>();
+        map.put(group, null);
+        userObj = map;
+      }
+      DefaultMutableTreeNode groupNode = new DefaultMutableTreeNode(userObj);
+      metadataNode.add(groupNode);
+      this.addMetadataNodes(groupNode, staticMetadata.getSubMetadata(group));
+    }
+  }
+
+  private PopupMenu createPopupMenu(final ViewState state) {
+    final String ACTIONS_POP_MENU_NAME = "Actions";
+    final String VIEW_CONDITION_MAP = "View...";
+    PopupMenu actionsMenu = new PopupMenu(ACTIONS_POP_MENU_NAME);
+    actionsMenu.add(new MenuItem(VIEW_CONDITION_MAP));
+    actionsMenu.addActionListener(new ActionListener() {
+
+      public void actionPerformed(ActionEvent e) {
+        if (e.getActionCommand().equals(VIEW_CONDITION_MAP)) {
+          DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree
+              .getSelectionPath().getLastPathComponent();
+          ModelGraph graphToFocus = null;
+          if (Boolean.parseBoolean(state
+              .getFirstPropertyValue(EXPAND_PRECONDITIONS))
+              || Boolean.parseBoolean(state
+                  .getFirstPropertyValue(EXPAND_POSTCONDITIONS))) {
+            // if (node.getUserObject() instanceof String &&
+            // (node.getUserObject().equals("pre-conditions") ||
+            // node.getUserObject().equals("post-conditions"))) {
+            ModelGraph graph = state.getSelected();
+            if (Boolean.parseBoolean(state
+                .getFirstPropertyValue(EXPAND_PRECONDITIONS)))
+              graphToFocus = graph.getPreConditions();
+            else
+              graphToFocus = graph.getPostConditions();
+          } else if (node.getUserObject() instanceof ModelGraph) {
+            graphToFocus = (ModelGraph) node.getUserObject();
+          }
+          DefaultTreeView.this.notifyListeners(new ViewChange.NEW_VIEW(
+              graphToFocus, DefaultTreeView.this));
+        }
+      }
+
+    });
+
+    final String ORDER_SUB_POP_MENU_NAME = "Order";
+    final String TO_FRONT_ITEM_NAME = "Move To Front";
+    final String TO_BACK_ITEM_NAME = "Move To Back";
+    final String FORWARD_ITEM_NAME = "Move Forward";
+    final String BACKWARDS_ITEM_NAME = "Move Backwards";
+    actionsMenu.add(orderSubMenu = new PopupMenu(ORDER_SUB_POP_MENU_NAME));
+    orderSubMenu.add(new MenuItem(TO_FRONT_ITEM_NAME));
+    orderSubMenu.add(new MenuItem(TO_BACK_ITEM_NAME));
+    orderSubMenu.add(new MenuItem(FORWARD_ITEM_NAME));
+    orderSubMenu.add(new MenuItem(BACKWARDS_ITEM_NAME));
+    orderSubMenu.addActionListener(new ActionListener() {
+
+      public void actionPerformed(ActionEvent e) {
+        ModelGraph graph = state.getSelected();
+        ModelGraph parent = graph.getParent();
+        if (e.getActionCommand().equals(TO_FRONT_ITEM_NAME)) {
+          if (parent.getChildren().remove(graph))
+            parent.getChildren().add(0, graph);
+        } else if (e.getActionCommand().equals(TO_BACK_ITEM_NAME)) {
+          if (parent.getChildren().remove(graph))
+            parent.getChildren().add(graph);
+        } else if (e.getActionCommand().equals(FORWARD_ITEM_NAME)) {
+          int index = parent.getChildren().indexOf(graph);
+          if (index != -1) {
+            parent.getChildren().remove(index);
+            parent.getChildren().add(Math.max(0, index + 1), graph);
+          }
+        } else if (e.getActionCommand().equals(BACKWARDS_ITEM_NAME)) {
+          int index = parent.getChildren().indexOf(graph);
+          if (index != -1) {
+            parent.getChildren().remove(index);
+            parent.getChildren().add(Math.max(0, index - 1), graph);
+          }
+        }
+        DefaultTreeView.this.notifyListeners();
+        DefaultTreeView.this.refreshView(state);
+      }
+
+    });
+    return actionsMenu;
+  }
+
+}
\ No newline at end of file
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/GlobalConfigView.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/GlobalConfigView.java
new file mode 100644
index 0000000..f998e76
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/GlobalConfigView.java
@@ -0,0 +1,267 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.view.impl;
+
+//JDK imports
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Rectangle;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTree;
+import javax.swing.border.EtchedBorder;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeCellRenderer;
+
+//Apache imports
+import org.apache.commons.lang.StringUtils;
+
+//OODT imports
+import org.apache.oodt.cas.workflow.gui.model.repo.XmlWorkflowModelRepository.ConfigGroup;
+import org.apache.oodt.cas.workflow.gui.perspective.view.View;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewState;
+
+/**
+ * 
+ * Displays information about global config properties loaded from the
+ * Workflows.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class GlobalConfigView extends View {
+
+  private static final long serialVersionUID = 3899104909278232407L;
+  private JTree tree;
+  private JTabbedPane tabbedPane;
+  private Map<String, ConfigGroup> globalConfig;
+
+  public GlobalConfigView(String name) {
+    super(name);
+    this.setLayout(new BorderLayout());
+  }
+
+  @Override
+  public void refreshView(ViewState state) {
+
+    Rectangle visibleRect = null;
+    if (this.tree != null)
+      visibleRect = this.tree.getVisibleRect();
+
+    DefaultMutableTreeNode root = new DefaultMutableTreeNode("GlobalConfig");
+
+    if (state != null && state.getGlobalConfigGroups() != null) {
+      if (globalConfig != null
+          && globalConfig.keySet().equals(
+              state.getGlobalConfigGroups().keySet())
+          && globalConfig.values().equals(
+              state.getGlobalConfigGroups().values()))
+        return;
+
+      this.removeAll();
+
+      for (ConfigGroup group : (globalConfig = state.getGlobalConfigGroups())
+          .values()) {
+        HashSet<String> keys = new HashSet<String>();
+        DefaultMutableTreeNode groupNode = new DefaultMutableTreeNode(
+            new Group(group.getName()));
+        root.add(groupNode);
+        for (String key : group.getMetadata().getAllKeys()) {
+          keys.add(key);
+          DefaultMutableTreeNode keyNode = new DefaultMutableTreeNode(new Key(
+              key));
+          groupNode.add(keyNode);
+          DefaultMutableTreeNode valueNode = new DefaultMutableTreeNode(
+              new Value(StringUtils.join(group.getMetadata()
+                  .getAllMetadata(key), ",")));
+          keyNode.add(valueNode);
+        }
+        if (group.getExtends() != null) {
+          List<String> extendsGroups = new Vector<String>(group.getExtends());
+          Collections.reverse(extendsGroups);
+          for (String extendsGroup : extendsGroups) {
+            List<String> groupKeys = state.getGlobalConfigGroups()
+                .get(extendsGroup).getMetadata().getAllKeys();
+            groupKeys.removeAll(keys);
+            if (groupKeys.size() > 0) {
+              for (String key : groupKeys) {
+                if (!keys.contains(key)) {
+                  keys.add(key);
+                  DefaultMutableTreeNode keyNode = new DefaultMutableTreeNode(
+                      new ExtendsKey(extendsGroup, key));
+                  groupNode.add(keyNode);
+                  DefaultMutableTreeNode valueNode = new DefaultMutableTreeNode(
+                      new ExtendsValue(StringUtils.join(state
+                          .getGlobalConfigGroups().get(extendsGroup)
+                          .getMetadata().getAllMetadata(key), ",")));
+                  keyNode.add(valueNode);
+                }
+              }
+            }
+          }
+        }
+      }
+
+      tree = new JTree(root);
+      tree.setShowsRootHandles(true);
+      tree.setRootVisible(false);
+
+      tree.setCellRenderer(new TreeCellRenderer() {
+
+        public Component getTreeCellRendererComponent(JTree tree, Object value,
+            boolean selected, boolean expanded, boolean leaf, int row,
+            boolean hasFocus) {
+          DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
+          if (node.getUserObject() instanceof Key) {
+            JPanel panel = new JPanel();
+            panel.setLayout(new BorderLayout());
+            JLabel label = new JLabel(node.getUserObject().toString());
+            label.setForeground(Color.darkGray);
+            panel.add(label, BorderLayout.CENTER);
+            panel.setBackground(selected ? Color.lightGray : Color.white);
+            return panel;
+          } else if (node.getUserObject() instanceof ExtendsKey) {
+            JPanel panel = new JPanel();
+            panel.setLayout(new BorderLayout());
+            ExtendsKey key = (ExtendsKey) node.getUserObject();
+            JLabel groupLabel = new JLabel("(" + key.getGroup() + ") ");
+            groupLabel.setForeground(Color.black);
+            JLabel keyLabel = new JLabel(key.getValue());
+            keyLabel.setForeground(Color.gray);
+            panel.add(groupLabel, BorderLayout.WEST);
+            panel.add(keyLabel, BorderLayout.CENTER);
+            panel.setBackground(selected ? Color.lightGray : Color.white);
+            return panel;
+          } else if (node.getUserObject() instanceof Group) {
+            JPanel panel = new JPanel();
+            panel.setLayout(new BorderLayout());
+            JLabel label = new JLabel(node.getUserObject().toString());
+            label.setForeground(Color.black);
+            label.setBackground(Color.white);
+            panel.add(label, BorderLayout.CENTER);
+            panel.setBackground(selected ? Color.lightGray : Color.white);
+            return panel;
+          } else if (node.getUserObject() instanceof Value) {
+            JPanel panel = new JPanel();
+            panel.setLayout(new BorderLayout());
+            panel.setBorder(new EtchedBorder(1));
+            JLabel label = new JLabel(node.getUserObject().toString());
+            label.setForeground(Color.black);
+            panel.add(label, BorderLayout.CENTER);
+            panel.setBackground(selected ? Color.lightGray : Color.white);
+            return panel;
+          } else if (node.getUserObject() instanceof ExtendsValue) {
+            JPanel panel = new JPanel();
+            panel.setLayout(new BorderLayout());
+            panel.setBorder(new EtchedBorder(1));
+            JLabel label = new JLabel(node.getUserObject().toString());
+            label.setForeground(Color.gray);
+            panel.add(label, BorderLayout.CENTER);
+            panel.setBackground(selected ? Color.lightGray : Color.white);
+            return panel;
+          } else {
+            return new JLabel();
+          }
+        }
+
+      });
+    }
+
+    this.setBorder(new EtchedBorder());
+    JLabel panelName = new JLabel("Global-Config Groups");
+    panelName.setBorder(new EtchedBorder());
+    this.add(panelName, BorderLayout.NORTH);
+    JScrollPane scrollPane = new JScrollPane(tree,
+        JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+        JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+
+    tabbedPane = new JTabbedPane();
+    tabbedPane.addTab("Tree", scrollPane);
+    tabbedPane.addTab("Table", new JPanel());
+
+    this.add(tabbedPane, BorderLayout.CENTER);
+
+    if (visibleRect != null)
+      this.tree.scrollRectToVisible(visibleRect);
+
+    this.revalidate();
+  }
+
+  public class StringNode {
+    private String value;
+
+    public StringNode(String value) {
+      this.value = value;
+    }
+
+    public String getValue() {
+      return this.value;
+    }
+
+    public String toString() {
+      return this.value;
+    }
+  }
+
+  public class Key extends StringNode {
+    public Key(String value) {
+      super(value);
+    }
+  }
+
+  public class ExtendsKey extends StringNode {
+    private String group;
+
+    public ExtendsKey(String group, String value) {
+      super(value);
+      this.group = group;
+    }
+
+    public String getGroup() {
+      return this.group;
+    }
+  }
+
+  public class ExtendsValue extends StringNode {
+    public ExtendsValue(String value) {
+      super(value);
+    }
+  }
+
+  public class Value extends StringNode {
+    public Value(String value) {
+      super(value);
+    }
+  }
+
+  public class Group extends StringNode {
+    public Group(String group) {
+      super(group);
+    }
+  }
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/GraphView.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/GraphView.java
new file mode 100644
index 0000000..69469d1
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/GraphView.java
@@ -0,0 +1,1100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.view.impl;
+
+//JDK imports
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Graphics2D;
+import java.awt.MenuItem;
+import java.awt.Point;
+import java.awt.PopupMenu;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DragGestureEvent;
+import java.awt.dnd.DragGestureListener;
+import java.awt.dnd.DragSource;
+import java.awt.dnd.DragSourceDragEvent;
+import java.awt.dnd.DragSourceDropEvent;
+import java.awt.dnd.DragSourceEvent;
+import java.awt.dnd.DragSourceListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.MouseWheelListener;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+import java.util.Map.Entry;
+import javax.swing.JScrollPane;
+import javax.swing.SwingConstants;
+import javax.swing.border.LineBorder;
+import javax.swing.tree.DefaultMutableTreeNode;
+
+//JGraph imports
+import org.jgraph.JGraph;
+import org.jgraph.graph.AttributeMap;
+import org.jgraph.graph.DefaultGraphCell;
+import org.jgraph.graph.DefaultEdge;
+import org.jgraph.graph.GraphConstants;
+import com.jgraph.layout.JGraphFacade;
+import com.jgraph.layout.hierarchical.JGraphHierarchicalLayout;
+
+//Jung imports
+import edu.uci.ics.jung.graph.DirectedSparseGraph;
+import edu.uci.ics.jung.graph.ObservableGraph;
+
+//OODT imports
+import org.apache.oodt.cas.workflow.gui.model.ModelGraph;
+import org.apache.oodt.cas.workflow.gui.model.ModelNode;
+import org.apache.oodt.cas.workflow.gui.perspective.view.View;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewChange;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewState;
+import org.apache.oodt.cas.workflow.gui.util.GuiUtils;
+import org.apache.oodt.cas.workflow.gui.util.IconLoader;
+import org.apache.oodt.cas.workflow.gui.util.Line;
+
+/**
+ * 
+ * This is where the money happens. The Graph visualization of OODT CAS
+ * workflows is displayed via this view, which magically integrates JGraph,
+ * Jung, and OODT.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class GraphView extends DefaultTreeView {
+
+  private static final long serialVersionUID = 5935578385254884387L;
+
+  private JungJGraphModelAdapter m_jgAdapter;
+  private JGraph jgraph;
+  private ObservableGraph<ModelNode, IdentifiableEdge> directedGraph;
+
+  private MyGraphListener myGraphListener;
+
+  private static final String VIEW_REF_WORKFLOW = "View Referrenced";
+  private static final String ACTIONS_POP_MENU_NAME = "Actions";
+  private static final String NEW_SUB_POP_MENU_NAME = "New";
+  private static final String NEW_TASK_ITEM_NAME = "Task";
+  private static final String NEW_CONDITION_ITEM_NAME = "Condition";
+  private static final String NEW_PARALLEL_ITEM_NAME = "Parallel";
+  private static final String NEW_SEQUENTIAL_ITEM_NAME = "Sequential";
+  private static final String EDGES_SUB_POP_MENU_NAME = "Edges";
+  private static final String TASK_LEVEL = "Task Level";
+  private static final String WORKFLOW_LEVEL = "Workflow Level";
+  private static final String DELETE_ITEM_NAME = "Delete";
+  private static final String FORMAT_ITEM_NAME = "Format";
+  private static final String ORDER_SUB_POP_MENU_NAME = "Order";
+  private static final String TO_FRONT_ITEM_NAME = "Move To Front";
+  private static final String TO_BACK_ITEM_NAME = "Move To Back";
+  private static final String FORWARD_ITEM_NAME = "Move Forward";
+  private static final String BACKWARDS_ITEM_NAME = "Move Backwards";
+
+  private HashMap<String, Pair> edgeMap;
+
+  private static final String SCALE = "GraphView/scale";
+  private static final String EDGE_DISPLAY_MODE = "GraphView/EdgeDisplay/Mode";
+  private static final String TASK_MODE = "Task";
+  private static final String WORKFLOW_MODE = "Workflow";
+
+  private static boolean scrollSelectedToVisible = false;
+
+  public GraphView(String name) {
+    super(name);
+  }
+
+  @Override
+  public void refreshView(final ViewState state) {
+    this.removeAll();
+    this.myGraphListener = new MyGraphListener(state);
+
+    Rectangle visible = null;
+    if (jgraph != null)
+      visible = jgraph.getVisibleRect();
+
+    Cursor cursor = null;
+    if (jgraph != null)
+      cursor = jgraph.getCursor();
+
+    this.edgeMap = new HashMap<String, Pair>();
+
+    directedGraph = new ObservableGraph<ModelNode, IdentifiableEdge>(
+        new DirectedSparseGraph<ModelNode, IdentifiableEdge>());
+    m_jgAdapter = new JungJGraphModelAdapter(directedGraph);
+
+    jgraph = new JGraph(m_jgAdapter);
+    for (MouseListener ml : jgraph.getMouseListeners())
+      jgraph.removeMouseListener(ml);
+    for (MouseMotionListener ml : jgraph.getMouseMotionListeners())
+      jgraph.removeMouseMotionListener(ml);
+    for (MouseWheelListener ml : jgraph.getMouseWheelListeners())
+      jgraph.removeMouseWheelListener(ml);
+    jgraph.setBackground(Color.white);
+    jgraph.setAntiAliased(true);
+    jgraph.setMoveable(false);
+    String scale = state.getFirstPropertyValue(SCALE);
+    if (scale == null)
+      state.setProperty(SCALE, scale = "1.0");
+    jgraph.setScale(Double.parseDouble(scale));
+
+    DragSource dragSource = DragSource.getDefaultDragSource();
+    dragSource.createDefaultDragGestureRecognizer(this.jgraph,
+        DnDConstants.ACTION_MOVE, new DragGestureListener() {
+
+          DefaultGraphCell moveCell = null;
+          ModelGraph moveGraph = null;
+
+          public void dragGestureRecognized(final DragGestureEvent dge) {
+            if (state.getMode() == View.Mode.MOVE
+                || state.getMode() == View.Mode.EDIT) {
+              Object moveOverCell = jgraph.getFirstCellForLocation(dge
+                  .getDragOrigin().getX(), dge.getDragOrigin().getY());
+              if (moveOverCell != null) {
+                if (moveOverCell instanceof DefaultEdge) {
+                  moveCell = null;
+                } else if (moveOverCell instanceof DefaultGraphCell) {
+                  moveCell = (DefaultGraphCell) moveOverCell;
+                  moveGraph = GuiUtils.find(state.getGraphs(),
+                      ((ModelNode) ((DefaultGraphCell) moveCell)
+                          .getUserObject()).getId());
+
+                  if (state.getMode() == View.Mode.MOVE)
+                    moveCell = GraphView.this.m_jgAdapter
+                        .getVertexCell((moveGraph = moveGraph.getRootParent())
+                            .getModel());
+                  else if (GuiUtils.isDummyNode(moveGraph.getModel()))
+                    moveCell = GraphView.this.m_jgAdapter
+                        .getVertexCell((moveGraph = moveGraph.getParent())
+                            .getModel());
+                  else if (moveGraph.getModel().isRef()) {
+                    while (moveGraph.getParent() != null
+                        && moveGraph.getParent().getModel().isRef())
+                      moveGraph = moveGraph.getParent();
+                    moveCell = GraphView.this.m_jgAdapter
+                        .getVertexCell(moveGraph.getModel());
+                  }
+                  final double scale = Double.parseDouble(state
+                      .getFirstPropertyValue(SCALE));
+                  final Rectangle2D bounds = (Rectangle2D) GraphView.this.jgraph
+                      .getAttributes(moveCell).get(GraphConstants.BOUNDS);
+                  Point offset = new Point(
+                      (int) (bounds.getX() * scale - dge.getDragOrigin().getX()),
+                      (int) (bounds.getY() * scale - dge.getDragOrigin().getY()));
+                  BufferedImage image = new BufferedImage((int) (bounds
+                      .getWidth() * scale), (int) (bounds.getHeight() * scale),
+                      BufferedImage.TYPE_INT_ARGB);
+                  Graphics2D g2d = image.createGraphics();
+                  g2d.setColor(Color.black);
+                  g2d.drawRect((int) (2 * scale), (int) (2 * scale),
+                      (int) ((bounds.getWidth() - 4) * scale),
+                      (int) ((bounds.getHeight() - 4) * scale));
+                  dge.startDrag(GraphView.this.getCursor(), image, offset,
+                      new Transferable() {
+
+                        public Object getTransferData(DataFlavor flavor)
+                            throws UnsupportedFlavorException, IOException {
+                          if (flavor.getHumanPresentableName().equals(
+                              DefaultGraphCell.class.getSimpleName()))
+                            return this;
+                          else
+                            throw new UnsupportedFlavorException(flavor);
+                        }
+
+                        public DataFlavor[] getTransferDataFlavors() {
+                          return new DataFlavor[] { new DataFlavor(
+                              DefaultGraphCell.class, DefaultGraphCell.class
+                                  .getSimpleName()) };
+                        }
+
+                        public boolean isDataFlavorSupported(DataFlavor flavor) {
+                          if (flavor.getHumanPresentableName().equals(
+                              DefaultGraphCell.class.getSimpleName()))
+                            return true;
+                          else
+                            return false;
+                        }
+
+                      }, new DragSourceListener() {
+
+                        private ModelGraph mouseOverGraph;
+                        private DefaultGraphCell mouseOverCell;
+
+                        public void dragDropEnd(DragSourceDropEvent dsde) {
+                          System.out.println("DRAG END!!!!");
+                          if (moveCell == null)
+                            return;
+                          Point dropPoint = new Point(dsde.getX()
+                              - jgraph.getLocationOnScreen().x, dsde.getY()
+                              - jgraph.getLocationOnScreen().y);
+                          DefaultGraphCell endCell = (DefaultGraphCell) GraphView.this.jgraph
+                              .getSelectionCell();
+                          if (endCell != null) {
+                            ModelGraph endGraph = GuiUtils.find(
+                                state.getGraphs(),
+                                ((ModelNode) endCell.getUserObject()).getId());
+                            if (!endGraph.getModel().isParentType()
+                                || GuiUtils.isSubGraph(moveGraph, endGraph))
+                              return;
+                            if (moveGraph.getParent() == null)
+                              state.removeGraph(moveGraph);
+                            else
+                              GuiUtils.removeNode(state.getGraphs(),
+                                  moveGraph.getModel());
+                            GraphView.this.removeShift(state, moveGraph);
+                            GuiUtils.addChild(state.getGraphs(), endGraph
+                                .getModel().getId(), moveGraph);
+                            GraphView.this.notifyListeners();
+                          } else {
+                            if (moveGraph.getParent() != null) {
+                              GuiUtils.removeNode(state.getGraphs(),
+                                  moveGraph.getModel());
+                              state.addGraph(moveGraph);
+                            }
+                            Point shiftPoint = new Point(
+                                (int) ((dropPoint.x - (dge.getDragOrigin().x - (bounds
+                                    .getX() * scale))) / scale),
+                                (int) ((dropPoint.y - (dge.getDragOrigin().y - (bounds
+                                    .getY() * scale))) / scale));
+                            GraphView.this.setShift(state, moveGraph,
+                                shiftPoint);
+                            GraphView.this.notifyListeners();
+                          }
+                        }
+
+                        public void dragEnter(DragSourceDragEvent dsde) {
+                          mouseOverCell = (DefaultGraphCell) GraphView.this.jgraph
+                              .getFirstCellForLocation(
+                                  dsde.getX() - jgraph.getLocationOnScreen().x,
+                                  dsde.getY() - jgraph.getLocationOnScreen().y);
+                          mouseOverGraph = GuiUtils.find(state.getGraphs(),
+                              ((ModelNode) mouseOverCell.getUserObject())
+                                  .getId());
+                        }
+
+                        public void dragExit(DragSourceEvent dse) {
+                          System.out.println("DRAG EXIT!!!!");
+                        }
+
+                        public void dragOver(DragSourceDragEvent dsde) {
+                          if (state.getMode().equals(Mode.EDIT)) {
+                            if (mouseOverCell != null) {
+                              Rectangle2D currentBounds = (Rectangle2D) mouseOverCell
+                                  .getAttributes().get(GraphConstants.BOUNDS);
+                              Point currentPoint = new Point(dsde.getX()
+                                  - jgraph.getLocationOnScreen().x, dsde.getY()
+                                  - jgraph.getLocationOnScreen().y);
+                              if (currentBounds.contains(currentPoint)) {
+                                for (ModelGraph child : mouseOverGraph
+                                    .getChildren()) {
+                                  DefaultGraphCell mouseOverCellLoc = GraphView.this.m_jgAdapter
+                                      .getVertexCell(child.getModel());
+                                  currentBounds = (Rectangle2D) mouseOverCellLoc
+                                      .getAttributes().get(
+                                          GraphConstants.BOUNDS);
+                                  if (currentBounds.contains(currentPoint)) {
+                                    mouseOverCell = mouseOverCellLoc;
+                                    mouseOverGraph = child;
+                                    break;
+                                  }
+                                }
+                              } else {
+                                if (mouseOverGraph.getParent() != null
+                                    && (!mouseOverGraph.isCondition() || (mouseOverGraph
+                                        .isCondition() && mouseOverGraph
+                                        .getParent().isCondition()))) {
+                                  mouseOverCell = GraphView.this.m_jgAdapter
+                                      .getVertexCell((mouseOverGraph = mouseOverGraph
+                                          .getParent()).getModel());
+                                  currentBounds = (Rectangle2D) mouseOverCell
+                                      .getAttributes().get(
+                                          GraphConstants.BOUNDS);
+                                } else {
+                                  mouseOverCell = null;
+                                  mouseOverGraph = null;
+                                }
+                              }
+                            } else {
+                              mouseOverCell = (DefaultGraphCell) GraphView.this.jgraph
+                                  .getFirstCellForLocation(
+                                      dsde.getX()
+                                          - jgraph.getLocationOnScreen().x,
+                                      dsde.getY()
+                                          - jgraph.getLocationOnScreen().y);
+                              if (mouseOverCell != null)
+                                mouseOverGraph = GuiUtils.find(state
+                                    .getGraphs(), ((ModelNode) mouseOverCell
+                                    .getUserObject()).getId());
+                              else
+                                mouseOverGraph = null;
+                            }
+                            if (mouseOverGraph != null) {
+                              if (GuiUtils.isDummyNode(mouseOverGraph
+                                  .getModel())) {
+                                mouseOverGraph = mouseOverGraph.getParent();
+                              } else {
+                                while (mouseOverGraph != null
+                                    && mouseOverGraph.getModel().isRef())
+                                  mouseOverGraph = mouseOverGraph.getParent();
+                              }
+                              if (mouseOverGraph != null)
+                                mouseOverCell = GraphView.this.m_jgAdapter
+                                    .getVertexCell(mouseOverGraph.getModel());
+                              else
+                                mouseOverCell = null;
+                            }
+                            GraphView.this.jgraph
+                                .setSelectionCells(new Object[] { mouseOverCell });
+                          }
+                        }
+
+                        public void dropActionChanged(DragSourceDragEvent dsde) {
+                          System.out.println("DRAG CHANGE!!!!");
+                        }
+
+                      });
+                }
+              }
+            }
+          }
+
+        });
+
+    List<Line> lines = GuiUtils.findLines(state.getGraphs());
+    for (Line line : lines) {
+      if (!this.directedGraph.containsVertex(line.getFromModel()))
+        this.directedGraph.addVertex(line.getFromModel());
+
+      if (line.getToModel() != null) {
+        if (!this.directedGraph.containsVertex(line.getToModel()))
+          this.directedGraph.addVertex(line.getToModel());
+        IdentifiableEdge edge = new IdentifiableEdge(line.getFromModel(), line.getToModel());
+        directedGraph.addEdge(edge, line.getFromModel(), line.getToModel());
+        this.edgeMap.put(edge.id, new Pair(line.getFromModel() != null ? line
+            .getFromModel().getId() : null, line.getToModel().getId()));
+      }
+    }
+
+    JGraphFacade facade = new JGraphFacade(jgraph);
+    facade.setIgnoresUnconnectedCells(false);
+    JGraphHierarchicalLayout layout = new JGraphHierarchicalLayout();
+    layout.setOrientation(SwingConstants.WEST);
+    layout.setIntraCellSpacing(70.0);
+    layout.setLayoutFromSinks(false);
+    layout.run(facade);
+    Map nested = facade.createNestedMap(true, true);
+    if (nested != null) {
+      this.hideDummyNodes(nested);
+      this.addGroups(state.getGraphs(), nested, state);
+      this.ensureNoOverlap(nested, state);
+      nested = this.shiftMap(nested, state);
+      jgraph.getGraphLayoutCache().edit(nested);
+    }
+
+    String edgeDisplayMode = state.getFirstPropertyValue(EDGE_DISPLAY_MODE);
+    if (edgeDisplayMode == null)
+      state.setProperty(EDGE_DISPLAY_MODE, edgeDisplayMode = WORKFLOW_MODE);
+    if (edgeDisplayMode.equals(WORKFLOW_MODE)) {
+      this.edgeMap = new HashMap<String, Pair>();
+      removeAllEdges(this.directedGraph);
+      lines = GuiUtils.findSequentialLines(state.getGraphs());
+      for (Line line : lines) {
+        IdentifiableEdge edge = new IdentifiableEdge(line.getFromModel(), line.getToModel());
+        directedGraph.addEdge(edge, line.getFromModel(), line.getToModel());
+        this.edgeMap.put(edge.id, new Pair(line.getFromModel() != null ? line
+            .getFromModel().getId() : null, line.getToModel().getId()));
+      }
+    }
+
+    if (state.getSelected() != null) {
+      ModelGraph graph = GuiUtils.find(state.getGraphs(), state.getSelected()
+          .getModel().getId());
+      if (graph != null) {
+        DefaultGraphCell cell = this.m_jgAdapter
+            .getVertexCell(graph.getModel());
+        if (cell != null)
+          this.jgraph.setSelectionCells(new Object[] { cell });
+        else
+          this.jgraph.setSelectionCells(new Object[] {});
+      } else
+        this.jgraph.setSelectionCells(new Object[] {});
+    } else {
+      this.jgraph.setSelectionCells(new Object[] {});
+    }
+
+    jgraph.addMouseListener(this.myGraphListener);
+
+    this.setLayout(new BorderLayout());
+    this.add(new JScrollPane(jgraph, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+        JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS), BorderLayout.CENTER);
+
+    if (scrollSelectedToVisible && state.getSelected() != null) {
+      this.jgraph.scrollCellToVisible(GraphView.this.m_jgAdapter
+          .getVertexCell(state.getSelected().getModel()));
+      scrollSelectedToVisible = false;
+    } else if (visible != null) {
+      this.jgraph.scrollRectToVisible(visible);
+    }
+
+    if (cursor != null)
+      this.jgraph.setCursor(cursor);
+
+    this.revalidate();
+  }
+
+  private void hideDummyNodes(Map nested) {
+    for (Object cell : nested.keySet()) {
+      if (cell instanceof DefaultEdge) {
+        // do nothing
+      } else if (cell instanceof DefaultGraphCell) {
+        if (GuiUtils.isDummyNode((ModelNode) ((DefaultGraphCell) cell)
+            .getUserObject())) {
+          ((Map<Object, Object>) nested.get(cell)).put("opaque", false);
+          ((Map<Object, Object>) nested.get(cell)).put("backgroundColor",
+              Color.white);
+        }
+      }
+    }
+  }
+
+  private Map shiftMap(Map nested, ViewState state) {
+    Map shiftedNested = new Hashtable(nested);
+    for (Object obj : shiftedNested.entrySet()) {
+      Entry entry = (Entry) obj;
+      if (entry.getKey() instanceof DefaultEdge) {
+        ArrayList<Point2D.Double> points = (ArrayList<Point2D.Double>) ((Map<Object, Object>) entry
+            .getValue()).get("points");
+        ArrayList<Point2D.Double> newPoints = new ArrayList<Point2D.Double>();
+        Point shift = this.getShift(state, (DefaultGraphCell) entry.getKey(),
+            nested);
+        for (Point2D.Double point : points)
+          newPoints
+              .add(new Point2D.Double(point.x + shift.x, point.y + shift.y));
+        ((Map<Object, Object>) entry.getValue()).put("points", newPoints);
+      } else if (entry.getKey() instanceof DefaultGraphCell) {
+        DefaultGraphCell cell = (DefaultGraphCell) entry.getKey();
+        Rectangle2D bounds = (Rectangle2D) ((Map<Object, Object>) entry
+            .getValue()).get("bounds");
+        Point shift = this.getShift(state, cell, nested);
+        AttributeMap.SerializableRectangle2D newBounds = new AttributeMap.SerializableRectangle2D(
+            bounds.getX() + shift.x, bounds.getY() + shift.y,
+            bounds.getWidth(), bounds.getHeight());
+        Map<Object, Object> newMap = new Hashtable<Object, Object>(
+            (Map<Object, Object>) entry.getValue());
+        newMap.put("bounds", newBounds);
+        shiftedNested.put(cell, newMap);
+      }
+    }
+    return shiftedNested;
+  }
+
+  private void ensureNoOverlap(Map nested, ViewState state) {
+    boolean changed;
+    do {
+      changed = false;
+      for (int i = 0; i < state.getGraphs().size(); i++) {
+        ModelGraph currentGraph = state.getGraphs().get(i);
+        if (this.ensureNoInternalOverlap(currentGraph, nested))
+          changed = true;
+        DefaultGraphCell currentCell = this.m_jgAdapter
+            .getVertexCell(currentGraph.getModel());
+        Rectangle2D currentBounds = (Rectangle2D) ((Map) nested
+            .get(currentCell)).get(GraphConstants.BOUNDS);
+        Point currentShift = this.getShift(state, currentCell, nested);
+        Rectangle currentShiftBounds = new Rectangle(
+            (int) (currentBounds.getX() + currentShift.getX()),
+            (int) (currentBounds.getY() + currentShift.getY()),
+            (int) currentBounds.getWidth(), (int) currentBounds.getHeight());
+        for (int j = 0; j < state.getGraphs().size(); j++) {
+          if (i == j)
+            continue;
+          ModelGraph graph = state.getGraphs().get(j);
+          DefaultGraphCell cell = this.m_jgAdapter.getVertexCell(graph
+              .getModel());
+          Rectangle2D bounds = (Rectangle2D) ((Map) nested.get(cell))
+              .get(GraphConstants.BOUNDS);
+          Point shift = this.getShift(state, cell, nested);
+          Rectangle shiftBounds = new Rectangle(
+              (int) (bounds.getX() + shift.getX()),
+              (int) (bounds.getY() + shift.getY()), (int) bounds.getWidth(),
+              (int) bounds.getHeight());
+          if (currentShiftBounds.intersects(shiftBounds)) {
+            changed = true;
+            if (currentShiftBounds.getY() < shiftBounds.getY()) {
+              Rectangle intersection = currentShiftBounds
+                  .intersection(shiftBounds);
+              if (currentShiftBounds.getY() + currentShiftBounds.getHeight() > shiftBounds
+                  .getY() + shiftBounds.getHeight()) {
+                this.setShift(state, graph,
+                    new Point((int) (currentShiftBounds.getX()
+                        + currentShiftBounds.getWidth() + 20.0),
+                        (int) shiftBounds.getY()));
+              } else {
+                this.setShift(
+                    state,
+                    graph,
+                    new Point((int) shiftBounds.getX(), (int) (shiftBounds
+                        .getY() + intersection.getHeight() + 20.0)));
+              }
+            } else {
+              Rectangle intersection = shiftBounds
+                  .intersection(currentShiftBounds);
+              if (shiftBounds.getY() + shiftBounds.getHeight() > currentShiftBounds
+                  .getY() + currentShiftBounds.getHeight()) {
+                this.setShift(
+                    state,
+                    currentGraph,
+                    new Point((int) (shiftBounds.getX()
+                        + shiftBounds.getWidth() + 20.0),
+                        (int) currentShiftBounds.getY()));
+              } else {
+                this.setShift(
+                    state,
+                    currentGraph,
+                    new Point((int) currentShiftBounds.getX(),
+                        (int) (currentShiftBounds.getY()
+                            + intersection.getHeight() + 20.0)));
+              }
+
+              currentShift = this.getShift(state, currentCell, nested);
+              currentShiftBounds = new Rectangle(
+                  (int) (currentBounds.getX() + currentShift.getX()),
+                  (int) (currentBounds.getY() + currentShift.getY()),
+                  (int) currentBounds.getWidth(),
+                  (int) currentBounds.getHeight());
+            }
+          }
+        }
+      }
+    } while (changed);
+  }
+
+  private boolean ensureNoInternalOverlap(final ModelGraph graph,
+      final Map nested) {
+    boolean changed = false;
+    if (graph.getChildren().size() > 1) {
+      List<ModelGraph> sortedChildren = new Vector<ModelGraph>(
+          graph.getChildren());
+      Collections.sort(sortedChildren, new Comparator<ModelGraph>() {
+
+        public int compare(ModelGraph o1, ModelGraph o2) {
+          DefaultGraphCell child1Cell = GraphView.this.m_jgAdapter
+              .getVertexCell(o1.getModel());
+          DefaultGraphCell child2Cell = GraphView.this.m_jgAdapter
+              .getVertexCell(o2.getModel());
+          Rectangle2D child1Bounds = (Rectangle2D) ((Map) nested
+              .get(child1Cell)).get(GraphConstants.BOUNDS);
+          Rectangle2D child2Bounds = (Rectangle2D) ((Map) nested
+              .get(child2Cell)).get(GraphConstants.BOUNDS);
+          if (graph.getModel().getExecutionType().equals("parallel"))
+            return Double.compare(child1Bounds.getMaxY(),
+                child2Bounds.getMaxY());
+          else
+            return Double.compare(child1Bounds.getX(), child2Bounds.getX());
+        }
+
+      });
+      changed = ensureNoInternalOverlap(sortedChildren.get(0), nested);
+      Rectangle2D graphRectangle = (Rectangle2D) ((Map) nested
+          .get(this.m_jgAdapter.getVertexCell(sortedChildren.get(0).getModel())))
+          .get(GraphConstants.BOUNDS);
+      for (int i = 1; i < sortedChildren.size(); i++) {
+        ModelGraph child2 = sortedChildren.get(i);
+        if (ensureNoInternalOverlap(child2, nested))
+          changed = true;
+        DefaultGraphCell child2Cell = this.m_jgAdapter.getVertexCell(child2
+            .getModel());
+        for (int j = i - 1; j >= 0; j--) {
+          ModelGraph child1 = sortedChildren.get(j);
+          DefaultGraphCell child1Cell = this.m_jgAdapter.getVertexCell(child1
+              .getModel());
+          Rectangle2D child1Bounds = (Rectangle2D) ((Map) nested
+              .get(child1Cell)).get(GraphConstants.BOUNDS);
+          Rectangle2D child2Bounds = (Rectangle2D) ((Map) nested
+              .get(child2Cell)).get(GraphConstants.BOUNDS);
+          if (child1Bounds.intersects(child2Bounds)) {
+            changed = true;
+            if (graph.getModel().getExecutionType().equals("parallel")) {
+              ((Map) nested.get(child2Cell)).put(GraphConstants.BOUNDS,
+                  new AttributeMap.SerializableRectangle2D(child2Bounds.getX(),
+                      child1Bounds.getMaxY() + 20.0, child2Bounds.getWidth(),
+                      child2Bounds.getHeight()));
+              this.shift(child2.getChildren(), nested, 0,
+                  child1Bounds.getMaxY() + 20.0 - child2Bounds.getY());
+            } else {
+              ((Map) nested.get(child2Cell)).put(
+                  GraphConstants.BOUNDS,
+                  new AttributeMap.SerializableRectangle2D(child1Bounds
+                      .getMaxX() + 20.0, child2Bounds.getY(), child2Bounds
+                      .getWidth(), child2Bounds.getHeight()));
+              this.shift(child2.getChildren(), nested, child1Bounds.getMaxX()
+                  + 20.0 - child2Bounds.getX(), 0);
+            }
+          }
+        }
+        graphRectangle = graphRectangle.createUnion((Rectangle2D) ((Map) nested
+            .get(child2Cell)).get(GraphConstants.BOUNDS));
+      }
+      ((Map) nested.get(this.m_jgAdapter.getVertexCell(graph.getModel()))).put(
+          GraphConstants.BOUNDS, new AttributeMap.SerializableRectangle2D(
+              graphRectangle.getX() - 5, graphRectangle.getY() - 20,
+              graphRectangle.getWidth() + 10, graphRectangle.getHeight() + 25));
+    }
+    return changed;
+  }
+
+  private void shift(List<ModelGraph> graphs, Map nested, double x, double y) {
+    for (int i = 0; i < graphs.size(); i++) {
+      ModelGraph graph = graphs.get(i);
+      DefaultGraphCell cell = this.m_jgAdapter.getVertexCell(graph.getModel());
+      Rectangle2D bounds = (Rectangle2D) ((Map) nested.get(cell))
+          .get(GraphConstants.BOUNDS);
+      ((Map) nested.get(cell)).put(
+          GraphConstants.BOUNDS,
+          new AttributeMap.SerializableRectangle2D(bounds.getX() + x, bounds
+              .getY() + y, bounds.getWidth(), bounds.getHeight()));
+      this.shift(graph.getChildren(), nested, x, y);
+    }
+  }
+
+  private void addGroups(List<ModelGraph> modelGraphs, Map nested,
+      ViewState state) {
+    for (ModelGraph modelGraph : modelGraphs)
+      this.addGroups(modelGraph, nested, state);
+  }
+
+  private DefaultGraphCell addGroups(ModelGraph modelGraph, Map nested,
+      ViewState state) {
+    if (modelGraph.getModel().isParentType()) {
+      double top_x = Double.MAX_VALUE, top_y = Double.MAX_VALUE, bottom_x = 0.0, bottom_y = 0.0;
+      this.directedGraph.addVertex(modelGraph.getModel());
+      DefaultGraphCell modelCell = this.m_jgAdapter.getVertexCell(modelGraph
+          .getModel());
+      Vector<DefaultGraphCell> group = new Vector<DefaultGraphCell>();
+      group.add(modelCell);
+
+      HashMap<Object, Object> map = new HashMap<Object, Object>();
+      for (int i = 0; i < modelGraph.getChildren().size(); i++) {
+        ModelGraph child = modelGraph.getChildren().get(i);
+        DefaultGraphCell curCell = addGroups(child, nested, state);
+        group.add(curCell);
+        Rectangle2D bounds = (Rectangle2D) ((Map<Object, Object>) nested
+            .get(curCell)).get("bounds");
+        if (bounds.getX() < top_x)
+          top_x = bounds.getX();
+        if (bounds.getY() < top_y)
+          top_y = bounds.getY();
+        if (bounds.getMaxX() > bottom_x)
+          bottom_x = bounds.getMaxX();
+        if (bounds.getMaxY() > bottom_y)
+          bottom_y = bounds.getMaxY();
+      }
+
+      map.put(GraphConstants.BOUNDS, new AttributeMap.SerializableRectangle2D(
+          top_x - 5, top_y - 20, bottom_x - top_x + 10, bottom_y - top_y + 25));
+      map.put(GraphConstants.FOREGROUND, Color.black);
+      if (modelGraph.getModel().isRef())
+        map.put(GraphConstants.BACKGROUND, Color.lightGray);
+      else
+        map.put(GraphConstants.BACKGROUND, Color.white);
+      if (modelGraph.isExcused())
+        map.put(GraphConstants.GRADIENTCOLOR, Color.gray);
+      map.put(GraphConstants.HORIZONTAL_ALIGNMENT, SwingConstants.LEFT);
+      map.put(GraphConstants.VERTICAL_ALIGNMENT, SwingConstants.TOP);
+      map.put(GraphConstants.BORDER, new LineBorder(modelGraph.getModel()
+          .getColor(), 1));
+      jgraph.getGraphLayoutCache().toBack(new Object[] { modelCell });
+      nested.put(modelCell, map);
+      return modelCell;
+    }
+    DefaultGraphCell cell = this.m_jgAdapter.getVertexCell(modelGraph
+        .getModel());
+    ((Map<Object, Object>) nested.get(cell)).put(GraphConstants.FOREGROUND,
+        Color.black);
+    if (modelGraph.isExcused())
+      ((Map<Object, Object>) nested.get(cell)).put(
+          GraphConstants.GRADIENTCOLOR, Color.gray);
+    else
+      ((Map<Object, Object>) nested.get(cell)).put(
+          GraphConstants.GRADIENTCOLOR, Color.white);
+    if (!((ModelNode) ((DefaultGraphCell) cell).getUserObject()).isRef())
+      ((Map<Object, Object>) nested.get(cell)).put(GraphConstants.BACKGROUND,
+          modelGraph.getModel().getColor());
+    else
+      ((Map<Object, Object>) nested.get(cell)).put(GraphConstants.BACKGROUND,
+          Color.lightGray);
+    return cell;
+  }
+  
+  private void removeAllEdges(ObservableGraph<ModelNode, IdentifiableEdge> graph){
+    List<IdentifiableEdge> edges = new Vector<IdentifiableEdge>();
+    
+    for(IdentifiableEdge edge: graph.getEdges()){
+       edges.add(edge);
+     }
+    
+    for(IdentifiableEdge edge: edges){
+       graph.removeEdge(edge);
+    }
+  }
+  
+
+  private class Pair {
+    String first, second;
+
+    public Pair(String first, String second) {
+      this.first = first;
+      this.second = second;
+    }
+
+    public String getFirst() {
+      return this.first;
+    }
+
+    public String getSecond() {
+      return this.second;
+    }
+  }
+
+  public class MyGraphListener implements MouseListener, ActionListener {
+
+    private Point curPoint;
+    private ViewState state;
+
+    public MyGraphListener(ViewState state) {
+      this.state = state;
+    }
+
+    public void mouseClicked(MouseEvent e) {
+      curPoint = e.getPoint();
+      if (e.getButton() == MouseEvent.BUTTON3) {
+        Object mouseOverCell = GraphView.this.jgraph.getFirstCellForLocation(
+            e.getX(), e.getY());
+        ModelGraph mouseOverGraph = null;
+        if (mouseOverCell != null) {
+          mouseOverGraph = (GuiUtils.find(state.getGraphs(),
+              ((ModelNode) ((DefaultMutableTreeNode) mouseOverCell)
+                  .getUserObject()).getId()));
+          if (mouseOverGraph != null) {
+            if (GuiUtils.isDummyNode(mouseOverGraph.getModel())) {
+              mouseOverGraph = mouseOverGraph.getParent();
+            } else {
+              while (mouseOverGraph != null
+                  && mouseOverGraph.getParent() != null
+                  && mouseOverGraph.getParent().getModel().isRef())
+                mouseOverGraph = mouseOverGraph.getParent();
+            }
+            if (mouseOverGraph != null)
+              mouseOverCell = GraphView.this.m_jgAdapter
+                  .getVertexCell(mouseOverGraph.getModel());
+            else
+              mouseOverCell = null;
+          }
+          state.setSelected(mouseOverGraph);
+        } else {
+          state.setSelected(null);
+        }
+        PopupMenu actionsMenu = createActionMenu(state);
+        GraphView.this.notifyListeners();
+        GraphView.this.jgraph.add(actionsMenu);
+        actionsMenu.show(GraphView.this.jgraph, e.getPoint().x, e.getPoint().y);
+      } else if (e.getButton() == MouseEvent.BUTTON1) {
+        if (state.getMode() == View.Mode.ZOOM_IN) {
+          state.setProperty(SCALE, Double.toString(Double.parseDouble(state
+              .getFirstPropertyValue(SCALE)) + 0.1));
+          state.setSelected(null);
+          GraphView.this.notifyListeners();
+        } else if (state.getMode() == View.Mode.ZOOM_OUT) {
+          state.setProperty(SCALE, Double.toString(Double.parseDouble(state
+              .getFirstPropertyValue(SCALE)) - 0.1));
+          state.setSelected(null);
+          GraphView.this.notifyListeners();
+        } else if (state.getMode() == View.Mode.EDIT) {
+          Object cell = GraphView.this.jgraph.getFirstCellForLocation(e.getX(),
+              e.getY());
+          if (cell != null) {
+            if (cell instanceof DefaultEdge) {
+            } else if (cell instanceof DefaultGraphCell) {
+              ModelGraph graph = GuiUtils.find(state.getGraphs(),
+                  ((ModelNode) ((DefaultGraphCell) cell).getUserObject())
+                      .getId());
+              if (graph.getModel().isRef())
+                while (graph.getParent() != null
+                    && graph.getParent().getModel().isRef())
+                  graph = graph.getParent();
+              if (GuiUtils.isDummyNode(graph.getModel()))
+                graph = graph.getParent();
+              state.setSelected(graph);
+              GraphView.this.notifyListeners();
+            }
+          } else if (cell == null && state.getSelected() != null) {
+            state.setSelected(null);
+            GraphView.this.notifyListeners();
+          }
+        } else if (state.getMode() == View.Mode.DELETE
+            && e.getClickCount() == 2) {
+          Object cell = GraphView.this.jgraph.getFirstCellForLocation(e.getX(),
+              e.getY());
+          if (cell != null) {
+            if (cell instanceof DefaultEdge) {
+              // do nothing
+            } else if (cell instanceof DefaultGraphCell) {
+              ModelGraph graph = GuiUtils.removeNode(state.getGraphs(),
+                  (ModelNode) ((DefaultGraphCell) cell).getUserObject());
+              GraphView.this.notifyListeners();
+            }
+          }
+        }
+      }
+    }
+
+    public void mouseEntered(MouseEvent e) {
+      if (state.getMode() == View.Mode.ZOOM_IN
+          || state.getMode() == View.Mode.ZOOM_OUT) {
+        Toolkit toolkit = Toolkit.getDefaultToolkit();
+        try {
+          GraphView.this.jgraph.setCursor(toolkit.createCustomCursor(
+              IconLoader.getIcon(IconLoader.ZOOM_CURSOR), new Point(0, 0),
+              "img"));
+        } catch (Exception e1) {
+          e1.printStackTrace();
+        }
+      } else if (state.getMode() == Mode.MOVE) {
+        GraphView.this.jgraph.setCursor(new Cursor(Cursor.HAND_CURSOR));
+      } else {
+        GraphView.this.jgraph.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
+      }
+    }
+
+    public void mouseExited(MouseEvent e) {
+    }
+
+    public void mousePressed(MouseEvent e) {
+    }
+
+    public void mouseReleased(MouseEvent e) {
+    }
+
+    public void actionPerformed(ActionEvent e) {
+      this.createNewGraph(e.getActionCommand());
+    }
+
+    private void createNewGraph(String actionCommand) {
+      ModelGraph newGraph = null;
+      if (actionCommand.equals(NEW_TASK_ITEM_NAME)) {
+        newGraph = new ModelGraph(new ModelNode(state.getFile(),
+            GuiUtils.createUniqueName()));
+      } else if (actionCommand.equals(NEW_PARALLEL_ITEM_NAME)) {
+        ModelNode node = new ModelNode(state.getFile(),
+            GuiUtils.createUniqueName());
+        node.setExecutionType("parallel");
+        newGraph = new ModelGraph(node);
+      } else if (actionCommand.equals(NEW_SEQUENTIAL_ITEM_NAME)) {
+        ModelNode node = new ModelNode(state.getFile(),
+            GuiUtils.createUniqueName());
+        node.setExecutionType("sequential");
+        newGraph = new ModelGraph(node);
+      } else {
+        return;
+      }
+      Object cell = GraphView.this.jgraph.getSelectionCell();
+      if (cell != null) {
+        if (cell instanceof DefaultGraphCell) {
+          ModelGraph graph = GuiUtils.find(state.getGraphs(),
+              ((ModelNode) ((DefaultGraphCell) cell).getUserObject()).getId());
+          if (graph != null)
+            graph.addChild(newGraph);
+        }
+      } else {
+        state.addGraph(newGraph);
+        GraphView.this.setShift(state, newGraph, curPoint);
+      }
+      GraphView.this.notifyListeners();
+    }
+
+  }
+
+  public void setShift(ViewState state, ModelGraph modelGraph, Point point) {
+    state.setProperty(modelGraph.getModel().getId() + "/Shift/x",
+        Integer.toString(point.x));
+    state.setProperty(modelGraph.getModel().getId() + "/Shift/y",
+        Integer.toString(point.y));
+  }
+
+  public void removeShift(ViewState state, ModelGraph modelGraph) {
+    state.removeProperty(modelGraph.getModel().getId() + "/Shift");
+  }
+
+  public Point getShift(ViewState state, DefaultGraphCell cell, Map nested) {
+    ModelGraph graph = null;
+    if (cell instanceof DefaultEdge) {
+      IdentifiableEdge edge = (IdentifiableEdge) cell.getUserObject();
+      Pair pair = GraphView.this.edgeMap.get(edge.id);
+      graph = GuiUtils.find(state.getGraphs(), pair.getFirst());
+    } else {
+      graph = GuiUtils.find(state.getGraphs(),
+          ((ModelNode) cell.getUserObject()).getId());
+    }
+    ModelGraph parent = GuiUtils.findRoot(state.getGraphs(), graph);
+    Point shiftPoint = null;
+    if (state.containsProperty(parent.getModel().getId() + "/Shift"))
+      shiftPoint = new Point(Integer.parseInt(state
+          .getFirstPropertyValue(parent.getModel().getId() + "/Shift/x")),
+          Integer.parseInt(state.getFirstPropertyValue(parent.getModel()
+              .getId() + "/Shift/y")));
+    if (shiftPoint == null) {
+      shiftPoint = new Point(100, 100);
+      this.setShift(state, parent, shiftPoint);
+      return shiftPoint;
+    } else {
+      Rectangle2D bounds = (Rectangle2D) ((Map<Object, Object>) nested
+          .get(GraphView.this.m_jgAdapter.getVertexCell(parent.getModel())))
+          .get(GraphConstants.BOUNDS);
+      return new Point(shiftPoint.x - (int) bounds.getX(), shiftPoint.y
+          - (int) bounds.getY());
+    }
+  }
+
+
+  private PopupMenu createActionMenu(final ViewState state) {
+    PopupMenu actionsMenu = new PopupMenu(ACTIONS_POP_MENU_NAME);
+    PopupMenu newSubMenu = new PopupMenu(NEW_SUB_POP_MENU_NAME);
+    MenuItem taskItem = new MenuItem(NEW_TASK_ITEM_NAME);
+    MenuItem condItem = new MenuItem(NEW_CONDITION_ITEM_NAME);
+    newSubMenu.add(taskItem);
+    newSubMenu.add(condItem);
+    newSubMenu.add(new MenuItem(NEW_PARALLEL_ITEM_NAME));
+    newSubMenu.add(new MenuItem(NEW_SEQUENTIAL_ITEM_NAME));
+    newSubMenu.addActionListener(this.myGraphListener);
+    actionsMenu.add(newSubMenu);
+    MenuItem viewReferrencedWorkflow = new MenuItem(VIEW_REF_WORKFLOW);
+    actionsMenu.add(viewReferrencedWorkflow);
+    MenuItem deleteItem = new MenuItem(DELETE_ITEM_NAME);
+    actionsMenu.add(deleteItem);
+    MenuItem formatItem = new MenuItem(FORMAT_ITEM_NAME);
+    actionsMenu.add(formatItem);
+
+    PopupMenu orderSubMenu = new PopupMenu(ORDER_SUB_POP_MENU_NAME);
+
+    ModelGraph modelGraph = state.getSelected();
+    newSubMenu.setEnabled(modelGraph == null
+        || modelGraph.getModel().isParentType());
+    deleteItem.setEnabled(modelGraph != null);
+    formatItem.setEnabled(true);
+    if (modelGraph != null) {
+      viewReferrencedWorkflow.setEnabled(modelGraph.getModel().isRef());
+      taskItem.setEnabled(!modelGraph.isCondition());
+      condItem.setEnabled(modelGraph.isCondition());
+      orderSubMenu.setEnabled(modelGraph.getParent() != null
+          && !(modelGraph.isCondition() && !modelGraph.getParent()
+              .isCondition()));
+    } else {
+      boolean isCondition = false;
+      if (state.getGraphs().size() > 0)
+        isCondition = state.getGraphs().get(0).isCondition();
+      viewReferrencedWorkflow.setEnabled(false);
+      taskItem.setEnabled(!isCondition);
+      condItem.setEnabled(isCondition);
+    }
+
+    actionsMenu.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        if (e.getActionCommand().equals(DELETE_ITEM_NAME)) {
+          GuiUtils.removeNode(state.getGraphs(), state.getSelected().getModel());
+          state.setSelected(null);
+          GraphView.this.notifyListeners();
+        } else if (e.getActionCommand().equals(FORMAT_ITEM_NAME)) {
+          GraphView.this.refreshView(state);
+        } else if (e.getActionCommand().equals(VIEW_REF_WORKFLOW)) {
+          scrollSelectedToVisible = true;
+          GraphView.this.notifyListeners(new ViewChange.VIEW_MODEL(state
+              .getSelected().getModel().getModelId(), GraphView.this));
+        }
+      }
+    });
+    PopupMenu edgesSubMenu = new PopupMenu(EDGES_SUB_POP_MENU_NAME);
+    edgesSubMenu.add(new MenuItem(TASK_LEVEL));
+    edgesSubMenu.add(new MenuItem(WORKFLOW_LEVEL));
+    actionsMenu.add(edgesSubMenu);
+    edgesSubMenu.addActionListener(new ActionListener() {
+
+      public void actionPerformed(ActionEvent e) {
+        if (e.getActionCommand().equals(TASK_LEVEL)) {
+          state.setProperty(EDGE_DISPLAY_MODE, TASK_MODE);
+        } else if (e.getActionCommand().equals(WORKFLOW_LEVEL)) {
+          state.setProperty(EDGE_DISPLAY_MODE, WORKFLOW_MODE);
+        }
+        GraphView.this.refreshView(state);
+      }
+
+    });
+    actionsMenu.add(orderSubMenu);
+    orderSubMenu.add(new MenuItem(TO_FRONT_ITEM_NAME));
+    orderSubMenu.add(new MenuItem(TO_BACK_ITEM_NAME));
+    orderSubMenu.add(new MenuItem(FORWARD_ITEM_NAME));
+    orderSubMenu.add(new MenuItem(BACKWARDS_ITEM_NAME));
+    orderSubMenu.addActionListener(new ActionListener() {
+
+      public void actionPerformed(ActionEvent e) {
+        ModelGraph graph = state.getSelected();
+        ModelGraph parent = graph.getParent();
+        if (e.getActionCommand().equals(TO_FRONT_ITEM_NAME)) {
+          if (parent.getChildren().remove(graph))
+            parent.getChildren().add(0, graph);
+        } else if (e.getActionCommand().equals(TO_BACK_ITEM_NAME)) {
+          if (parent.getChildren().remove(graph))
+            parent.getChildren().add(graph);
+        } else if (e.getActionCommand().equals(FORWARD_ITEM_NAME)) {
+          int index = parent.getChildren().indexOf(graph);
+          if (index != -1) {
+            parent.getChildren().remove(index);
+            parent.getChildren().add(
+                Math.min(parent.getChildren().size(), index + 1), graph);
+          }
+        } else if (e.getActionCommand().equals(BACKWARDS_ITEM_NAME)) {
+          int index = parent.getChildren().indexOf(graph);
+          if (index != -1) {
+            parent.getChildren().remove(index);
+            parent.getChildren().add(Math.max(0, index - 1), graph);
+          }
+        }
+        GraphView.this.notifyListeners();
+      }
+
+    });
+    return actionsMenu;
+  }
+
+}
\ No newline at end of file
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/IdentifiableEdge.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/IdentifiableEdge.java
new file mode 100644
index 0000000..c14175b
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/IdentifiableEdge.java
@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.view.impl;
+
+//JDK imports
+import java.util.UUID;
+
+import org.apache.oodt.cas.workflow.gui.model.ModelNode;
+
+/**
+ * 
+ * An ID'ed edge identified by {@link UUID#randomUUID()}.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class IdentifiableEdge {
+
+  String id;
+  
+  ModelNode from;
+  
+  ModelNode to;
+
+  public IdentifiableEdge(ModelNode from, ModelNode to) {
+    id = UUID.randomUUID().toString();
+    this.from = from;
+    this.to = to;
+  }
+
+  public String toString() {
+    return "";
+  }
+
+  /**
+   * @return the id
+   */
+  public String getId() {
+    return id;
+  }
+
+  /**
+   * @param id the id to set
+   */
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  /**
+   * @return the from
+   */
+  public ModelNode getFrom() {
+    return from;
+  }
+
+  /**
+   * @param from the from to set
+   */
+  public void setFrom(ModelNode from) {
+    this.from = from;
+  }
+
+  /**
+   * @return the to
+   */
+  public ModelNode getTo() {
+    return to;
+  }
+
+  /**
+   * @param to the to to set
+   */
+  public void setTo(ModelNode to) {
+    this.to = to;
+  }
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/JungJGraphModelAdapter.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/JungJGraphModelAdapter.java
new file mode 100644
index 0000000..a9179ef
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/JungJGraphModelAdapter.java
@@ -0,0 +1,246 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.view.impl;
+
+//JDK imports
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.geom.Rectangle2D;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.BorderFactory;
+
+//OODT imports
+import org.apache.oodt.cas.workflow.gui.model.ModelNode;
+
+//JGraph imports
+import org.jgraph.event.GraphModelEvent;
+import org.jgraph.event.GraphModelListener;
+import org.jgraph.graph.AttributeMap;
+import org.jgraph.graph.ConnectionSet;
+import org.jgraph.graph.DefaultEdge;
+import org.jgraph.graph.DefaultGraphCell;
+import org.jgraph.graph.DefaultGraphModel;
+import org.jgraph.graph.DefaultPort;
+import org.jgraph.graph.GraphConstants;
+
+//Jung imports
+import edu.uci.ics.jung.graph.ObservableGraph;
+import edu.uci.ics.jung.graph.event.GraphEvent;
+import edu.uci.ics.jung.graph.event.GraphEventListener;
+
+/**
+ * 
+ * This class integrates OODT, Jung and JGraph.
+ * 
+ * Not necessarily in that order. It acts as a facade, wrapping a Jung graph
+ * (and its modifications) and then using that to update the current state of
+ * the associated JGraph model.
+ * 
+ * @author mattmann
+ * 
+ */
+public class JungJGraphModelAdapter extends DefaultGraphModel {
+
+  private static final long serialVersionUID = 205647349848965651L;
+
+  private ObservableGraph<ModelNode, IdentifiableEdge> jungGraph;
+
+  private Map<String, DefaultGraphCell> cellMap;
+
+  private static final Logger LOG = Logger
+      .getLogger(JungJGraphModelAdapter.class.getName());
+
+  public JungJGraphModelAdapter(
+      final ObservableGraph<ModelNode, IdentifiableEdge> jungGraph) {
+    this.jungGraph = jungGraph;
+    this.jungGraph.addGraphEventListener(new WorkflowChangeListener(this));
+    this.cellMap = new HashMap<String, DefaultGraphCell>();
+    this.addGraphModelListener(new GraphModelListener() {
+
+      @Override
+      public void graphChanged(GraphModelEvent e) {
+        Object[] added = e.getChange().getInserted();
+        Object[] removed = e.getChange().getRemoved();
+        Object[] changed = e.getChange().getChanged();
+
+        if (added != null && added.length > 0) {
+          for (Object a : added) {
+            LOG.log(Level.FINE, "Jgraph notification of object added: ["
+                + a.getClass().getName() + "]");
+
+            if (a instanceof org.jgraph.graph.DefaultEdge) {
+              LOG.log(Level.FINE, "Edge added to jgraph");
+              org.jgraph.graph.DefaultEdge edge = (org.jgraph.graph.DefaultEdge) a;
+              if (!jungGraph.getEdges().contains(edge.getUserObject())) {
+                jungGraph.addEdge(
+                    new IdentifiableEdge((ModelNode) edge.getSource(),
+                        (ModelNode) edge.getTarget()), (ModelNode) edge
+                        .getSource(), (ModelNode) edge.getTarget());
+              }
+            } else if (a instanceof org.jgraph.graph.DefaultGraphCell) {
+              LOG.log(Level.FINE, "Vertex added to jgraph");
+              org.jgraph.graph.DefaultGraphCell cell = (org.jgraph.graph.DefaultGraphCell) a;
+
+              if (!jungGraph.getVertices().contains(cell.getUserObject())) {
+                jungGraph.addVertex((ModelNode) cell.getUserObject());
+              }
+            }
+          }
+        }
+
+        if (removed != null && removed.length > 0) {
+          for (Object r : removed) {
+            LOG.log(Level.FINE, "Jgraph notification of object removed: ["
+                + r.getClass().getName() + "]");
+
+            if (r instanceof org.jgraph.graph.DefaultEdge) {
+              LOG.log(Level.FINE, "Edge removed from jgraph");
+              org.jgraph.graph.DefaultEdge edge = (org.jgraph.graph.DefaultEdge) r;
+              if (jungGraph.getEdges().contains(edge.getUserObject())) {
+                jungGraph.removeEdge((IdentifiableEdge) edge.getUserObject());
+              }
+            } else if (r instanceof org.jgraph.graph.DefaultGraphCell) {
+              LOG.log(Level.FINE, "Vertex removed from jgraph");
+              org.jgraph.graph.DefaultGraphCell cell = (org.jgraph.graph.DefaultGraphCell) r;
+
+              if (jungGraph.getVertices().contains(cell.getUserObject())) {
+                jungGraph.removeVertex((ModelNode) cell.getUserObject());
+              }
+            }
+          }
+        }
+
+      }
+    });
+
+  }
+
+  public DefaultGraphCell getVertexCell(ModelNode node) {
+    if (cellMap.get(node.getId()) != null) {
+      return cellMap.get(node.getId());
+    }
+
+    DefaultGraphCell cell = new DefaultGraphCell(node);
+    cell.add(new DefaultPort());
+    return cell;
+  }
+
+  private AttributeMap getEdgeAttributes(DefaultEdge edge) {
+    AttributeMap eMap = new AttributeMap();
+    GraphConstants.setLineEnd(eMap, GraphConstants.ARROW_TECHNICAL);
+    GraphConstants.setEndFill(eMap, true);
+    GraphConstants.setEndSize(eMap, 10);
+    GraphConstants.setForeground(eMap, Color.decode("#25507C"));
+    GraphConstants.setFont(eMap,
+        GraphConstants.DEFAULTFONT.deriveFont(Font.BOLD, 12));
+    GraphConstants.setLineColor(eMap, Color.decode("#7AA1E6"));
+    AttributeMap map = new AttributeMap();
+    map.put(edge, eMap);
+    return map;
+  }
+
+  private AttributeMap getVertexAttributes(DefaultGraphCell cell) {
+    AttributeMap vMap = new AttributeMap();
+
+    Color c = Color.decode("#FF9900");
+    GraphConstants.setBounds(vMap, new Rectangle2D.Double(50, 50, 90, 30));
+    GraphConstants.setBorder(vMap, BorderFactory.createRaisedBevelBorder());
+    GraphConstants.setBackground(vMap, c);
+    GraphConstants.setForeground(vMap, Color.white);
+    GraphConstants.setFont(vMap,
+        GraphConstants.DEFAULTFONT.deriveFont(Font.BOLD, 12));
+    GraphConstants.setOpaque(vMap, true);
+
+    AttributeMap map = new AttributeMap();
+    map.put(cell, vMap);
+    return map;
+  }
+
+  private class WorkflowChangeListener implements
+      GraphEventListener<ModelNode, IdentifiableEdge> {
+
+    private JungJGraphModelAdapter adapter;
+
+    public WorkflowChangeListener(JungJGraphModelAdapter adapter) {
+      this.adapter = adapter;
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * edu.uci.ics.jung.graph.event.GraphEventListener#handleGraphEvent(edu.
+     * uci.ics.jung.graph.event.GraphEvent)
+     */
+    @Override
+    public void handleGraphEvent(GraphEvent<ModelNode, IdentifiableEdge> e) {
+      if (e.getType().equals(GraphEvent.Type.EDGE_ADDED)) {
+        LOG.log(Level.FINE, "EDGE ADDED!");
+        GraphEvent.Edge<ModelNode, IdentifiableEdge> event = (GraphEvent.Edge<ModelNode, IdentifiableEdge>) e;
+        addJGraphEdge(event.getEdge());
+      } else if (e.getType().equals(GraphEvent.Type.EDGE_REMOVED)) {
+        LOG.log(Level.FINE, "EDGE REMOVED!");
+      } else if (e.getType().equals(GraphEvent.Type.VERTEX_ADDED)) {
+        LOG.log(Level.FINE, "VERTEX ADDED!");
+        GraphEvent.Vertex<ModelNode, IdentifiableEdge> event = (GraphEvent.Vertex<ModelNode, IdentifiableEdge>) e;
+        addJGraphVertex(event.getVertex());
+      } else if (e.getType().equals(GraphEvent.Type.VERTEX_REMOVED)) {
+        LOG.log(Level.FINE, "VERTEX REMOVED!");
+      }
+
+    }
+
+  }
+
+  private void addJGraphVertex(ModelNode node) {
+    DefaultGraphCell cell = new DefaultGraphCell(node);
+    cell.add(new DefaultPort());
+    insert(new Object[] { cell }, getVertexAttributes(cell), null, null, null);
+    cellMap.put(node.getId(), cell);
+  }
+
+  private void addJGraphEdge(IdentifiableEdge e) {
+    ConnectionSet set = new ConnectionSet();
+    DefaultEdge theEdge = new DefaultEdge(e);
+    DefaultGraphCell from = null;
+    DefaultGraphCell to = null;
+    String fromVertexId = e.getFrom().getId();
+    String toVertexId = e.getTo().getId();
+    if (!cellMap.containsKey(fromVertexId)) {
+      addJGraphVertex(e.getFrom());
+    }
+    from = cellMap.get(fromVertexId);
+
+    if (!cellMap.containsKey(toVertexId)) {
+      addJGraphVertex(e.getTo());
+    }
+
+    to = cellMap.get(toVertexId);
+
+    set.connect(theEdge, (DefaultPort) from.getChildAt(0),
+        (DefaultPort) to.getChildAt(0));
+    insert(new Object[] { theEdge }, getEdgeAttributes(theEdge), set, null,
+        null);
+  }
+
+}
diff --git a/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/TreeProjectView.java b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/TreeProjectView.java
new file mode 100644
index 0000000..e5a28cf
--- /dev/null
+++ b/0.10/app/weditor/src/main/java/org/apache/oodt/cas/workflow/gui/perspective/view/impl/TreeProjectView.java
@@ -0,0 +1,151 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oodt.cas.workflow.gui.perspective.view.impl;
+
+//JDK imports
+import java.awt.BorderLayout;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.List;
+import javax.swing.JLabel;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.border.EtchedBorder;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreePath;
+
+//OODT imports
+import org.apache.oodt.cas.workflow.gui.perspective.view.MultiStateView;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewChange;
+import org.apache.oodt.cas.workflow.gui.perspective.view.ViewState;
+
+/**
+ * 
+ * 
+ * Shows the files/workflows associated with a project in the Workflow Editor
+ * GUI.
+ * 
+ * @author bfoster
+ * @author mattmann
+ * 
+ */
+public class TreeProjectView extends MultiStateView {
+
+  private static final long serialVersionUID = 1867428699533484861L;
+
+  private JTree tree;
+
+  private ViewState selectedState;
+
+  public TreeProjectView(String name) {
+    super(name);
+    this.setLayout(new BorderLayout());
+  }
+
+  @Override
+  public void refreshView(final ViewState activeState,
+      final List<ViewState> states) {
+    this.removeAll();
+    this.selectedState = activeState;
+    DefaultMutableTreeNode selected = null;
+    DefaultMutableTreeNode root = new DefaultMutableTreeNode("Projects");
+    for (ViewState state : states)
+      if (selectedState != null
+          && state.getFile().equals(selectedState.getFile()))
+        root.add(selected = new DefaultMutableTreeNode(state.getFile()
+            .getName()));
+      else
+        root.add(new DefaultMutableTreeNode(state.getFile().getName()));
+
+    tree = new JTree(root);
+    tree.setEditable(true);
+    tree.getModel().addTreeModelListener(new TreeModelListener() {
+
+      public void treeNodesChanged(TreeModelEvent e) {
+        //TODO: something with the view nodes here
+      }
+
+      public void treeNodesInserted(TreeModelEvent e) {
+      }
+
+      public void treeNodesRemoved(TreeModelEvent e) {
+      }
+
+      public void treeStructureChanged(TreeModelEvent e) {
+      }
+
+    });
+    tree.addMouseListener(new MouseListener() {
+
+      public void mouseClicked(MouseEvent e) {
+        if (e.getButton() == MouseEvent.BUTTON1) {
+          if (tree.getSelectionPath() != null) {
+            DefaultMutableTreeNode selectedComp = (DefaultMutableTreeNode) tree
+                .getSelectionPath().getLastPathComponent();
+            String stateName = (String) ((DefaultMutableTreeNode) selectedComp)
+                .getUserObject();
+            for (ViewState state : states) {
+              if (state.getFile().getName().equals(stateName)) {
+                selectedState = state;
+                if (e.getClickCount() == 2)
+                  TreeProjectView.this
+                      .notifyListeners(new ViewChange.NEW_ACTIVE_STATE(
+                          selectedState, TreeProjectView.this));
+                break;
+              }
+            }
+          }
+        }
+      }
+
+      public void mouseEntered(MouseEvent e) {
+      }
+
+      public void mouseExited(MouseEvent e) {
+      }
+
+      public void mousePressed(MouseEvent e) {
+      }
+
+      public void mouseReleased(MouseEvent e) {
+      }
+
+    });
+    if (selected != null)
+      tree.setSelectionPath(new TreePath(new DefaultMutableTreeNode[] { root,
+          selected }));
+    else if (states.size() > 0)
+      tree.setSelectionPath(new TreePath(new DefaultMutableTreeNode[] {