23
23
24
24
import java .util .Arrays ;
25
25
import java .util .Collection ;
26
+ import java .util .Collections ;
26
27
import java .util .Map ;
27
- import java .util .Map .Entry ;
28
28
import java .util .Set ;
29
29
30
30
import org .apache .commons .lang .StringUtils ;
37
37
import org .eclipse .keti .acs .commons .policy .condition .ConditionAssertionFailedException ;
38
38
import org .eclipse .keti .acs .commons .policy .condition .ConditionParsingException ;
39
39
import org .eclipse .keti .acs .commons .policy .condition .ConditionScript ;
40
- import org .eclipse .keti .acs .commons .policy .condition .ConditionShell ;
41
40
import org .eclipse .keti .acs .commons .policy .condition .ResourceHandler ;
42
41
import org .eclipse .keti .acs .commons .policy .condition .SubjectHandler ;
43
- import org .slf4j .Logger ;
44
- import org .slf4j .LoggerFactory ;
45
- import org .springframework .stereotype .Component ;
46
42
47
- import groovy .lang .Binding ;
48
43
import groovy .lang .GroovyShell ;
49
44
import groovy .lang .GroovySystem ;
50
45
import groovy .lang .Script ;
54
49
55
50
*/
56
51
@ SuppressWarnings ("nls" )
57
- @ Component
58
- public class GroovyConditionShell implements ConditionShell {
59
- private static final Logger LOGGER = LoggerFactory .getLogger (GroovyConditionShell .class );
52
+ public class GroovyConditionShell {
60
53
54
+ private final GroovyConditionCache conditionCache ;
61
55
private final GroovyShell shell ;
62
56
63
- public GroovyConditionShell () {
57
+ public GroovyConditionShell (final GroovyConditionCache conditionCache ) {
58
+ this .conditionCache = conditionCache ;
64
59
65
60
SecureASTCustomizer secureASTCustomizer = createSecureASTCustomizer ();
66
61
ImportCustomizer importCustomizer = createImportCustomizer ();
@@ -70,66 +65,78 @@ public GroovyConditionShell() {
70
65
compilerConfiguration .addCompilationCustomizers (secureASTCustomizer );
71
66
compilerConfiguration .addCompilationCustomizers (importCustomizer );
72
67
compilerConfiguration .addCompilationCustomizers (astTransformationCustomizer );
68
+ compilerConfiguration .getOptimizationOptions ().put (CompilerConfiguration .INVOKEDYNAMIC , true );
73
69
74
- this .shell = new GroovyShell (GroovyConditionShell . class .getClassLoader (), compilerConfiguration );
70
+ this .shell = new GroovyShell (this . getClass () .getClassLoader (), compilerConfiguration );
75
71
}
76
72
77
- /*
78
- * (non-Javadoc)
73
+ /**
74
+ * Validates the script & generates condition script object.
79
75
*
80
- * @see org.eclipse.keti.acs.commons.conditions.ConditionShell#parse(java.lang. String )
76
+ * @param script
77
+ * the policy condition string
78
+ * @return a Script object instance capable of executing the policy condition.
79
+ * @throws ConditionParsingException
80
+ * on validation error
81
81
*/
82
- @ Override
83
82
public ConditionScript parse (final String script ) throws ConditionParsingException {
83
+ return parse (script , true );
84
+ }
85
+
86
+ private ConditionScript parse (
87
+ final String script ,
88
+ final boolean removeLoadedClasses
89
+ ) throws ConditionParsingException {
84
90
if (StringUtils .isEmpty (script )) {
85
91
throw new IllegalArgumentException ("Script is null or empty." );
86
92
}
87
93
88
94
try {
89
- Script groovyScript = this .shell .parse (script );
90
- return new GroovyConditionScript (groovyScript );
95
+ ConditionScript compiledScript = conditionCache .get (script );
96
+ if (compiledScript == null ) {
97
+ Script groovyScript = this .shell .parse (script );
98
+ compiledScript = new GroovyConditionScript (groovyScript );
99
+ conditionCache .put (script , compiledScript );
100
+ }
101
+ return compiledScript ;
91
102
} catch (CompilationFailedException e ) {
92
- throw new ConditionParsingException ("Failed to validate the condition script." , script , e );
103
+ throw new ConditionParsingException ("Failed to parse the condition script." , script , e );
104
+ } finally {
105
+ if (removeLoadedClasses ) {
106
+ removeLoadedClasses ();
107
+ }
93
108
}
94
109
}
95
110
96
- /*
97
- * (non-Javadoc)
111
+ /**
112
+ * Validates & executes the policy condition script.
98
113
*
99
- * @see org.eclipse.keti.acs.commons.conditions.ConditionShell#execute(java.lang. String, java.util.Map )
114
+ * @param script
115
+ * the policy condition string
116
+ * @param boundVariables
117
+ * variable bindings of the script
118
+ * @return result of executing the policy condition script.
119
+ * @throws ConditionParsingException
120
+ * on script validation error
100
121
*/
101
- @ Override
102
122
public boolean execute (final String script , final Map <String , Object > boundVariables )
103
123
throws ConditionParsingException {
104
- if (StringUtils .isEmpty (script )) {
105
- throw new IllegalArgumentException ("Script is null or empty." );
106
- }
124
+ ConditionScript conditionScript = parse (script , false );
107
125
108
126
try {
109
- Script groovyScript = this .shell .parse (script );
110
-
111
- if (LOGGER .isDebugEnabled ()) {
112
- StringBuilder msgBuilder = new StringBuilder ();
113
- msgBuilder .append ("The script is bound to the following variables:\n " );
114
- for (Entry <String , Object > entry : boundVariables .entrySet ()) {
115
- msgBuilder .append ("* " ).append (entry .getKey ()).append (":" ).append (entry .getValue ()).append ("\n " );
116
- }
117
- LOGGER .debug (msgBuilder .toString ());
118
- }
119
-
120
- Binding binding = new Binding (boundVariables );
121
- groovyScript .setBinding (binding );
122
- boolean result = (boolean ) groovyScript .run ();
123
-
124
- this .shell .getClassLoader ().clearCache ();
125
- GroovySystem .getMetaClassRegistry ().removeMetaClass (groovyScript .getClass ());
126
-
127
- return result ;
128
- } catch (CompilationFailedException e ) {
129
- throw new ConditionParsingException ("Failed to parse the condition script." , script , e );
127
+ return conditionScript .execute (boundVariables );
130
128
} catch (ConditionAssertionFailedException e ) {
131
129
return false ;
130
+ } finally {
131
+ removeLoadedClasses ();
132
+ }
133
+ }
134
+
135
+ private void removeLoadedClasses () {
136
+ for (Class <?> groovyClass : shell .getClassLoader ().getLoadedClasses ()) {
137
+ GroovySystem .getMetaClassRegistry ().removeMetaClass (groovyClass );
132
138
}
139
+ shell .resetLoadedClasses ();
133
140
}
134
141
135
142
private static ImportCustomizer createImportCustomizer () {
@@ -147,18 +154,18 @@ private static SecureASTCustomizer createSecureASTCustomizer() {
147
154
// Disallow method definition.
148
155
secureASTCustomizer .setMethodDefinitionAllowed (false );
149
156
// Disallow all imports by setting a blank whitelist.
150
- secureASTCustomizer .setImportsWhitelist (Arrays . asList ( new String [] {} ));
157
+ secureASTCustomizer .setImportsWhitelist (Collections . emptyList ( ));
151
158
// Disallow star imports by setting a blank whitelist.
152
159
secureASTCustomizer .setStarImportsWhitelist (Arrays .asList (
153
- new String [] { "org.crsh.command.*" , "org.crsh.cli.*" , "org.crsh.groovy.*" ,
154
- "org.eclipse.keti.acs.commons.policy.condition.*" } ));
160
+ "org.crsh.command.*" , "org.crsh.cli.*" , "org.crsh.groovy.*" ,
161
+ "org.eclipse.keti.acs.commons.policy.condition.*" ));
155
162
// Set white list for constant type classes.
156
163
secureASTCustomizer .setConstantTypesClassesWhiteList (Arrays .asList (
157
- new Class [] { Boolean .class , boolean .class , Collection .class , Double .class , double .class , Float .class ,
158
- float .class , Integer .class , int .class , Long .class , long .class , Object .class , String .class } ));
164
+ Boolean .class , boolean .class , Collection .class , Double .class , double .class , Float .class ,
165
+ float .class , Integer .class , int .class , Long .class , long .class , Object .class , String .class ));
159
166
secureASTCustomizer .setReceiversClassesWhiteList (Arrays .asList (
160
- new Class [] { Boolean .class , Collection .class , Integer .class , Iterable .class , Object .class , Set .class ,
161
- String .class } ));
167
+ Boolean .class , Collection .class , Integer .class , Iterable .class , Object .class , Set .class ,
168
+ String .class ));
162
169
return secureASTCustomizer ;
163
170
}
164
171
0 commit comments