You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ma...@apache.org on 2012/10/08 03:38:08 UTC

svn commit: r1395430 [3/13] - in /incubator/ambari/branches/AMBARI-666: ./ ambari-agent/src/main/puppet/manifestloader/ ambari-agent/src/main/puppet/modules/configgenerator/manifests/ ambari-agent/src/main/puppet/modules/hdp-hadoop/manifests/ ambari-ag...

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/JDBCManagementController.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/JDBCManagementController.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/JDBCManagementController.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/JDBCManagementController.java Mon Oct  8 01:37:59 2012
@@ -18,121 +18,656 @@
 
 package org.apache.ambari.api.controller.jdbc;
 
-import org.apache.ambari.api.controller.spi.ManagementController;
-import org.apache.ambari.api.controller.spi.Predicate;
-import org.apache.ambari.api.controller.spi.Request;
-import org.apache.ambari.api.controller.spi.Resource;
-
+import org.apache.ambari.api.controller.internal.PropertyIdImpl;
+import org.apache.ambari.api.controller.internal.ResourceImpl;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.ClusterRequest;
+import org.apache.ambari.server.controller.ClusterResponse;
+import org.apache.ambari.server.controller.HostRequest;
+import org.apache.ambari.server.controller.HostResponse;
+import org.apache.ambari.server.controller.ServiceComponentHostRequest;
+import org.apache.ambari.server.controller.ServiceComponentHostResponse;
+import org.apache.ambari.server.controller.ServiceComponentRequest;
+import org.apache.ambari.server.controller.ServiceComponentResponse;
+import org.apache.ambari.server.controller.ServiceRequest;
+import org.apache.ambari.server.controller.ServiceResponse;
+import org.apache.ambari.server.controller.TrackActionResponse;
+import org.apache.ambari.server.controller.predicate.AndPredicate;
+import org.apache.ambari.server.controller.predicate.BasePredicate;
+import org.apache.ambari.server.controller.predicate.EqualsPredicate;
+import org.apache.ambari.server.controller.predicate.PredicateVisitorAcceptor;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.api.controller.utilities.PredicateHelper;
+import org.apache.ambari.api.controller.utilities.Properties;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 /**
  * Generic JDBC implementation of a management controller.
  */
