/
PluginsHandler.groovy
243 lines (201 loc) · 8.02 KB
/
PluginsHandler.groovy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/*
* Copyright 2009-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package groovyx.gaelyk.plugins
import groovy.transform.CompileStatic
import groovyx.gaelyk.GaelykBindingEnhancer
import groovyx.gaelyk.logging.GroovyLogger
import groovyx.gaelyk.routes.Route
import javax.servlet.ServletContext
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import org.codehaus.groovy.control.CompilerConfiguration
/**
* Configure the installed plugins.
*
* @author Guillaume Laforge
* @author Vladimir Orany
* @author Marcin Erdmann
*/
@Singleton(lazy = true)
class PluginsHandler {
// indicates whether the plugins have already been loaded or not
// as both the template and groovlet servlets can call the handler
// we can't know which one will initialize the plugins first
private boolean initialized = false
Map<String, Object> bindingVariables = [:]
List<Route> routes = []
List<Closure> beforeActions = []
List<Closure> afterActions = []
List<String> installed = []
final Closure defaultScriptContentLoader = { String path ->
def file = new File(path)
if(file.exists()){
return file.text
}
InputStream is = Thread.currentThread().contextClassLoader.getResourceAsStream(path)
if(!is) return ""
is.text
}
Closure scriptContent = defaultScriptContentLoader
GroovyLogger log = new GroovyLogger('gaelyk.pluginshandler')
@CompileStatic
void reinit() {
initialized = false
bindingVariables = [:]
routes = []
beforeActions = []
afterActions = []
installed = []
scriptContent = defaultScriptContentLoader
}
/**
* Initializes the plugins
*/
synchronized void initPlugins(ServletContext servletContext, ignoreBinary = false) {
if (!initialized) {
log.config "Loading plugin descriptors"
Iterable<PluginBaseScript> loader = ignoreBinary ? []: ServiceLoader.load(PluginBaseScript)
Iterable<PluginBaseScript> loadedPlugins = loadPluginsList().collect { String pluginName ->
def pluginPath = "WEB-INF/plugins/${pluginName}.groovy"
String content = scriptContent(pluginPath)
if (content) {
log.config "Loading plugin $pluginName"
def config = new CompilerConfiguration()
config.scriptBaseClass = PluginBaseScript.class.name
// evaluate the plugin descriptor
def shell = new GroovyShell(config)
return (PluginBaseScript) shell.parse(content, "${pluginName}.groovy")
} else {
log.config "Plugin $pluginName doesn't exist"
return null
}
}.grep()
int counter = 0
int routesStep = 330
Closure init = { PluginBaseScript pluginScript ->
String pluginName = pluginScript.getClass().getSimpleName()
log.config "Initializing plugin $pluginName"
// creates a binding for the plugin descriptor file
def binding = new Binding()
// and inject the GAE services
GaelykBindingEnhancer.bind(binding)
// and plugin logger
binding.setVariable("log", new GroovyLogger("gaelyk.plugins.${pluginName}", true))
binding.setVariable("servletContext", servletContext)
pluginScript.binding = binding
// resolve the routes index conflict
if(pluginScript.firstRouteIndex == 0){
pluginScript.firstRouteIndex = (++counter) * routesStep
}
pluginScript.run()
// use getters directly,
// otherwise property access returns variables from the binding of the scripts
bindingVariables.putAll pluginScript.getBindingVariables()
routes.addAll pluginScript.getRoutes()
if (pluginScript.getBeforeAction()) beforeActions.add pluginScript.getBeforeAction()
if (pluginScript.getAfterAction()) afterActions .add pluginScript.getAfterAction()
log.config "Plugin $pluginName initialized!"
installed << pluginName
}
try {
for(PluginBaseScript plugin in loader){
init plugin
}
} catch(ServiceConfigurationError e){
log.config e.message
}
for(PluginBaseScript plugin in loadedPlugins){
init plugin
}
log.config "Found ${installed.size()} plugin(s)"
// reverse the order of the "after" actions so they are executed in reverse order
if (afterActions) afterActions = afterActions.reverse()
initialized = true
}
}
/**
* @return the list of plugins
*/
synchronized List loadPluginsList() {
String pluginsListFileContent = scriptContent("WEB-INF/plugins.groovy")
if (pluginsListFileContent) {
def config = new CompilerConfiguration()
config.scriptBaseClass = PluginsListBaseScript.class.name
// creates a binding for the list of plugins file,
def binding = new LazyBinding()
// and inject the GAE services
GaelykBindingEnhancer.bind(binding)
// evaluate the list of plugins
def shell = new GroovyShell(binding, config)
PluginsListBaseScript script = (PluginsListBaseScript) shell.parse(pluginsListFileContent, "plugins.groovy")
script.run()
return script.getPlugins()
}
return []
}
@CompileStatic
boolean isInstalled(String pluginName){
pluginName in installed
}
/**
* Add the variables in the binding, as defined by the plugin descriptors.
*
* @param binding the binding to add the variables to
*/
void enrich(Binding binding) {
bindingVariables.each { String k, Object v -> binding.setVariable(k, v) }
}
/**
* Execute all the "before" actions
*
* @param request
* @param response
*/
void executeBeforeActions(HttpServletRequest request, HttpServletResponse response) {
beforeActions.each { Closure action ->
cloneDelegateAndExecute action, request, response
}
}
/**
* Execute all the "after" actions
*
* @param request
* @param response
*/
void executeAfterActions(HttpServletRequest request, HttpServletResponse response, Object executionResult = null) {
afterActions.each { Closure action ->
cloneDelegateAndExecute action, request, response, executionResult
}
}
private void cloneDelegateAndExecute(Closure action, HttpServletRequest request, HttpServletResponse response, result = null) {
Binding binding = new Binding()
GaelykBindingEnhancer.bind(binding)
def delegate = [
*:bindingVariables,
request: request,
response: response,
log: action.owner.log,
binding: bindingVariables
]
if(result){
delegate.result = result
}
Closure cloned = action.clone()
cloned.resolveStrategy = Closure.DELEGATE_FIRST
cloned.delegate = delegate
cloned()
}
}