/src/main/groovy/com/onresolve/jira/groovy/ScriptManagerImpl.groovy
Groovy | 355 lines | 241 code | 49 blank | 65 comment | 26 complexity | 413a3ec808981bd4c8f3a6336b23be3e MD5 | raw file
- package com.onresolve.jira.groovy
-
- import com.atlassian.core.action.ActionUtils
- import com.atlassian.core.ofbiz.CoreFactory
- import com.atlassian.core.ofbiz.util.OFBizPropertyUtils
- import com.atlassian.core.util.ClassLoaderUtils
- import com.atlassian.event.api.EventPublisher
- import com.atlassian.jira.ComponentManager
- import com.atlassian.jira.InfrastructureException
- import com.atlassian.jira.action.admin.ListenerDelete
- import com.atlassian.jira.component.ComponentAccessor
- import com.atlassian.jira.config.properties.PropertiesManager
- import com.atlassian.jira.event.ListenerManager
- import com.atlassian.jira.extension.Startable
- import com.atlassian.jira.util.collect.MapBuilder
- import com.atlassian.jira.util.json.JSONArray
- import com.atlassian.jira.util.json.JSONObject
- import com.onresolve.jira.groovy.canned.util.JoinClassLoader
- import com.onresolve.jira.groovy.customfield.CustomFieldConfiguration
- import com.onresolve.jira.groovy.listener.GroovyListener
- import com.onresolve.jira.groovy.listener.ScriptRunnerUberListener
- import com.opensymphony.module.propertyset.PropertySet
- import org.apache.log4j.Category
- import org.ofbiz.core.entity.GenericValue
- import org.springframework.beans.factory.DisposableBean
- import org.springframework.beans.factory.InitializingBean
- import webwork.dispatcher.ActionResult
- import com.onresolve.jira.groovy.canned.CannedScript
- import org.codehaus.groovy.control.CompilerConfiguration
-
- class ScriptManagerImpl implements ScriptManager, InitializingBean, DisposableBean, Startable {
- private Properties props
- private GroovyClassLoader gclx;
- PropertiesManager pm = ComponentManager.getComponentInstanceOfType(PropertiesManager.class)
- public static String CONFIG_CUSTOMFIELDS = "com.onresolve.jira.groovy.groovyrunner:customfields"
- public static String CONFIG_LISTENERS = "com.onresolve.jira.groovy.groovyrunner:groovyrunner"
-
- Category log = Category.getInstance(ScriptManagerImpl.class)
- private final String LISTENER_NAME = "Script Runner Uber-Listener";
- private final EventPublisher eventPublisher;
-
- // ref to listener class that we can unregister if we need to
- def listener;
-
- ScriptManagerImpl(EventPublisher eventPublisher) {
- this.eventPublisher = eventPublisher
- }
-
-
- public Properties getProperties() {
- if (!props) {
- log.debug ("Loading properties")
-
- props = new Properties(System.getProperties());
- InputStream propsResource = ClassLoaderUtils.getResourceAsStream("com/onresolve/jira/groovy/groovyRunner.properties", CannedScriptRunner.class);
- if (propsResource == null) throw new InfrastructureException("Could not locate groovyRunner.properties in classpath");
- props.load(propsResource);
- }
-
- return props
- }
-
- public GroovyClassLoader getGcl() {
-
- if (!gclx) {
- def webAppClassLoader = ComponentAccessor.class.getClassLoader()
-
- def resourceDirs = System.getProperty("plugin.resource.directories")
- def joinClassLoader
- if (resourceDirs) {
- def urls = resourceDirs?.split(/,/)?.collect {new File(it).toURI().toURL()}
- def altResClassLoader = new URLClassLoader(urls as URL[], webAppClassLoader)
-
- // if this way round: this.class.getClassLoader(), altResClassLoader
- // we get classcast exceptions, eg:
- // GroovyCastException: Cannot cast object 'com.atlassian.jira.config.DefaultConstantsManager@f4af9b'
- // with class 'com.atlassian.jira.config.DefaultConstantsManager' to class 'com.atlassian.jira.config.ConstantsManager'
-
- // we can't get class reloading, because the ones in the bundle are found first,
- // unless we remove all the classes from the build
- joinClassLoader = new JoinClassLoader(altResClassLoader, this.class.getClassLoader());
-
- // if the other way round we get the comment below
- }
- else {
- // todo: this is likely wrong for getting OSGi components using ComponentManager.getComponentInstanceOfType
- // classcast exceptions
- joinClassLoader = new JoinClassLoader(webAppClassLoader, this.class.getClassLoader());
- }
-
- GroovyClassLoader gcl = new GroovyClassLoader(joinClassLoader)
- gcl.setShouldRecompile(true);
- gcl.setResourceLoader(new JiraGroovyResourceLoader(gcl, this))
- gclx = gcl
- }
- return gclx;
- }
-
- public void setupConfig() {
- CustomFieldConfiguration configuration = new CustomFieldConfiguration()
- configuration.setId(10080)
- configuration.setScript("return \"some new bollo\"")
- configuration.setTemplate("\$!value")
-
- saveConfig(configuration.asJsonString())
-
- // saveConfig(new JSONObject(
- // ["10070":
- // [
- // script: "return \"some bollo\"",
- // template: "\$!value",
- // templateFile: "",
- // ]
- // ]))
- }
-
- public void saveConfig(JSONObject configs) {
- pm.getPropertySet().setText(CONFIG_CUSTOMFIELDS, (configs as JSONObject).toString())
- }
-
- public Map<Long,CustomFieldConfiguration> getConfigs() {
- String prop = pm.getPropertySet().getText(CONFIG_CUSTOMFIELDS) ?: [:]
- // log.debug("getConfigs: $prop")
- JSONObject jsonObject = new JSONObject(prop)
- Map<Long,CustomFieldConfiguration> rt = [:]
- Map map = getAsActualType(jsonObject) as Map
- map.each {String k, Map v ->
- CustomFieldConfiguration cfc = new CustomFieldConfiguration()
- cfc.setId(k as Long)
- cfc.setScript(v.get(CustomFieldConfiguration.SCRIPT) as String)
- cfc.setTemplate(v.get(CustomFieldConfiguration.TEMPLATE) as String)
- cfc.setModelTemplate(v.get(CustomFieldConfiguration.MODEL_TEMPLATE) as String)
- cfc.setCustomTemplate(v.get(CustomFieldConfiguration.CUSTOM_TEMPLATE) as String)
- cfc.setScriptFile(v.get(CustomFieldConfiguration.SCRIPT_FILE) as String)
- rt.put(k as Long, cfc)
- }
-
- return rt
- }
-
- // todo: exists in 3 places now
- // todo: this is the version that I think is fixed for nulls
- public Object getAsActualType(Object o) {
- Object n = null
- if (o instanceof JSONArray) {
- n = []
- (0..o.length() - 1).each {int i ->
- n.add getAsActualType(o.get(i))
- }
- }
- else if (o instanceof JSONObject) {
- n = [:]
- o.keys().each {String k ->
- if (o.get(k) == JSONObject.NULL) {
- n.put(k, null)
- }
- else {
- n.put(k, getAsActualType(o.get(k)))
- }
- }
- }
- else {
- return o
- }
- return n
- }
-
- public CustomFieldConfiguration getConfigFor(Long cfId) {
-
- def configs = getConfigs()
- configs.containsKey(cfId) ? configs.get(cfId) : null
-
- /*
- String prop = pm.getPropertySet().getText(CONFIG_CUSTOMFIELDS)
-
- // log.debug("getConfigFor: $prop")
- if (!prop) {
- return null
- }
- try {
- JSONObject map = new JSONObject(prop)
- Map configs = getAsActualType(map) as Map
- if (configs.size() == 0) {
- return null
- }
- if (configs.containsKey(cfId.toString())) {
- // todo: this is bullshit
- return new CustomFieldConfiguration(new JSONObject(configs[cfId.toString()] as Map).toString())
- }
- else {
- return null
- }
- }
- catch (Exception e) {
- log.error ("property parse exception: ${e.message}", e)
- return null
- }
- */
- }
-
- public void deleteLegacyUberListener() {
-
- // move groovy listeners to properties format
- final Collection<GenericValue> listenerConfigs = CoreFactory.getGenericDelegator().findAll("ListenerConfig");
- PropertiesManager pm = ComponentManager.getComponentInstanceOfType(PropertiesManager)
- PropertySet ps = pm.getPropertySet()
-
- def cannedScriptListener = new CannedScriptListener(this)
- List configs = getListenerConfigs()
- // log.debug("configs: $configs")
-
- def allEvents = ComponentManager.getInstance().getEventTypeManager().getEventTypesMap().keySet()*.toString()
- listenerConfigs.each {gv ->
- if (gv.get("clazz") == "com.onresolve.jira.groovy.listener.GroovyListener") {
- Map params = getListenerParams(gv)
- log.debug("move class: " + params.get("class"))
- configs.add(
- [
- events:allEvents,
- projects: [""],
- clazz: "com.onresolve.jira.groovy.canned.workflow.listeners.CustomListener",
- params: [
- clazz: params.get("class"),
- ]
- ]
- )
- log.debug("Save: " + (configs as JSONArray).toString())
- cannedScriptListener.saveConfig(configs)
- deleteListener(gv.get("name") as String, GroovyListener.class)
- }
- }
-
- // delete both types of old listeners
- deleteListener(LISTENER_NAME, ScriptRunnerUberListener.class)
- }
-
- private deleteListener(String listenerName, Class clazz) {
- ListenerManager listenerManager = ComponentManager.getComponentInstanceOfType(ListenerManager.class)
- listenerManager.refresh()
-
- if (listenerManager.getListeners().containsKey(listenerName)) {
- if (listenerManager.respondsTo("deleteListener")) {
- listenerManager.deleteListener(clazz)
- }
- else {
- ActionResult aResult = CoreFactory.getActionDispatcher().execute(ListenerDelete.class.getName(),
- ["name": listenerName, "clazz": clazz.getName()])
- ActionUtils.checkForErrors(aResult);
- }
- log.info "Deleted listener $listenerName"
- }
- else {
- log.debug("Listener $listenerName wasn't present")
- }
- }
-
- private Map getListenerParams(GenericValue listenerConfig) {
- final PropertySet ps = OFBizPropertyUtils.getPropertySet(listenerConfig);
-
- final Collection<String> paramKeys = ps.getKeys(PropertySet.STRING);
- final MapBuilder<String, String> params = MapBuilder.newBuilder();
- for (final String key: paramKeys) {
- params.add(key, ps.getString(key));
- }
- return params.toMap()
- }
-
- void afterPropertiesSet() {
- // migrate properties to new format
- pm.getPropertySet().setText(CONFIG_LISTENERS, (getListenerConfigs() as JSONArray).toString())
-
-
- // attempt to init the gse, because if it's called from the dash we can end up with it null
- // possibly soemthing to do with classloaders
- CannedScriptRunner.getGse()
-
- // delete legacy listener
- try {
- deleteLegacyUberListener()
- }
- catch (Exception e) {
- log.debug "Listeners weren't ready, not a problem."
- }
-
-
- // register new listener
- listener = new ScriptRunnerUberListener(this)
- eventPublisher.register(listener)
- }
-
- /**
- * Called when the plugin is being disabled or removed.
- * @throws Exception
- */
- @Override
- public void destroy() throws Exception {
- // unregister ourselves with the EventPublisher
- if (listener) {
- log.debug("Unregistering previously registered listener instance")
- eventPublisher.unregister(listener);
- }
- }
-
- void start() {
- try {
- deleteLegacyUberListener()
- }
- catch (Exception e) {
- log.warn ("Error migrating listeners to new format", e)
- }
- }
-
- public List getListenerConfigs() {
- PropertiesManager pm = ComponentManager.getComponentInstanceOfType(PropertiesManager)
- String prop = pm.getPropertySet().getText(CONFIG_LISTENERS)
-
- if (!prop) {
- return []
- }
- try {
- // old storage method
- // if this is a Config parse it and convert to JSON
- if (prop.startsWith("config")) {
- log.debug("prop now: " + prop.class)
- def configSlurper = getGcl().loadClass("groovy.util.ConfigSlurper", false, true)
- def restoredCo = configSlurper.newInstance().parse(prop as String)
- log.debug("restoredCo: $restoredCo")
- return restoredCo?.config as List
- }
- else {
- // convert params to proper objects
- JSONArray array = new JSONArray(prop)
- if (array.length() == 0) {
- return []
- }
- List list = getAsActualType(array) as List
- return list
-
- }
- }
- catch (Exception e) {
- log.error ("property parse exception: ${e.message}")
- return []
- }
- }
-
- public Map executeScript(String clsName, Map params) {
-
- log.warn ("Execute canned script $clsName")
- return new CannedScriptRunner(this).runCannedScript(clsName, params);
-
- // def instance = Class.forName(clsName).newInstance() as CannedScript
- // instance.doScript(params)
- }
- }