-public class JDBCManagementController implements ManagementController {
-
+public class JDBCManagementController implements AmbariManagementController {
+  /**
+   * The connection factory.
+   */
   private final ConnectionFactory connectionFactory;
 
-  public JDBCManagementController(ConnectionFactory connectionFactory) {
+  /**
+   * Mapping of resource type to the name of the primary table for the resource.
+   */
+  private final Map<Resource.Type, String> resourceTables;
+
+  /**
+   * Primary key mappings.
+   */
+  private final Map<String, Set<PropertyId>> primaryKeys = new HashMap<String, Set<PropertyId>>();
+
+  /**
+   * Key mappings used for joins.
+   */
+  private final Map<String, Map<PropertyId, PropertyId>> importedKeys = new HashMap<String, Map<PropertyId, PropertyId>>();
+
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct a new JDBC management controller with the given JDBC connection.
+   *
+   * @param connectionFactory  the connection factory
+   */
+  public JDBCManagementController(ConnectionFactory connectionFactory, Map<Resource.Type, String> resourceTables) {
     this.connectionFactory = connectionFactory;
+    this.resourceTables = resourceTables;
   }
 
+  // ----- AmbariManagementController ----------------------------------------
+
   @Override
-  public void createClusters(Request request) {
-    JDBCHelper.createResources(connectionFactory, request);
+  public TrackActionResponse createCluster(ClusterRequest request) throws AmbariException {
+//    createResources(Resource.Type.Cluster, request);
+    return null;
   }
 
   @Override
-  public void createServices(Request request) {
-    JDBCHelper.createResources(connectionFactory, request);
+  public TrackActionResponse createService(ServiceRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public void createComponents(Request request) {
-    JDBCHelper.createResources(connectionFactory, request);
+  public TrackActionResponse createComponent(ServiceComponentRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public void createHosts(Request request) {
-    JDBCHelper.createResources(connectionFactory, request);
+  public TrackActionResponse createHost(HostRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public void createHostComponents(Request request) {
-    JDBCHelper.createResources(connectionFactory, request);
+  public TrackActionResponse createHostComponent(ServiceComponentHostRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public Set<Resource> getClusters(Request request, Predicate predicate) {
-    return JDBCHelper.getResources(connectionFactory, Resource.Type.Cluster, request, predicate);
+  public Set<ClusterResponse> getClusters(ClusterRequest request) throws AmbariException {
+//    return getResources(Resource.Type.Cluster, request, predicate);
+    return null;
   }
 
   @Override
-  public Set<Resource> getServices(Request request, Predicate predicate) {
-    return JDBCHelper.getResources(connectionFactory, Resource.Type.Service, request, predicate);
+  public Set<ServiceResponse> getServices(ServiceRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public Set<Resource> getComponents(Request request, Predicate predicate) {
-    return JDBCHelper.getResources(connectionFactory, Resource.Type.Component, request, predicate);
+  public Set<ServiceComponentResponse> getComponents(ServiceComponentRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public Set<Resource> getHosts(Request request, Predicate predicate) {
-    return JDBCHelper.getResources(connectionFactory, Resource.Type.Host, request, predicate);
+  public Set<HostResponse> getHosts(HostRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public Set<Resource> getHostComponents(Request request, Predicate predicate) {
-    return JDBCHelper.getResources(connectionFactory, Resource.Type.HostComponent, request, predicate);
+  public Set<ServiceComponentHostResponse> getHostComponents(ServiceComponentHostRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public void updateClusters(Request request, Predicate predicate) {
-    JDBCHelper.updateResources(connectionFactory, request, predicate);
+  public TrackActionResponse updateCluster(ClusterRequest request) throws AmbariException {
+//    updateResources(Resource.Type.Cluster, request, predicate);
+    return null;
   }
 
   @Override
-  public void updateServices(Request request, Predicate predicate) {
-    JDBCHelper.updateResources(connectionFactory, request, predicate);
+  public TrackActionResponse updateService(ServiceRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public void updateComponents(Request request, Predicate predicate) {
-    JDBCHelper.updateResources(connectionFactory, request, predicate);
+  public TrackActionResponse updateComponent(ServiceComponentRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public void updateHosts(Request request, Predicate predicate) {
-    JDBCHelper.updateResources(connectionFactory, request, predicate);
+  public TrackActionResponse updateHost(HostRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public void updateHostComponents(Request request, Predicate predicate) {
-    JDBCHelper.updateResources(connectionFactory, request, predicate);
+  public TrackActionResponse updateHostComponent(ServiceComponentHostRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public void deleteClusters(Predicate predicate) {
-    JDBCHelper.deleteResources(connectionFactory, predicate);
+  public TrackActionResponse deleteCluster(ClusterRequest request) throws AmbariException {
+//    deleteResources(Resource.Type.Cluster, predicate);
+    return null;
   }
 
   @Override
-  public void deleteServices(Predicate predicate) {
-    JDBCHelper.deleteResources(connectionFactory, predicate);
+  public TrackActionResponse deleteService(ServiceRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public void deleteComponents(Predicate predicate) {
-    JDBCHelper.deleteResources(connectionFactory, predicate);
+  public TrackActionResponse deleteComponent(ServiceComponentRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public void deleteHosts(Predicate predicate) {
-    JDBCHelper.deleteResources(connectionFactory, predicate);
+  public TrackActionResponse deleteHost(HostRequest request) throws AmbariException {
+    return null;
   }
 
   @Override
-  public void deleteHostComponents(Predicate predicate) {
-    JDBCHelper.deleteResources(connectionFactory, predicate);
+  public TrackActionResponse deleteHostComponent(ServiceComponentHostRequest request) throws AmbariException {
+    return null;
+  }
+
+
+  // ----- Helper methods ----------------------------------------------------
+
+  /**
+   * Create the resources defined by the properties in the given request object.
+   *
+   * @param type     the resource type
+   * @param request  the request object which defines the set of properties
+   *                 for the resource to be created
+   */
+  private void createResources(Resource.Type type, Request request) {
+    try {
+      Connection connection = connectionFactory.getConnection();
+
+      try {
+
+        Set<Map<PropertyId, Object>> propertySet = request.getProperties();
+
+        for (Map<PropertyId, Object> properties : propertySet) {
+          String sql = getInsertSQL(resourceTables.get(type), properties);
+
+          Statement statement = connection.createStatement();
+
+          statement.execute(sql);
+        }
+      } finally {
+        connection.close();
+      }
+
+    } catch (SQLException e) {
+      throw new IllegalStateException("DB error : ", e);
+    }
+  }
+
+  /**
+   * Get a set of {@link Resource resources} based on the given request and predicate
+   * information.
+   *
+   * @param type       the resource type
+   * @param request    the request object which defines the desired set of properties
+   * @param predicate  the predicate object which can be used to filter which
+   *                   resources are returned
+   * @return a set of resources based on the given request and predicate information
+   */
+  private Set<Resource> getResources(Resource.Type type, Request request, Predicate predicate) {
+
+    Set<Resource> resources = new HashSet<Resource>();
+    Set<PropertyId> propertyIds = new HashSet<PropertyId>(request.getPropertyIds());
+    if (predicate != null) {
+      propertyIds.addAll(PredicateHelper.getPropertyIds(predicate));
+    }
+
+    try {
+      Connection connection = connectionFactory.getConnection();
+
+      try {
+
+        for (String table : getTables(propertyIds)) {
+          getImportedKeys(connection, table);
+        }
+        String sql = getSelectSQL(propertyIds, predicate);
+        Statement statement = connection.createStatement();
+
+        ResultSet rs = statement.executeQuery(sql);
+        ResultSetMetaData metaData = rs.getMetaData();
+        int columnCount = metaData.getColumnCount();
+
+        while (rs.next()) {
+          final ResourceImpl resource = new ResourceImpl(type);
+          for (int i = 1; i <= columnCount; ++i) {
+            PropertyIdImpl propertyId = new PropertyIdImpl(metaData.getColumnName(i), metaData.getTableName(i), false);
+            if (propertyIds.contains(propertyId)) {
+              resource.setProperty(propertyId, rs.getString(i));
+            }
+          }
+          resources.add(resource);
+        }
+
+      } finally {
+        connection.close();
+      }
+
+    } catch (SQLException e) {
+      throw new IllegalStateException("DB error : ", e);
+    }
+
+    return resources;
+  }
+
+  /**
+   * Update the host resources selected by the given predicate with the properties
+   * from the given request object.
+   *
+   * @param type       the resource type
+   * @param request    the request object which defines the set of properties
+   *                   for the resources to be updated
+   * @param predicate  the predicate object which can be used to filter which
+   *                   host resources are updated
+   */
+  private void updateResources(Resource.Type type, Request request, Predicate predicate) {
+    try {
+      Connection connection = connectionFactory.getConnection();
+      try {
+        Set<Map<PropertyId, Object>> propertySet = request.getProperties();
+
+        Map<PropertyId, Object> properties = propertySet.iterator().next();
+
+        String resourceTable = resourceTables.get(type);
+
+        predicate = getPredicate(connection, resourceTable, predicate);
+
+        if (predicate == null) {
+          return;
+        }
+
+        String sql = getUpdateSQL(resourceTable, properties, predicate);
+
+        Statement statement = connection.createStatement();
+
+        statement.execute(sql);
+      } finally {
+        connection.close();
+      }
+
+    } catch (SQLException e) {
+      throw new IllegalStateException("DB error : ", e);
+    }
+  }
+
+  /**
+   * Delete the resources selected by the given predicate.
+   *
+   * @param type      the resource type
+   * @param predicate the predicate object which can be used to filter which
+   *                  resources are deleted
+   */
+  private void deleteResources(Resource.Type type, Predicate predicate) {
+    try {
+      Connection connection = connectionFactory.getConnection();
+      try {
+        String resourceTable = resourceTables.get(type);
+
+        predicate = getPredicate(connection, resourceTable, predicate);
+
+        if (predicate == null) {
+          return;
+        }
+
+        String sql = getDeleteSQL(resourceTable, predicate);
+
+        Statement statement = connection.createStatement();
+        statement.execute(sql);
+      } finally {
+        connection.close();
+      }
+
+    } catch (SQLException e) {
+      throw new IllegalStateException("DB error : ", e);
+    }
+  }
+
+  /**
+   * Lazily populate the imported key mappings for the given table.
+   *
+   * @param connection  the connection to use to obtain the database meta data
+   * @param table       the table
+   *
+   * @throws SQLException thrown if the meta data for the given connection cannot be obtained
+   */
+  private void getImportedKeys(Connection connection, String table) throws SQLException {
+    if (!this.importedKeys.containsKey(table)) {
+
+      Map<PropertyId, PropertyId> importedKeys = new HashMap<PropertyId, PropertyId>();
+      this.importedKeys.put(table, importedKeys);
+
+      DatabaseMetaData metaData = connection.getMetaData();
+
+      ResultSet rs = metaData.getImportedKeys(connection.getCatalog(), null, table);
+
+      while (rs.next()) {
+
+        PropertyId pkPropertyId = Properties.getPropertyId(
+            rs.getString("PKCOLUMN_NAME"), rs.getString("PKTABLE_NAME"));
+
+        PropertyId fkPropertyId = Properties.getPropertyId(
+            rs.getString("FKCOLUMN_NAME"), rs.getString("FKTABLE_NAME"));
+
+        importedKeys.put(pkPropertyId, fkPropertyId);
+      }
+    }
+  }
+
+  /**
+   * Lazily populate the primary key mappings for the given table.
+   *
+   * @param connection  the connection to use to obtain the database meta data
+   * @param table       the table
+   *
+   * @throws SQLException thrown if the meta data for the given connection cannot be obtained
+   */
+  private void getPrimaryKeys(Connection connection, String table) throws SQLException {
+
+    if (!this.primaryKeys.containsKey(table)) {
+
+      Set<PropertyId> primaryKeys = new HashSet<PropertyId>();
+      this.primaryKeys.put(table, primaryKeys);
+
+      DatabaseMetaData metaData = connection.getMetaData();
+
+      ResultSet rs = metaData.getPrimaryKeys(connection.getCatalog(), null, table);
+
+      while (rs.next()) {
+
+        PropertyId pkPropertyId = Properties.getPropertyId(
+            rs.getString("COLUMN_NAME"), rs.getString("TABLE_NAME"));
+
+        primaryKeys.add(pkPropertyId);
+      }
+    }
+  }
+
+  /**
+   * Create a new predicate if the given predicate doesn't work for the given table.  Use the
+   * given predicate and join to the given table to get the primary key values to create a new
+   * predicate. (Could probably do this with INNER JOIN???)
+   *
+   * @param connection  the JDBC connection
+   * @param table       the resource table
+   * @param predicate   the predicate
+   *
+   * @return the new predicate
+   *
+   * @throws SQLException thrown if an exception occurred operating on the given connection
+   */
+  private Predicate getPredicate(Connection connection, String table, Predicate predicate) throws SQLException {
+
+    Set<String> predicateTables = getTables(PredicateHelper.getPropertyIds(predicate));
+
+    if (predicateTables.size() > 1 || !predicateTables.contains(table)) {
+      for (String predicateTable : predicateTables){
+        getImportedKeys(connection, predicateTable);
+      }
+
+      getPrimaryKeys(connection, table);
+      getImportedKeys(connection, table);
+
+      Set<PropertyId>   pkPropertyIds = primaryKeys.get(table);
+      String            sql           = getSelectSQL(pkPropertyIds, predicate);
+      Statement         statement     = connection.createStatement();
+      ResultSet         rs            = statement.executeQuery(sql);
+      ResultSetMetaData metaData      = rs.getMetaData();
+      int               columnCount   = metaData.getColumnCount();
+
+      Set<BasePredicate> predicates = new HashSet<BasePredicate>();
+      while (rs.next()) {
+        for (int i = 1; i <= columnCount; ++i) {
+          PropertyIdImpl propertyId = new PropertyIdImpl(metaData.getColumnName(i), metaData.getTableName(i), false);
+          if (pkPropertyIds.contains(propertyId)) {
+            predicates.add(new EqualsPredicate(propertyId, rs.getString(i)));
+          }
+        }
+      }
+
+      predicate = predicates.size() == 0 ? null : predicates.size() > 1 ?
+          new AndPredicate(predicates.toArray(new BasePredicate[2])) :
+          predicates.iterator().next();
+    }
+    return predicate;
+  }
+
+  /**
+   * Get an insert SQL statement based on the given properties.
+   *
+   * @param table      the table
+   * @param properties  the properties
+   *
+   * @return the insert SQL
+   */
+  private String getInsertSQL(String table, Map<PropertyId, Object> properties) {
+
+    StringBuilder columns = new StringBuilder();
+    StringBuilder values = new StringBuilder();
+
+    for (Map.Entry<PropertyId, Object> entry : properties.entrySet()) {
+      PropertyId propertyId = entry.getKey();
+      Object propertyValue = entry.getValue();
+
+      if (columns.length() > 0) {
+        columns.append(", ");
+      }
+      columns.append(propertyId.getName());
+
+      if (values.length() > 0) {
+        values.append(", ");
+      }
+
+      if (propertyValue instanceof String) {
+        values.append("'");
+        values.append(propertyValue);
+        values.append("'");
+      } else {
+        values.append(propertyValue);
+      }
+    }
+
+    return "insert into " + table + " (" +
+        columns + ") values (" + values + ")";
+  }
+
+  /**
+   * Get a select SQL statement based on the given property ids and predicate.
+   *
+   * @param propertyIds  the property ids
+   * @param predicate    the predicate
+   *
+   * @return the select SQL
+   */
+  private String getSelectSQL(Set<PropertyId> propertyIds, Predicate predicate) {
+
+    StringBuilder columns = new StringBuilder();
+    Set<String> tableSet = new HashSet<String>();
+
+    for (PropertyId propertyId : propertyIds) {
+      if (columns.length() > 0) {
+        columns.append(", ");
+      }
+      columns.append(propertyId.getCategory()).append(".").append(propertyId.getName());
+      tableSet.add(propertyId.getCategory());
+    }
+
+    boolean haveWhereClause = false;
+    StringBuilder whereClause = new StringBuilder();
+    if (predicate != null && predicate instanceof PredicateVisitorAcceptor) {
+
+      SQLPredicateVisitor visitor = new SQLPredicateVisitor();
+      PredicateHelper.visit(predicate, visitor);
+      whereClause.append(visitor.getSQL());
+
+      for (PropertyId propertyId : PredicateHelper.getPropertyIds(predicate)) {
+        tableSet.add(propertyId.getCategory());
+      }
+      haveWhereClause = true;
+    }
+
+    StringBuilder joinClause = new StringBuilder();
+
+    if (tableSet.size() > 1) {
+
+      for (String table : tableSet) {
+        Map<PropertyId, PropertyId> joinKeys = importedKeys.get(table);
+        if (joinKeys != null) {
+          for (Map.Entry<PropertyId, PropertyId> entry : joinKeys.entrySet()) {
+            String category1 = entry.getKey().getCategory();
+            String category2 = entry.getValue().getCategory();
+            if (tableSet.contains(category1) && tableSet.contains(category2)) {
+              if (haveWhereClause) {
+                joinClause.append(" AND ");
+              }
+              joinClause.append(category1).append(".").append(entry.getKey().getName());
+              joinClause.append(" = ");
+              joinClause.append(category2).append(".").append(entry.getValue().getName());
+              tableSet.add(category1);
+              tableSet.add(category2);
+
+              haveWhereClause = true;
+            }
+          }
+        }
+      }
+    }
+
+    StringBuilder tables = new StringBuilder();
+
+    for (String table : tableSet) {
+      if (tables.length() > 0) {
+        tables.append(", ");
+      }
+      tables.append(table);
+    }
+
+    String sql = "select " + columns + " from " + tables;
+
+    if (haveWhereClause) {
+      sql = sql + " where " + whereClause + joinClause;
+    }
+    return sql;
+  }
+
+  /**
+   * Get a delete SQL statement based on the given predicate.
+   *
+   * @param table      the table
+   * @param predicate  the predicate
+   *
+   * @return the delete SQL statement
+   */
+  private String getDeleteSQL(String table, Predicate predicate) {
+
+    StringBuilder whereClause = new StringBuilder();
+    if (predicate instanceof BasePredicate) {
+
+      BasePredicate basePredicate = (BasePredicate) predicate;
+
+      SQLPredicateVisitor visitor = new SQLPredicateVisitor();
+      basePredicate.accept(visitor);
+      whereClause.append(visitor.getSQL());
+
+      return "delete from " + table + " where " + whereClause;
+    }
+    throw new IllegalStateException("Can't generate SQL.");
+  }
+
+  /**
+   * Get an update SQL statement based on the given properties and predicate.
+   *
+   * @param table       the table
+   * @param properties  the properties
+   * @param predicate   the predicate
+   *
+   * @return the update SQL statement
+   */
+  private String getUpdateSQL(String table, Map<PropertyId, Object> properties, Predicate predicate) {
+
+    if (predicate instanceof BasePredicate) {
+
+      StringBuilder whereClause = new StringBuilder();
+
+      BasePredicate basePredicate = (BasePredicate) predicate;
+
+      SQLPredicateVisitor visitor = new SQLPredicateVisitor();
+      basePredicate.accept(visitor);
+      whereClause.append(visitor.getSQL());
+
+      StringBuilder setClause = new StringBuilder();
+      for (Map.Entry<PropertyId, Object> entry : properties.entrySet()) {
+
+        if (setClause.length() > 0) {
+          setClause.append(", ");
+        }
+        setClause.append(entry.getKey().getName());
+        setClause.append(" = ");
+        Object propertyValue = entry.getValue();
+
+        if (propertyValue instanceof String) {
+          setClause.append("'");
+          setClause.append(propertyValue);
+          setClause.append("'");
+        } else {
+          setClause.append(propertyValue);
+        }
+      }
+
+      return "update " + table + " set " + setClause + " where " + whereClause;
+    }
+    throw new IllegalStateException("Can't generate SQL.");
+  }
+
+  /**
+   * Get the set of tables associated with the given property ids.
+   *
+   * @param propertyIds  the property ids
+   *
+   * @return the set of tables
+   */
+  private static Set<String> getTables(Set<PropertyId> propertyIds) {
+    Set<String> tables = new HashSet<String>();
+    for (PropertyId propertyId : propertyIds) {
+      tables.add(propertyId.getCategory());
+    }
+    return tables;
   }
 }
+

Added: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/JDBCProviderModule.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/JDBCProviderModule.java?rev=1395430&view=auto
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/JDBCProviderModule.java (added)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/JDBCProviderModule.java Mon Oct  8 01:37:59 2012
@@ -0,0 +1,44 @@
+/**
+ * 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.ambari.api.controller.jdbc;
+
+import org.apache.ambari.api.controller.ProviderModule;
+import org.apache.ambari.api.controller.utilities.DBHelper;
+import org.apache.ambari.api.controller.utilities.Properties;
+import org.apache.ambari.server.controller.spi.PropertyProvider;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceProvider;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class JDBCProviderModule implements ProviderModule {
+
+  private static final List<PropertyProvider> PROPERTY_PROVIDERS = new LinkedList<PropertyProvider>();
+
+  @Override
+  public ResourceProvider getResourceProvider(Resource.Type type) {
+    return new JDBCResourceProvider(DBHelper.CONNECTION_FACTORY, type,
+        PROPERTY_PROVIDERS, Properties.getPropertyIds(type, "DB"),
+        Properties.getKeyPropertyIds(type));
+  }
+}

Added: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/JDBCResourceProvider.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/JDBCResourceProvider.java?rev=1395430&view=auto
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/JDBCResourceProvider.java (added)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/JDBCResourceProvider.java Mon Oct  8 01:37:59 2012
@@ -0,0 +1,424 @@
+/**
+ * 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.ambari.api.controller.jdbc;
+
+import org.apache.ambari.api.controller.internal.PropertyIdImpl;
+import org.apache.ambari.api.controller.internal.ResourceImpl;
+import org.apache.ambari.api.controller.internal.SchemaImpl;
+import org.apache.ambari.api.controller.utilities.PredicateHelper;
+import org.apache.ambari.api.controller.utilities.Properties;
+import org.apache.ambari.server.controller.predicate.BasePredicate;
+import org.apache.ambari.server.controller.predicate.PredicateVisitorAcceptor;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.spi.PropertyProvider;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceProvider;
+import org.apache.ambari.server.controller.spi.Schema;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Generic JDBC based resource provider.
+ */
+public class JDBCResourceProvider implements ResourceProvider {
+
+  private final Resource.Type type;
+
+  private final Set<PropertyId> propertyIds;
+
+  private final ConnectionFactory connectionFactory;
+
+  /**
+   * The list of property providers for this provider's resource type.
+   */
+  private final List<PropertyProvider> propertyProviders;
+
+  /**
+   * The schema for this provider's resource type.
+   */
+  private final Schema schema;
+
+  /**
+   * Key mappings used for joins.
+   */
+  private final Map<String, Map<PropertyId, PropertyId>> importedKeys = new HashMap<String, Map<PropertyId, PropertyId>>();
+
+  public JDBCResourceProvider(ConnectionFactory connectionFactory,
+                              Resource.Type type,
+                              List<PropertyProvider> propertyProviders,
+                              Set<PropertyId> propertyIds,
+                              Map<Resource.Type, PropertyId> keyPropertyIds) {
+    this.connectionFactory = connectionFactory;
+    this.type = type;
+    this.propertyProviders = propertyProviders;
+    this.propertyIds = propertyIds;
+    this.schema = new SchemaImpl(this, keyPropertyIds);
+  }
+
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate) {
+
+    Set<Resource> resources = new HashSet<Resource>();
+    Set<PropertyId> propertyIds = new HashSet<PropertyId>(request.getPropertyIds());
+    if (propertyIds.isEmpty()) {
+      propertyIds.addAll(this.propertyIds);
+    } else {
+      if (predicate != null) {
+        propertyIds.addAll(PredicateHelper.getPropertyIds(predicate));
+      }
+      propertyIds.retainAll(this.propertyIds);
+    }
+
+    try {
+      Connection connection = connectionFactory.getConnection();
+
+      try {
+
+        for (String table : getTables(propertyIds)) {
+          getImportedKeys(connection, table);
+        }
+
+        String sql = getSelectSQL(propertyIds, predicate);
+        Statement statement = connection.createStatement();
+
+        ResultSet rs = statement.executeQuery(sql);
+
+        while (rs.next()) {
+          ResultSetMetaData metaData = rs.getMetaData();
+          int columnCount = metaData.getColumnCount();
+
+          final ResourceImpl resource = new ResourceImpl(type);
+          for (int i = 1; i <= columnCount; ++i) {
+            PropertyIdImpl propertyId = new PropertyIdImpl(metaData.getColumnName(i), metaData.getTableName(i), false);
+            if (propertyIds.contains(propertyId)) {
+              resource.setProperty(propertyId, rs.getString(i));
+            }
+          }
+          resources.add(resource);
+        }
+
+      } finally {
+        connection.close();
+      }
+
+    } catch (SQLException e) {
+      throw new IllegalStateException("DB error : ", e);
+    }
+
+    return resources;
+  }
+
+  @Override
+  public void createResources(Request request) {
+    try {
+      Connection connection = connectionFactory.getConnection();
+
+      try {
+
+        Set<Map<PropertyId, Object>> propertySet = request.getProperties();
+
+        for (Map<PropertyId, Object> properties : propertySet) {
+          String sql = getInsertSQL(properties);
+
+          Statement statement = connection.createStatement();
+
+          statement.execute(sql);
+        }
+      } finally {
+        connection.close();
+      }
+
+    } catch (SQLException e) {
+      throw new IllegalStateException("DB error : ", e);
+    }
+  }
+
+  @Override
+  public void updateResources(Request request, Predicate predicate) {
+
+    try {
+      Connection connection = connectionFactory.getConnection();
+      try {
+        Set<Map<PropertyId, Object>> propertySet = request.getProperties();
+
+        Map<PropertyId, Object> properties = propertySet.iterator().next();
+
+        String sql = getUpdateSQL(properties, predicate);
+
+        System.out.println(sql);
+
+        Statement statement = connection.createStatement();
+
+        statement.execute(sql);
+      } finally {
+        connection.close();
+      }
+
+    } catch (SQLException e) {
+      throw new IllegalStateException("DB error : ", e);
+    }
+  }
+
+  @Override
+  public void deleteResources(Predicate predicate) {
+    try {
+      Connection connection = connectionFactory.getConnection();
+      try {
+        String sql = getDeleteSQL(predicate);
+
+        Statement statement = connection.createStatement();
+        statement.execute(sql);
+      } finally {
+        connection.close();
+      }
+
+    } catch (SQLException e) {
+      throw new IllegalStateException("DB error : ", e);
+    }
+  }
+
+
+  private String getInsertSQL(Map<PropertyId, Object> properties) {
+
+    StringBuilder columns = new StringBuilder();
+    StringBuilder values = new StringBuilder();
+    String table = null;
+
+
+    for (Map.Entry<PropertyId, Object> entry : properties.entrySet()) {
+      PropertyId propertyId    = entry.getKey();
+      String     propertyValue = (String) entry.getValue();
+
+      table = propertyId.getCategory();
+
+
+      if (columns.length() > 0) {
+        columns.append(", ");
+      }
+      columns.append(propertyId.getName());
+
+      if (values.length() > 0) {
+        values.append(", ");
+      }
+      values.append("'");
+      values.append(propertyValue);
+      values.append("'");
+    }
+
+    return "insert into " + table + " (" +
+      columns + ") values (" +values + ")";
+  }
+
+  private String getSelectSQL(Set<PropertyId> propertyIds, Predicate predicate) {
+
+    StringBuilder columns = new StringBuilder();
+    Set<String> tableSet = new HashSet<String>();
+
+    for (PropertyId propertyId : propertyIds) {
+      if (columns.length() > 0) {
+        columns.append(", ");
+      }
+      columns.append(propertyId.getCategory()).append(".").append(propertyId.getName());
+      tableSet.add(propertyId.getCategory());
+    }
+
+
+    boolean haveWhereClause = false;
+    StringBuilder whereClause = new StringBuilder();
+    if (predicate != null &&
+        propertyIds.containsAll(PredicateHelper.getPropertyIds(predicate)) &&
+        predicate instanceof PredicateVisitorAcceptor) {
+
+      SQLPredicateVisitor visitor = new SQLPredicateVisitor();
+      ((PredicateVisitorAcceptor) predicate).accept(visitor);
+      whereClause.append(visitor.getSQL());
+      haveWhereClause = true;
+    }
+
+    StringBuilder joinClause = new StringBuilder();
+
+    if (tableSet.size() > 1) {
+
+      for (String table : tableSet) {
+        Map<PropertyId, PropertyId> joinKeys = importedKeys.get(table);
+        if (joinKeys != null) {
+          for (Map.Entry<PropertyId, PropertyId> entry : joinKeys.entrySet()) {
+            String category1 = entry.getKey().getCategory();
+            String category2 = entry.getValue().getCategory();
+            if (tableSet.contains(category1) && tableSet.contains(category2)) {
+              if (haveWhereClause) {
+                joinClause.append(" AND ");
+              }
+              joinClause.append(category1).append(".").append(entry.getKey().getName());
+              joinClause.append(" = ");
+              joinClause.append(category2).append(".").append(entry.getValue().getName());
+              tableSet.add(category1);
+              tableSet.add(category2);
+
+              haveWhereClause = true;
+            }
+          }
+        }
+      }
+    }
+
+    StringBuilder tables = new StringBuilder();
+
+    for (String table : tableSet) {
+      if (tables.length() > 0) {
+        tables.append(", ");
+      }
+      tables.append(table);
+    }
+
+    String sql = "select " + columns + " from " + tables;
+
+    if (haveWhereClause) {
+      sql = sql + " where " + whereClause + joinClause;
+    }
+
+    System.out.println(sql);
+
+    return sql;
+  }
+
+  private String getDeleteSQL(Predicate predicate) {
+
+    StringBuilder whereClause = new StringBuilder();
+    if (predicate instanceof BasePredicate) {
+
+      BasePredicate basePredicate = (BasePredicate) predicate;
+
+      SQLPredicateVisitor visitor = new SQLPredicateVisitor();
+      basePredicate.accept(visitor);
+      whereClause.append(visitor.getSQL());
+
+      String table = basePredicate.getPropertyIds().iterator().next().getCategory();
+
+      return "delete from " + table + " where " + whereClause;
+    }
+    throw new IllegalStateException("Can't generate SQL.");
+  }
+
+  private String getUpdateSQL(Map<PropertyId, Object> properties, Predicate predicate) {
+
+    if (predicate instanceof BasePredicate) {
+
+      StringBuilder whereClause = new StringBuilder();
+
+      BasePredicate basePredicate = (BasePredicate) predicate;
+
+      SQLPredicateVisitor visitor = new SQLPredicateVisitor();
+      basePredicate.accept(visitor);
+      whereClause.append(visitor.getSQL());
+
+      String table = basePredicate.getPropertyIds().iterator().next().getCategory();
+
+
+      StringBuilder setClause = new StringBuilder();
+      for (Map.Entry<PropertyId, Object> entry : properties.entrySet()) {
+
+        if (setClause.length() > 0) {
+          setClause.append(", ");
+        }
+        setClause.append(entry.getKey().getName());
+        setClause.append(" = ");
+        setClause.append("'");
+        setClause.append(entry.getValue());
+        setClause.append("'");
+      }
+
+      return "update " + table + " set " + setClause + " where " + whereClause;
+    }
+    throw new IllegalStateException("Can't generate SQL.");
+  }
+
+  @Override
+  public Set<PropertyId> getPropertyIds() {
+    return propertyIds;
+  }
+
+  @Override
+  public List<PropertyProvider> getPropertyProviders() {
+    return propertyProviders;
+  }
+
+  @Override
+  public Schema getSchema() {
+    return schema;
+  }
+
+  /**
+   * Lazily populate the imported key mappings for the given table.
+   *
+   * @param connection  the connection to use to obtain the database meta data
+   * @param table       the table
+   *
+   * @throws SQLException thrown if the meta data for the given connection cannot be obtained
+   */
+  private void getImportedKeys(Connection connection, String table) throws SQLException {
+    if (!this.importedKeys.containsKey(table)) {
+
+      Map<PropertyId, PropertyId> importedKeys = new HashMap<PropertyId, PropertyId>();
+      this.importedKeys.put(table, importedKeys);
+
+      DatabaseMetaData metaData = connection.getMetaData();
+
+      ResultSet rs = metaData.getImportedKeys(connection.getCatalog(), null, table);
+
+      while (rs.next()) {
+
+        PropertyId pkPropertyId = Properties.getPropertyId(
+            rs.getString("PKCOLUMN_NAME"), rs.getString("PKTABLE_NAME"));
+
+        PropertyId fkPropertyId = Properties.getPropertyId(
+            rs.getString("FKCOLUMN_NAME"), rs.getString("FKTABLE_NAME"));
+
+        importedKeys.put(pkPropertyId, fkPropertyId);
+      }
+    }
+  }
+
+  /**
+   * Get the set of tables associated with the given property ids.
+   *
+   * @param propertyIds  the property ids
+   *
+   * @return the set of tables
+   */
+  private static Set<String> getTables(Set<PropertyId> propertyIds) {
+    Set<String> tables = new HashSet<String>();
+    for (PropertyId propertyId : propertyIds) {
+      tables.add(propertyId.getCategory());
+    }
+    return tables;
+  }
+}

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/SQLPredicateVisitor.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/SQLPredicateVisitor.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/SQLPredicateVisitor.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/SQLPredicateVisitor.java Mon Oct  8 01:37:59 2012
@@ -18,20 +18,26 @@
 
 package org.apache.ambari.api.controller.jdbc;
 
-import org.apache.ambari.api.controller.predicate.ArrayPredicate;
-import org.apache.ambari.api.controller.predicate.BasePredicate;
-import org.apache.ambari.api.controller.predicate.ComparisonPredicate;
-import org.apache.ambari.api.controller.predicate.PredicateVisitor;
-import org.apache.ambari.api.controller.predicate.UnaryPredicate;
-import org.apache.ambari.api.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.predicate.ArrayPredicate;
+import org.apache.ambari.server.controller.predicate.BasePredicate;
+import org.apache.ambari.server.controller.predicate.ComparisonPredicate;
+import org.apache.ambari.server.controller.predicate.PredicateVisitor;
+import org.apache.ambari.server.controller.predicate.UnaryPredicate;
+import org.apache.ambari.server.controller.spi.PropertyId;
 
 /**
- *
+ * Predicate visitor used to generate a SQL where clause from a predicate graph.
  */
 public class SQLPredicateVisitor implements PredicateVisitor {
 
+  /**
+   * The string builder.
+   */
   private final StringBuilder stringBuilder = new StringBuilder();
 
+
+  // ----- PredicateVisitor --------------------------------------------------
+
   @Override
   public void acceptComparisonPredicate(ComparisonPredicate predicate) {
     PropertyId propertyId = predicate.getPropertyId();
@@ -71,6 +77,8 @@ public class SQLPredicateVisitor impleme
   }
 
 
+  // ----- SQLPredicateVisitor -----------------------------------------------
+
   public String getSQL() {
     return stringBuilder.toString();
   }

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/SQLiteConnectionFactory.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/SQLiteConnectionFactory.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/SQLiteConnectionFactory.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jdbc/SQLiteConnectionFactory.java Mon Oct  8 01:37:59 2012
@@ -23,14 +23,28 @@ import java.sql.DriverManager;
 import java.sql.SQLException;
 
 /**
- *
+ * Connection factory implementation for SQLite.
  */
 public class SQLiteConnectionFactory implements ConnectionFactory {
 
-  public static final String CONNECTION_URL = "jdbc:sqlite:";
+  /**
+   * The connection URL minus the db file.
+   */
+  private static final String CONNECTION_URL = "jdbc:sqlite:";
 
+  /**
+   * The filename of the SQLite db file.
+   */
   private final String dbFile;
 
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Create a connection factory.
+   *
+   * @param dbFile  the SQLite DB filename
+   */
   public SQLiteConnectionFactory(String dbFile) {
     this.dbFile = dbFile;
     try {
@@ -40,6 +54,9 @@ public class SQLiteConnectionFactory imp
     }
   }
 
+
+  // ----- ConnectionFactory -------------------------------------------------
+
   @Override
   public Connection getConnection() throws SQLException {
     return DriverManager.getConnection(CONNECTION_URL + dbFile);

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jmx/JMXPropertyProvider.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jmx/JMXPropertyProvider.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jmx/JMXPropertyProvider.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/jmx/JMXPropertyProvider.java Mon Oct  8 01:37:59 2012
@@ -19,11 +19,11 @@
 package org.apache.ambari.api.controller.jmx;
 
 import org.apache.ambari.api.controller.internal.PropertyIdImpl;
-import org.apache.ambari.api.controller.spi.Predicate;
-import org.apache.ambari.api.controller.spi.PropertyId;
-import org.apache.ambari.api.controller.spi.PropertyProvider;
-import org.apache.ambari.api.controller.spi.Request;
-import org.apache.ambari.api.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.spi.PropertyProvider;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.api.controller.utilities.PredicateHelper;
 import org.apache.ambari.api.controller.utilities.Properties;
 

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/ClusterControllerHelper.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/ClusterControllerHelper.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/ClusterControllerHelper.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/ClusterControllerHelper.java Mon Oct  8 01:37:59 2012
@@ -18,57 +18,32 @@
 
 package org.apache.ambari.api.controller.utilities;
 
+import org.apache.ambari.api.controller.ProviderModule;
 import org.apache.ambari.api.controller.internal.ClusterControllerImpl;
-import org.apache.ambari.api.controller.internal.PropertyIdImpl;
-import org.apache.ambari.api.controller.internal.ResourceProviderImpl;
-import org.apache.ambari.api.controller.internal.SchemaImpl;
-import org.apache.ambari.api.controller.jdbc.JDBCManagementController;
-import org.apache.ambari.api.controller.spi.ClusterController;
-import org.apache.ambari.api.controller.spi.ManagementController;
-import org.apache.ambari.api.controller.spi.PropertyId;
-import org.apache.ambari.api.controller.spi.PropertyProvider;
-import org.apache.ambari.api.controller.spi.Resource;
-import org.apache.ambari.api.controller.spi.ResourceProvider;
-import org.apache.ambari.api.controller.spi.Schema;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import org.apache.ambari.api.controller.internal.DefaultProviderModule;
+import org.apache.ambari.server.controller.spi.ClusterController;
 
 /**
  * Temporary class to bootstrap a cluster controller.  TODO : Replace this global state with injection.
  */
 public class ClusterControllerHelper {
+
+  private static String PROVIDER_MODULE_CLASS = System.getProperty("provider.module.class",
+      "org.apache.ambari.api.controller.internal.DefaultProviderModule");
+
   private static ClusterController controller;
 
   public static synchronized ClusterController getClusterController() {
     if (controller == null) {
-      controller = new ClusterControllerImpl(getResourceSchemas());
+      try {
+        Class implClass = Class.forName(PROVIDER_MODULE_CLASS);
+        ProviderModule providerModule = (ProviderModule) implClass.newInstance();
+        return new ClusterControllerImpl(providerModule);
+
+      } catch (Exception e) {
+        throw new IllegalStateException("Can't create provider module " + PROVIDER_MODULE_CLASS);
+      }
     }
     return controller;
   }
-
-  private static Map<Resource.Type, Schema> getResourceSchemas() {
-    Map<Resource.Type, Schema> schemas = new HashMap<Resource.Type, Schema>();
-
-    schemas.put(Resource.Type.Cluster, getResourceSchema(Resource.Type.Cluster));
-    schemas.put(Resource.Type.Service, getResourceSchema(Resource.Type.Service));
-    schemas.put(Resource.Type.Host, getResourceSchema(Resource.Type.Host));
-    schemas.put(Resource.Type.Component, getResourceSchema(Resource.Type.Component));
-    schemas.put(Resource.Type.HostComponent, getResourceSchema(Resource.Type.HostComponent));
-
-    return schemas;
-  }
-
-  private static Schema getResourceSchema(Resource.Type type) {
-
-    ManagementController managementController = new JDBCManagementController(DBHelper.CONNECTION_FACTORY);
-
-    ResourceProvider resourceProvider = ResourceProviderImpl.getResourceProvider(type, Properties.getPropertyIds(type, "DB"), managementController);
-
-    List<PropertyProvider> propertyProviders = new LinkedList<PropertyProvider>();
-
-    return new SchemaImpl(resourceProvider, propertyProviders, Properties.getKeyPropertyIds(type));
-  }
 }

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/DBHelper.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/DBHelper.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/DBHelper.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/DBHelper.java Mon Oct  8 01:37:59 2012
@@ -54,7 +54,6 @@ public class DBHelper {
         String sql = "select attributes from hosts";
 
         Statement statement = connection.createStatement();
-        statement.setQueryTimeout(30);  // set timeout to 30 sec.
 
         ResultSet rs = statement.executeQuery(sql);
 

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/PredicateBuilder.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/PredicateBuilder.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/PredicateBuilder.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/PredicateBuilder.java Mon Oct  8 01:37:59 2012
@@ -18,17 +18,17 @@
 package org.apache.ambari.api.controller.utilities;
 
 import org.apache.ambari.api.controller.internal.PropertyIdImpl;
-import org.apache.ambari.api.controller.predicate.AndPredicate;
-import org.apache.ambari.api.controller.predicate.BasePredicate;
-import org.apache.ambari.api.controller.predicate.Comparables;
-import org.apache.ambari.api.controller.predicate.EqualsPredicate;
-import org.apache.ambari.api.controller.predicate.GreaterEqualsPredicate;
-import org.apache.ambari.api.controller.predicate.GreaterPredicate;
-import org.apache.ambari.api.controller.predicate.LessEqualsPredicate;
-import org.apache.ambari.api.controller.predicate.LessPredicate;
-import org.apache.ambari.api.controller.predicate.NotPredicate;
-import org.apache.ambari.api.controller.predicate.OrPredicate;
-import org.apache.ambari.api.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.predicate.AndPredicate;
+import org.apache.ambari.server.controller.predicate.BasePredicate;
+import org.apache.ambari.server.controller.predicate.Comparables;
+import org.apache.ambari.server.controller.predicate.EqualsPredicate;
+import org.apache.ambari.server.controller.predicate.GreaterEqualsPredicate;
+import org.apache.ambari.server.controller.predicate.GreaterPredicate;
+import org.apache.ambari.server.controller.predicate.LessEqualsPredicate;
+import org.apache.ambari.server.controller.predicate.LessPredicate;
+import org.apache.ambari.server.controller.predicate.NotPredicate;
+import org.apache.ambari.server.controller.predicate.OrPredicate;
+import org.apache.ambari.server.controller.spi.PropertyId;
 
 import java.util.LinkedList;
 import java.util.List;

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/PredicateHelper.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/PredicateHelper.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/PredicateHelper.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/PredicateHelper.java Mon Oct  8 01:37:59 2012
@@ -17,9 +17,11 @@
  */
 package org.apache.ambari.api.controller.utilities;
 
-import org.apache.ambari.api.controller.predicate.BasePredicate;
-import org.apache.ambari.api.controller.spi.Predicate;
-import org.apache.ambari.api.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.predicate.BasePredicate;
+import org.apache.ambari.server.controller.predicate.PredicateVisitor;
+import org.apache.ambari.server.controller.predicate.PredicateVisitorAcceptor;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.PropertyId;
 
 import java.util.Collections;
 import java.util.Set;
@@ -35,4 +37,10 @@ public class PredicateHelper {
     }
     return Collections.emptySet();
   }
+
+  public static void visit(Predicate predicate, PredicateVisitor visitor) {
+    if (predicate instanceof PredicateVisitorAcceptor) {
+      ((PredicateVisitorAcceptor) predicate).accept(visitor);
+    }
+  }
 }

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/Properties.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/Properties.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/Properties.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/controller/utilities/Properties.java Mon Oct  8 01:37:59 2012
@@ -18,8 +18,8 @@
 package org.apache.ambari.api.controller.utilities;
 
 import org.apache.ambari.api.controller.internal.PropertyIdImpl;
-import org.apache.ambari.api.controller.spi.PropertyId;
-import org.apache.ambari.api.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.spi.Resource;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.type.TypeReference;
 

Added: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/CreateHandler.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/CreateHandler.java?rev=1395430&view=auto
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/CreateHandler.java (added)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/CreateHandler.java Mon Oct  8 01:37:59 2012
@@ -0,0 +1,33 @@
+/**
+ * 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.ambari.api.handlers;
+
+import org.apache.ambari.api.services.Request;
+import org.apache.ambari.api.services.Result;
+
+/**
+ * Responsible for create requests.
+ */
+public class CreateHandler implements RequestHandler {
+  @Override
+  public Result handleRequest(Request request) {
+    //TODO: implement
+    return null;
+  }
+}

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/DelegatingRequestHandler.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/DelegatingRequestHandler.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/DelegatingRequestHandler.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/DelegatingRequestHandler.java Mon Oct  8 01:37:59 2012
@@ -22,14 +22,23 @@ import org.apache.ambari.api.services.Re
 import org.apache.ambari.api.services.Result;
 
 /**
- *
+ * Request handler implementation that all requests are funneled through.
+ * Provides common handler functionality and delegates to concrete handler.
  */
 public class DelegatingRequestHandler implements RequestHandler {
   @Override
   public Result handleRequest(Request request) {
-    return getRequestHandlerFactory().getRequestHandler(request.getRequestType()).handleRequest(request);
+    Result result = getRequestHandlerFactory().getRequestHandler(request.getRequestType()).handleRequest(request);
+    request.getResultPostProcessor().process(result);
+
+    return result;
   }
 
+  /**
+   * Obtain a factory for the request specific concrete request handlers.
+   *
+   * @return A request handler factory
+   */
   RequestHandlerFactory getRequestHandlerFactory() {
     return new RequestHandlerFactory();
   }

Added: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/DeleteHandler.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/DeleteHandler.java?rev=1395430&view=auto
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/DeleteHandler.java (added)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/DeleteHandler.java Mon Oct  8 01:37:59 2012
@@ -0,0 +1,33 @@
+/**
+ * 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.ambari.api.handlers;
+
+import org.apache.ambari.api.services.Request;
+import org.apache.ambari.api.services.Result;
+
+/**
+ * Responsible for delete requests.
+ */
+public class DeleteHandler implements RequestHandler {
+  @Override
+  public Result handleRequest(Request request) {
+    //TODO: implement
+    return null;
+  }
+}

Added: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/ReadHandler.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/ReadHandler.java?rev=1395430&view=auto
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/ReadHandler.java (added)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/ReadHandler.java Mon Oct  8 01:37:59 2012
@@ -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.ambari.api.handlers;
+
+import org.apache.ambari.api.services.Request;
+import org.apache.ambari.api.services.Result;
+import org.apache.ambari.api.query.Query;
+import org.apache.ambari.server.AmbariException;
+
+/**
+ * Responsible for read requests.
+ */
+public class ReadHandler implements RequestHandler {
+
+  @Override
+  public Result handleRequest(Request request) {
+    Query query = request.getResourceDefinition().getQuery();
+
+    //Partial response
+    //todo: could be encapsulated in request/query
+    for (String s : request.getPartialResponseFields()) {
+      int i = s.lastIndexOf('/');
+      if (i == -1) {
+        query.addProperty(null, s);
+      } else {
+        query.addProperty(s.substring(0, i), s.substring(i + 1));
+      }
+    }
+
+    try {
+      return query.execute();
+    } catch (AmbariException e) {
+      //TODO: exceptions
+      throw new RuntimeException("An exception occurred processing the request: " + e, e);
+    }
+  }
+}

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/RequestHandler.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/RequestHandler.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/RequestHandler.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/RequestHandler.java Mon Oct  8 01:37:59 2012
@@ -20,10 +20,17 @@ package org.apache.ambari.api.handlers;
 
 import org.apache.ambari.api.services.Request;
 import org.apache.ambari.api.services.Result;
+import org.apache.ambari.server.AmbariException;
 
 /**
- *
+ * Responsible for handling of requests and returning a result.
  */
 public interface RequestHandler {
+  /**
+   * Handle the given request and return a result.
+   *
+   * @param request the request to handle
+   * @return the result of the request
+   */
   public Result handleRequest(Request request);
 }

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/RequestHandlerFactory.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/RequestHandlerFactory.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/RequestHandlerFactory.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/RequestHandlerFactory.java Mon Oct  8 01:37:59 2012
@@ -21,16 +21,29 @@ package org.apache.ambari.api.handlers;
 import org.apache.ambari.api.services.Request;
 
 /**
- *
+ * Factory for {@link RequestHandler}
+ * Returns the appropriate request handler based on the request.
  */
 public class RequestHandlerFactory {
-  public RequestHandler getRequestHandler(Request.RequestType requestType) {
+  /**
+   * Return an instance of the correct request handler based on the request type.
+   *
+   * @param requestType the request type.  Is one of {@link Request.Type}
+   * @return a request handler for the request
+   */
+  public RequestHandler getRequestHandler(Request.Type requestType) {
     switch (requestType) {
       case GET:
-        return new ReadRequestHandler();
+        return new ReadHandler();
+      case PUT:
+        return new CreateHandler();
+      case POST:
+        return new UpdateHandler();
+      case DELETE:
+        return new DeleteHandler();
       default:
         //todo:
-        throw new UnsupportedOperationException("Only GET requests are supported at this time");
+        throw new UnsupportedOperationException("Unsupported Request Type: " + requestType);
     }
   }
 }

Added: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/UpdateHandler.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/UpdateHandler.java?rev=1395430&view=auto
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/UpdateHandler.java (added)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/handlers/UpdateHandler.java Mon Oct  8 01:37:59 2012
@@ -0,0 +1,33 @@
+/**
+ * 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.ambari.api.handlers;
+
+import org.apache.ambari.api.services.Request;
+import org.apache.ambari.api.services.Result;
+
+/**
+ * Responsible for update requests.
+ */
+public class UpdateHandler implements RequestHandler {
+  @Override
+  public Result handleRequest(Request request) {
+    //TODO: implement
+    return null;
+  }
+}

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/query/Query.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/query/Query.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/query/Query.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/query/Query.java Mon Oct  8 01:37:59 2012
@@ -19,25 +19,37 @@
 package org.apache.ambari.api.query;
 
 import org.apache.ambari.api.services.Result;
-import org.apache.ambari.api.controller.spi.PropertyId;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.spi.PropertyId;
 
-import java.util.Map;
-import java.util.Set;
 
 /**
- *
+ * Responsible for querying the back end for read requests
  */
 public interface Query {
-  public void addAllProperties(Map<String, Set<String>> setProperties);
-
-  public void addProperty(String path, String property);
 
+  /**
+   * Add a property to the query.
+   * This is the select portion of the query.
+   *
+   * @param group    the group name that contains the property
+   * @param property the property name
+   */
+  public void addProperty(String group, String property);
+
+  /**
+   * Add a property to the query.
+   * This is the select portion of the query.
+   *
+   * @param property the property id which contains the group, property name
+   *                 and whether the property is temporal
+   */
   public void addProperty(PropertyId property);
 
-  //todo: signature - need path
-  public void retainAllProperties(Set<String> setFields);
-
-  public void clearAllProperties();
-
-  public Result execute();
+  /**
+   * Execute the query.
+   *
+   * @return the result of the query.
+   */
+  public Result execute() throws AmbariException;
 }

Modified: incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/query/QueryImpl.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/query/QueryImpl.java?rev=1395430&r1=1395429&r2=1395430&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/query/QueryImpl.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-api/src/main/java/org/apache/ambari/api/query/QueryImpl.java Mon Oct  8 01:37:59 2012
@@ -20,107 +20,162 @@ package org.apache.ambari.api.query;
 
 import org.apache.ambari.api.controller.internal.PropertyIdImpl;
 import org.apache.ambari.api.controller.internal.RequestImpl;
-import org.apache.ambari.api.controller.predicate.AndPredicate;
-import org.apache.ambari.api.controller.predicate.BasePredicate;
-import org.apache.ambari.api.controller.predicate.EqualsPredicate;
 import org.apache.ambari.api.controller.utilities.ClusterControllerHelper;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.predicate.AndPredicate;
+import org.apache.ambari.server.controller.predicate.BasePredicate;
+import org.apache.ambari.server.controller.predicate.EqualsPredicate;
 import org.apache.ambari.api.services.Result;
 import org.apache.ambari.api.services.ResultImpl;
-import org.apache.ambari.api.controller.spi.*;
+import org.apache.ambari.server.controller.spi.*;
 import org.apache.ambari.api.resource.ResourceDefinition;
+import org.apache.ambari.api.util.TreeNode;
 
 import java.util.*;
 
 /**
- *
+ * Default read query.
  */
 public class QueryImpl implements Query {
+  /**
+   * Resource definition of resource being operated on.
+   */
   ResourceDefinition m_resourceDefinition;
-  Predicate m_predicate;
-  private Map<String, Set<String>> m_mapProperties = new HashMap<String, Set<String>>();
-  private Map<ResourceDefinition, Query> m_mapSubQueries = new HashMap<ResourceDefinition, Query>();
-
 
+  /**
+   * Properties of the query which make up the select portion of the query.
+   */
+  private Map<String, Set<String>> m_mapQueryProperties = new HashMap<String, Set<String>>();
+
+  /**
+   * All properties that are available for the resource.
+   */
+  private Map<String, Set<String>> m_mapAllProperties;
+
+  /**
+   * Sub-resources of the resource which is being operated on.
+   */
+  private Map<String, ResourceDefinition> m_mapSubResources = new HashMap<String, ResourceDefinition>();
+
+
+  /**
+   * Constructor.
+   *
+   * @param resourceDefinition the resource definition of the resource being operated on
+   */
   public QueryImpl(ResourceDefinition resourceDefinition) {
     m_resourceDefinition = resourceDefinition;
+    m_mapAllProperties = Collections.unmodifiableMap(getClusterController().
+        getSchema(resourceDefinition.getType()).getCategories());
   }
 
   @Override
-  public Result execute() {
-    initialize();
-
-    Result result = createResult();
-    Iterable<Resource> iterResource = getClusterController().getResources(
-        m_resourceDefinition.getType(), createRequest(), m_predicate);
-
-    List<Resource> listResources = new ArrayList<Resource>();
-    for (Resource resource : iterResource) {
-      listResources.add(resource);
-    }
-    //todo: tree?
-    result.addResources("/", listResources);
-
-    for (Map.Entry<ResourceDefinition, Query> entry : m_mapSubQueries.entrySet()) {
-      Query query = entry.getValue();
-      ResourceDefinition resDef = entry.getKey();
-
-      //todo: this ensures that the sub query is only executed if needed.  Refactor.
-      if (m_mapProperties.isEmpty() || m_mapProperties.containsKey(resDef.getId() == null ?
-          resDef.getPluralName() : resDef.getSingularName())) {
-        Map<String, List<Resource>> mapSubResults = query.execute().getResources();
-        //todo: only getting sub-resource one level deep at this time
-        List<Resource> listSubResources = mapSubResults.get("/");
-        String subResourceName = resDef.getId() == null ? resDef.getPluralName() : resDef.getSingularName();
-        result.addResources(subResourceName, listSubResources);
+  public void addProperty(String path, String property) {
+    if (m_mapAllProperties.containsKey(path) && m_mapAllProperties.get(path).contains(property)) {
+      // local property
+      Set<String> setProps = m_mapQueryProperties.get(path);
+      if (setProps == null) {
+        setProps = new HashSet<String>();
+        m_mapQueryProperties.put(path, setProps);
       }
-    }
-
-    return result;
-  }
-
-  //todo: refactor
-  public void initialize() {
-    m_predicate = createPredicate(m_resourceDefinition);
-
-    if (m_resourceDefinition.getId() != null) {
-      //sub-resource queries
-      for (ResourceDefinition resource : m_resourceDefinition.getChildren()) {
-        m_mapSubQueries.put(resource, resource.getQuery());
+      setProps.add(property);
+    } else if (m_mapAllProperties.containsKey(property)) {
+      // no path specified because path is provided as property
+      //local category
+      Set<String> setProps = m_mapQueryProperties.get(property);
+      if (setProps == null) {
+        setProps = new HashSet<String>();
+        m_mapQueryProperties.put(property, setProps);
       }
-      for (ResourceDefinition resource : m_resourceDefinition.getRelations()) {
-        m_mapSubQueries.put(resource, resource.getQuery());
+      // add all props for category
+      setProps.addAll(m_mapAllProperties.get(property));
+    } else {
+      // not a local category/property
+      boolean success = addPropertyToSubResource(path, property);
+      if (!success) {
+        //TODO
+        throw new RuntimeException("Attempted to add invalid property to resource.  Resource=" +
+            m_resourceDefinition.getType() + ", Property: Category=" + path + " Field=" + property);
       }
     }
   }
 
   @Override
-  public void addAllProperties(Map<String, Set<String>> mapProperties) {
-    m_mapProperties.putAll(mapProperties);
+  public void addProperty(PropertyId property) {
+    addProperty(property.getCategory(), property.getName());
   }
 
   @Override
-  public void addProperty(String path, String property) {
-    Set<String> setProps = m_mapProperties.get(path);
-    if (setProps == null) {
-      setProps = new HashSet<String>();
-      m_mapProperties.put(path, setProps);
+  public Result execute() throws AmbariException {
+    Result result = createResult();
+
+    if (m_resourceDefinition.getId() == null) {
+      // collection, add pk only
+      Schema schema = getClusterController().getSchema(m_resourceDefinition.getType());
+      addProperty(schema.getKeyPropertyId(m_resourceDefinition.getType()));
+      result.getResultTree().setProperty("isCollection", "true");
     }
-    setProps.add(property);
-  }
 
-  @Override
-  public void addProperty(PropertyId property) {
-    addProperty(property.getCategory(), property.getName());
-  }
+    if (m_mapQueryProperties.isEmpty() && m_mapSubResources.isEmpty()) {
+      //Add sub resource properties for default case where no fields are specified.
+      m_mapSubResources.putAll(m_resourceDefinition.getSubResources());
+    }
 
-  @Override
-  public void retainAllProperties(Set<String> setFields) {
-    //todo
+    Predicate predicate = createPredicate(m_resourceDefinition);
+    Iterable<Resource> iterResource = getClusterController().getResources(
+        m_resourceDefinition.getType(), createRequest(), predicate);
+
+    for (Resource resource : iterResource) {
+      TreeNode<Resource> node = result.getResultTree().addChild(resource, null);
+
+      for (Map.Entry<String, ResourceDefinition> entry : m_mapSubResources.entrySet()) {
+        String subResCategory = entry.getKey();
+        ResourceDefinition r = entry.getValue();
+
+        r.setParentId(m_resourceDefinition.getType(), resource.getPropertyValue(
+            getClusterController().getSchema(m_resourceDefinition.getType()).
+                getKeyPropertyId(m_resourceDefinition.getType())));
+
+        TreeNode<Resource> childResult = r.getQuery().execute().getResultTree();
+        childResult.setName(subResCategory);
+        childResult.setProperty("isCollection", "false");
+        node.addChild(childResult);
+      }
+    }
+
+    return result;
   }
 
-  @Override
-  public void clearAllProperties() {
-    m_mapProperties.clear();
+
+  private boolean addPropertyToSubResource(String path, String property) {
+    boolean resourceAdded = false;
+
+    // cases:
+    // path is null, property is path
+    // path is single token and prop in non null
+    // path is multi level and prop is non null
+
+    if (path == null) {
+      path = property;
+      property = null;
+    }
+
+    int i = path.indexOf("/");
+    String p = i == -1 ? path : path.substring(0, i);
+
+    ResourceDefinition subResource = m_resourceDefinition.getSubResources().get(p);
+    if (subResource != null) {
+      m_mapSubResources.put(p, subResource);
+      //todo: handle case of trailing /
+      //todo: for example fields=subResource/
+
+      if (property != null || !path.equals(p)) {
+        //only add if a sub property is set or if a sub category is specified
+        subResource.getQuery().addProperty(i == -1 ? null : path.substring(i + 1), property);
+      }
+      resourceAdded = true;
+    }
+    return resourceAdded;
   }
 
   private Predicate createPredicate(ResourceDefinition resourceDefinition) {
@@ -129,31 +184,33 @@ public class QueryImpl implements Query 
     Map<Resource.Type, String> mapResourceIds = resourceDefinition.getResourceIds();
     Schema schema = getClusterController().getSchema(resourceType);
 
-    BasePredicate[] predicates = new BasePredicate[mapResourceIds.size()];
-    int count = 0;
+    Set<Predicate> setPredicates = new HashSet<Predicate>();
     for (Map.Entry<Resource.Type, String> entry : mapResourceIds.entrySet()) {
-      predicates[count++] = new EqualsPredicate(schema.getKeyPropertyId(entry.getKey()), entry.getValue());
+      //todo: this is a hack for host_component and component queries where serviceId is not available for
+      //todo: host_component queries and host is not available for component queries.
+      //todo: this should be rectified when the data model is changed for host_component
+      if (entry.getValue() != null) {
+        setPredicates.add(new EqualsPredicate(schema.getKeyPropertyId(entry.getKey()), entry.getValue()));
+      }
     }
 
-    if (predicates.length == 1) {
-      return predicates[0];
-    } else if (predicates.length > 1) {
-      return new AndPredicate(predicates);
+    if (setPredicates.size() == 1) {
+      return setPredicates.iterator().next();
+    } else if (setPredicates.size() > 1) {
+      return new AndPredicate(setPredicates.toArray(new BasePredicate[setPredicates.size()]));
     } else {
       return null;
     }
   }
 
-  //todo: how to get Controller?
   ClusterController getClusterController() {
     return ClusterControllerHelper.getClusterController();
   }
 
-  //todo
   Request createRequest() {
     Set<PropertyId> setProperties = new HashSet<PropertyId>();
-    //todo: convert property names to PropertyId's.
-    for (Map.Entry<String, Set<String>> entry : m_mapProperties.entrySet()) {
+
+    for (Map.Entry<String, Set<String>> entry : m_mapQueryProperties.entrySet()) {
       String group = entry.getKey();
       for (String property : entry.getValue()) {
         setProperties.add(new PropertyIdImpl(property, group, false));
@@ -162,10 +219,8 @@ public class QueryImpl implements Query 
     return new RequestImpl(setProperties, null);
   }
 
-  //todo
   Result createResult() {
     return new ResultImpl();
   }
 
-
 }