You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2013/03/20 21:44:50 UTC
svn commit: r1459041 [3/18] - in /incubator/ambari/branches/branch-1.2: ./
ambari-agent/ ambari-agent/conf/unix/
ambari-agent/src/main/puppet/modules/hdp-ganglia/files/
ambari-agent/src/main/puppet/modules/hdp-ganglia/manifests/
ambari-agent/src/main/p...
Modified: incubator/ambari/branches/branch-1.2/ambari-server/docs/api/v1/services-service.md
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/docs/api/v1/services-service.md?rev=1459041&r1=1459040&r2=1459041&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/docs/api/v1/services-service.md (original)
+++ incubator/ambari/branches/branch-1.2/ambari-server/docs/api/v1/services-service.md Wed Mar 20 20:44:43 2013
@@ -28,43 +28,45 @@ Refers to a specific service identified
200 OK
{
- "href" : "http://your.ambari.server/api/v1/clusters/MyCluster/services/HDFS",
- "ServiceInfo" : {
- "cluster_name" : "MyCluster",
- "service_name" : "HDFS"
- },
- "components" : [
- {
- "href" : "http://your.ambari.server/api/v1/clusters/MyCluster/services/HDFS/components/NAMENODE",
- "ServiceComponentInfo" : {
- "cluster_name" : "MyCluster",
- "component_name" : "NAMENODE",
- "service_name" : "HDFS"
- }
- },
- {
- "href" : "http://your.ambari.server/api/v1/clusters/MyCluster/services/HDFS/components/DATANODE",
- "ServiceComponentInfo" : {
- "cluster_name" : "MyCluster",
- "component_name" : "DATANODE",
- "service_name" : "HDFS"
- }
- },
- {
- "href" : "http://your.ambari.server/api/v1/clusters/MyCluster/services/HDFS/components/HDFS_CLIENT",
- "ServiceComponentInfo" : {
- "cluster_name" : "MyCluster",
- "component_name" : "HDFS_CLIENT",
- "service_name" : "HDFS"
- }
- },
- {
- "href" : "http://your.ambari.server/api/v1/clusters/MyCluster/services/HDFS/components/SECONDARY_NAMENODE",
- "ServiceComponentInfo" : {
- "cluster_name" : "MyCluster",
- "component_name" : "SECONDARY_NAMENODE",
- "service_name" : "HDFS"
- }
- } ]
+ "href" : "http://your.ambari.server/api/v1/clusters/c1/services/HDFS",
+ "ServiceInfo" : {
+ "cluster_name" : "c1",
+ "service_name" : "HDFS",
+ "state" : "STARTED"
+ },
+ "components" : [
+ {
+ "href" : "http://your.ambari.server/api/v1/clusters/c1/services/HDFS/components/NAMENODE",
+ "ServiceComponentInfo" : {
+ "cluster_name" : "c1",
+ "component_name" : "NAMENODE",
+ "service_name" : "HDFS"
+ }
+ },
+ {
+ "href" : "http://your.ambari.server/api/v1/clusters/c1/services/HDFS/components/DATANODE",
+ "ServiceComponentInfo" : {
+ "cluster_name" : "c1",
+ "component_name" : "DATANODE",
+ "service_name" : "HDFS"
+ }
+ },
+ {
+ "href" : "http://your.ambari.server/api/v1/clusters/c1/services/HDFS/components/HDFS_CLIENT",
+ "ServiceComponentInfo" : {
+ "cluster_name" : "c1",
+ "component_name" : "HDFS_CLIENT",
+ "service_name" : "HDFS"
+ }
+ },
+ {
+ "href" : "http://your.ambari.server/api/v1/clusters/c1/services/HDFS/components/SECONDARY_NAMENODE",
+ "ServiceComponentInfo" : {
+ "cluster_name" : "c1",
+ "component_name" : "SECONDARY_NAMENODE",
+ "service_name" : "HDFS"
+ }
+ }
+ ]
}
Modified: incubator/ambari/branches/branch-1.2/ambari-server/docs/api/v1/services.md
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/docs/api/v1/services.md?rev=1459041&r1=1459040&r2=1459041&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/docs/api/v1/services.md (original)
+++ incubator/ambari/branches/branch-1.2/ambari-server/docs/api/v1/services.md Wed Mar 20 20:44:43 2013
@@ -28,28 +28,28 @@ Returns a collection of the services in
200 OK
{
- "href" : "http://your.ambari.server/api/v1/clusters/MyCluster/services",
- "items" : [
- {
- "href" : "http://your.ambari.server/api/v1/clusters/MyCluster/services/NAGIOS",
- "ServiceInfo" : {
- "cluster_name" : "MyCluster",
- "service_name" : "NAGIOS"
- }
- },
- {
- "href" : "http://your.ambari.server/api/v1/clusters/MyCluster/services/HCATALOG",
- "ServiceInfo" : {
- "cluster_name" : "MyCluster",
- "service_name" : "HCATALOG"
- }
- },
- {
- "href" : "http://your.ambari.server/api/v1/clusters/MyCluster/services/PIG",
- "ServiceInfo" : {
- "cluster_name" : "MyCluster",
- "service_name" : "PIG"
- }
- }
- ]
- }
+ "href" : "http://your.ambari.server/api/v1/clusters/c1/services",
+ "items" : [
+ {
+ "href" : "http://your.ambari.server/api/v1/clusters/c1/services/NAGIOS",
+ "ServiceInfo" : {
+ "cluster_name" : "c1",
+ "service_name" : "NAGIOS"
+ }
+ },
+ {
+ "href" : "http://your.ambari.server/api/v1/clusters/c1/services/HCATALOG",
+ "ServiceInfo" : {
+ "cluster_name" : "c1",
+ "service_name" : "HCATALOG"
+ }
+ },
+ {
+ "href" : "http://your.ambari.server/api/v1/clusters/c1/services/PIG",
+ "ServiceInfo" : {
+ "cluster_name" : "c1",
+ "service_name" : "PIG"
+ }
+ }
+ ]
+ }
\ No newline at end of file
Modified: incubator/ambari/branches/branch-1.2/ambari-server/pom.xml
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/pom.xml?rev=1459041&r1=1459040&r2=1459041&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/pom.xml (original)
+++ incubator/ambari/branches/branch-1.2/ambari-server/pom.xml Wed Mar 20 20:44:43 2013
@@ -16,7 +16,7 @@
<parent>
<groupId>org.apache.ambari</groupId>
<artifactId>ambari-project</artifactId>
- <version>1.2.1-SNAPSHOT</version>
+ <version>1.2.2-SNAPSHOT</version>
<relativePath>../ambari-project</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -24,14 +24,19 @@
<artifactId>ambari-server</artifactId>
<packaging>jar</packaging>
<name>Ambari Server</name>
- <version>1.2.1-SNAPSHOT</version>
+ <version>1.2.2-SNAPSHOT</version>
<description>Ambari Server</description>
<properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<python.ver>python >= 2.6</python.ver>
</properties>
<build>
<plugins>
<plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.0</version>
+ </plugin>
+ <plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
@@ -248,7 +253,24 @@
<groupname>root</groupname>
<sources>
<source>
- <location>src/main/resources/upgrade/ddl/Ambari-DDL-Postgres-UPGRADE-1.2.1.sql</location>
+ <location>src/main/resources/upgrade/ddl/Ambari-DDL-Postgres-UPGRADE-1.2.2.sql</location>
+ </source>
+ <source>
+ <location>src/main/resources/upgrade/ddl/Ambari-DDL-Postgres-UPGRADE-1.2.2.Fix.sql</location>
+ </source>
+ <source>
+ <location>src/main/resources/upgrade/ddl/Ambari-DDL-Postgres-UPGRADE-1.2.2.Check.sql</location>
+ </source>
+ </sources>
+ </mapping>
+ <mapping>
+ <directory>/var/lib/ambari-server/resources/upgrade/dml</directory>
+ <filemode>755</filemode>
+ <username>root</username>
+ <groupname>root</groupname>
+ <sources>
+ <source>
+ <location>src/main/resources/upgrade/dml/Ambari-DML-Postgres-UPGRADE_STACK.sql</location>
</source>
</sources>
</mapping>
Modified: incubator/ambari/branches/branch-1.2/ambari-server/sbin/ambari-server
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/sbin/ambari-server?rev=1459041&r1=1459040&r2=1459041&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/sbin/ambari-server (original)
+++ incubator/ambari/branches/branch-1.2/ambari-server/sbin/ambari-server Wed Mar 20 20:44:43 2013
@@ -77,6 +77,10 @@ case "$1" in
echo -e "Upgrading ambari-server"
$PYTHON /usr/sbin/ambari-server.py $@
;;
+ upgradestack)
+ echo -e "Upgrading stack of ambari-server"
+ $PYTHON /usr/sbin/ambari-server.py $@
+ ;;
setup)
echo -e "Run postgresql initdb"
initdb_res=`/sbin/service postgresql initdb`
@@ -89,7 +93,7 @@ case "$1" in
$PYTHON /usr/sbin/ambari-server.py $@
;;
*)
- echo "Usage: /usr/sbin/ambari-server {start|stop|restart|setup|upgrade} [options]"
+ echo "Usage: /usr/sbin/ambari-server {start|stop|restart|setup|upgrade|upgradestack} [options]"
exit 1
esac
Modified: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionDBAccessorImpl.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionDBAccessorImpl.java?rev=1459041&r1=1459040&r2=1459041&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionDBAccessorImpl.java (original)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionDBAccessorImpl.java Wed Mar 20 20:44:43 2013
@@ -181,6 +181,8 @@ public class ActionDBAccessorImpl implem
hostRoleCommand.setTaskId(hostRoleCommandEntity.getTaskId());
ExecutionCommandEntity executionCommandEntity = hostRoleCommand.constructExecutionCommandEntity();
executionCommandEntity.setHostRoleCommand(hostRoleCommandEntity);
+
+ executionCommandEntity.setTaskId(hostRoleCommandEntity.getTaskId());
hostRoleCommandEntity.setExecutionCommand(executionCommandEntity);
executionCommandDAO.create(hostRoleCommandEntity.getExecutionCommand());
Modified: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionManager.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionManager.java?rev=1459041&r1=1459040&r2=1459041&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionManager.java (original)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionManager.java Wed Mar 20 20:44:43 2013
@@ -17,11 +17,9 @@
*/
package org.apache.ambari.server.actionmanager;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
-
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
import org.apache.ambari.server.agent.ActionQueue;
import org.apache.ambari.server.agent.CommandReport;
import org.apache.ambari.server.controller.HostsMap;
@@ -30,9 +28,10 @@ import org.apache.ambari.server.utils.St
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.google.inject.name.Named;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
/**
@@ -75,6 +74,9 @@ public class ActionManager {
LOG.info("Persisting stage into db: " + s.toString());
}
db.persistActions(stages);
+
+ // Now scheduler should process actions
+ scheduler.awake();
}
public List<Stage> getRequestStatus(long requestId) {
Modified: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionScheduler.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionScheduler.java?rev=1459041&r1=1459040&r2=1459041&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionScheduler.java (original)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionScheduler.java Wed Mar 20 20:44:43 2013
@@ -17,32 +17,25 @@
*/
package org.apache.ambari.server.actionmanager;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.Role;
import org.apache.ambari.server.ServiceComponentNotFoundException;
import org.apache.ambari.server.agent.ActionQueue;
import org.apache.ambari.server.agent.ExecutionCommand;
import org.apache.ambari.server.controller.HostsMap;
-import org.apache.ambari.server.state.Cluster;
-import org.apache.ambari.server.state.Clusters;
-import org.apache.ambari.server.state.Service;
-import org.apache.ambari.server.state.ServiceComponent;
-import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.*;
import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
import org.apache.ambari.server.state.svccomphost.ServiceComponentHostOpFailedEvent;
-import org.apache.ambari.server.utils.StageUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-//This class encapsulates the action scheduler thread.
-//Action schedule frequently looks at action database and determines if
-//there is an action that can be scheduled.
+import java.util.*;
+
+/**
+ * This class encapsulates the action scheduler thread.
+ * Action schedule frequently looks at action database and determines if
+ * there is an action that can be scheduled.
+ */
class ActionScheduler implements Runnable {
private static Logger LOG = LoggerFactory.getLogger(ActionScheduler.class);
@@ -56,6 +49,14 @@ class ActionScheduler implements Runnabl
private final Clusters fsmObject;
private boolean taskTimeoutAdjustment = true;
private final HostsMap hostsMap;
+ private final Object wakeupSyncObject = new Object();
+
+ /**
+ * true if scheduler should run ASAP.
+ * We need this flag to avoid sleep in situations, when
+ * we receive awake() request during running a scheduler iteration.
+ */
+ private boolean activeAwakeRequest = false;
public ActionScheduler(long sleepTimeMilliSec, long actionTimeoutMilliSec,
ActionDBAccessor db, ActionQueue actionQueue, Clusters fsmObject,
@@ -79,11 +80,28 @@ class ActionScheduler implements Runnabl
schedulerThread.interrupt();
}
+ /**
+ * Should be called from another thread when we want scheduler to
+ * make a run ASAP (for example, to process desired configs of SCHs).
+ * The method is guaranteed to return quickly.
+ */
+ public void awake() {
+ synchronized (wakeupSyncObject) {
+ activeAwakeRequest = true;
+ wakeupSyncObject.notify();
+ }
+ }
+
@Override
public void run() {
while (shouldRun) {
try {
- Thread.sleep(sleepTime);
+ synchronized (wakeupSyncObject) {
+ if (!activeAwakeRequest) {
+ wakeupSyncObject.wait(sleepTime);
+ }
+ activeAwakeRequest = false;
+ }
doWork();
} catch (InterruptedException ex) {
LOG.warn("Scheduler thread is interrupted going to stop", ex);
Modified: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java?rev=1459041&r1=1459040&r2=1459041&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java (original)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java Wed Mar 20 20:44:43 2013
@@ -223,13 +223,9 @@ public class HeartBeatHandler {
if (prevState.equals(State.INSTALLED)
|| prevState.equals(State.START_FAILED)
|| prevState.equals(State.STARTED)
+ || prevState.equals(State.STARTING)
+ || prevState.equals(State.STOPPING)
|| prevState.equals(State.STOP_FAILED)) {
- if (prevState == State.START_FAILED
- && liveState == State.INSTALLED) {
- LOG.info("Ignoring INSTALLED state update for " +
- "START_FAILED component");
- continue;
- }
scHost.setState(liveState);
if (!prevState.equals(liveState)) {
LOG.info("State of service component " + componentName
Modified: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java?rev=1459041&r1=1459040&r2=1459041&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java (original)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java Wed Mar 20 20:44:43 2013
@@ -18,10 +18,12 @@
package org.apache.ambari.server.api.handlers;
+import org.apache.ambari.server.api.predicate.InvalidQueryException;
import org.apache.ambari.server.api.resources.ResourceInstance;
import org.apache.ambari.server.api.services.Request;
import org.apache.ambari.server.api.services.Result;
import org.apache.ambari.server.api.services.ResultImpl;
+import org.apache.ambari.server.api.services.ResultStatus;
import org.apache.ambari.server.api.services.persistence.PersistenceManager;
import org.apache.ambari.server.api.services.persistence.PersistenceManagerImpl;
import org.apache.ambari.server.api.util.TreeNode;
@@ -57,7 +59,13 @@ public abstract class BaseManagementHand
public Result handleRequest(Request request) {
ResourceInstance resource = request.getResource();
- Predicate queryPredicate = request.getQueryPredicate();
+ Predicate queryPredicate;
+ try {
+ queryPredicate = request.getQueryPredicate();
+ } catch (InvalidQueryException e) {
+ return new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST,
+ "Invalid Request: " + e.getMessage()));
+ }
if (queryPredicate != null) {
resource.getQuery().setUserPredicate(queryPredicate);
}
Modified: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java?rev=1459041&r1=1459040&r2=1459041&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java (original)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java Wed Mar 20 20:44:43 2013
@@ -18,6 +18,7 @@
package org.apache.ambari.server.api.handlers;
+import org.apache.ambari.server.api.predicate.InvalidQueryException;
import org.apache.ambari.server.api.services.Request;
import org.apache.ambari.server.api.services.ResultImpl;
import org.apache.ambari.server.api.services.ResultStatus;
@@ -51,9 +52,12 @@ public class ReadHandler implements Requ
return new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST, e.getMessage()));
}
- query.setUserPredicate(request.getQueryPredicate());
Result result;
+ Predicate p = null;
try {
+ p = request.getQueryPredicate();
+ query.setUserPredicate(p);
+
result = query.execute();
result.setResultStatus(new ResultStatus(ResultStatus.STATUS.OK));
} catch (SystemException e) {
@@ -63,7 +67,7 @@ public class ReadHandler implements Requ
} catch (UnsupportedPropertyException e) {
result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST, e.getMessage()));
} catch (NoSuchResourceException e) {
- if (request.getQueryPredicate() == null) {
+ if (p == null) {
// no predicate specified, resource requested by id
result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.NOT_FOUND, e.getMessage()));
} else {
@@ -74,6 +78,9 @@ public class ReadHandler implements Requ
} catch (IllegalArgumentException e) {
result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST,
"Invalid Request: " + e.getMessage()));
+ } catch (InvalidQueryException e) {
+ result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST,
+ "Invalid Request: " + e.getMessage()));
} catch (RuntimeException e) {
if (LOG.isErrorEnabled()) {
LOG.error("Caught a runtime exception executing a query", e);
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/InvalidQueryException.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/InvalidQueryException.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/InvalidQueryException.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/InvalidQueryException.java Wed Mar 20 20:44:43 2013
@@ -0,0 +1,43 @@
+/**
+ * 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.server.api.predicate;
+
+/**
+ * Exception indicating that a query compilation error occurred.
+ */
+public class InvalidQueryException extends Exception {
+ /**
+ * Constructor.
+ *
+ * @param msg msg
+ */
+ public InvalidQueryException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param msg msg
+ * @param throwable root cause
+ */
+ public InvalidQueryException(String msg, Throwable throwable) {
+ super(msg, throwable);
+ }
+}
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/PredicateCompiler.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/PredicateCompiler.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/PredicateCompiler.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/PredicateCompiler.java Wed Mar 20 20:44:43 2013
@@ -0,0 +1,49 @@
+/**
+ * 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.server.api.predicate;
+
+import org.apache.ambari.server.controller.spi.Predicate;
+
+/**
+ * Compiler which takes a query expression as input and produces a predicate instance as output.
+ */
+public class PredicateCompiler {
+
+ /**
+ * Lexer instance used to translate expressions into stream of tokens.
+ */
+ private QueryLexer lexer = new QueryLexer();
+
+ /**
+ * Parser instance used to produce a predicate instance from a stream of tokens.
+ */
+ private QueryParser parser = new QueryParser();
+
+ /**
+ * Generate a predicate from a query expression.
+ *
+ * @param exp query expression
+ *
+ * @return a predicate instance
+ * @throws InvalidQueryException if unable to compile the expression
+ */
+ public Predicate compile(String exp) throws InvalidQueryException {
+ return parser.parse(lexer.tokens(exp));
+ }
+}
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java Wed Mar 20 20:44:43 2013
@@ -0,0 +1,501 @@
+/**
+ * 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.server.api.predicate;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Scans a query expression and generates an array of tokens.
+ * Each token contains type and value information.
+ *
+ * First, the query expression is broken down into string tokens using
+ * a regular expression which splits on a set of deliminators which includes
+ * operators and brackets.
+ *
+ * Second, each string token is converted into a Token with type and value information.
+ */
+public class QueryLexer {
+ /**
+ * All valid deliminators.
+ */
+ private static final String[] ALL_DELIMS =
+ {".in\\(",".isEmpty\\(","<=",">=","!=","=","<",">","&","|","!","(", ")"};
+
+ /**
+ * Map of token type to list of valid handlers for next token.
+ */
+ private static final Map<Token.TYPE, List<TokenHandler>> TOKEN_HANDLERS =
+ new HashMap<Token.TYPE, List<TokenHandler>>();
+
+ /**
+ * Set of property names to ignore.
+ */
+ private static final Set<String> SET_IGNORE = new HashSet<String>();
+
+ /**
+ * Constructor.
+ * Register token handlers.
+ */
+ public QueryLexer() {
+ //todo: refactor handler registration
+ List<TokenHandler> listHandlers = new ArrayList<TokenHandler>();
+ listHandlers.add(new LogicalUnaryOperatorTokenHandler());
+ listHandlers.add(new OpenBracketTokenHandler());
+ listHandlers.add(new PropertyOperandTokenHandler());
+
+ TOKEN_HANDLERS.put(Token.TYPE.BRACKET_OPEN, listHandlers);
+ TOKEN_HANDLERS.put(Token.TYPE.LOGICAL_OPERATOR, listHandlers);
+ TOKEN_HANDLERS.put(Token.TYPE.LOGICAL_UNARY_OPERATOR, listHandlers);
+
+ listHandlers= new ArrayList<TokenHandler>();
+ listHandlers.add(new RelationalOperatorTokenHandler());
+ listHandlers.add(new RelationalOperatorFuncTokenHandler());
+ TOKEN_HANDLERS.put(Token.TYPE.PROPERTY_OPERAND, listHandlers);
+
+ listHandlers = new ArrayList<TokenHandler>();
+ listHandlers.add(new ValueOperandTokenHandler());
+ TOKEN_HANDLERS.put(Token.TYPE.RELATIONAL_OPERATOR, listHandlers);
+
+ listHandlers = new ArrayList<TokenHandler>();
+ listHandlers.add(new CloseBracketTokenHandler());
+ listHandlers.add(new ValueOperandTokenHandler());
+ TOKEN_HANDLERS.put(Token.TYPE.RELATIONAL_OPERATOR_FUNC, listHandlers);
+
+ listHandlers = new ArrayList<TokenHandler>();
+ listHandlers.add(new CloseBracketTokenHandler());
+ listHandlers.add(new LogicalOperatorTokenHandler());
+ TOKEN_HANDLERS.put(Token.TYPE.VALUE_OPERAND, listHandlers);
+ TOKEN_HANDLERS.put(Token.TYPE.BRACKET_CLOSE, listHandlers);
+ }
+
+
+ /**
+ * Scan the provided query and generate a token stream to be used by the query parser.
+ *
+ * @param exp the query expression to scan
+ *
+ * @return an array of tokens
+ * @throws InvalidQueryException if the query is invalid
+ */
+ public Token[] tokens(String exp) throws InvalidQueryException {
+
+ ScanContext ctx = new ScanContext();
+ for (String tok : parseStringTokens(exp)) {
+ List<TokenHandler> listHandlers = TOKEN_HANDLERS.get(ctx.getLastTokenType());
+ boolean processed = false;
+ int idx = 0;
+
+ while (!processed && idx < listHandlers.size()) {
+ processed = listHandlers.get(idx++).handleToken(tok, ctx);
+ }
+
+ if (! processed) {
+ throw new InvalidQueryException("Invalid Query Token: token='" +
+ tok + "\', previous token type=" + ctx.getLastTokenType());
+ }
+ }
+ return ctx.getTokenList().toArray(new Token[ctx.getTokenList().size()]);
+ }
+
+ /**
+ * Uses a regular expression to scan a query expression and produce a list of string tokens.
+ * These tokens are the exact strings that exist in the original syntax.
+ *
+ * @param exp the query expression
+ *
+ * @return list of string tokens from the query expression
+ */
+ private List<String> parseStringTokens(String exp) {
+ Pattern pattern = generatePattern();
+ Matcher matcher = pattern.matcher(exp);
+ List<String> listStrTokens = new ArrayList<String>();
+ int pos = 0;
+
+ while (matcher.find()) { // while there's a delimiter in the string
+ if (pos != matcher.start()) {
+ // add anything between the current and previous delimiter to the tokens list
+ listStrTokens.add(exp.substring(pos, matcher.start()));
+ }
+ listStrTokens.add(matcher.group()); // add the delimiter
+ pos = matcher.end(); // Remember end of delimiter
+ }
+ if (pos != exp.length()) {
+ // Add any chars remaining in the string after last delimiter
+ listStrTokens.add(exp.substring(pos));
+ }
+ return listStrTokens;
+ }
+
+ /**
+ * Generate the regex pattern to tokenize the query expression.
+ *
+ * @return the regex pattern
+ */
+ private Pattern generatePattern() {
+ StringBuilder sb = new StringBuilder();
+ sb.append('(');
+ for (String delim : ALL_DELIMS) { // For each delimiter
+ if (sb.length() != 1) sb.append('|');
+ sb.append('\\');
+ sb.append(delim);
+ }
+ sb.append(')');
+
+ return Pattern.compile(sb.toString());
+ }
+
+ /**
+ * Add property names that the lexer should ignore.
+ */
+ static {
+ // ignore values
+ SET_IGNORE.add("fields");
+ SET_IGNORE.add("_");
+ }
+
+ /**
+ * Scan context. Provides contextual information related to the current scan.
+ */
+ private class ScanContext {
+ /**
+ * The last token type scanned.
+ */
+ private Token.TYPE m_lastType;
+
+ /**
+ * The last property operand value
+ */
+ private String m_propertyName;
+
+ /**
+ * List of tokens generated by the scan
+ */
+ private List<Token> m_listTokens = new ArrayList<Token>();
+
+ /**
+ * Whether the current expression should be ignored.
+ * This is used to ignore portions of the query string that are
+ * not query specific.
+ */
+ private boolean m_ignore = false;
+
+ /**
+ * Constructor.
+ */
+ private ScanContext() {
+ //init last type to the logical op type
+ m_lastType = Token.TYPE.LOGICAL_OPERATOR;
+ }
+
+ /**
+ * Set the ignore tokens flag.
+ *
+ * @param ignore true to ignore tokens; false otherwise
+ */
+ public void setIgnoreTokens(boolean ignore) {
+ m_ignore = ignore;
+ }
+
+ /**
+ * Get the type of the last token.
+ *
+ * @return the type of the last token
+ */
+ public Token.TYPE getLastTokenType() {
+ return m_lastType;
+ }
+
+ /**
+ * Set the type of the last token.
+ *
+ * @param lastType the type of the last token
+ */
+ public void setLastTokenType(Token.TYPE lastType) {
+ m_lastType = lastType;
+ }
+
+ /**
+ * Get the current property operand value.
+ * This is used to hold the property operand name until it is added since,
+ * the following relational operator token is added first.
+ *
+ * @return the current property operand value
+ */
+ public String getPropertyOperand() {
+ return m_propertyName;
+ }
+
+ /**
+ * Set the current property operand value.
+ * This is used to hold the property operand name until it is added since,
+ * the following relational operator token is added first.
+ */
+ public void setPropertyOperand(String prop) {
+ m_propertyName = prop;
+ }
+
+ /**
+ * Add a token.
+ *
+ * @param token the token to add
+ */
+ public void addToken(Token token) {
+ if (! m_ignore) {
+ m_listTokens.add(token);
+ }
+ }
+
+ /**
+ * Get the list of generated tokens.
+ *
+ * @return the list of generated tokens
+ */
+ public List<Token> getTokenList() {
+ return m_listTokens;
+ }
+ }
+
+ /**
+ * Token handler base class.
+ * Token handlers are responsible for processing specific token type.
+ */
+ private abstract class TokenHandler {
+ /**
+ * Provides base token handler functionality then delegates to the individual concrete handlers.
+ *
+ * @param token the token to process
+ * @param ctx the scan context
+ *
+ * @return true if this handler processed the token; false otherwise
+ * @throws InvalidQueryException if an invalid token is encountered
+ */
+ public boolean handleToken(String token, ScanContext ctx) throws InvalidQueryException {
+ if (handles(token, ctx.getLastTokenType())) {
+ _handleToken(token, ctx);
+ ctx.setLastTokenType(getType());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Process a token.
+ *
+ * @param token the token to process
+ * @param ctx the current scan context
+ * @throws InvalidQueryException if an invalid token is encountered
+ */
+ public abstract void _handleToken(String token, ScanContext ctx) throws InvalidQueryException;
+
+ /**
+ * Get the token handler type.
+ *
+ * @return the token handler type
+ */
+ public abstract Token.TYPE getType();
+
+ /**
+ * Determine if a handler handles a specific token type.
+ *
+ * @param token the token type
+ * @param previousTokenType the previous token type
+ *
+ * @return true if the handler handles the specified type; false otherwise
+ */
+ public abstract boolean handles(String token, Token.TYPE previousTokenType);
+ }
+
+ /**
+ * Property Operand token handler.
+ */
+ private class PropertyOperandTokenHandler extends TokenHandler {
+
+ @Override
+ public void _handleToken(String token, ScanContext ctx) throws InvalidQueryException {
+ //don't add prop name token until after operator token
+ if (! SET_IGNORE.contains(token)) {
+ ctx.setPropertyOperand(token);
+ } else {
+ ctx.setIgnoreTokens(true);
+ if (!ctx.getTokenList().isEmpty()) {
+ // remove '&' token that separates ignored token and query
+ ctx.getTokenList().remove(ctx.getTokenList().size() -1);
+ }
+ }
+ }
+
+ @Override
+ public Token.TYPE getType() {
+ return Token.TYPE.PROPERTY_OPERAND;
+ }
+
+ @Override
+ public boolean handles(String token, Token.TYPE previousTokenType) {
+ return token.matches("[^!&\\|<=|>=|!=|=|<|>\\(\\)]+");
+ }
+ }
+
+ /**
+ * Value Operand token handler.
+ */
+ private class ValueOperandTokenHandler extends TokenHandler {
+ @Override
+ public void _handleToken(String token, ScanContext ctx) throws InvalidQueryException {
+ ctx.addToken(new Token(Token.TYPE.VALUE_OPERAND, token));
+ }
+
+ @Override
+ public Token.TYPE getType() {
+ return Token.TYPE.VALUE_OPERAND;
+ }
+
+ @Override
+ public boolean handles(String token, Token.TYPE previousTokenType) {
+ return token.matches("[^!&\\|<=|>=|!=|=|<|>]+");
+ }
+ }
+
+ /**
+ * Open Bracket token handler.
+ */
+ private class OpenBracketTokenHandler extends TokenHandler {
+ @Override
+ public void _handleToken(String token, ScanContext ctx) throws InvalidQueryException {
+ ctx.addToken(new Token(Token.TYPE.BRACKET_OPEN, token));
+ }
+
+ @Override
+ public Token.TYPE getType() {
+ return Token.TYPE.BRACKET_OPEN;
+ }
+
+ @Override
+ public boolean handles(String token, Token.TYPE previousTokenType) {
+ return token.matches("\\(");
+ }
+ }
+
+ /**
+ * Close Bracket token handler.
+ */
+ private class CloseBracketTokenHandler extends TokenHandler {
+ @Override
+ public void _handleToken(String token, ScanContext ctx) throws InvalidQueryException {
+ ctx.addToken(new Token(Token.TYPE.BRACKET_CLOSE, token));
+ }
+
+ @Override
+ public Token.TYPE getType() {
+ return Token.TYPE.BRACKET_CLOSE;
+ }
+
+ @Override
+ public boolean handles(String token, Token.TYPE previousTokenType) {
+ return token.matches("\\)");
+ }
+ }
+
+ /**
+ * Relational Operator token handler.
+ */
+ private class RelationalOperatorTokenHandler extends TokenHandler {
+ @Override
+ public void _handleToken(String token, ScanContext ctx) throws InvalidQueryException {
+ ctx.addToken(new Token(Token.TYPE.RELATIONAL_OPERATOR, token));
+ ctx.addToken(new Token(Token.TYPE.PROPERTY_OPERAND, ctx.getPropertyOperand()));
+ }
+
+ @Override
+ public Token.TYPE getType() {
+ return Token.TYPE.RELATIONAL_OPERATOR;
+ }
+
+ @Override
+ public boolean handles(String token, Token.TYPE previousTokenType) {
+ return token.matches("<=|>=|!=|=|<|>");
+ }
+ }
+
+ /**
+ * Relational Operator function token handler.
+ */
+ private class RelationalOperatorFuncTokenHandler extends TokenHandler {
+ @Override
+ public void _handleToken(String token, ScanContext ctx) throws InvalidQueryException {
+ ctx.addToken(new Token(Token.TYPE.RELATIONAL_OPERATOR_FUNC, token));
+ ctx.addToken(new Token(Token.TYPE.PROPERTY_OPERAND, ctx.getPropertyOperand()));
+ }
+
+ @Override
+ public Token.TYPE getType() {
+ return Token.TYPE.RELATIONAL_OPERATOR_FUNC;
+ }
+
+ //todo: add a unary relational operator func
+ @Override
+ public boolean handles(String token, Token.TYPE previousTokenType) {
+ return token.matches("\\.[a-zA-Z]+\\(");
+ }
+ }
+
+
+ /**
+ * Logical Operator token handler.
+ */
+ private class LogicalOperatorTokenHandler extends TokenHandler {
+ @Override
+ public void _handleToken(String token, ScanContext ctx) throws InvalidQueryException {
+ ctx.addToken(new Token(Token.TYPE.LOGICAL_OPERATOR, token));
+ ctx.setIgnoreTokens(false);
+ }
+
+ @Override
+ public Token.TYPE getType() {
+ return Token.TYPE.LOGICAL_OPERATOR;
+ }
+
+ @Override
+ public boolean handles(String token, Token.TYPE previousTokenType) {
+ return token.matches("[!&\\|]");
+ }
+ }
+
+ /**
+ * Logical Unary Operator token handler.
+ */
+ private class LogicalUnaryOperatorTokenHandler extends TokenHandler {
+ @Override
+ public void _handleToken(String token, ScanContext ctx) throws InvalidQueryException {
+ ctx.addToken(new Token(Token.TYPE.LOGICAL_UNARY_OPERATOR, token));
+ }
+
+ @Override
+ public Token.TYPE getType() {
+ return Token.TYPE.LOGICAL_UNARY_OPERATOR;
+ }
+
+ @Override
+ public boolean handles(String token, Token.TYPE previousTokenType) {
+ return "!".equals(token);
+ }
+ }
+}
\ No newline at end of file
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryParser.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryParser.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryParser.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryParser.java Wed Mar 20 20:44:43 2013
@@ -0,0 +1,514 @@
+/**
+ * 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.server.api.predicate;
+
+import org.apache.ambari.server.api.predicate.expressions.Expression;
+import org.apache.ambari.server.api.predicate.expressions.LogicalExpressionFactory;
+import org.apache.ambari.server.api.predicate.expressions.RelationalExpression;
+import org.apache.ambari.server.api.predicate.operators.*;
+import org.apache.ambari.server.controller.spi.Predicate;
+
+import java.util.*;
+
+/**
+ * Parser which produces a predicate instance from an array of tokens,
+ * which is generated by the lexer.
+ */
+public class QueryParser {
+
+ /**
+ * Map of token type to token handlers.
+ */
+ private static final Map<Token.TYPE, TokenHandler> TOKEN_HANDLERS =
+ new HashMap<Token.TYPE, TokenHandler>();
+
+ /**
+ * Constructor.
+ * Register token handlers.
+ *
+ */
+ public QueryParser() {
+ TOKEN_HANDLERS.put(Token.TYPE.BRACKET_OPEN, new BracketOpenTokenHandler());
+ TOKEN_HANDLERS.put(Token.TYPE.BRACKET_CLOSE, new BracketCloseTokenHandler());
+ TOKEN_HANDLERS.put(Token.TYPE.RELATIONAL_OPERATOR, new RelationalOperatorTokenHandler());
+ TOKEN_HANDLERS.put(Token.TYPE.LOGICAL_OPERATOR, new LogicalOperatorTokenHandler());
+ TOKEN_HANDLERS.put(Token.TYPE.LOGICAL_UNARY_OPERATOR, new LogicalUnaryOperatorTokenHandler());
+ TOKEN_HANDLERS.put(Token.TYPE.PROPERTY_OPERAND, new PropertyOperandTokenHandler());
+ TOKEN_HANDLERS.put(Token.TYPE.VALUE_OPERAND, new ValueOperandTokenHandler());
+ TOKEN_HANDLERS.put(Token.TYPE.RELATIONAL_OPERATOR_FUNC, new RelationalOperatorFuncTokenHandler());
+ }
+
+ /**
+ * Generate a Predicate instance from an array of tokens.
+ * Each input token contains a type and a value.
+ *
+ * Based on the token type and location, the tokens are first translated into a list of
+ * expressions, both relational and logical. These expressions are then merged into a tree
+ * of expressions with a single root following operator precedence and explicit grouping rules.
+ * Depending on the query, this merging of expressions into a tree of expressions may occur in
+ * several passes, one pass per level of precedence starting at the highest level of precedence.
+ *
+ * The predicate is built by traversing the expression tree in-order with each node expressing itself
+ * as a predicate.
+ *
+ * @param tokens an array of tokens which represent the query,
+ * each token contains type and value information
+ *
+ * @return a new predicate instance based on the supplied tokens
+ * @throws InvalidQueryException if unable to parse the tokens and produce a predicate
+ */
+ public Predicate parse(Token[] tokens) throws InvalidQueryException {
+ ParseContext ctx = parseExpressions(tokens);
+
+ List<Expression> listExpressions = ctx.getExpressions();
+ List<Expression> listMergedExpressions = mergeExpressions(listExpressions, ctx.getMaxPrecedence());
+
+ return listMergedExpressions.isEmpty() ? null :
+ listMergedExpressions.get(0).toPredicate();
+ }
+
+ /**
+ * Create parse context from an array of tokens. The parse context contains a list of expressions
+ * and other information about the expressions an parsed tokens.
+ *
+ * @param tokens an array of tokens which represent the query,
+ * each token contains type and value information
+ *
+ * @return a parse context which contains a list of expressions
+ * @throws InvalidQueryException if unable to properly parse the tokens into a parse context
+ */
+ private ParseContext parseExpressions(Token[] tokens) throws InvalidQueryException {
+ ParseContext ctx = new ParseContext(tokens);
+
+ while (ctx.getCurrentTokensIndex() < tokens.length) {
+ TOKEN_HANDLERS.get(tokens[ctx.getCurrentTokensIndex()].getType()).handleToken(ctx);
+ }
+
+ if (ctx.getPrecedenceLevel() != 0) {
+ throw new InvalidQueryException("Invalid query string: mismatched parentheses.");
+ }
+
+ return ctx;
+ }
+
+ /**
+ * Merge list of expressions into a tree of logical/relational expressions.
+ * This is done recursively in several passes, one per level of precedence starting at the
+ * highest precedence level. Recursion exits when a single expression remains.
+ *
+ * @param listExpressions list of expressions to merge
+ * @param precedenceLevel the precedence level that is to be merged
+ *
+ * @return tree of expressions with a single root expression
+ */
+ private List<Expression> mergeExpressions(List<Expression> listExpressions, int precedenceLevel) {
+ if (listExpressions.size() > 1) {
+ Stack<Expression> stack = new Stack<Expression>();
+
+ stack.push(listExpressions.remove(0));
+ while (! listExpressions.isEmpty()) {
+ Expression exp = stack.pop();
+ Expression left = stack.empty() ? null : stack.pop();
+ Expression right = listExpressions.remove(0);
+ stack.addAll(exp.merge(left, right, precedenceLevel));
+ }
+ return mergeExpressions(new ArrayList<Expression>(stack), precedenceLevel - 1);
+ }
+ return listExpressions;
+ }
+
+ /**
+ * A parse context which contains information related to parsing the provided tokens into expressions.
+ */
+ private class ParseContext {
+ /**
+ * The current context precedence level. This is dictated by bracket tokens.
+ */
+ private int m_precedence = 0;
+
+ /**
+ * Current position in tokens array
+ */
+ private int m_tokensIdx = 0;
+
+ /**
+ * Tokens
+ */
+ private Token[] m_tokens;
+
+ /**
+ * The type of the previous token used in validation.
+ */
+ private Token.TYPE m_previousTokenType = null;
+
+ /**
+ * The list of expressions which are generated from the tokens.
+ */
+ private List<Expression> m_listExpressions = new ArrayList<Expression>();
+
+ /**
+ * Highest precedence level in expression.
+ */
+ int m_maxPrecedence = 0;
+
+ public ParseContext(Token[] tokens) {
+ m_tokens = tokens;
+ }
+
+ /**
+ * Get array of all tokens.
+ * @return token array
+ */
+ public Token[] getTokens() {
+ return m_tokens;
+ }
+
+ /**
+ * Get the current position in the tokens array.
+ * @return the current tokens index
+ */
+ public int getCurrentTokensIndex() {
+ return m_tokensIdx;
+ }
+
+ /**
+ * Set the current position in the tokens array.
+ * Each handler should set this value after processing a token(s).
+ * @param idx current tokens index
+ */
+ public void setCurrentTokensIndex(int idx) {
+ m_tokensIdx = idx;
+ }
+
+ /**
+ * Increment the context precedence level.
+ *
+ * @param val how much the level is increased by
+ */
+ public void incPrecedenceLevel(int val) {
+ m_precedence += val;
+ }
+
+ /**
+ * Decrement the context precedence level.
+ *
+ * @param val how much the level is decremented by
+ * @throws InvalidQueryException if the level is decremented below 0
+ */
+ public void decPrecedenceLevel(int val) throws InvalidQueryException {
+ m_precedence -= val;
+ if (m_precedence < 0) {
+ throw new InvalidQueryException("Invalid query string: mismatched parentheses.");
+ }
+ }
+
+ /**
+ * Get the current context precedence level.
+ *
+ * @return current context precedence level
+ */
+ public int getPrecedenceLevel() {
+ return m_precedence;
+ }
+
+ /**
+ * Get the list of generated expressions.
+ *
+ * @return the list of generated expressions
+ */
+ public List<Expression> getExpressions() {
+ return m_listExpressions;
+ }
+
+ /**
+ * Get the last expression.
+ *
+ * @return the last expression
+ */
+ public Expression getPrecedingExpression() {
+ return m_listExpressions == null ? null :
+ m_listExpressions.get(m_listExpressions.size() - 1);
+ }
+
+ /**
+ * Get the highest operator precedence in the list of generated expressions.
+ *
+ * @return the max operator precedence
+ */
+ public int getMaxPrecedence() {
+ return m_maxPrecedence;
+ }
+
+ /**
+ * Update the max precedence level.
+ * The max precedence level is only updated if the provided level > the current level.
+ *
+ * @param precedenceLevel the new value
+ */
+ public void updateMaxPrecedence(int precedenceLevel) {
+ if (precedenceLevel > m_maxPrecedence) {
+ m_maxPrecedence = precedenceLevel;
+ }
+ }
+
+ /**
+ * Add a generated expression.
+ *
+ * @param exp the expression to add
+ */
+ public void addExpression(Expression exp) {
+ m_listExpressions.add(exp);
+ }
+
+ /**
+ * Set the token type of the current token
+ *
+ * @param type type of the current token
+ */
+ private void setTokenType(Token.TYPE type) {
+ m_previousTokenType = type;
+ }
+
+ /**
+ * Get the last token type set.
+ *
+ * @return the last token type set
+ */
+ public Token.TYPE getPreviousTokenType() {
+ return m_previousTokenType;
+ }
+ }
+
+
+ /**
+ * Base token handler.
+ * Token handlers are responsible for handling the processing of a specific token type.
+ */
+ private abstract class TokenHandler {
+ /**
+ * Process a token. Handles common token processing functionality then delegates to the individual
+ * concrete handlers.
+ *
+ * @param ctx the current parse context
+ * @throws InvalidQueryException if unable to process the token
+ */
+ public void handleToken(ParseContext ctx) throws InvalidQueryException {
+ Token token = ctx.getTokens()[ctx.getCurrentTokensIndex()];
+ if (! validate(ctx.getPreviousTokenType())) {
+ throw new InvalidQueryException("Unexpected token encountered in query string. Last Token Type=" +
+ ctx.getPreviousTokenType() + ", Current Token[type=" + token.getType() +
+ ", value='" + token.getValue() + "']");
+ }
+ ctx.setTokenType(token.getType());
+
+ int idxIncrement = _handleToken(ctx);
+ ctx.setCurrentTokensIndex(ctx.getCurrentTokensIndex() + idxIncrement);
+ }
+
+ /**
+ * Process a token.
+ *
+ * @param ctx the current parse context
+ * @throws InvalidQueryException if unable to process the token
+ */
+ public abstract int _handleToken(ParseContext ctx) throws InvalidQueryException;
+
+ /**
+ * Validate the token based on the previous token.
+ *
+ * @param previousTokenType the type of the previous token
+ * @return true if validation is successful, false otherwise
+ */
+ public abstract boolean validate(Token.TYPE previousTokenType);
+ }
+
+ /**
+ * Open Bracket token handler.
+ */
+ private class BracketOpenTokenHandler extends TokenHandler {
+
+ @Override
+ public int _handleToken(ParseContext ctx) {
+ ctx.incPrecedenceLevel(Operator.MAX_OP_PRECEDENCE);
+ return 1;
+ }
+
+ @Override
+ public boolean validate(Token.TYPE previousTokenType) {
+ return previousTokenType == null ||
+ previousTokenType == Token.TYPE.BRACKET_OPEN ||
+ previousTokenType == Token.TYPE.LOGICAL_OPERATOR ||
+ previousTokenType == Token.TYPE.LOGICAL_UNARY_OPERATOR;
+ }
+ }
+
+ /**
+ * Close Bracket token handler
+ */
+ private class BracketCloseTokenHandler extends TokenHandler {
+ @Override
+ public int _handleToken(ParseContext ctx) throws InvalidQueryException{
+ ctx.decPrecedenceLevel(Operator.MAX_OP_PRECEDENCE);
+
+ return 1;
+ }
+
+ @Override
+ public boolean validate(Token.TYPE previousTokenType) {
+ return previousTokenType == Token.TYPE.VALUE_OPERAND ||
+ previousTokenType == Token.TYPE.BRACKET_CLOSE;
+ }
+ }
+
+ /**
+ * Relational Operator token handler
+ */
+ private class RelationalOperatorTokenHandler extends TokenHandler {
+ @Override
+ public int _handleToken(ParseContext ctx) throws InvalidQueryException {
+ Token token = ctx.getTokens()[ctx.getCurrentTokensIndex()];
+ RelationalOperator relationalOp = RelationalOperatorFactory.createOperator(token.getValue());
+ //todo: use factory to create expression
+ ctx.addExpression(new RelationalExpression(relationalOp));
+
+ return 1;
+ }
+
+ @Override
+ public boolean validate(Token.TYPE previousTokenType) {
+ return previousTokenType == null ||
+ previousTokenType == Token.TYPE.BRACKET_OPEN ||
+ previousTokenType == Token.TYPE.LOGICAL_OPERATOR ||
+ previousTokenType == Token.TYPE.LOGICAL_UNARY_OPERATOR;
+ }
+ }
+
+ /**
+ * Relational Operator function token handler
+ */
+ private class RelationalOperatorFuncTokenHandler extends TokenHandler {
+ @Override
+ public int _handleToken(ParseContext ctx) throws InvalidQueryException {
+ Token[] tokens = ctx.getTokens();
+ int idx = ctx.getCurrentTokensIndex();
+ Token token = tokens[idx];
+ RelationalOperator relationalOp = RelationalOperatorFactory.createOperator(token.getValue());
+
+ ctx.addExpression(new RelationalExpression(relationalOp));
+ ctx.setCurrentTokensIndex(++idx);
+
+ TokenHandler propertyHandler = new PropertyOperandTokenHandler();
+ propertyHandler.handleToken(ctx);
+
+ // handle right operand if applicable to operator
+ idx = ctx.getCurrentTokensIndex();
+ if (ctx.getCurrentTokensIndex() < tokens.length &&
+ tokens[idx].getType().equals(Token.TYPE.VALUE_OPERAND)) {
+ TokenHandler valueHandler = new ValueOperandTokenHandler();
+ valueHandler.handleToken(ctx);
+ }
+
+ // skip closing bracket
+ idx = ctx.getCurrentTokensIndex();
+ if (idx >= tokens.length || tokens[idx].getType() != Token.TYPE.BRACKET_CLOSE) {
+ throw new InvalidQueryException("Missing closing bracket for in expression.") ;
+ }
+ return 1;
+ }
+
+ @Override
+ public boolean validate(Token.TYPE previousTokenType) {
+ return previousTokenType == null ||
+ previousTokenType == Token.TYPE.BRACKET_OPEN ||
+ previousTokenType == Token.TYPE.LOGICAL_OPERATOR ||
+ previousTokenType == Token.TYPE.LOGICAL_UNARY_OPERATOR;
+ }
+ }
+
+ /**
+ * Logical Operator token handler
+ */
+ private class LogicalOperatorTokenHandler extends TokenHandler {
+ @Override
+ public int _handleToken(ParseContext ctx) throws InvalidQueryException {
+ Token token = ctx.getTokens()[ctx.getCurrentTokensIndex()];
+ LogicalOperator logicalOp = LogicalOperatorFactory.createOperator(token.getValue(), ctx.getPrecedenceLevel());
+ ctx.updateMaxPrecedence(logicalOp.getPrecedence());
+ ctx.addExpression(LogicalExpressionFactory.createLogicalExpression(logicalOp));
+
+ return 1;
+ }
+
+ @Override
+ public boolean validate(Token.TYPE previousTokenType) {
+ return previousTokenType == Token.TYPE.VALUE_OPERAND ||
+ previousTokenType == Token.TYPE.BRACKET_CLOSE;
+ }
+ }
+
+ /**
+ * Logical Unary Operator token handler
+ */
+ private class LogicalUnaryOperatorTokenHandler extends LogicalOperatorTokenHandler {
+ @Override
+ public boolean validate(Token.TYPE previousTokenType) {
+ return previousTokenType == null ||
+ previousTokenType == Token.TYPE.BRACKET_OPEN ||
+ previousTokenType == Token.TYPE.LOGICAL_OPERATOR;
+ }
+ }
+
+ /**
+ * Property Operand token handler
+ */
+ private class PropertyOperandTokenHandler extends TokenHandler {
+ @Override
+ public int _handleToken(ParseContext ctx) throws InvalidQueryException {
+ Token token = ctx.getTokens()[ctx.getCurrentTokensIndex()];
+ ctx.getPrecedingExpression().setLeftOperand(token.getValue());
+
+ return 1;
+ }
+
+ @Override
+ public boolean validate(Token.TYPE previousTokenType) {
+ return previousTokenType == Token.TYPE.RELATIONAL_OPERATOR ||
+ previousTokenType == Token.TYPE.RELATIONAL_OPERATOR_FUNC;
+ }
+ }
+
+ /**
+ * Value Operand token handler
+ */
+ private class ValueOperandTokenHandler extends TokenHandler {
+ @Override
+ public int _handleToken(ParseContext ctx) throws InvalidQueryException {
+ Token token = ctx.getTokens()[ctx.getCurrentTokensIndex()];
+ ctx.getPrecedingExpression().setRightOperand(token.getValue());
+
+ return 1;
+ }
+
+ @Override
+ public boolean validate(Token.TYPE previousTokenType) {
+ return previousTokenType == Token.TYPE.PROPERTY_OPERAND;
+ }
+ }
+}
+
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/Token.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/Token.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/Token.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/Token.java Wed Mar 20 20:44:43 2013
@@ -0,0 +1,110 @@
+/**
+ * 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.server.api.predicate;
+
+/**
+ * Token representation which is generated by the lexer.
+ * Contains type and value information.
+ */
+public class Token {
+
+ /**
+ * Token types.
+ */
+ public enum TYPE {
+ /** Property name operand. This is the left operand in relational expressions. */
+ PROPERTY_OPERAND,
+ /** Value operand. This is the right operand in relational expressions. */
+ VALUE_OPERAND,
+ /** Relational operator */
+ RELATIONAL_OPERATOR,
+ /** Relational operator function */
+ RELATIONAL_OPERATOR_FUNC,
+ /** Logical operator */
+ LOGICAL_OPERATOR,
+ /** Logical unary operator such as !*/
+ LOGICAL_UNARY_OPERATOR,
+ /** Opening bracket */
+ BRACKET_OPEN,
+ /** Closing bracket */
+ BRACKET_CLOSE
+ }
+
+ /**
+ * Token type.
+ */
+ private TYPE m_type;
+
+ /**
+ * Token value.
+ */
+ private String m_value;
+
+
+ /**
+ * Constructor.
+ *
+ * @param type type
+ * @param value value
+ */
+ public Token(TYPE type, String value) {
+ m_type = type;
+ m_value = value;
+ }
+
+ /**
+ * Get the token type.
+ * @return token type
+ */
+ public TYPE getType() {
+ return m_type;
+ }
+
+ /**
+ * Get the token value.
+ * @return token value
+ */
+ public String getValue() {
+ return m_value;
+ }
+
+ @Override
+ public String toString() {
+ return "Token{ type=" + m_type + ", value='" + m_value + "' }";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Token token = (Token) o;
+
+ return m_type == token.m_type &&
+ (m_value == null ? token.m_value == null : m_value.equals(token.m_value));
+ }
+
+ @Override
+ public int hashCode() {
+ int result = m_type.hashCode();
+ result = 31 * result + (m_value != null ? m_value.hashCode() : 0);
+
+ return result;
+ }
+}
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/AbstractExpression.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/AbstractExpression.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/AbstractExpression.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/AbstractExpression.java Wed Mar 20 20:44:43 2013
@@ -0,0 +1,106 @@
+/**
+ * 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.server.api.predicate.expressions;
+
+import org.apache.ambari.server.api.predicate.operators.Operator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class for expressions.
+ */
+public abstract class AbstractExpression<T> implements Expression<T> {
+
+ /**
+ * The operator.
+ */
+ private final Operator m_op;
+
+ /**
+ * The left operand.
+ * */
+ private T m_left = null;
+
+ /**
+ * The right operand.
+ */
+ private T m_right = null;
+
+ /**
+ * Constructor.
+ *
+ * @param op the expressions operator
+ */
+ protected AbstractExpression(Operator op) {
+ m_op = op;
+ }
+
+ @Override
+ public void setLeftOperand(T left) {
+ m_left = left;
+ }
+
+ @Override
+ public void setRightOperand(T right) {
+ m_right = right;
+ }
+
+ @Override
+ public T getLeftOperand() {
+ return m_left;
+ }
+
+ @Override
+ public T getRightOperand() {
+ return m_right;
+ }
+
+ @Override
+ public Operator getOperator() {
+ return m_op;
+ }
+
+ @Override
+ public List<Expression> merge(Expression left, Expression right, int precedence) {
+ return defaultMerge(left, right);
+ }
+
+ /**
+ * Base merge implementation.
+ * No merge is done, simply returns the left expression, this and the right expression.
+ *
+ * @param left the expression to the left of this expression
+ * @param right the expression to the right of this expression
+ *
+ * @return a list containing the un-merged left expression, this and right expression
+ */
+ protected List<Expression> defaultMerge(Expression left, Expression right) {
+ List<Expression> listExpressions = new ArrayList<Expression>();
+ if (left != null) {
+ listExpressions.add(left);
+ }
+ listExpressions.add(this);
+ if (right != null) {
+ listExpressions.add(right);
+ }
+
+ return listExpressions;
+ }
+}
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/Expression.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/Expression.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/Expression.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/Expression.java Wed Mar 20 20:44:43 2013
@@ -0,0 +1,89 @@
+/**
+ * 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.server.api.predicate.expressions;
+
+import org.apache.ambari.server.api.predicate.InvalidQueryException;
+import org.apache.ambari.server.api.predicate.operators.Operator;
+import org.apache.ambari.server.controller.spi.Predicate;
+
+import java.util.List;
+
+/**
+ * Expression representation.
+ * There are two types of expressions, relational and logical.
+ * Each expression has an operator and either 2 operands for binary
+ * expressions or 1 operand for unary expressions.
+ */
+public interface Expression<T> {
+
+ /**
+ * Merge expression with surrounding expressions.
+ *
+ * @param left the preceding expression
+ * @param right the following expression
+ * @param precedence the precedence level being merged. Only expressions at this precedence level
+ * should be merged. Others should simply return the left expression, themselves and
+ * the right expression in that order.
+ *
+ * @return a list of expressions after merging. Do not return any null elements.
+ */
+ public List<Expression> merge(Expression left, Expression right, int precedence);
+
+
+ /**
+ * Get the predicate representation of the expression.
+ * @return a predicate instance for the expression
+ */
+ public Predicate toPredicate() throws InvalidQueryException;
+
+ /**
+ * Set the expressions left operand.
+ *
+ * @param left the left operand
+ */
+ public void setLeftOperand(T left);
+
+ /**
+ * Set the expressions right operand.
+ *
+ * @param right the right operand
+ */
+ public void setRightOperand(T right);
+
+ /**
+ * Get the left operand expression.
+ *
+ * @return the left operand
+ */
+ public T getLeftOperand();
+
+ /**
+ * Get the right operand expression.
+ *
+ * @return the right operand.
+ */
+ public T getRightOperand();
+
+ /**
+ * Get the expression operator.
+ *
+ * @return the logical operator for the expression
+ */
+ public Operator getOperator();
+}
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/LogicalExpression.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/LogicalExpression.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/LogicalExpression.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/LogicalExpression.java Wed Mar 20 20:44:43 2013
@@ -0,0 +1,61 @@
+/**
+ * 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.server.api.predicate.expressions;
+
+import org.apache.ambari.server.api.predicate.InvalidQueryException;
+import org.apache.ambari.server.api.predicate.operators.LogicalOperator;
+import org.apache.ambari.server.controller.spi.Predicate;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Logical expression implementation.
+ * Always a binary expression that consists of a logical operator and
+ * expressions for the left and right operands.
+ */
+public class LogicalExpression extends AbstractExpression<Expression> {
+
+ /**
+ * Constructor.
+ *
+ * @param op the logical operator of the expression
+ */
+ public LogicalExpression(LogicalOperator op) {
+ super(op);
+ }
+
+
+ @Override
+ public Predicate toPredicate() throws InvalidQueryException {
+ return ((LogicalOperator) getOperator()).
+ toPredicate(getLeftOperand().toPredicate(), getRightOperand().toPredicate());
+ }
+
+ @Override
+ public List<Expression> merge(Expression left, Expression right, int precedence) {
+ if (getOperator().getPrecedence() == precedence && getLeftOperand() == null) {
+ setLeftOperand(left);
+ setRightOperand(right);
+ return Collections.<Expression>singletonList(this);
+ } else {
+ return defaultMerge(left, right);
+ }
+ }
+}
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/LogicalExpressionFactory.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/LogicalExpressionFactory.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/LogicalExpressionFactory.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/LogicalExpressionFactory.java Wed Mar 20 20:44:43 2013
@@ -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.ambari.server.api.predicate.expressions;
+
+import org.apache.ambari.server.api.predicate.InvalidQueryException;
+import org.apache.ambari.server.api.predicate.operators.LogicalOperator;
+
+/**
+ * Factory of logical expression instances.
+ */
+public class LogicalExpressionFactory {
+ /**
+ * Create a logical expression instance.
+ *
+ * @param op the logical operator
+ *
+ * @return a new logical expression instance
+ * @throws InvalidQueryException
+ */
+ public static LogicalExpression createLogicalExpression(LogicalOperator op) throws InvalidQueryException {
+ switch (op.getType()) {
+ case AND:
+ case OR:
+ return new LogicalExpression(op);
+ case NOT :
+ return new NotLogicalExpression(op);
+ default:
+ throw new RuntimeException("An invalid logical operator type was encountered: " + op);
+ }
+ }
+}
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/NotLogicalExpression.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/NotLogicalExpression.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/NotLogicalExpression.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/NotLogicalExpression.java Wed Mar 20 20:44:43 2013
@@ -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.ambari.server.api.predicate.expressions;
+
+import org.apache.ambari.server.api.predicate.InvalidQueryException;
+import org.apache.ambari.server.api.predicate.operators.LogicalOperator;
+import org.apache.ambari.server.controller.predicate.BasePredicate;
+import org.apache.ambari.server.controller.predicate.NotPredicate;
+import org.apache.ambari.server.controller.spi.Predicate;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A 'NOT' logical expression representation.
+ * Negates a corresponding right operand.
+ */
+public class NotLogicalExpression extends LogicalExpression {
+ /**
+ * Constructor.
+ *
+ * @param op the logical operator
+ */
+ public NotLogicalExpression(LogicalOperator op) {
+ super(op);
+ }
+
+ @Override
+ public List<Expression> merge(Expression left, Expression right, int precedence) {
+ if (getOperator().getPrecedence() == precedence && getRightOperand() == null) {
+ List<Expression> listExpressions = new ArrayList<Expression>();
+ if (left != null) {
+ listExpressions.add(left);
+ }
+ setRightOperand(right);
+ listExpressions.add(this);
+ return listExpressions;
+ } else {
+ // do nothing, already merged
+ return defaultMerge(left, right);
+ }
+ }
+
+ @Override
+ public Predicate toPredicate() throws InvalidQueryException {
+ //todo: remove need to down cast to BasePredicate
+ return new NotPredicate((BasePredicate) getRightOperand().toPredicate());
+ }
+}
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/RelationalExpression.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/RelationalExpression.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/RelationalExpression.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/expressions/RelationalExpression.java Wed Mar 20 20:44:43 2013
@@ -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.ambari.server.api.predicate.expressions;
+
+import org.apache.ambari.server.api.predicate.InvalidQueryException;
+import org.apache.ambari.server.api.predicate.operators.RelationalOperator;
+import org.apache.ambari.server.controller.spi.Predicate;
+
+/**
+ * Relational Expression.
+ * Consists of a property name for the left operand, a relational operator
+ * and a value as the right operand.
+ */
+public class RelationalExpression extends AbstractExpression<String> {
+
+ /**
+ * Constructor.
+ *
+ * @param op relational operator
+ */
+ public RelationalExpression(RelationalOperator op) {
+ super(op);
+ }
+
+ @Override
+ public Predicate toPredicate() throws InvalidQueryException {
+ return ((RelationalOperator) getOperator()).
+ toPredicate(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public String toString() {
+ return "RelationalExpression{ property='" + getLeftOperand() + "\', value='"
+ + getRightOperand() + "\', op=" + getOperator() + " }";
+ }
+}
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/operators/AbstractOperator.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/operators/AbstractOperator.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/operators/AbstractOperator.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/operators/AbstractOperator.java Wed Mar 20 20:44:43 2013
@@ -0,0 +1,69 @@
+/**
+ * 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.server.api.predicate.operators;
+
+/**
+ * Base operator implementation.
+ */
+public abstract class AbstractOperator implements Operator {
+ /**
+ * The precedence value for the current context.
+ */
+ private final int m_ctxPrecedence;
+
+
+ /**
+ * Constructor.
+ *
+ * @param ctxPrecedence the context precedence value
+ */
+ protected AbstractOperator(int ctxPrecedence) {
+ m_ctxPrecedence = ctxPrecedence;
+ }
+
+ /**
+ * Return the base precedence for this operator.
+ * This is the value that is specific to the operator
+ * type and doesn't take context into account.
+ *
+ * @return the base precedence for this operator type
+ */
+ public int getBasePrecedence() {
+ // this value is used for all relational operators
+ // logical operators override this value
+ return -1;
+ }
+
+ @Override
+ public int getPrecedence() {
+ return getBasePrecedence() + m_ctxPrecedence;
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+
+ /**
+ * Get the name of the operator.
+ *
+ * @return the operator name
+ */
+ public abstract String getName();
+}
Added: incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/operators/AndOperator.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/operators/AndOperator.java?rev=1459041&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/operators/AndOperator.java (added)
+++ incubator/ambari/branches/branch-1.2/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/operators/AndOperator.java Wed Mar 20 20:44:43 2013
@@ -0,0 +1,64 @@
+/**
+ * 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.server.api.predicate.operators;
+
+import org.apache.ambari.server.controller.predicate.AndPredicate;
+import org.apache.ambari.server.controller.predicate.BasePredicate;
+import org.apache.ambari.server.controller.spi.Predicate;
+
+/**
+ * And operator implementation.
+ */
+public class AndOperator extends AbstractOperator implements LogicalOperator {
+
+ /**
+ * Constructor.
+ *
+ * @param ctxPrecedence precedence value for the current context
+ */
+ public AndOperator(int ctxPrecedence) {
+ super(ctxPrecedence);
+ }
+
+ @Override
+ public TYPE getType() {
+ return TYPE.AND;
+ }
+
+ @Override
+ public String getName() {
+ return "AndOperator";
+ }
+
+ @Override
+ public int getBasePrecedence() {
+ return 2;
+ }
+
+ @Override
+ public Predicate toPredicate(Predicate left, Predicate right) {
+ //todo: refactor to not need down casts
+ return new AndPredicate((BasePredicate) left, (BasePredicate) right);
+ }
+
+ @Override
+ public String toString() {
+ return getName() + "[precedence=" + getPrecedence() + "]";
+ }
+}