.classpath | 1 modules/core/dbutils/pom.xml | 2 modules/core/dbutils/src/main/scripts/dbsetup-build.xml | 2 modules/core/dbutils/src/main/scripts/dbsetup/search-data.xml | 25 modules/core/dbutils/src/main/scripts/dbsetup/search-schema.xml | 23 modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml | 53 ++ modules/core/domain/src/main/java/org/rhq/core/domain/criteria/SavedSearchCriteria.java | 142 +++++ modules/core/domain/src/main/java/org/rhq/core/domain/search/SavedSearch.java | 253 ++++++++++ modules/core/domain/src/main/java/org/rhq/core/domain/search/SearchContext.java | 38 + modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClient.java | 2 modules/enterprise/server/jar/pom.xml | 47 + modules/enterprise/server/jar/src/main/antlr3/org/rhq/enterprise/server/search/RHQL.g | 181 +++++++ modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerBean.java | 120 ++++ modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerLocal.java | 31 + modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerRemote.java | 119 ++++ modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/util/CriteriaQueryGenerator.java | 17 modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/util/LookupUtil.java | 6 modules/enterprise/server/jar/src/main/resources/single-line-rhql.txt | 126 ++++ modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/search/RHQLTest.java | 171 ++++++ 19 files changed, 1353 insertions(+), 6 deletions(-)
New commits: commit d0ef49e101ef7887e834b0a9021c0c0654a7f5d5 Merge: 613b993... 8a0951a... Author: Joseph Marques joseph@redhat.com Date: Fri Feb 26 15:34:50 2010 -0500
Merge branch 'master' into search
commit 613b993b85ac47117fda5d7f7810318e891cdf0e Author: Joseph Marques joseph@redhat.com Date: Fri Feb 26 15:32:41 2010 -0500
construct AST from parse tree for RHQL grammar
diff --git a/modules/enterprise/server/jar/src/main/antlr3/org/rhq/enterprise/server/search/RHQL.g b/modules/enterprise/server/jar/src/main/antlr3/org/rhq/enterprise/server/search/RHQL.g index 00a0dab..ded91a1 100644 --- a/modules/enterprise/server/jar/src/main/antlr3/org/rhq/enterprise/server/search/RHQL.g +++ b/modules/enterprise/server/jar/src/main/antlr3/org/rhq/enterprise/server/search/RHQL.g @@ -27,11 +27,43 @@ * @author Joseph Marques */ grammar RHQL; - options { language=Java; backtrack=true; memoize=true; + output=AST; + ASTLabelType=CommonTree; +} + +/* + * Imaginary nodes serve to fully abstract the parse tree from the AST. This + * allows us, for example, to support the conjunctive ('and') operator between + * conditional expressions, but gives us the flexibility to modify the lexical + * element that represents that operation. In the future, we might want to + * support '&&' instead of 'and', or even support them both. The inclusion of + * imaginary tokens creates a more stable AST that downstream grammars can use + * without fear that every change to the syntax will break their tree parser. + */ +tokens { + OR; + AND; + + CONTEXT; + LINEAGE; + PATH; + PARAM; + IDENT; + VALUE; + + OP_EQUALS; + OP_EQUALS_STRICT; + OP_NULL; + OP_IN; + + OP_NOT_EQUALS; + OP_NOT_EQUALS_STRICT; + OP_NOT_NULL; + OP_NOT_IN; }
@header { @@ -41,26 +73,29 @@ options { package org.rhq.enterprise.server.search; }
+ /* * parser rules */
searchExpression - : conditionalExpression + : conditionalExpression { System.out.println($conditionalExpression.tree.toStringTree()); } ;
conditionalExpression - : conditionalFactor ( 'or' conditionalFactor )* - ; + : conds+=conditionalFactor ( WS* ( 'or' ) WS* conds+=conditionalFactor )* -> { $conds.size() == 1 }? ^($conds) + -> ^(OR conditionalFactor+) + ; // use rewrite predicates to eliminate superfluous 'or' node if only one child
conditionalFactor - : conditionalPrimary ( ( 'and' )? conditionalPrimary )* - ; + : conds+=conditionalPrimary ( WS* ( 'and' WS* )? conds+=conditionalPrimary )* -> { $conds.size() == 1 }? ^($conds) + -> ^(AND conditionalPrimary+) + ; // use rewrite predicates to eliminate superfluous 'and' node if only one child
conditionalPrimary - : simpleConditionalExpression - | '(' conditionalExpression ')' - ; + : WS* simpleConditionalExpression WS* -> simpleConditionalExpression + | '(' WS* conditionalExpression WS* ')' -> conditionalExpression + ; // avoid building nodes for parens, tree structure implies existence appropriately -- ignore captured WS
simpleConditionalExpression : comparisonConditionalExpression @@ -69,69 +104,78 @@ simpleConditionalExpression ;
comparisonConditionalExpression - : context comparisonOperator identifier - ; + : c=context WS* op=comparisonOperator WS* ident=identifier -> ^($op $c ^(VALUE $ident)) + ; // rewrite tree output so operator is always the root -- ignore captured WS
nullComparisonConditionalExpression - : context nullOperator - ; + : c=context WS* op=nullOperator -> ^($op $c) + ; // rewrite tree output so operator is always the root -- ignore captured WS
inExpression - : context inOperator '[' '='? identifier ( ',' '='? identifier )* ']' - ; + : c=context WS* op=inOperator WS* '[' WS* ids+=identifier WS* ( ',' WS* ids+=identifier WS* )* ']' + -> ^($op $c ^(VALUE $ids+)) + ; // rewrite tree output so operator is always the root -- ignore captured WS
context - : ( lineage '.' )? path ( '[' identifier ']' )? + : ( l=lineage '.' )? p=path ( '[' ident=identifier ']' )? -> ^(CONTEXT ^(LINEAGE $l)? ^(PATH $p) ^(PARAM $ident)?) ;
lineage - : path ( '(' INT ')' )? - ; + : path ( '('! LEVEL ')'! )? + ; // avoid building nodes for brackets, tree structure implies existence appropriately
path : ID+ ;
identifier - : quotedValue - | value + : doubleQuotedValue -> ^(IDENT doubleQuotedValue) + | quotedValue -> ^(IDENT quotedValue) + | openEndedvalue -> ^(IDENT openEndedvalue) ;
+doubleQuotedValue + : '"'! ~('"')* '"'! + ; // avoid building nodes for the double-quote characters + + quotedValue - : ''' ~(''')* ''' - ; + : '''! ~(''')* '''! + ; // avoid building nodes for the sinngle-quote characters
-value - : ~(''') ~(']'|','|')')* - ; +openEndedvalue + : ~(']' | ',' | ')' | '(' | 'or' | 'and' | WS )* + ; // consume until we find a char to terminate the current phrase ']' ',' ')' or begin the next '(' 'or' 'and'
comparisonOperator - : '=' - | '==' - | '!=' - | '!==' - ; + : '=' -> ^(OP_EQUALS) + | '==' -> ^(OP_EQUALS_STRICT) + | '!=' -> ^(OP_NOT_EQUALS) + | '!==' -> ^(OP_NOT_EQUALS_STRICT) + ; // use imaginary nodes for all operators, which further removes the AST from the real lexical elements
nullOperator - : 'is' 'not'? 'null' - ; + : 'is' WS+ (negation='not' WS+)? 'null' -> { $negation == null }? ^(OP_NULL) + -> ^(OP_NOT_NULL) + ; // use imaginary nodes for all operators, which further removes the AST from the real lexical elements
inOperator - : 'not'? 'in' - ; + : (negation+='not' WS+)? 'in' -> { $negation == null }? ^(OP_IN) + -> ^(OP_NOT_IN) + ; // use imaginary nodes for all operators, which further removes the AST from the real lexical elements
/* * lexical elements - */ + */
ID : 'a'..'z' ;
-INT - : '0'..'9' +LEVEL + : '0'..'5' ;
WS - : ( ' ' | '\n' | '\r' )+ { $channel = HIDDEN; } + : ( ' ' | '\n' | '\r' )+ ; \ No newline at end of file diff --git a/modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/search/RHQLTest.java b/modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/search/RHQLTest.java index 637e339..ceab3e3 100644 --- a/modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/search/RHQLTest.java +++ b/modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/search/RHQLTest.java @@ -62,6 +62,7 @@ public class RHQLTest extends AssertJUnit { @Override public TestResult call() throws Exception { try { + System.out.println(line); ANTLRStringStream input = new ANTLRStringStream(line); // Create an input character stream from standard in RHQLLexer lexer = new RHQLLexer(input); // Create an echoLexer that feeds from that stream CommonTokenStream tokens = new CommonTokenStream(lexer); // Create a stream of tokens fed by the lexer
commit eac78662084095c22fb80326643fbf59f0e1a863 Author: Joseph Marques joseph@redhat.com Date: Fri Feb 26 15:31:54 2010 -0500
modify RHQL test case file to adhere to simpler syntax for IN-clause:
* remove multi-word non-quoted identifers for the IN-clause * remove ability to change case-sensitivity comparison for IN-clause
diff --git a/modules/enterprise/server/jar/src/main/resources/single-line-rhql.txt b/modules/enterprise/server/jar/src/main/resources/single-line-rhql.txt index 2a5a195..f8c241a 100644 --- a/modules/enterprise/server/jar/src/main/resources/single-line-rhql.txt +++ b/modules/enterprise/server/jar/src/main/resources/single-line-rhql.txt @@ -7,11 +7,11 @@ context !== value context is null context is not null context in [value] -context in [value1, =value2] -context in [=value1, value2, value3] +context in [value1, value2] +context in [value1, value2, value3] context not in [value] -context not in [value1, =value2] -context not in [=value1, value2, value3] +context not in [value1, value2] +context not in [value1, value2, value3]
# simple term, all operators, quoted value context = 'quoted value' @@ -21,19 +21,11 @@ context !== 'quoted value' context is null context is not null context in ['quoted value1'] -context in ['quoted value1', ='quoted value2'] -context in [='quoted value1', 'quoted value2', 'quoted value3'] +context in ['quoted value1', 'quoted value2'] +context in ['quoted value1', 'quoted value2', 'quoted value3'] context not in ['quoted value1'] -context not in ['quoted value1', ='quoted value2'] -context not in [='quoted value1', 'quoted value2', 'quoted value3'] - -# simple term, 'in' operator, non-quoted but multi-word values -context in [quoted value1] -context in [quoted value1, =quoted value2] -context in [=quoted value1, quoted value2, quoted value3] -context not in [quoted value1] -context not in [quoted value1, =quoted value2] -context not in [=quoted value1, quoted value2, quoted value3] +context not in ['quoted value1', 'quoted value2'] +context not in ['quoted value1', 'quoted value2', 'quoted value3']
# parameterized term, all operators context[param] = value @@ -43,11 +35,11 @@ context[param] !== value context[param] is null context[param] is not null context[param] in [value] -context[param] in [value1, =value2] -context[param] in [=value1, value2, value3] +context[param] in [value1, value2] +context[param] in [value1, value2, value3] context[param] not in [value] -context[param] not in [value1, =value2] -context[param] not in [=value1, value2, value3] +context[param] not in [value1, value2] +context[param] not in [value1, value2, value3]
# deeply nested parenthetical simple terms ((context = value)) @@ -63,11 +55,11 @@ context[param] not in [=value1, value2, value3] (context is null) (context is not null) (context in [value]) -(context in [value1, =value2]) -(context in [=value1, value2, value3]) +(context in [value1, value2]) +(context in [value1, value2, value3]) (context not in [value]) -(context not in [value1, =value2]) -(context not in [=value1, value2, value3]) +(context not in [value1, value2]) +(context not in [value1, value2, value3])
# parameterized parenthetical term, all operators (context[param] = value) @@ -77,11 +69,11 @@ context[param] not in [=value1, value2, value3] (context[param] is null) (context[param] is not null) (context[param] in [value]) -(context[param] in [value1, =value2]) -(context[param] in [=value1, value2, value3]) +(context[param] in [value1, value2]) +(context[param] in [value1, value2, value3]) (context[param] not in [value]) -(context[param] not in [value1, =value2]) -(context[param] not in [=value1, value2, value3]) +(context[param] not in [value1, value2]) +(context[param] not in [value1, value2, value3])
# single-pair parenthetical combinations @@ -131,4 +123,4 @@ context = value1 and ((context = value2)) ((context = value1)) context = value2 (context = value1) (context = value2) (context = value1 (context = value2)) -context = value1 ((context1 = value2)) \ No newline at end of file +context = value1 ((context = value2)) \ No newline at end of file
commit 602b8780654df9298dc36f6e8523e2ebb3e0c906 Author: Joseph Marques joseph@redhat.com Date: Thu Feb 25 12:34:39 2010 -0500
add missing @WebMethod annotation to saved search manager remote interface
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerRemote.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerRemote.java index ffd4255..231167e 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerRemote.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerRemote.java @@ -112,6 +112,7 @@ public interface SavedSearchManagerRemote { * @return the {@link PageList} of {@link SavedSearch} entities that match the criteria filters, an empty list * will be returned if no results were found or none matches the given filters */ + @WebMethod public PageList<SavedSearch> findSavedSearchesByCriteria( // @WebParam(name = "subject") Subject subject, // @WebParam(name = "criteria") SavedSearchCriteria criteria);
commit 07a482e999b87952b9cdbd2573fe6b04c4b8ff8e Author: Joseph Marques joseph@redhat.com Date: Wed Feb 24 22:04:30 2010 -0500
abstract RHQL grammar support:
* initial combined lexer/parser grammar file * test harness to verify grammar correctness * 100 acceptance test cases exercising all major parts of generic syntax
diff --git a/modules/enterprise/server/jar/src/main/antlr3/org/rhq/enterprise/server/search/RHQL.g b/modules/enterprise/server/jar/src/main/antlr3/org/rhq/enterprise/server/search/RHQL.g new file mode 100644 index 0000000..00a0dab --- /dev/null +++ b/modules/enterprise/server/jar/src/main/antlr3/org/rhq/enterprise/server/search/RHQL.g @@ -0,0 +1,137 @@ +/* + * RHQ Management Platform + * Copyright (C) 2005-2008 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation, and/or the GNU Lesser + * General Public License, version 2.1, also as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * Antlr v3 grammar file to parse RHQL search expressions. + * + * @author Joseph Marques + */ +grammar RHQL; + +options { + language=Java; + backtrack=true; + memoize=true; +} + +@header { + package org.rhq.enterprise.server.search; +} +@lexer::header { + package org.rhq.enterprise.server.search; +} + +/* + * parser rules + */ + +searchExpression + : conditionalExpression + ; + +conditionalExpression + : conditionalFactor ( 'or' conditionalFactor )* + ; + +conditionalFactor + : conditionalPrimary ( ( 'and' )? conditionalPrimary )* + ; + +conditionalPrimary + : simpleConditionalExpression + | '(' conditionalExpression ')' + ; + +simpleConditionalExpression + : comparisonConditionalExpression + | nullComparisonConditionalExpression + | inExpression + ; + +comparisonConditionalExpression + : context comparisonOperator identifier + ; + +nullComparisonConditionalExpression + : context nullOperator + ; + +inExpression + : context inOperator '[' '='? identifier ( ',' '='? identifier )* ']' + ; + +context + : ( lineage '.' )? path ( '[' identifier ']' )? + ; + +lineage + : path ( '(' INT ')' )? + ; + +path + : ID+ + ; + +identifier + : quotedValue + | value + ; + +quotedValue + : ''' ~(''')* ''' + ; + +value + : ~(''') ~(']'|','|')')* + ; + +comparisonOperator + : '=' + | '==' + | '!=' + | '!==' + ; + +nullOperator + : 'is' 'not'? 'null' + ; + +inOperator + : 'not'? 'in' + ; + +/* + * lexical elements + */ + +ID + : 'a'..'z' + ; + +INT + : '0'..'9' + ; + +WS + : ( ' ' | '\n' | '\r' )+ { $channel = HIDDEN; } + ; \ No newline at end of file diff --git a/modules/enterprise/server/jar/src/main/resources/single-line-rhql.txt b/modules/enterprise/server/jar/src/main/resources/single-line-rhql.txt new file mode 100644 index 0000000..2a5a195 --- /dev/null +++ b/modules/enterprise/server/jar/src/main/resources/single-line-rhql.txt @@ -0,0 +1,134 @@ + +# simple term, all operators +context = value +context == value +context != value +context !== value +context is null +context is not null +context in [value] +context in [value1, =value2] +context in [=value1, value2, value3] +context not in [value] +context not in [value1, =value2] +context not in [=value1, value2, value3] + +# simple term, all operators, quoted value +context = 'quoted value' +context == 'quoted value' +context != 'quoted value' +context !== 'quoted value' +context is null +context is not null +context in ['quoted value1'] +context in ['quoted value1', ='quoted value2'] +context in [='quoted value1', 'quoted value2', 'quoted value3'] +context not in ['quoted value1'] +context not in ['quoted value1', ='quoted value2'] +context not in [='quoted value1', 'quoted value2', 'quoted value3'] + +# simple term, 'in' operator, non-quoted but multi-word values +context in [quoted value1] +context in [quoted value1, =quoted value2] +context in [=quoted value1, quoted value2, quoted value3] +context not in [quoted value1] +context not in [quoted value1, =quoted value2] +context not in [=quoted value1, quoted value2, quoted value3] + +# parameterized term, all operators +context[param] = value +context[param] == value +context[param] != value +context[param] !== value +context[param] is null +context[param] is not null +context[param] in [value] +context[param] in [value1, =value2] +context[param] in [=value1, value2, value3] +context[param] not in [value] +context[param] not in [value1, =value2] +context[param] not in [=value1, value2, value3] + +# deeply nested parenthetical simple terms +((context = value)) +(((context = value))) +((((context = value)))) +(((((context = value))))) + +# simple parenthetical term, all operators +(context = value) +(context == value) +(context != value) +(context !== value) +(context is null) +(context is not null) +(context in [value]) +(context in [value1, =value2]) +(context in [=value1, value2, value3]) +(context not in [value]) +(context not in [value1, =value2]) +(context not in [=value1, value2, value3]) + +# parameterized parenthetical term, all operators +(context[param] = value) +(context[param] == value) +(context[param] != value) +(context[param] !== value) +(context[param] is null) +(context[param] is not null) +(context[param] in [value]) +(context[param] in [value1, =value2]) +(context[param] in [=value1, value2, value3]) +(context[param] not in [value]) +(context[param] not in [value1, =value2]) +(context[param] not in [=value1, value2, value3]) + + +# single-pair parenthetical combinations +# simple expression, 'or' boolean operator +context = value1 or context = value2 +(context = value1) or context = value2 +context = value1 or (context = value2) +(context = value1) or (context = value2) + +# single-pair parenthetical combinations +# simple expression, 'and' boolean operator +context = value and context = value +(context = value) and context = value +context = value and (context = value) +(context = value) and (context = value) + +# single-pair parenthetical combinations +# simple expression, no boolean operator (implies 'and' semantics) +context = value context = value +(context = value) context = value +context = value (context = value) +(context = value) (context = value) + + +# double-pair parenthetical combinations +# simple expression, 'or' boolean operator +((context = value1 or context = value2)) +((context = value1) or context = value2) +((context = value1)) or context = value2 +(context = value1) or (context = value2) +(context = value1 or (context = value2)) +context = value1 or ((context = value2)) + +# double-pair parenthetical combinations +# simple expression, 'and' boolean operator +((context = value1 and context = value2)) +((context = value1) and context = value2) +((context = value1)) and context = value2 +(context = value1) and (context = value2) +(context = value1 and (context = value2)) +context = value1 and ((context = value2)) + +# double-pair parenthetical combinations +# simple expression, no boolean operator (implies 'and' semantics) +((context = value1 context = value2)) +((context = value1) context = value2) +((context = value1)) context = value2 +(context = value1) (context = value2) +(context = value1 (context = value2)) +context = value1 ((context1 = value2)) \ No newline at end of file diff --git a/modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/search/RHQLTest.java b/modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/search/RHQLTest.java new file mode 100644 index 0000000..637e339 --- /dev/null +++ b/modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/search/RHQLTest.java @@ -0,0 +1,170 @@ +/* + * RHQ Management Platform + * Copyright (C) 2005-2008 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation, and/or the GNU Lesser + * General Public License, version 2.1, also as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.rhq.enterprise.server.search; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.CommonTokenStream; +import org.testng.AssertJUnit; +import org.testng.annotations.AfterSuite; +import org.testng.annotations.BeforeSuite; +import org.testng.annotations.Test; + +/** + * Test harness to verify correctness of RHQL grammar + * + * @author Joseph Marques + */ +public class RHQLTest extends AssertJUnit { + + private enum TestResult { + SUCCESS, FAILURE, TIMEOUT, SKIPPED; + } + + private class AntlrTask implements Callable<TestResult> { + private String line; + + public AntlrTask(String line) { + this.line = line; + } + + @Override + public TestResult call() throws Exception { + try { + ANTLRStringStream input = new ANTLRStringStream(line); // Create an input character stream from standard in + RHQLLexer lexer = new RHQLLexer(input); // Create an echoLexer that feeds from that stream + CommonTokenStream tokens = new CommonTokenStream(lexer); // Create a stream of tokens fed by the lexer + RHQLParser parser = new RHQLParser(tokens); // Create a parser that feeds off the token stream + parser.searchExpression(); // Begin parsing at 'searchExpression' rule + return TestResult.SUCCESS; + } catch (Throwable t) { + return TestResult.FAILURE; + } + } + }; + + private ExecutorService executor; + + @BeforeSuite + public void init() { + executor = Executors.newSingleThreadExecutor(); + } + + @AfterSuite + public void teardown() { + executor.shutdownNow(); + } + + @Test + public void testSingleLineRHQL() throws Exception { + BufferedReader reader = null; + List<String> successes = new java.util.ArrayList<String>(); + List<String> failures = new java.util.ArrayList<String>(); + int count = 0; + try { + InputStream stream = getClass().getClassLoader().getResourceAsStream("single-line-rhql.txt"); + reader = new BufferedReader(new InputStreamReader(stream)); + String line = null; + long timeout = 2000; + while ((line = reader.readLine()) != null) { + TestResult result = testSuccess(line, timeout); + if (result == TestResult.SKIPPED) { + continue; + } + count++; + if (result == TestResult.SUCCESS) { + successes.add(line); + } else if (result == TestResult.FAILURE) { + failures.add(line); + } else if (result == TestResult.TIMEOUT) { + System.out.println("Parsing took more than " + timeout + + "ms, does your grammar have an infinite loop?"); + } + } + } catch (Exception e) { + System.out.println("Error testing single line RHQL: " + e); + throw e; + } finally { + if (reader != null) { + reader.close(); + } + } + + System.out.println(); + for (String success : successes) { + System.out.println("Parse success: " + success); + } + for (String failure : failures) { + System.out.println("Parse failure: " + failure); + } + + System.out.println(); + System.out.printf("RHQL expressions parsed: %1$s, Failures: %2$s", count, failures.size()); + System.out.println(); + + assert failures.size() == 0; + } + + private TestResult testSuccess(final String line, long timeout) { + if (shouldSkip(line)) { + return TestResult.SKIPPED; // skip empty lines used for visual separation in test file + } + + AntlrTask task = new AntlrTask(line); + Future<TestResult> futureTask = executor.submit(task); + TestResult result = null; + try { + result = futureTask.get(timeout, TimeUnit.MILLISECONDS); + } catch (ExecutionException ee) { + } catch (TimeoutException te) { + futureTask.cancel(true); + result = TestResult.TIMEOUT; + } catch (InterruptedException ie) { + futureTask.cancel(true); + } + + return result; + } + + private boolean shouldSkip(String line) { + line = line.trim(); + if (line.equals("")) { + return true; // ignore empty lines + } + if (line.startsWith("#")) { + return true; // ignore comments + } + return false; + } +}
commit 7369db803076bc12f02bcc6131b9fe1e5303ae47 Author: Joseph Marques joseph@redhat.com Date: Wed Feb 24 01:57:08 2010 -0500
antlr should compile the grammar files before the standard maven compile phase
diff --git a/modules/enterprise/server/jar/pom.xml b/modules/enterprise/server/jar/pom.xml index b322503..533c0e9 100644 --- a/modules/enterprise/server/jar/pom.xml +++ b/modules/enterprise/server/jar/pom.xml @@ -467,10 +467,10 @@ <version>3.2</version> <executions> <execution> - <phase>compile</phase> - <goals> - <goal>antlr</goal> - </goals> + <phase>generate-sources</phase> + <goals> + <goal>antlr</goal> + </goals> <configuration> <conversionTimeout>30000</conversionTimeout> <debug>false</debug>
commit d17f4616c524e65decd6af3db65df8a83d18c12e Author: Joseph Marques joseph@redhat.com Date: Tue Feb 23 16:54:20 2010 -0500
add anltr support to server/jar maven module, update eclipse classpath accordingly;
diff --git a/.classpath b/.classpath index 660d74d..1a35400 100644 --- a/.classpath +++ b/.classpath @@ -19,6 +19,7 @@ <classpathentry kind="src" path="modules/plugins/jboss-cache/src/main/java"/> <classpathentry kind="src" path="modules/plugins/jboss-cache-v3/src/main/java"/> <classpathentry kind="src" path="etc/samples/perspectives/sample-perspective/app/src/main/java"/> + <classpathentry kind="src" path="modules/enterprise/server/jar/target/generated-sources/antlr3"/> <classpathentry kind="var" path="M2_REPO/org/jboss/integration/jboss-profileservice-spi/5.1.0.SP1/jboss-profileservice-spi-5.1.0.SP1.jar"/> <classpathentry kind="var" path="M2_REPO/org/jboss/man/jboss-managed/2.1.1.GA/jboss-managed-2.1.1.GA.jar"/> <classpathentry kind="var" path="M2_REPO/org/jboss/man/jboss-metatype/2.1.1.GA/jboss-metatype-2.1.1.GA.jar"/> diff --git a/modules/enterprise/server/jar/pom.xml b/modules/enterprise/server/jar/pom.xml index e109c41..b322503 100644 --- a/modules/enterprise/server/jar/pom.xml +++ b/modules/enterprise/server/jar/pom.xml @@ -366,6 +366,13 @@ <artifactId>test-utils</artifactId> <version>${version}</version> </dependency> + + <dependency> + <groupId>org.antlr</groupId> + <artifactId>antlr</artifactId> + <version>3.2</version> + <scope>compile</scope> + </dependency> </dependencies>
<build> @@ -454,6 +461,41 @@ </configuration> </plugin>
+ <plugin> + <groupId>org.antlr</groupId> + <artifactId>antlr3-maven-plugin</artifactId> + <version>3.2</version> + <executions> + <execution> + <phase>compile</phase> + <goals> + <goal>antlr</goal> + </goals> + <configuration> + <conversionTimeout>30000</conversionTimeout> + <debug>false</debug> + <dfa>false</dfa> + <nfa>false</nfa> + <excludes> + + </excludes> + <includes> + + </includes> + <libDirectory>src/main/antlr3/imports</libDirectory> + <messageFormat>antlr</messageFormat> + <outputDirectory>target/generated-sources/antlr3</outputDirectory> + <printGrammar>false</printGrammar> + <profile>false</profile> + <report>false</report> + <sourceDirectory>src/main/antlr3</sourceDirectory> + <trace>false</trace> + <verbose>true</verbose> + </configuration> + </execution> + </executions> + </plugin> + </plugins> </build>
commit fc1118af81ff953f2c05fb696629d799169b22f1 Author: Joseph Marques joseph@redhat.com Date: Tue Feb 23 13:21:29 2010 -0500
fix some whitespace issues
diff --git a/modules/enterprise/server/jar/pom.xml b/modules/enterprise/server/jar/pom.xml index f14e145..e109c41 100644 --- a/modules/enterprise/server/jar/pom.xml +++ b/modules/enterprise/server/jar/pom.xml @@ -414,7 +414,7 @@ Build-Jdk=${java.version} Build-OS-Name=${os.name} Build-OS-Version=${os.version} -</echo> + </echo> </tasks> </configuration> <goals> @@ -593,8 +593,7 @@ <link href="../domain" /> <link href="../plugin-api" /> <link href="http://java.sun.com/j2se/1.5.0/docs/api/" /> - <bottom><![CDATA[Copyright © 2008-2009 <a href="http://rhq-project.org/">Red Hat, Inc.</a>. All Rights Reserved. -]]></bottom> + <bottom><![CDATA[Copyright © 2008-2009 <a href="http://rhq-project.org/">Red Hat, Inc.</a>. All Rights Reserved.]]></bottom> </javadoc> </tasks> </configuration>
commit 1c79730a4d3ce260e2f3d6ce9b8e971da158e1d7 Author: Joseph Marques joseph@redhat.com Date: Tue Feb 23 13:10:18 2010 -0500
add some default, global saved searches for the resource context
diff --git a/modules/core/dbutils/src/main/scripts/dbsetup/search-data.xml b/modules/core/dbutils/src/main/scripts/dbsetup/search-data.xml new file mode 100644 index 0000000..1a11114 --- /dev/null +++ b/modules/core/dbutils/src/main/scripts/dbsetup/search-data.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<dbsetup name="search-data.xml"> + + <!-- overlord-created, global, default saved searches --> + <table name="RHQ_SAVED_SEARCH"> + <data ID="1" + CONTEXT="Resource" + NAME="Downed Platforms" + DESCRIPTION="All downed machines across the entire enterprise" + PATTERN="down platform" + LAST_COMPUTE_TIME="0" + SUBJECT="1" + GLOBAL="TRUE" /> + <data ID="2" + CONTEXT="Resource" + NAME="Downed Servers" + DESCRIPTION="All downed servers across the entire enterprise" + PATTERN="down server" + LAST_COMPUTE_TIME="0" + SUBJECT="1" + GLOBAL="TRUE" /> + </table> + +</dbsetup> diff --git a/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml b/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml index 834e9ce..6fe555e 100644 --- a/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml +++ b/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml @@ -3202,13 +3202,18 @@ </statement> </schema-directSQL> </schemaSpec> - + <schemaSpec version="2.82"> <schema-directSQL> <statement desc="Creating table RHQ_SAVED_SEARCH"> CREATE TABLE RHQ_SAVED_SEARCH ( ID INTEGER ) </statement> </schema-directSQL> + <schema-directSQL> + <statement desc="Creating primary key for RHQ_AFFINITY_GROUP"> + ALTER TABLE RHQ_SAVED_SEARCH ADD PRIMARY KEY ( ID ) + </statement> + </schema-directSQL> <schema-addColumn table="RHQ_SAVED_SEARCH" column="CONTEXT" columnType="VARCHAR2" precision="25" /> <schema-alterColumn table="RHQ_SAVED_SEARCH" column="CONTEXT" nullable="FALSE" />
@@ -3227,6 +3232,29 @@ <schema-addColumn table="RHQ_SAVED_SEARCH" column="GLOBAL" columnType="BOOLEAN" /> <schema-alterColumn table="RHQ_SAVED_SEARCH" column="GLOBAL" nullable="FALSE" /> </schemaSpec> + + <schemaSpec version="2.82.1"> + <schema-directSQL> + <statement targetDBVendor="postgresql" desc="Inserting global default saved search 'Downed Platforms'"> + INSERT INTO rhq_saved_search (id, context, name, description, pattern, last_compute_time, subject_id, global) + VALUES (1, 'Resource', 'Downed Platforms', 'All downed machines across the entire enterprise', 'down platform', 0, 1, true) + </statement> + <statement targetDBVendor="oracle" desc="Inserting global default saved search 'Downed Platforms'"> + INSERT INTO rhq_saved_search (id, context, name, description, pattern, last_compute_time, subject_id, global) + VALUES (1, 'Resource', 'Downed Platforms', 'All downed machines across the entire enterprise', 'down platform', 0, 1, 1) + </statement> + </schema-directSQL> + <schema-directSQL> + <statement targetDBVendor="postgresql" desc="Inserting global default saved search 'Downed Servers'"> + INSERT INTO rhq_saved_search (id, context, name, description, pattern, last_compute_time, subject_id, global) + VALUES (2, 'Resource', 'Downed Servers', 'All downed servers across the entire enterprise', 'down server', 0, 1, true) + </statement> + <statement targetDBVendor="oracle" desc="Inserting global default saved search 'Downed Servers'"> + INSERT INTO rhq_saved_search (id, context, name, description, pattern, last_compute_time, subject_id, global) + VALUES (2, 'Resource', 'Downed Servers', 'All downed servers across the entire enterprise', 'down server', 0, 1, 1) + </statement> + </schema-directSQL> + </schemaSpec> </dbupgrade> </target> </project>
commit 68f3634b787768dd5ee138a04a5398fdd4cb6404 Author: Joseph Marques joseph@redhat.com Date: Tue Feb 23 12:07:56 2010 -0500
fix copy/paste error for saved search entity annotation
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/search/SavedSearch.java b/modules/core/domain/src/main/java/org/rhq/core/domain/search/SavedSearch.java index 9142772..d6b50e7 100644 --- a/modules/core/domain/src/main/java/org/rhq/core/domain/search/SavedSearch.java +++ b/modules/core/domain/src/main/java/org/rhq/core/domain/search/SavedSearch.java @@ -87,7 +87,7 @@ public class SavedSearch { @Column(name = "SUBJECT_ID", nullable = false) private Subject subject;
- @Column(name = "RESOURCE_ID", insertable = false, updatable = false) + @Column(name = "SUBJECT_ID", insertable = false, updatable = false) private int subjectId;
@Column(name = "GLOBAL", nullable = false)
commit 2325fa6314a7ebcbf1d932fb42e9f9559e1d079b Author: Joseph Marques joseph@redhat.com Date: Tue Feb 23 03:02:28 2010 -0500
add auto-completion capability for saved search manager to the CLI
diff --git a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClient.java b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClient.java index acafe6f..a1a5b38 100644 --- a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClient.java +++ b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClient.java @@ -54,6 +54,7 @@ import org.rhq.enterprise.server.resource.ResourceFactoryManagerRemote; import org.rhq.enterprise.server.resource.ResourceManagerRemote; import org.rhq.enterprise.server.resource.ResourceTypeManagerRemote; import org.rhq.enterprise.server.resource.group.ResourceGroupManagerRemote; +import org.rhq.enterprise.server.search.SavedSearchManagerRemote; import org.rhq.enterprise.server.support.SupportManagerRemote; import org.rhq.enterprise.server.system.ServerVersion; import org.rhq.enterprise.server.system.SystemManagerRemote; @@ -94,6 +95,7 @@ public class RemoteClient { ResourceGroupManager(ResourceGroupManagerRemote.class), // ResourceTypeManager(ResourceTypeManagerRemote.class), // RoleManager(RoleManagerRemote.class), // + SavedSearchManager(SavedSearchManagerRemote.class), // SubjectManager(SubjectManagerRemote.class), // SupportManager(SupportManagerRemote.class), // SystemManager(SystemManagerRemote.class) //
commit 9bfad0211e66635e09d02d8959a81db33f6175b7 Author: Joseph Marques joseph@redhat.com Date: Tue Feb 23 02:51:14 2010 -0500
add LookupUtil method to retrieve the saved search manager from the UI layer
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/util/LookupUtil.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/util/LookupUtil.java index dc9ae11..c21526a 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/util/LookupUtil.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/util/LookupUtil.java @@ -165,6 +165,8 @@ import org.rhq.enterprise.server.resource.metadata.ResourceMetadataManagerBean; import org.rhq.enterprise.server.resource.metadata.ResourceMetadataManagerLocal; import org.rhq.enterprise.server.scheduler.SchedulerBean; import org.rhq.enterprise.server.scheduler.SchedulerLocal; +import org.rhq.enterprise.server.search.SavedSearchManagerBean; +import org.rhq.enterprise.server.search.SavedSearchManagerLocal; import org.rhq.enterprise.server.subsystem.AlertSubsystemManagerBean; import org.rhq.enterprise.server.subsystem.AlertSubsystemManagerLocal; import org.rhq.enterprise.server.subsystem.ConfigurationSubsystemManagerBean; @@ -516,6 +518,10 @@ public final class LookupUtil { return lookupLocal(SchedulerBean.class); }
+ public static SavedSearchManagerLocal getSavedSearchManager() { + return lookupLocal(SavedSearchManagerBean.class); + } + public static SubjectManagerLocal getSubjectManager() { return lookupLocal(SubjectManagerBean.class); }
commit 7b68ec1372bd5af42793b5d15020e7b2f66bb931 Author: Joseph Marques joseph@redhat.com Date: Tue Feb 23 02:49:08 2010 -0500
add criteria-based retrieval for the saved search manager
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/criteria/SavedSearchCriteria.java b/modules/core/domain/src/main/java/org/rhq/core/domain/criteria/SavedSearchCriteria.java new file mode 100644 index 0000000..bd70b6f --- /dev/null +++ b/modules/core/domain/src/main/java/org/rhq/core/domain/criteria/SavedSearchCriteria.java @@ -0,0 +1,142 @@ +/* + * RHQ Management Platform + * Copyright (C) 2005-2008 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation, and/or the GNU Lesser + * General Public License, version 2.1, also as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.rhq.core.domain.criteria; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; + +import org.rhq.core.domain.search.SavedSearch; +import org.rhq.core.domain.search.SearchContext; +import org.rhq.core.domain.util.PageOrdering; + +/** + * @author Joseph Marques + */ +@XmlAccessorType(XmlAccessType.FIELD) +@SuppressWarnings("unused") +public class SavedSearchCriteria extends Criteria { + public static final long serialVersionUID = 1L; + + private Integer filterId; + private SearchContext filterContext; + private String filterName; + private String filterDescription; + private String filterPattern; + private Long filterLastComputeTimeMin; + private Long filterLastComputeTimeMax; + private Long filterResultCountMin; + private Long filterResultCountMax; + private Integer filterSubjectId; + private Boolean filterGlobal; + + private boolean fetchSubject; + + private PageOrdering sortContext; + private PageOrdering sortName; + private PageOrdering sortLastComputeTime; + private PageOrdering sortResultCount; + private PageOrdering sortGlobal; + + public SavedSearchCriteria() { + super(SavedSearch.class); + + filterOverrides.put("lastComputeTimeMin", "lastComputeTime >= ?"); + filterOverrides.put("lastComputeTimeMax", "lastComputeTime <= ?"); + filterOverrides.put("resultCountMin", "resultCount >= ?"); + filterOverrides.put("resultCountMax", "resultCount <= ?"); + } + + public void addFilterId(Integer filterId) { + this.filterId = filterId; + } + + public void addFilterSearchContext(SearchContext filterContext) { + this.filterContext = filterContext; + } + + public void addFilterName(String filterName) { + this.filterName = filterName; + } + + public void addFilterDescription(String filterDescription) { + this.filterDescription = filterDescription; + } + + public void addFilterPattern(String filterPattern) { + this.filterPattern = filterPattern; + } + + public void addFilterResultCountMin(Long filterResultCountMin) { + this.filterResultCountMin = filterResultCountMin; + } + + public void addFilterResultCountMax(Long filterResultCountMax) { + this.filterResultCountMax = filterResultCountMax; + } + + public void addFilterLastComputeTimeMin(Long filterLastComputeTimeMin) { + this.filterLastComputeTimeMin = filterLastComputeTimeMin; + } + + public void addFilterLastComputeTimeMax(Long filterLastComputeTimeMax) { + this.filterLastComputeTimeMax = filterLastComputeTimeMax; + } + + public void addFilterSubjectId(Integer filterSubjectId) { + this.filterSubjectId = filterSubjectId; + } + + public void addFilterGlobal(Boolean filterGlobal) { + this.filterGlobal = filterGlobal; + } + + public void setFetchSubject(boolean fetchSubject) { + this.fetchSubject = fetchSubject; + } + + public void addSortContext(PageOrdering sortContext) { + addSortField("context"); + this.sortContext = sortContext; + } + + public void addSortName(PageOrdering sortName) { + addSortField("name"); + this.sortName = sortName; + } + + public void addSortLastComputeTime(PageOrdering sortLastComputeTime) { + addSortField("lastComputeTime"); + this.sortLastComputeTime = sortLastComputeTime; + } + + public void addSortResultCount(PageOrdering sortResultCount) { + addSortField("resultCount"); + this.sortResultCount = sortResultCount; + } + + public void addSortGlobal(PageOrdering sortGlobal) { + addSortField("global"); + this.sortGlobal = sortGlobal; + } + +} diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerBean.java index f672ac7..1485197 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerBean.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerBean.java @@ -24,10 +24,14 @@ import javax.persistence.EntityManager; import javax.persistence.PersistenceContext;
import org.rhq.core.domain.auth.Subject; +import org.rhq.core.domain.criteria.SavedSearchCriteria; import org.rhq.core.domain.search.SavedSearch; +import org.rhq.core.domain.util.PageList; import org.rhq.enterprise.server.RHQConstants; import org.rhq.enterprise.server.authz.AuthorizationManagerLocal; import org.rhq.enterprise.server.authz.PermissionException; +import org.rhq.enterprise.server.util.CriteriaQueryGenerator; +import org.rhq.enterprise.server.util.CriteriaQueryRunner;
/** * This bean provides functionality to CRUD saved search patterns. @@ -79,6 +83,18 @@ public class SavedSearchManagerBean implements SavedSearchManagerLocal /* local return savedSearch; }
+ public PageList<SavedSearch> findSavedSearchesByCriteria(Subject subject, SavedSearchCriteria criteria) { + CriteriaQueryGenerator generator = new CriteriaQueryGenerator(criteria); + + if (!authorizationManager.isInventoryManager(subject)) { + generator.setAuthorizationCustomConditionFragment("(subject.id=" + subject.getId() + " OR global=true)"); + } + + CriteriaQueryRunner<SavedSearch> queryRunner = new CriteriaQueryRunner<SavedSearch>(criteria, generator, + entityManager); + return queryRunner.execute(); + } + private void validateManipulatePermission(Subject subject, SavedSearch savedSearch) { if (savedSearch.isGlobal()) { if (!authorizationManager.isInventoryManager(subject)) { diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerRemote.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerRemote.java index a1615ec..ffd4255 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerRemote.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerRemote.java @@ -25,7 +25,9 @@ import javax.jws.WebService; import javax.jws.soap.SOAPBinding;
import org.rhq.core.domain.auth.Subject; +import org.rhq.core.domain.criteria.SavedSearchCriteria; import org.rhq.core.domain.search.SavedSearch; +import org.rhq.core.domain.util.PageList; import org.rhq.enterprise.server.authz.PermissionException; import org.rhq.enterprise.server.system.ServerVersion;
@@ -99,4 +101,18 @@ public interface SavedSearchManagerRemote { public SavedSearch getSavedSearchById( // @WebParam(name = "subject") Subject subject, // @WebParam(name = "savedSearchId") int savedSearchId); + + /** + * Returns the {@link PageList} of {@link SavedSearch} entities that match the criteria filters that are visible + * to the user + * + * @param subject the logged in user requesting the {@link PageList} of {@link SavedSearch} to be returned + * @param criteria the {@link SavedSearchCriteria} object that will filter the returned results + * + * @return the {@link PageList} of {@link SavedSearch} entities that match the criteria filters, an empty list + * will be returned if no results were found or none matches the given filters + */ + public PageList<SavedSearch> findSavedSearchesByCriteria( // + @WebParam(name = "subject") Subject subject, // + @WebParam(name = "criteria") SavedSearchCriteria criteria); }
commit 8bee964f9c9fb494fc08a3baaa307361d826b445 Author: Joseph Marques joseph@redhat.com Date: Tue Feb 23 02:48:32 2010 -0500
enhance the CriteriaQueryGenerator to allow more flexible authorization:
support users to set a completely arbitrary authorization fragment, which will be inserted into the list of conditions for the generated query
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/util/CriteriaQueryGenerator.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/util/CriteriaQueryGenerator.java index 9c62eed..bb0c072 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/util/CriteriaQueryGenerator.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/util/CriteriaQueryGenerator.java @@ -58,13 +58,14 @@ public final class CriteriaQueryGenerator {
public enum AuthorizationTokenType { RESOURCE, // specifies the resource alias to join on for standard res-group-role-subject authorization checking - GROUP; // specifies the group alias to join on for standard group-role-subject authorization checking + GROUP; // specifies the group alias to join on for standard group-role-subject authorization checking }
private Criteria criteria;
private String authorizationJoinFragment; private String authorizationPermsFragment; + private String authorizationCustomConditionFragment; private int authorizationSubjectId;
private String alias; @@ -89,6 +90,10 @@ public final class CriteriaQueryGenerator { this.alias = aliasBuilder.toString(); }
+ public void setAuthorizationCustomConditionFragment(String fragment) { + this.authorizationCustomConditionFragment = fragment; + } + public void setAuthorizationResourceFragment(AuthorizationTokenType type, int subjectId) { String defaultFragment = null; if (type == AuthorizationTokenType.RESOURCE) { @@ -260,6 +265,16 @@ public final class CriteriaQueryGenerator { } }
+ if (authorizationCustomConditionFragment != null) { + if (firstCrit) { + firstCrit = false; + } else { + // always want AND for security, regardless of conjunctiveFragment + results.append(NL).append(" AND "); + } + results.append(this.authorizationCustomConditionFragment); + } + if (countQuery == false) { boolean overridden = true; PageControl pc = criteria.getPageControlOverrides();
commit d065f478b829887eda067308a10f98edef113b66 Author: Joseph Marques joseph@redhat.com Date: Tue Feb 23 00:50:35 2010 -0500
initial SLSB support basic CRUD, with security restrictions, for saved search patterns
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerBean.java new file mode 100644 index 0000000..f672ac7 --- /dev/null +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerBean.java @@ -0,0 +1,104 @@ +/* + * RHQ Management Platform + * Copyright (C) 2005-2008 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +package org.rhq.enterprise.server.search; + +import javax.ejb.EJB; +import javax.ejb.Stateless; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.rhq.core.domain.auth.Subject; +import org.rhq.core.domain.search.SavedSearch; +import org.rhq.enterprise.server.RHQConstants; +import org.rhq.enterprise.server.authz.AuthorizationManagerLocal; +import org.rhq.enterprise.server.authz.PermissionException; + +/** + * This bean provides functionality to CRUD saved search patterns. + * + * @author Joseoh Marques + */ +@Stateless +public class SavedSearchManagerBean implements SavedSearchManagerLocal /* local already implements remote interface */{ + + @PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME) + private EntityManager entityManager; + + @EJB + private AuthorizationManagerLocal authorizationManager; + + /** + * @see SavedSearchManagerRemote#createSavedSearch(Subject, SavedSearch) + */ + public void createSavedSearch(Subject subject, SavedSearch savedSearch) { + validateManipulatePermission(subject, savedSearch); + entityManager.persist(savedSearch); + return; + } + + /** + * @see SavedSearchManagerRemote#updateSavedSearch(Subject, SavedSearch) + */ + public void updateSavedSearch(Subject subject, SavedSearch savedSearch) { + validateManipulatePermission(subject, savedSearch); + entityManager.merge(savedSearch); + return; + } + + /** + * @see SavedSearchManagerRemote#deleteSavedSearch(Subject, int) + */ + public void deleteSavedSearch(Subject subject, int savedSearchId) { + SavedSearch savedSearch = entityManager.find(SavedSearch.class, savedSearchId); + validateManipulatePermission(subject, savedSearch); + entityManager.remove(savedSearch); + } + + /** + * @see SavedSearchManagerRemote#getSavedSearchById(Subject, int) + */ + public SavedSearch getSavedSearchById(Subject subject, int savedSearchId) { + SavedSearch savedSearch = entityManager.find(SavedSearch.class, savedSearchId); + validateReadPermission(subject, savedSearch); + return savedSearch; + } + + private void validateManipulatePermission(Subject subject, SavedSearch savedSearch) { + if (savedSearch.isGlobal()) { + if (!authorizationManager.isInventoryManager(subject)) { + throw new PermissionException("Only inventory managers can manipulate global saved searches"); + } + // note: inventory managers can modify any saved search pattern, not just their own + } else { + if (subject.equals(savedSearch.getSubject())) { + throw new PermissionException("Users without inventory manager permission " + + "can only manipulate their own saved searches"); + } + } + } + + private void validateReadPermission(Subject subject, SavedSearch savedSearch) { + if (!savedSearch.isGlobal()) { + if (subject.equals(savedSearch.getSubject())) { + throw new PermissionException("Users without inventory manager permission " + + "can only view their own or global saved saved searches"); + } + } + } +} diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerLocal.java new file mode 100644 index 0000000..7ced552 --- /dev/null +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerLocal.java @@ -0,0 +1,31 @@ +/* + * RHQ Management Platform + * Copyright (C) 2005-2008 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +package org.rhq.enterprise.server.search; + +import javax.ejb.Local; + +/** + * The local interface to the SavedSearchManager. + * + * @author Joseph Marques + */ +@Local +public interface SavedSearchManagerLocal extends SavedSearchManagerRemote { + // there are currently no methods in the local interface that do not exist in the remote interface +} diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerRemote.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerRemote.java new file mode 100644 index 0000000..a1615ec --- /dev/null +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/search/SavedSearchManagerRemote.java @@ -0,0 +1,102 @@ +/* + * RHQ Management Platform + * Copyright (C) 2005-2008 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +package org.rhq.enterprise.server.search; + +import javax.ejb.Remote; +import javax.jws.WebMethod; +import javax.jws.WebParam; +import javax.jws.WebService; +import javax.jws.soap.SOAPBinding; + +import org.rhq.core.domain.auth.Subject; +import org.rhq.core.domain.search.SavedSearch; +import org.rhq.enterprise.server.authz.PermissionException; +import org.rhq.enterprise.server.system.ServerVersion; + +/** + * The remote interface to the SavedSearchManager. + * + * @author Joseph Marques + */ +@SOAPBinding(style = SOAPBinding.Style.DOCUMENT) +@WebService(targetNamespace = ServerVersion.namespace) +@Remote +public interface SavedSearchManagerRemote { + + /** + * Persisted a new {@link SavedSearch} with the given primary key + * + * @param subject the logged in user requesting the {@link SavedSearch} deletion + * @param savedSearchId the primary key of the {@link SavedSearch} to be deleted + * + * @throws PermissionException if the user is not authorized to create the {@link SavedSearch}. Only inventory + * managers can create global saved searches. Regular users can only create {@link SavedSearch}es against + * their own accounts. + */ + @WebMethod + public void createSavedSearch( // + @WebParam(name = "subject") Subject subject, // + @WebParam(name = "savedSearch") SavedSearch savedSearch); + + /** + * Saves all changes to the passed {@link SavedSearch} database, correlating it to the record already + * persisted with the same primary key + * + * @param subject the logged in user requesting the {@link SavedSearch} persisted modification + * @param savedSearchId the {@link SavedSearch} which will have its modifications persisted + *. + * @throws PermissionException if the user is not authorized to modify the {@link SavedSearch}. Only inventory + * managers can update global saved searches. Regular users can only update {@link SavedSearch}es from + * their own accounts. + */ + @WebMethod + public void updateSavedSearch( // + @WebParam(name = "subject") Subject subject, // + @WebParam(name = "savedSearch") SavedSearch savedSearch); + + /** + * Deletes the {@link SavedSearch} with the given primary key + * + * @param subject the logged in user requesting the {@link SavedSearch} deletion + * @param savedSearchId the primary key of the {@link SavedSearch} to be deleted + * + * @throws PermissionException if the user is not authorized to delete the {@link SavedSearch}. Only inventory + * managers can delete global saved searches. Regular users can only delete {@link SavedSearch}es from + * their own accounts. + */ + @WebMethod + public void deleteSavedSearch( // + @WebParam(name = "subject") Subject subject, // + @WebParam(name = "savedSearchId") int savedSearchId); + + /** + * Returns the {@link SavedSearch} with the given primary key + * + * @param subject the logged in user requesting the {@link SavedSearch} to be loaded + * @param savedSearchId the primary key of the {@link SavedSearch} to be loaded + * + * @return the {@link SavedSearch} or <code>null</code> if it wasn't found + * @throws PermissionException if the user is not authorized to view the {@link SavedSearch}. Regular users can + * only view {@link SavedSearch}es from their own accounts. + */ + @WebMethod + public SavedSearch getSavedSearchById( // + @WebParam(name = "subject") Subject subject, // + @WebParam(name = "savedSearchId") int savedSearchId); +}
commit c95388946134a47b002f5821c3a168db6f549355 Author: Joseph Marques joseph@redhat.com Date: Mon Feb 22 21:06:31 2010 -0500
saved search hibernate entities
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/search/SavedSearch.java b/modules/core/domain/src/main/java/org/rhq/core/domain/search/SavedSearch.java new file mode 100644 index 0000000..9142772 --- /dev/null +++ b/modules/core/domain/src/main/java/org/rhq/core/domain/search/SavedSearch.java @@ -0,0 +1,253 @@ +/* + * RHQ Management Platform + * Copyright (C) 2005-2008 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation, and/or the GNU Lesser + * General Public License, version 2.1, also as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.rhq.core.domain.search; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; + +import org.rhq.core.domain.auth.Subject; + +/** + * The data model for saved searches. Each users has his or her own set of + * saved searches, but inventory managers are allowed to "promote" their + * saved searches to GLOBAL status, which makes them available to all users + * under that installation of RHQ. + * + * There are two levels of pre-computed data within this structured. After + * the {@link SavedSearch} is created, the pattern will be translated into its + * JPQL equivalent and stored. If the {@link SavedSearch} is ever modified + * and saved, the JPQL will be re-computed. + * + * Periodically, the count-query version of the {@link SavedSearch} will be + * executed (using the stored JPQL), and the number of matching records will + * be stored. If this {@link SavedSearch} ever needs to be displayed on the + * user interface, it will by default retrieve this cached result count. + * + * @author Joseph Marques + */ +@Entity +@SequenceGenerator(name = "id", sequenceName = "RHQ_SAVED_SEARCH_ID_SEQ") +@Table(name = "RHQ_SAVED_SEARCH") +public class SavedSearch { + + @Id + @Column(name = "ID", nullable = false) + @GeneratedValue(strategy = GenerationType.AUTO, generator = "id") + private Integer id; + + @Column(name = "CONTEXT", nullable = false) + @Enumerated(EnumType.STRING) + private SearchContext context; + + @Column(name = "NAME") + private String name; + + @Column(name = "DESCRIPTION") + private String description; + + @Column(name = "PATTERN", nullable = false) + private String pattern; + + @Column(name = "JPQL_TRANSLATION") + private String jpqlTranslation; + + @Column(name = "LAST_COMPUTE_TIME", nullable = false) + private long lastComputeTime; + + @Column(name = "RESULT_COUNT") + private Long resultCount; + + @Column(name = "SUBJECT_ID", nullable = false) + private Subject subject; + + @Column(name = "RESOURCE_ID", insertable = false, updatable = false) + private int subjectId; + + @Column(name = "GLOBAL", nullable = false) + private boolean global; + + protected SavedSearch() { + // no-arg ctor for Hibernate + } + + public SavedSearch(SearchContext context, String name, String pattern, Subject subject) { + // call setters to go through parameter validation + setContext(context); + setPattern(pattern); + setSubject(subject); + setName(name); // name can be null, to allow for saving searches quickly + + this.description = null; + this.jpqlTranslation = null; // null value for pre-computed JPQL implies computation is needed + this.lastComputeTime = 0; // further imply that computation needs to occur + this.resultCount = null; // NULL resultCount implies either computation failed or hasn't begun yet + this.global = false; // user must promote saved search to be a global after creation + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public SearchContext getContext() { + return context; + } + + private void setContext(SearchContext context) { + if (context == null) { + throw new IllegalArgumentException("All saved searches must be bound to a SearchContext"); + } + this.context = context; + } + + public String getName() { + return name; + } + + private void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + if (pattern == null || pattern.trim().equals("")) { + throw new IllegalArgumentException("All saved searches must have a non-empty pattern"); + } + this.pattern = pattern; + } + + public String getJpqlTranslation() { + return jpqlTranslation; + } + + public void setJpqlTransation(String jpqlTranslation) { + this.jpqlTranslation = jpqlTranslation; + } + + public long getLastComputeTime() { + return lastComputeTime; + } + + public void setLastComputeTime(long lastComputeTime) { + this.lastComputeTime = lastComputeTime; + } + + public Long getResultCount() { + return resultCount; + } + + public void setResultCount(Long resultCount) { + this.resultCount = resultCount; + } + + public Subject getSubject() { + return subject; + } + + private void setSubject(Subject subject) { + if (subject == null) { + throw new IllegalArgumentException("All saved searches must be owned by a specific user"); + } + this.subject = subject; + this.subjectId = subject.getId(); + } + + public int getSubjectId() { + return subjectId; + } + + public boolean isGlobal() { + return global; + } + + public void setGlobal(boolean global) { + this.global = global; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + subjectId; + result = (prime * result) + context.hashCode(); + result = (prime * result) + ((name == null) ? 0 : name.hashCode()); + result = (prime * result) + pattern.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if ((obj == null) || (!(obj instanceof SavedSearch))) { + return false; + } + + final SavedSearch other = (SavedSearch) obj; + + if (subjectId != other.subjectId) { + return false; + } + + if (context != other.context) { + return false; + } + + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + + if (!pattern.equals(other.pattern)) { + return false; + } + + return true; + } +} diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/search/SearchContext.java b/modules/core/domain/src/main/java/org/rhq/core/domain/search/SearchContext.java new file mode 100644 index 0000000..bb045d5 --- /dev/null +++ b/modules/core/domain/src/main/java/org/rhq/core/domain/search/SearchContext.java @@ -0,0 +1,38 @@ +/* + * RHQ Management Platform + * Copyright (C) 2005-2008 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation, and/or the GNU Lesser + * General Public License, version 2.1, also as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.rhq.core.domain.search; + +/** + * The search mechanism is generic and, thus, suited to finding data in any + * RHQ subsystem, including its own. Each individual page in the UI can have + * different advanced syntax. This enum will serve as a unique identifier + * for which context is currently active. You can think of it as a piece of + * meta-data that annotates each saved search, representing which context it + * belongs to. This will also affect which grammar is used to auto-complete + * search expressions. + * + * @author Joseph Marques + */ +public enum SearchContext { + Resource, Group; +}
commit c9d080cc510d6e4bf5c26c34f7b9050544736da3 Author: Joseph Marques joseph@redhat.com Date: Mon Feb 22 21:06:19 2010 -0500
saved search dbupgrade scripts
diff --git a/modules/core/dbutils/pom.xml b/modules/core/dbutils/pom.xml index 1d4d3dd..2fb6a90 100644 --- a/modules/core/dbutils/pom.xml +++ b/modules/core/dbutils/pom.xml @@ -22,7 +22,7 @@
<properties> <scm.module.path>modules/core/dbutils/</scm.module.path> - <db.schema.version>2.79</db.schema.version> + <db.schema.version>2.82</db.schema.version> </properties>
<dependencies> diff --git a/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml b/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml index 9af6aa5..834e9ce 100644 --- a/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml +++ b/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml @@ -3202,6 +3202,31 @@ </statement> </schema-directSQL> </schemaSpec> + + <schemaSpec version="2.82"> + <schema-directSQL> + <statement desc="Creating table RHQ_SAVED_SEARCH"> + CREATE TABLE RHQ_SAVED_SEARCH ( ID INTEGER ) + </statement> + </schema-directSQL> + <schema-addColumn table="RHQ_SAVED_SEARCH" column="CONTEXT" columnType="VARCHAR2" precision="25" /> + <schema-alterColumn table="RHQ_SAVED_SEARCH" column="CONTEXT" nullable="FALSE" /> + + <schema-addColumn table="RHQ_SAVED_SEARCH" column="NAME" columnType="VARCHAR2" precision="200" /> + <schema-addColumn table="RHQ_SAVED_SEARCH" column="DESCRIPTION" columnType="VARCHAR2" precision="500" /> + <schema-addColumn table="RHQ_SAVED_SEARCH" column="PATTERN" columnType="VARCHAR2" precision="1000" /> + <schema-alterColumn table="RHQ_SAVED_SEARCH" column="PATTERN" nullable="FALSE" /> + + <schema-addColumn table="RHQ_SAVED_SEARCH" column="JPQL_TRANSLATION" columnType="VARCHAR2" precision="4000" /> + <schema-addColumn table="RHQ_SAVED_SEARCH" column="LAST_COMPUTE_TIME" columnType="LONG" /> + <schema-alterColumn table="RHQ_SAVED_SEARCH" column="LAST_COMPUTE_TIME" nullable="FALSE" /> + <schema-addColumn table="RHQ_SAVED_SEARCH" column="RESULT_COUNT" columnType="LONG" /> + + <schema-addColumn table="RHQ_SAVED_SEARCH" column="SUBJECT_ID" columnType="INTEGER" /> + <schema-alterColumn table="RHQ_SAVED_SEARCH" column="SUBJECT_ID" nullable="FALSE" /> + <schema-addColumn table="RHQ_SAVED_SEARCH" column="GLOBAL" columnType="BOOLEAN" /> + <schema-alterColumn table="RHQ_SAVED_SEARCH" column="GLOBAL" nullable="FALSE" /> + </schemaSpec> </dbupgrade> </target> </project>
commit afc78a9b2bfcb14b5b079ffb29aa82c4e6b0e81f Author: Joseph Marques joseph@redhat.com Date: Mon Feb 22 21:06:07 2010 -0500
saved searches dbsetup scripts
diff --git a/modules/core/dbutils/src/main/scripts/dbsetup-build.xml b/modules/core/dbutils/src/main/scripts/dbsetup-build.xml index 047d615..f5e4eae 100644 --- a/modules/core/dbutils/src/main/scripts/dbsetup-build.xml +++ b/modules/core/dbutils/src/main/scripts/dbsetup-build.xml @@ -39,7 +39,7 @@ To run the default target, you must set one of the following properties to true: <property name="dbsetup.scripts.dir" value="${basedir}/src/main/scripts/dbsetup" /> <property name="dbsetup.output.dir" value="${basedir}/target/dbsetup" /> <!-- away from classes so it doesn't go into the jar --> <!-- define the setup creation processing order. note, removals are done in the reverse order --> - <property name="dbsetup.subsystems" value="config,cluster,inventory,auth,authz,operation,event,alert,sysconfig,scheduler,amps,measurement,content,resource-request,jms,obsolete"/> + <property name="dbsetup.subsystems" value="search,config,cluster,inventory,auth,authz,operation,event,alert,sysconfig,scheduler,amps,measurement,content,resource-request,jms,obsolete"/> <property name="dbsetup.combined.schema" value="${dbsetup.output.dir}/all-schema.xml" /> <property name="dbsetup.combined.data" value="${dbsetup.output.dir}/all-data.xml" /> <property name="dbsetup.tstamp.file" value="${dbsetup.output.dir}/dbsetup-combine.tstamp" /> diff --git a/modules/core/dbutils/src/main/scripts/dbsetup/search-schema.xml b/modules/core/dbutils/src/main/scripts/dbsetup/search-schema.xml new file mode 100644 index 0000000..7c0de23 --- /dev/null +++ b/modules/core/dbutils/src/main/scripts/dbsetup/search-schema.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<dbsetup name="amps-schema.xml" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="urn:xmlns:rhq-dbsetup-schema"> + + <table name="RHQ_SAVED_SEARCH"> + <column name="ID" default="sequence-only" initial="10001" primarykey="true" required="true" type="INTEGER"/> + <column name="CONTEXT" type="VARCHAR2" size="25" required="true"/> + + <column name="NAME" type="VARCHAR2" size="200" required="true"/> + <column name="DESCRIPTION" type="VARCHAR2" size="500" required="false"/> + <column name="PATTERN" type="VARCHAR2" size="1000" required="true"/> + + <column name="JPQL_TRANSLATION" type="VARCHAR2" size="4000" required="false"/> + <column name="LAST_COMPUTE_TIME" type="LONG" required="true"/> + <column name="RESULT_COUNT" type="LONG" required="false"/> + + <column name="SUBJECT_ID" type="INTEGER" references="RHQ_SUBJECT" required="true"/> + <column name="ENABLED" type="BOOLEAN" required="true"/> + </table> + +</dbsetup> \ No newline at end of file
commit 6d87241b69c095bd2928f4e3b03bc9dcfbeeefd1 Author: Heiko W. Rupp pilhuhn@fedorapeople.org Date: Sat Feb 20 10:49:01 2010 +0100
Fix potential NPE
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/alert/notification/AlertNotificationLog.java b/modules/core/domain/src/main/java/org/rhq/core/domain/alert/notification/AlertNotificationLog.java index 5ed6d4f..b90def3 100644 --- a/modules/core/domain/src/main/java/org/rhq/core/domain/alert/notification/AlertNotificationLog.java +++ b/modules/core/domain/src/main/java/org/rhq/core/domain/alert/notification/AlertNotificationLog.java @@ -113,8 +113,8 @@ public class AlertNotificationLog implements Serializable { @PrePersist @PreUpdate public void trimMessage() { - if (message.length()>255) - message = message.substring(0,254); + if (message!=null && message.length()>255) + message = message.substring(0,255); }
protected AlertNotificationLog() {
commit 2d38d85bb4500c1a873aba7b193fb9984d00539d Author: Heiko W. Rupp pilhuhn@fedorapeople.org Date: Sat Feb 20 09:47:10 2010 +0100
Truncate the message at 140 chars, as identi.ca bails out on longer messages; seems to improve stability with Twitter too. Introduce abbreviated versions of the messages as space on Microblog etc is scarce. BZ 555091
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/alert/notification/AlertNotificationLog.java b/modules/core/domain/src/main/java/org/rhq/core/domain/alert/notification/AlertNotificationLog.java index ba75940..5ed6d4f 100644 --- a/modules/core/domain/src/main/java/org/rhq/core/domain/alert/notification/AlertNotificationLog.java +++ b/modules/core/domain/src/main/java/org/rhq/core/domain/alert/notification/AlertNotificationLog.java @@ -38,6 +38,8 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; +import javax.persistence.PrePersist; +import javax.persistence.PreUpdate; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import javax.persistence.Transient; @@ -108,6 +110,13 @@ public class AlertNotificationLog implements Serializable { @Transient transient List<String> transientEmails = new ArrayList<String>();
+ @PrePersist + @PreUpdate + public void trimMessage() { + if (message.length()>255) + message = message.substring(0,254); + } + protected AlertNotificationLog() { } // JPA
@@ -130,7 +139,6 @@ public class AlertNotificationLog implements Serializable {
public AlertNotificationLog(Alert alert, String senderName, ResultState state, String message) { this.alert = alert; - this.sender = sender; this.resultState = state; this.message = message;
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/AlertManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/AlertManagerBean.java index 454b9a9..ff4025f 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/AlertManagerBean.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/AlertManagerBean.java @@ -780,7 +780,7 @@ public class AlertManagerBean implements AlertManagerLocal, AlertManagerRemote { Map<String, String> alertMessage = emailManager.getAlertEmailMessage( prettyPrintResourceHierarchy(alertDefinition.getResource()), alertDefinition.getResource().getName(), alertDefinition.getName(), alertDefinition.getPriority().toString(), new Date(alert.getCtime()).toString(), - prettyPrintAlertConditions(alert.getConditionLogs()), prettyPrintAlertURL(alert)); + prettyPrintAlertConditions(alert.getConditionLogs(), false), prettyPrintAlertURL(alert)); String messageSubject = alertMessage.keySet().iterator().next(); String messageBody = alertMessage.values().iterator().next();
@@ -834,13 +834,14 @@ public class AlertManagerBean implements AlertManagerLocal, AlertManagerRemote { /** * Create a human readable description of the conditions that led to this alert. * @param alert Alert to create human readable condition description + * @param shortVersion if true the messages printed are abbreviated to save space * @return human readable condition log */ - public String prettyPrintAlertConditions(Alert alert) { - return prettyPrintAlertConditions(alert.getConditionLogs()); + public String prettyPrintAlertConditions(Alert alert, boolean shortVersion) { + return prettyPrintAlertConditions(alert.getConditionLogs(), shortVersion); }
- private String prettyPrintAlertConditions(Set<AlertConditionLog> conditionLogs) { + private String prettyPrintAlertConditions(Set<AlertConditionLog> conditionLogs, boolean shortVersion) { StringBuilder builder = new StringBuilder();
int conditionCounter = 1; @@ -858,16 +859,27 @@ public class AlertManagerBean implements AlertManagerLocal, AlertManagerRemote {
builder.append(NEW_LINE);
- builder.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_EMAIL_CONDITION_LOG_FORMAT, - conditionCounter, prettyPrintAlertCondition(aLog.getCondition()), new SimpleDateFormat( - "yyyy/MM/dd HH:mm:ss z").format(new Date(aLog.getCtime())), formattedValue)); + String format; + if (shortVersion) + format = AlertI18NResourceKeys.ALERT_EMAIL_CONDITION_LOG_FORMAT_SHORT; + else + format = AlertI18NResourceKeys.ALERT_EMAIL_CONDITION_LOG_FORMAT; + SimpleDateFormat dateFormat; + if (shortVersion) + dateFormat= new SimpleDateFormat( + "yy/MM/dd HH:mm:ss z"); + else + dateFormat= new SimpleDateFormat( + "yyyy/MM/dd HH:mm:ss z"); + builder.append(AlertI18NFactory.getMessage(format, + conditionCounter, prettyPrintAlertCondition(aLog.getCondition(), shortVersion), dateFormat.format(new Date(aLog.getCtime())), formattedValue)); conditionCounter++; }
return builder.toString(); }
- private String prettyPrintAlertCondition(AlertCondition condition) { + private String prettyPrintAlertCondition(AlertCondition condition, boolean shortVersion) { StringBuilder builder = new StringBuilder();
AlertConditionCategory category = condition.getCategory(); @@ -915,19 +927,38 @@ public class AlertManagerBean implements AlertManagerLocal, AlertManagerRemote { } } else if ((category == AlertConditionCategory.RESOURCE_CONFIG) || (category == AlertConditionCategory.CHANGE) || (category == AlertConditionCategory.TRAIT)) { - builder.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_CURRENT_LIST_VALUE_CHANGED)); + + if (shortVersion) + builder.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_CURRENT_LIST_VALUE_CHANGED_SHORT)); + else + builder.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_CURRENT_LIST_VALUE_CHANGED)); + } else if (category == AlertConditionCategory.EVENT) { if ((condition.getOption() != null) && (condition.getOption().length() > 0)) { + String propsCbEventSeverityRegexMatch; + if (shortVersion) + propsCbEventSeverityRegexMatch = AlertI18NResourceKeys.ALERT_CONFIG_PROPS_CB_EVENT_SEVERITY_REGEX_MATCH_SHORT; + else + propsCbEventSeverityRegexMatch = AlertI18NResourceKeys.ALERT_CONFIG_PROPS_CB_EVENT_SEVERITY_REGEX_MATCH; + builder.append(AlertI18NFactory.getMessage( - AlertI18NResourceKeys.ALERT_CONFIG_PROPS_CB_EVENT_SEVERITY_REGEX_MATCH, condition.getName(), + propsCbEventSeverityRegexMatch, condition.getName(), condition.getOption())); } else { - builder.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_CONFIG_PROPS_CB_EVENT_SEVERITY, - condition.getName())); + if (shortVersion) + builder.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_CONFIG_PROPS_CB_EVENT_SEVERITY_SHORT, + condition.getName())); + else + builder.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_CONFIG_PROPS_CB_EVENT_SEVERITY, + condition.getName())); } } else if (category == AlertConditionCategory.AVAILABILITY) { - builder.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_CONFIG_PROPS_CB_AVAILABILITY, - condition.getOption())); + if (shortVersion) + builder.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_CONFIG_PROPS_CB_AVAILABILITY_SHORT, + condition.getOption())); + else + builder.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_CONFIG_PROPS_CB_AVAILABILITY, + condition.getOption())); } else { // do nothing } diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/AlertManagerLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/AlertManagerLocal.java index 996f245..e482ffa 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/AlertManagerLocal.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/AlertManagerLocal.java @@ -96,9 +96,10 @@ public interface AlertManagerLocal { /** * Create a human readable description of the conditions that led to this alert. * @param alert Alert to create human readable condition description + * @param shortVersion if true the messages printed are abbreviated to save space * @return human readable condition log */ - String prettyPrintAlertConditions(Alert alert); + String prettyPrintAlertConditions(Alert alert, boolean shortVersion);
/** * Tells us if the definition of the passed alert will be disabled after this alert was fired diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/i18n/AlertI18NResourceKeys.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/i18n/AlertI18NResourceKeys.java index 8734771..1de3d85 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/i18n/AlertI18NResourceKeys.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/i18n/AlertI18NResourceKeys.java @@ -32,20 +32,41 @@ public interface AlertI18NResourceKeys { @I18NMessage(locale = "de", value = "Verfgbarkeit wird {0}") }) String ALERT_CONFIG_PROPS_CB_AVAILABILITY = "alert.config.props.CB.Availability";
+ @I18NMessages( { @I18NMessage("Avail goes {0}"), + @I18NMessage(locale = "de", value = "Verf. wird {0}") }) + String ALERT_CONFIG_PROPS_CB_AVAILABILITY_SHORT = "alert.config.props.CB.Availability.short"; + @I18NMessages( { @I18NMessage("Event Severity: {0}"), @I18NMessage(locale = "de", value = "Schwere des Ereignesses: {0}") }) String ALERT_CONFIG_PROPS_CB_EVENT_SEVERITY = "alert.config.props.CB.EventSeverity";
+ @I18NMessages( { @I18NMessage("Sev: {0}"), + @I18NMessage(locale = "de", value = "Schwere: {0}") }) + String ALERT_CONFIG_PROPS_CB_EVENT_SEVERITY_SHORT = "alert.config.props.CB.EventSeverity.short"; + @I18NMessages( { @I18NMessage("Event Severity: {0} and matching expression "{1}""), @I18NMessage(locale = "de", value = "Schwere des Ereignesses: {0} und zugehriger Ausdruck "{1}"") }) String ALERT_CONFIG_PROPS_CB_EVENT_SEVERITY_REGEX_MATCH = "alert.config.props.CB.EventSeverity.RegexMatch";
+ @I18NMessages( { @I18NMessage("Sev: {0} & exp "{1}""), + @I18NMessage(locale = "de", value = "Schwere: {0} & Ausdruck "{1}"") }) + String ALERT_CONFIG_PROPS_CB_EVENT_SEVERITY_REGEX_MATCH_SHORT = "alert.config.props.CB.EventSeverity.RegexMatch.short"; + @I18NMessages( { @I18NMessage("value changed"), @I18NMessage(locale = "de", value = "Der Wert hat sich gendert") }) String ALERT_CURRENT_LIST_VALUE_CHANGED = "alert.current.list.ValueChanged";
+ @I18NMessages( { @I18NMessage("val chg"), @I18NMessage(locale = "de", value = "Wertnd.") }) + String ALERT_CURRENT_LIST_VALUE_CHANGED_SHORT = "alert.current.list.ValueChanged.short"; + @I18NMessages( { @I18NMessage("\ - Condition {0}: {1}\n\\n" + "\ - Date/Time: {2}\n\\n" + "\ - Details: {3}\n\\n"), @I18NMessage(locale = "de", value = " - Bedingung {0}: {1}\n\\n - Datum/Uhrzeit: {2}\n\\n" + "\ - Details: {3}\n\\n") }) String ALERT_EMAIL_CONDITION_LOG_FORMAT = "alert.email.condition.log.format"; + + @I18NMessages( { + @I18NMessage("\ - Cond {0}: {1}\n\\n" + "\ - Time: {2}\n\\n" + "\ - Det: {3}\n\\n"), + @I18NMessage(locale = "de", value = " - Bed {0}: {1}\n\\n - Zeit: {2}\n\\n" + + "\ - Det: {3}\n\\n") }) + String ALERT_EMAIL_CONDITION_LOG_FORMAT_SHORT = "alert.email.condition.log.format.short"; } \ No newline at end of file diff --git a/modules/enterprise/server/plugins/alert-irc/src/main/java/org/rhq/enterprise/server/plugins/alertIrc/IrcSender.java b/modules/enterprise/server/plugins/alert-irc/src/main/java/org/rhq/enterprise/server/plugins/alertIrc/IrcSender.java index 93eecc6..95abdb9 100644 --- a/modules/enterprise/server/plugins/alert-irc/src/main/java/org/rhq/enterprise/server/plugins/alertIrc/IrcSender.java +++ b/modules/enterprise/server/plugins/alert-irc/src/main/java/org/rhq/enterprise/server/plugins/alertIrc/IrcSender.java @@ -65,7 +65,7 @@ public class IrcSender extends AlertSender<IrcAlertComponent> { b.append("): "); b.append(alertManager.prettyPrintAlertURL(alert)); b.append("\n"); - b.append(alertManager.prettyPrintAlertConditions(alert)); + b.append(alertManager.prettyPrintAlertConditions(alert, false));
return b.toString(); } diff --git a/modules/enterprise/server/plugins/alert-microblog/src/main/java/org/rhq/enterprise/server/plugins/alertMicroblog/MicroblogSender.java b/modules/enterprise/server/plugins/alert-microblog/src/main/java/org/rhq/enterprise/server/plugins/alertMicroblog/MicroblogSender.java index 257b315..b13763e 100644 --- a/modules/enterprise/server/plugins/alert-microblog/src/main/java/org/rhq/enterprise/server/plugins/alertMicroblog/MicroblogSender.java +++ b/modules/enterprise/server/plugins/alert-microblog/src/main/java/org/rhq/enterprise/server/plugins/alertMicroblog/MicroblogSender.java @@ -57,14 +57,19 @@ public class MicroblogSender extends AlertSender { b.append("' ("); b.append(alert.getAlertDefinition().getResource().getId()); b.append("): "); - b.append(alertManager.prettyPrintAlertConditions(alert)); + b.append(alertManager.prettyPrintAlertConditions(alert, true)); b.append("-by @JBossJopr"); // TODO not for production :-) // TODO use some alert url shortening service
SenderResult result ; String txt = "user@baseUrl [" + user + "@" + baseUrl + "]:"; try { - Status status = twitter.updateStatus(b.toString()); + String msg = b.toString(); + if (msg.length()>140) + msg = msg.substring(0,140); + + Status status = twitter.updateStatus(msg); + result = new SenderResult(ResultState.SUCCESS,"Send notification to " + txt + ", msg-id: " + status.getId()); } catch (TwitterException e) {
diff --git a/modules/enterprise/server/plugins/alert-mobicents/src/main/java/org/rhq/enterprise/server/plugins/alertMobicents/MobicentsSender.java b/modules/enterprise/server/plugins/alert-mobicents/src/main/java/org/rhq/enterprise/server/plugins/alertMobicents/MobicentsSender.java index 649d6ed..b14c85d 100644 --- a/modules/enterprise/server/plugins/alert-mobicents/src/main/java/org/rhq/enterprise/server/plugins/alertMobicents/MobicentsSender.java +++ b/modules/enterprise/server/plugins/alert-mobicents/src/main/java/org/rhq/enterprise/server/plugins/alertMobicents/MobicentsSender.java @@ -74,7 +74,7 @@ public class MobicentsSender extends AlertSender { // Switch locale to english, as the voice synthesizer expects this for now Locale currentLocale = Locale.getDefault(); Locale.setDefault(Locale.ENGLISH); - b.append(alertManager.prettyPrintAlertConditions(alert)); + b.append(alertManager.prettyPrintAlertConditions(alert, false)); Locale.setDefault(currentLocale);
boolean willBeDisabled = alertManager.willDefinitionBeDisabled(alert); diff --git a/modules/enterprise/server/plugins/alert-scriptlang/src/main/java/org.rhq.enterprise.server.plugins.alertScriptlang/ScriptLangSender.java b/modules/enterprise/server/plugins/alert-scriptlang/src/main/java/org.rhq.enterprise.server.plugins.alertScriptlang/ScriptLangSender.java index 985d890..bac51fb 100644 --- a/modules/enterprise/server/plugins/alert-scriptlang/src/main/java/org.rhq.enterprise.server.plugins.alertScriptlang/ScriptLangSender.java +++ b/modules/enterprise/server/plugins/alert-scriptlang/src/main/java/org.rhq.enterprise.server.plugins.alertScriptlang/ScriptLangSender.java @@ -93,7 +93,7 @@ public class ScriptLangSender extends AlertSender<ScriptLangComponent> { Object[] args = new Object[3]; args[0] = alert; args[1] = alertManager.prettyPrintAlertURL(alert); - args[2] = alertManager.prettyPrintAlertConditions(alert); + args[2] = alertManager.prettyPrintAlertConditions(alert, false); result = ((Invocable) engine).invokeFunction("sendAlert", args);
if (result == null) { diff --git a/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSender.java b/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSender.java index 3fd5d79..045c3bb 100644 --- a/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSender.java +++ b/modules/enterprise/server/plugins/alert-snmp/src/main/java/org/rhq/enterprise/server/plugins/alertSnmp/SnmpSender.java @@ -68,7 +68,7 @@ public class SnmpSender extends AlertSender { String result; List<Resource> lineage = resourceManager.getResourceLineage(alert.getAlertDefinition().getResource().getId()); String platformName = lineage.get(0).getName(); - String conditions = alertManager.prettyPrintAlertConditions(alert); + String conditions = alertManager.prettyPrintAlertConditions(alert, false); String alertUrl = alertManager.prettyPrintAlertURL(alert);
SenderResult res ;
commit 798ac30ead634c43a72beed52446a38d9bfc1f09 Author: John Mazzitelli mazz@redhat.com Date: Fri Feb 19 17:51:53 2010 -0500
i think this should report an error always on lock acq failure. we'll leave the flag alone - don't set it to true - let the next call take care of whether or not the init callback should be invoked or not
diff --git a/modules/enterprise/comm/src/main/java/org/rhq/enterprise/communications/command/client/JBossRemotingRemoteCommunicator.java b/modules/enterprise/comm/src/main/java/org/rhq/enterprise/communications/command/client/JBossRemotingRemoteCommunicator.java index 8c37b50..cd3b479 100644 --- a/modules/enterprise/comm/src/main/java/org/rhq/enterprise/communications/command/client/JBossRemotingRemoteCommunicator.java +++ b/modules/enterprise/comm/src/main/java/org/rhq/enterprise/communications/command/client/JBossRemotingRemoteCommunicator.java @@ -588,11 +588,9 @@ public class JBossRemotingRemoteCommunicator implements RemoteCommunicator { writeLock.unlock(); } } else { - if (m_needToCallInitializeCallback) { - Throwable t = new Throwable("Initialize callback lock could not be acquired"); - LOG.error(CommI18NResourceKeys.INITIALIZE_CALLBACK_FAILED, t.getMessage()); - return new GenericCommandResponse(command, false, null, t); - } + Throwable t = new Throwable("Initialize callback lock could not be acquired"); + LOG.error(CommI18NResourceKeys.INITIALIZE_CALLBACK_FAILED, t.getMessage()); + return new GenericCommandResponse(command, false, null, t); } } return null;
commit 9746ea32492b638eacb741d608d9ce17349c925d Author: John Mazzitelli mazz@redhat.com Date: Fri Feb 19 17:44:11 2010 -0500
should not worry about the lock failure if the init callback doesn't need to be invoked anyway
diff --git a/modules/enterprise/comm/src/main/java/org/rhq/enterprise/communications/command/client/JBossRemotingRemoteCommunicator.java b/modules/enterprise/comm/src/main/java/org/rhq/enterprise/communications/command/client/JBossRemotingRemoteCommunicator.java index 762b9b4..8c37b50 100644 --- a/modules/enterprise/comm/src/main/java/org/rhq/enterprise/communications/command/client/JBossRemotingRemoteCommunicator.java +++ b/modules/enterprise/comm/src/main/java/org/rhq/enterprise/communications/command/client/JBossRemotingRemoteCommunicator.java @@ -588,10 +588,11 @@ public class JBossRemotingRemoteCommunicator implements RemoteCommunicator { writeLock.unlock(); } } else { - m_needToCallInitializeCallback = true; // can't invoke callback, we'll want to still call it later - Throwable t = new Throwable("Initialize callback lock could not be acquired"); - LOG.error(CommI18NResourceKeys.INITIALIZE_CALLBACK_FAILED, t.getMessage()); - return new GenericCommandResponse(command, false, null, t); + if (m_needToCallInitializeCallback) { + Throwable t = new Throwable("Initialize callback lock could not be acquired"); + LOG.error(CommI18NResourceKeys.INITIALIZE_CALLBACK_FAILED, t.getMessage()); + return new GenericCommandResponse(command, false, null, t); + } } } return null;
commit c2cca917c88986ca48c84bbbdaf3408753299bf1 Author: John Mazzitelli mazz@redhat.com Date: Fri Feb 19 17:38:16 2010 -0500
BZ 537396 - to not block indefinitely if a lock cannot be acquired before attempting to invoke the initialize callback
diff --git a/modules/enterprise/comm/src/main/java/org/rhq/enterprise/communications/command/client/JBossRemotingRemoteCommunicator.java b/modules/enterprise/comm/src/main/java/org/rhq/enterprise/communications/command/client/JBossRemotingRemoteCommunicator.java index 3775212..762b9b4 100644 --- a/modules/enterprise/comm/src/main/java/org/rhq/enterprise/communications/command/client/JBossRemotingRemoteCommunicator.java +++ b/modules/enterprise/comm/src/main/java/org/rhq/enterprise/communications/command/client/JBossRemotingRemoteCommunicator.java @@ -21,6 +21,9 @@ package org.rhq.enterprise.communications.command.client; import java.net.MalformedURLException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import mazz.i18n.Logger;
@@ -95,10 +98,22 @@ public class JBossRemotingRemoteCommunicator implements RemoteCommunicator { private InitializeCallback m_initializeCallback;
/** - * When the first element is <code>true</code>, the initialize callback will need to be called prior - * to sending any commands. This is an array because the array itself is used for its lock. + * When <code>true</code>, the initialize callback will need to be called prior + * to sending any commands. Used in conjunection with its associated RW lock. */ - private final boolean[] m_needToCallInitializeCallback; + private boolean m_needToCallInitializeCallback; + + /** + * RW lock when needing to access its associated atomic boolean flag. + */ + private final ReentrantReadWriteLock m_needToCallInitializeCallbackLock; + + /** + * Number of minutes to wait while attempting to aquire a lock before attempting + * to invoke the initialize callback. If this amount of minutes expires before the lock + * is acquired, an error will occur and the initialize callback will have to be attempted later. + */ + private final long m_initializeCallbackLockAcquisitionTimeoutMins;
/** * Constructor for {@link JBossRemotingRemoteCommunicator} that initializes the client with no invoker locator @@ -200,7 +215,17 @@ public class JBossRemotingRemoteCommunicator implements RemoteCommunicator { m_clientConfiguration.putAll(client_config); }
- m_needToCallInitializeCallback = new boolean[] { false }; + m_needToCallInitializeCallback = false; + m_needToCallInitializeCallbackLock = new ReentrantReadWriteLock(); + + long mins; + try { + String minsStr = System.getProperty("rhq.communications.initial-callback-lock-wait-mins", "60"); + mins = Long.parseLong(minsStr); + } catch (Throwable t) { + mins = 60L; + } + m_initializeCallbackLockAcquisitionTimeoutMins = mins;
return; } @@ -319,7 +344,7 @@ public class JBossRemotingRemoteCommunicator implements RemoteCommunicator { if (m_remotingClient != null) { m_remotingClient.disconnect(); m_remotingClient = null; - m_needToCallInitializeCallback[0] = (getInitializeCallback() != null); // specifically do not synchronize, just set it + m_needToCallInitializeCallback = (getInitializeCallback() != null); // specifically do not synchrononize by using lock, just set it }
LOG.info(CommI18NResourceKeys.COMMUNICATOR_CHANGING_ENDPOINT, m_invokerLocator, locator); @@ -377,7 +402,7 @@ public class JBossRemotingRemoteCommunicator implements RemoteCommunicator {
public void setInitializeCallback(InitializeCallback callback) { m_initializeCallback = callback; - m_needToCallInitializeCallback[0] = (callback != null); // specifically do not synchronize, just set it + m_needToCallInitializeCallback = (callback != null); // specifically do not synchrononize by using lock, just set it }
public String getRemoteEndpoint() { @@ -403,7 +428,7 @@ public class JBossRemotingRemoteCommunicator implements RemoteCommunicator { public void connect() throws Exception { if ((m_remotingClient != null) && !m_remotingClient.isConnected()) { m_remotingClient.connect(); - m_needToCallInitializeCallback[0] = (getInitializeCallback() != null); // specifically do not synchronize, just set it + m_needToCallInitializeCallback = (getInitializeCallback() != null); // specifically do not synchrononize by using lock, just set it }
return; @@ -412,7 +437,7 @@ public class JBossRemotingRemoteCommunicator implements RemoteCommunicator { public void disconnect() { if (m_remotingClient != null) { m_remotingClient.disconnect(); - m_needToCallInitializeCallback[0] = (getInitializeCallback() != null); // specifically do not synchronize, just set it + m_needToCallInitializeCallback = (getInitializeCallback() != null); // specifically do not synchrononize by using lock, just set it }
return; @@ -535,18 +560,38 @@ public class JBossRemotingRemoteCommunicator implements RemoteCommunicator { private CommandResponse invokeInitializeCallbackIfNeeded(Command command) { InitializeCallback callback = getInitializeCallback(); if (callback != null) { - // block here - in effect, this will stop all commands from going out until the callback is done - synchronized (m_needToCallInitializeCallback) { - if (m_needToCallInitializeCallback[0]) { - try { - m_needToCallInitializeCallback[0] = !callback.sendingInitialCommand(this, command); - LOG.debug(CommI18NResourceKeys.INITIALIZE_CALLBACK_DONE, m_needToCallInitializeCallback[0]); - } catch (Throwable t) { - m_needToCallInitializeCallback[0] = true; // callback failed, we'll want to call it again - LOG.error(t, CommI18NResourceKeys.INITIALIZE_CALLBACK_FAILED, ThrowableUtil.getAllMessages(t)); - return new GenericCommandResponse(command, false, null, t); + // block here - in effect, this will stop all commands from going out until the callback is done + // to avoid infinite blocking, we'll only wait for a set time (though long). + + WriteLock writeLock = m_needToCallInitializeCallbackLock.writeLock(); + boolean locked; + try { + locked = writeLock.tryLock(m_initializeCallbackLockAcquisitionTimeoutMins, TimeUnit.MINUTES); + } catch (InterruptedException ie) { + locked = false; + } + + if (locked) { + try { + if (m_needToCallInitializeCallback) { + try { + m_needToCallInitializeCallback = (!callback.sendingInitialCommand(this, command)); + LOG.debug(CommI18NResourceKeys.INITIALIZE_CALLBACK_DONE, m_needToCallInitializeCallback); + } catch (Throwable t) { + m_needToCallInitializeCallback = true; // callback failed, we'll want to call it again + LOG.error(t, CommI18NResourceKeys.INITIALIZE_CALLBACK_FAILED, ThrowableUtil + .getAllMessages(t)); + return new GenericCommandResponse(command, false, null, t); + } } + } finally { + writeLock.unlock(); } + } else { + m_needToCallInitializeCallback = true; // can't invoke callback, we'll want to still call it later + Throwable t = new Throwable("Initialize callback lock could not be acquired"); + LOG.error(CommI18NResourceKeys.INITIALIZE_CALLBACK_FAILED, t.getMessage()); + return new GenericCommandResponse(command, false, null, t); } } return null;
commit 55ba6f8778820bdd417e99909220136a2e1e091f Author: John Mazzitelli mazz@redhat.com Date: Fri Feb 19 11:10:53 2010 -0500
BZ 566724 - if classloader fails to be created, master PC will skip the plugin
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/plugin/pc/MasterServerPluginContainer.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/plugin/pc/MasterServerPluginContainer.java index f46eaf5..bd4feac 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/plugin/pc/MasterServerPluginContainer.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/plugin/pc/MasterServerPluginContainer.java @@ -136,16 +136,23 @@ public class MasterServerPluginContainer { String pluginName = descriptor.getName(); ServerPluginType pluginType = new ServerPluginType(descriptor); PluginKey pluginKey = PluginKey.createServerPluginKey(pluginType.stringify(), pluginName); - ClassLoader classLoader = this.classLoaderManager.obtainServerPluginClassLoader(pluginKey); - log.debug("Pre-loading server plugin [" + pluginKey + "] from [" + pluginUrl - + "] into its plugin container"); try { - ServerPluginEnvironment env = new ServerPluginEnvironment(pluginUrl, classLoader, descriptor); - boolean enabled = !allDisabledPlugins.contains(pluginKey); - pc.loadPlugin(env, enabled); - log.info("Preloaded server plugin [" + pluginKey.getPluginName() + "]"); + ClassLoader classLoader = this.classLoaderManager.obtainServerPluginClassLoader(pluginKey); + log.debug("Pre-loading server plugin [" + pluginKey + "] from [" + pluginUrl + + "] into its plugin container"); + try { + ServerPluginEnvironment env = new ServerPluginEnvironment(pluginUrl, classLoader, + descriptor); + boolean enabled = !allDisabledPlugins.contains(pluginKey); + pc.loadPlugin(env, enabled); + log.info("Preloaded server plugin [" + pluginName + "]"); + } catch (Exception e) { + log.warn("Failed to preload server plugin [" + pluginName + "] from URL [" + pluginUrl + + "]", e); + } } catch (Exception e) { - log.warn("Failed to preload server plugin [" + pluginUrl + "]", e); + log.warn("Failed to preload server plugin [" + pluginName + + "]; cannot get its classloader from URL [ " + pluginUrl + "]", e); } } else { log.warn("There is no server plugin container to support plugin: " + pluginUrl); diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/plugin/pc/ServerPluginClassLoader.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/plugin/pc/ServerPluginClassLoader.java index 9f27ce9..fa26970 100644 --- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/plugin/pc/ServerPluginClassLoader.java +++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/plugin/pc/ServerPluginClassLoader.java @@ -64,7 +64,7 @@ public class ServerPluginClassLoader extends URLClassLoader { try { unpackedDirectory = unpackEmbeddedJars(pluginJarName, pluginUrl, classpathUrlList, tmpDirectory); } catch (Exception e) { - throw new Exception("Failed to unpack embedded JARs within: " + pluginUrl); + throw new Exception("Failed to unpack embedded JARs within: " + pluginUrl, e); } } }
commit 2ffc564d7ff69a427755fa374419bc5980e47f94 Author: Heiko W. Rupp pilhuhn@fedorapeople.org Date: Fri Feb 19 13:37:36 2010 +0100
Fix link to definitions BZ 566004
diff --git a/modules/enterprise/gui/portal-war/src/main/webapp/rhq/resource/alert/listAlertHistory.xhtml b/modules/enterprise/gui/portal-war/src/main/webapp/rhq/resource/alert/listAlertHistory.xhtml index b6e386c..fd09f82 100644 --- a/modules/enterprise/gui/portal-war/src/main/webapp/rhq/resource/alert/listAlertHistory.xhtml +++ b/modules/enterprise/gui/portal-war/src/main/webapp/rhq/resource/alert/listAlertHistory.xhtml @@ -148,8 +148,7 @@ </onc:sortableColumnHeader> </f:facet>
- <h:outputLink value="/alerts/Config.do"> - <f:param name="mode" value="viewRoles"/> + <h:outputLink value="/rhq/resource/alert/viewAlert.xhtml"> <f:param name="id" value="#{Resource.id}"/> <f:param name="ad" value="#{item.alert.alertDefinition.id}"/> <h:outputText value="#{item.alert.alertDefinition.name}" />
rhq-commits@lists.fedorahosted.org