You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ScriptPlugin.java 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /*
  2. * Copyright (c) 2006-2013 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.addons.scriptplugin;
  23. import com.dmdirc.actions.CoreActionType;
  24. import com.dmdirc.config.IdentityManager;
  25. import com.dmdirc.interfaces.ActionController;
  26. import com.dmdirc.interfaces.ActionListener;
  27. import com.dmdirc.interfaces.actions.ActionType;
  28. import com.dmdirc.logger.ErrorLevel;
  29. import com.dmdirc.logger.Logger;
  30. import com.dmdirc.plugins.BasePlugin;
  31. import com.dmdirc.util.io.StreamUtils;
  32. import com.dmdirc.util.validators.ValidationResponse;
  33. import java.io.File;
  34. import java.io.FileInputStream;
  35. import java.io.FileOutputStream;
  36. import java.io.IOException;
  37. import java.util.HashMap;
  38. import java.util.List;
  39. import java.util.Map;
  40. import javax.script.ScriptEngineManager;
  41. /**
  42. * This allows javascript scripts to be used in DMDirc.
  43. */
  44. public final class ScriptPlugin extends BasePlugin implements ActionListener {
  45. /** Script Directory */
  46. private final String scriptDir;
  47. /** Script Engine Manager */
  48. private ScriptEngineManager scriptFactory = new ScriptEngineManager();
  49. /** Instance of the javaScriptHelper class */
  50. private JavaScriptHelper jsHelper = new JavaScriptHelper();
  51. /** Store Script State Name,Engine */
  52. private Map<String, ScriptEngineWrapper> scripts = new HashMap<String, ScriptEngineWrapper>();
  53. /** Used to store permanent variables */
  54. protected TypedProperties globalVariables = new TypedProperties();
  55. /** The action controller to use. */
  56. private final ActionController actionController;
  57. /**
  58. * Creates a new instance of the Script Plugin.
  59. *
  60. * @param actionController The action controller to register listeners with
  61. * @param identityManager The Identity Manager that controls the current config
  62. */
  63. public ScriptPlugin(final ActionController actionController, final IdentityManager identityManager) {
  64. super();
  65. scriptDir = identityManager.getConfigDir() + "scripts/";
  66. this.actionController = actionController;
  67. // Add the JS Helper to the scriptFactory
  68. getScriptFactory().put("globalHelper", getJavaScriptHelper());
  69. getScriptFactory().put("globalVariables", getGlobalVariables());
  70. registerCommand(new ScriptCommand(this), ScriptCommand.INFO);
  71. }
  72. /** {@inheritDoc} */
  73. @Override
  74. public void onLoad() {
  75. // Register the plugin_loaded action initially, this will be called
  76. // after this method finishes for us to register the rest.
  77. actionController.registerListener(this, CoreActionType.PLUGIN_LOADED);
  78. // Make sure our scripts dir exists
  79. final File newDir = new File(scriptDir);
  80. if (!newDir.exists()) { newDir.mkdirs(); }
  81. final File savedVariables = new File(scriptDir+"storedVariables");
  82. if (savedVariables.exists()) {
  83. FileInputStream fis = null;
  84. try {
  85. fis = new FileInputStream(savedVariables);
  86. globalVariables.load(fis);
  87. } catch (IOException e) {
  88. Logger.userError(ErrorLevel.LOW, "Error reading savedVariables from '"+savedVariables.getPath()+"': "+e.getMessage(), e);
  89. } finally {
  90. StreamUtils.close(fis);
  91. }
  92. }
  93. super.onLoad();
  94. }
  95. /** {@inheritDoc} */
  96. @Override
  97. public void onUnload() {
  98. actionController.unregisterListener(this);
  99. final File savedVariables = new File(scriptDir+"storedVariables");
  100. FileOutputStream fos = null;
  101. try {
  102. fos = new FileOutputStream(savedVariables);
  103. globalVariables.store(fos, "# DMDirc Script Plugin savedVariables");
  104. } catch (IOException e) {
  105. Logger.userError(ErrorLevel.LOW, "Error reading savedVariables to '"+savedVariables.getPath()+"': "+e.getMessage(), e);
  106. } finally {
  107. StreamUtils.close(fos);
  108. }
  109. super.onUnload();
  110. }
  111. /**
  112. * Register all the action types.
  113. * This will unregister all the actions first.
  114. */
  115. private void registerAll() {
  116. actionController.registerListener(this);
  117. for (Map.Entry<String, List<ActionType>> entry
  118. : actionController.getGroupedTypes().entrySet()) {
  119. final List<ActionType> types = entry.getValue();
  120. actionController.registerListener(this,
  121. types.toArray(new ActionType[types.size()]));
  122. }
  123. }
  124. /** {@inheritDoc} */
  125. @Override
  126. public void processEvent(final ActionType type, final StringBuffer format, final Object... arguments) {
  127. // Plugins may to register/unregister action types, so lets reregister all
  128. // the action types. This
  129. if (type.equals(CoreActionType.PLUGIN_LOADED) || type.equals(CoreActionType.PLUGIN_UNLOADED)) {
  130. registerAll();
  131. }
  132. callFunctionAll("action_"+type.toString().toLowerCase(), arguments);
  133. }
  134. /**
  135. * Get a clone of the scripts map.
  136. *
  137. * @return a clone of the scripts map
  138. */
  139. protected Map<String, ScriptEngineWrapper> getScripts() { return new HashMap<String, ScriptEngineWrapper>(scripts); }
  140. /**
  141. * Get a reference to the scriptFactory.
  142. *
  143. * @return a reference to the scriptFactory
  144. */
  145. protected ScriptEngineManager getScriptFactory() { return scriptFactory; }
  146. /**
  147. * Get a reference to the JavaScriptHelper
  148. *
  149. * @return a reference to the JavaScriptHelper
  150. */
  151. protected JavaScriptHelper getJavaScriptHelper() { return jsHelper; }
  152. /**
  153. * Get a reference to the GlobalVariables Properties
  154. *
  155. * @return a reference to the GlobalVariables Properties
  156. */
  157. protected TypedProperties getGlobalVariables() { return globalVariables; }
  158. /**
  159. * Get the name of the directory where scripts should be stored.
  160. *
  161. * @return The name of the directory where scripts should be stored.
  162. */
  163. protected String getScriptDir() { return scriptDir; }
  164. /** Reload all scripts */
  165. public void rehash() {
  166. for (final ScriptEngineWrapper engine : scripts.values()) {
  167. engine.reload();
  168. }
  169. // Advise the Garbage collector that now would be a good time to run
  170. System.gc();
  171. }
  172. /**
  173. * Call a function in all scripts.
  174. *
  175. * @param functionName Name of function
  176. * @param args Arguments for function
  177. */
  178. private void callFunctionAll(final String functionName, final Object... args) {
  179. for (final ScriptEngineWrapper engine : scripts.values()) {
  180. engine.callFunction(functionName, args);
  181. }
  182. }
  183. /**
  184. * Unload a script file.
  185. *
  186. * @param scriptFilename Path to script
  187. */
  188. public void unloadScript(final String scriptFilename) {
  189. if (scripts.containsKey(scriptFilename)) {
  190. // Tell it that its about to be unloaded.
  191. (scripts.get(scriptFilename)).callFunction("onUnload");
  192. // Remove the script
  193. scripts.remove(scriptFilename);
  194. // Advise the Garbage collector that now would be a good time to run
  195. System.gc();
  196. }
  197. }
  198. /**
  199. * Load a script file into a new jsEngine
  200. *
  201. * @param scriptFilename Path to script
  202. * @return true for Success (or already loaded), false for fail. (Fail occurs if script already exists, or if it has errors)
  203. */
  204. public boolean loadScript(final String scriptFilename) {
  205. if (!scripts.containsKey(scriptFilename)) {
  206. try {
  207. final ScriptEngineWrapper wrapper = new ScriptEngineWrapper(this, scriptFilename);
  208. scripts.put(scriptFilename, wrapper);
  209. } catch (Exception e) {
  210. Logger.userError(ErrorLevel.LOW, "Error loading '"+scriptFilename+"': "+e.getMessage(), e);
  211. return false;
  212. }
  213. }
  214. return true;
  215. }
  216. /** {@inheritDoc} */
  217. @Override
  218. public ValidationResponse checkPrerequisites() {
  219. if (getScriptFactory().getEngineByName("JavaScript") == null) {
  220. return new ValidationResponse("JavaScript Scripting Engine not found.");
  221. } else {
  222. return new ValidationResponse();
  223. }
  224. }
  225. /**
  226. * Get the reason for checkPrerequisites failing.
  227. *
  228. * @return Human-Readble reason for checkPrerequisites failing.
  229. */
  230. public String checkPrerequisitesReason() {
  231. if (getScriptFactory().getEngineByName("JavaScript") == null) {
  232. return "JavaScript Scripting Engine not found.";
  233. } else {
  234. return "";
  235. }
  236. }
  237. }