1 /* Copyright 2004-2005 the original author or authors. 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 package org.codehaus.groovy.grails.cli.support; 16 17 import grails.build.GrailsBuildListener; 18 import grails.util.BuildSettings; 19 import grails.util.GrailsNameUtils; 20 import grails.util.GrailsUtil; 21 import grails.util.PluginBuildSettings; 22 import groovy.lang.Binding; 23 import groovy.lang.Closure; 24 import groovy.lang.GroovyClassLoader; 25 import groovy.lang.MissingPropertyException; 26 import groovy.lang.Script; 27 28 import java.io.File; 29 import java.io.IOException; 30 import java.util.ArrayList; 31 import java.util.HashMap; 32 import java.util.LinkedList; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.regex.Matcher; 36 import java.util.regex.Pattern; 37 38 import org.apache.tools.ant.BuildEvent; 39 import org.apache.tools.ant.BuildListener; 40 import org.springframework.core.io.Resource; 41 42 /** 43 * @author Graeme Rocher 44 * @since 1.1 45 */ 46 public class GrailsBuildEventListener implements BuildListener{ 47 48 private static final Pattern EVENT_NAME_PATTERN = Pattern.compile("event([A-Z]\\w*)"); 49 private GroovyClassLoader classLoader; 50 private Binding binding; 51 protected Map<String, List<Closure>> globalEventHooks = new HashMap<String, List<Closure>>(); 52 private BuildSettings buildSettings; 53 54 /** 55 * The objects that are listening for build events 56 */ 57 private List<GrailsBuildListener> buildListeners = new LinkedList<GrailsBuildListener>(); 58 GrailsBuildEventListener(GroovyClassLoader scriptClassLoader, Binding binding, BuildSettings buildSettings)59 public GrailsBuildEventListener(GroovyClassLoader scriptClassLoader, Binding binding, BuildSettings buildSettings) { 60 this.classLoader = scriptClassLoader; 61 this.binding = binding; 62 this.buildSettings = buildSettings; 63 } 64 initialize()65 public void initialize() { 66 loadEventHooks(buildSettings); 67 loadGrailsBuildListeners(); 68 } 69 setClassLoader(GroovyClassLoader classLoader)70 public void setClassLoader(GroovyClassLoader classLoader) { 71 this.classLoader = classLoader; 72 } 73 setGlobalEventHooks(Map<String, List<Closure>> globalEventHooks)74 public void setGlobalEventHooks(Map<String, List<Closure>> globalEventHooks) { 75 this.globalEventHooks = globalEventHooks; 76 } 77 loadEventHooks(@uppressWarningsR) BuildSettings buildSettings)78 protected void loadEventHooks(@SuppressWarnings("hiding") BuildSettings buildSettings) { 79 if (buildSettings == null) { 80 return; 81 } 82 83 loadEventsScript(findEventsScript(new File(buildSettings.getUserHome(),".grails/scripts"))); 84 loadEventsScript(findEventsScript(new File(buildSettings.getBaseDir(), "scripts"))); 85 86 PluginBuildSettings pluginSettings = (PluginBuildSettings) binding.getVariable("pluginSettings"); 87 for (Resource pluginBase : pluginSettings.getPluginDirectories()) { 88 try { 89 loadEventsScript(findEventsScript(new File(pluginBase.getFile(), "scripts"))); 90 } 91 catch (IOException ex) { 92 throw new RuntimeException(ex); 93 } 94 } 95 } 96 loadGrailsBuildListeners()97 protected void loadGrailsBuildListeners() { 98 for (Object listener : buildSettings.getBuildListeners()) { 99 if (listener instanceof String) { 100 addGrailsBuildListener((String)listener); 101 } 102 else if (listener instanceof Class<?>) { 103 addGrailsBuildListener((Class<?>)listener); 104 } 105 else { 106 throw new IllegalStateException("buildSettings.getBuildListeners() returned a " + listener.getClass().getName()); 107 } 108 } 109 } 110 loadEventsScript(File eventScript)111 public void loadEventsScript(File eventScript) { 112 if (eventScript == null) { 113 return; 114 } 115 116 try { 117 Class<?> scriptClass = classLoader.parseClass(eventScript); 118 if (scriptClass == null) { 119 System.err.println("Could not load event script (script may be empty): " + eventScript); 120 return; 121 } 122 123 Script script = (Script) scriptClass.newInstance(); 124 script.setBinding(new Binding(binding.getVariables()) { 125 @Override 126 public void setVariable(String var, Object o) { 127 final Matcher matcher = EVENT_NAME_PATTERN.matcher(var); 128 if (matcher.matches() && (o instanceof Closure)) { 129 String eventName = matcher.group(1); 130 List<Closure> hooks = globalEventHooks.get(eventName); 131 if (hooks == null) { 132 hooks = new ArrayList<Closure>(); 133 globalEventHooks.put(eventName, hooks); 134 } 135 hooks.add((Closure) o); 136 } 137 super.setVariable(var, o); 138 } 139 }); 140 script.run(); 141 } 142 catch (Throwable e) { 143 GrailsUtil.deepSanitize(e); 144 e.printStackTrace(); 145 System.out.println("Error loading event script from file [" + eventScript + "] " + e.getMessage()); 146 } 147 } 148 findEventsScript(File dir)149 protected File findEventsScript(File dir) { 150 File f = new File(dir, "_Events.groovy"); 151 if (!f.exists()) { 152 f = new File(dir, "Events.groovy"); 153 if (f.exists()) { 154 GrailsUtil.deprecated("Use of 'Events.groovy' is DEPRECATED. Please rename to '_Events.groovy'."); 155 } 156 } 157 158 return f.exists() ? f : null; 159 } 160 buildStarted(BuildEvent buildEvent)161 public void buildStarted(BuildEvent buildEvent) { 162 // do nothing 163 } 164 buildFinished(BuildEvent buildEvent)165 public void buildFinished(BuildEvent buildEvent) { 166 // do nothing 167 } 168 targetStarted(BuildEvent buildEvent)169 public void targetStarted(BuildEvent buildEvent) { 170 String targetName = buildEvent.getTarget().getName(); 171 String eventName = GrailsNameUtils.getClassNameRepresentation(targetName) + "Start"; 172 triggerEvent(eventName, binding); 173 } 174 175 /** 176 * Triggers and event for the given name and binding 177 * @param eventName The name of the event 178 */ triggerEvent(String eventName)179 public void triggerEvent(String eventName) { 180 triggerEvent(eventName, binding); 181 } 182 183 /** 184 * Triggers an event for the given name and arguments 185 * @param eventName The name of the event 186 * @param arguments The arguments 187 */ triggerEvent(String eventName, Object... arguments)188 public void triggerEvent(String eventName, Object... arguments) { 189 List<Closure> handlers = globalEventHooks.get(eventName); 190 if (handlers != null) { 191 for (Closure handler : handlers) { 192 handler.setDelegate(binding); 193 try { 194 handler.call(arguments); 195 } 196 catch (MissingPropertyException mpe) { 197 // ignore 198 } 199 } 200 } 201 202 for (GrailsBuildListener buildListener : buildListeners) { 203 buildListener.receiveGrailsBuildEvent(eventName, arguments); 204 } 205 } 206 targetFinished(BuildEvent buildEvent)207 public void targetFinished(BuildEvent buildEvent) { 208 String targetName = buildEvent.getTarget().getName(); 209 String eventName = GrailsNameUtils.getClassNameRepresentation(targetName) + "End"; 210 triggerEvent(eventName, binding); 211 } 212 taskStarted(BuildEvent buildEvent)213 public void taskStarted(BuildEvent buildEvent) { 214 // do nothing 215 } 216 taskFinished(BuildEvent buildEvent)217 public void taskFinished(BuildEvent buildEvent) { 218 // do nothing 219 } 220 messageLogged(BuildEvent buildEvent)221 public void messageLogged(BuildEvent buildEvent) { 222 // do nothing 223 } 224 addGrailsBuildListener(String listenerClassName)225 protected void addGrailsBuildListener(String listenerClassName) { 226 Class<?> listenerClass; 227 try { 228 listenerClass = classLoader.loadClass(listenerClassName); 229 } 230 catch (ClassNotFoundException e) { 231 throw new RuntimeException("Could not load grails build listener class", e); 232 } 233 addGrailsBuildListener(listenerClass); 234 } 235 236 @SuppressWarnings("rawtypes") addGrailsBuildListener(Class listenerClass)237 protected void addGrailsBuildListener(Class listenerClass) { 238 if (!GrailsBuildListener.class.isAssignableFrom(listenerClass)) { 239 throw new RuntimeException("Intended grails build listener class of " + listenerClass.getName() + " does not implement " + GrailsBuildListener.class.getName()); 240 } 241 242 try { 243 GrailsBuildListener listener = (GrailsBuildListener)listenerClass.newInstance(); 244 addGrailsBuildListener(listener); 245 } 246 catch (Exception e) { 247 throw new RuntimeException("Could not instantiate " + listenerClass.getName(), e); 248 } 249 } 250 addGrailsBuildListener(GrailsBuildListener listener)251 public void addGrailsBuildListener(GrailsBuildListener listener) { 252 buildListeners.add(listener); 253 } 254 } 255