/server/src/com/mirth/connect/server/util/javascript/JavaScriptScopeUtil.java

https://github.com/get-vitamin-c/mirth-connect · Java · 416 lines · 254 code · 64 blank · 98 comment · 23 complexity · f8c71ed9a4985faa44925621aa15624a MD5 · raw file

  1. /*
  2. * Copyright (c) Mirth Corporation. All rights reserved.
  3. *
  4. * http://www.mirthcorp.com
  5. *
  6. * The software in this package is published under the terms of the MPL license a copy of which has
  7. * been included with this distribution in the LICENSE.txt file.
  8. */
  9. package com.mirth.connect.server.util.javascript;
  10. import java.util.List;
  11. import java.util.Map;
  12. import java.util.Map.Entry;
  13. import java.util.Properties;
  14. import org.apache.commons.collections.MapUtils;
  15. import org.apache.log4j.Logger;
  16. import org.mozilla.javascript.Context;
  17. import org.mozilla.javascript.ContextFactory;
  18. import org.mozilla.javascript.ImporterTopLevel;
  19. import org.mozilla.javascript.Script;
  20. import org.mozilla.javascript.Scriptable;
  21. import org.mozilla.javascript.ScriptableObject;
  22. import org.mozilla.javascript.Undefined;
  23. import com.mirth.connect.donkey.model.message.ConnectorMessage;
  24. import com.mirth.connect.donkey.model.message.Message;
  25. import com.mirth.connect.server.controllers.ConfigurationController;
  26. import com.mirth.connect.server.userutil.AlertSender;
  27. import com.mirth.connect.server.userutil.Attachment;
  28. import com.mirth.connect.server.userutil.ChannelMap;
  29. import com.mirth.connect.server.userutil.ImmutableResponse;
  30. import com.mirth.connect.server.userutil.MessageObject;
  31. import com.mirth.connect.server.userutil.MuleContext;
  32. import com.mirth.connect.server.userutil.VMRouter;
  33. import com.mirth.connect.server.util.GlobalChannelVariableStoreFactory;
  34. import com.mirth.connect.server.util.GlobalVariableStore;
  35. import com.mirth.connect.server.util.TemplateValueReplacer;
  36. import com.mirth.connect.userutil.ImmutableConnectorMessage;
  37. import com.mirth.connect.userutil.ImmutableMessage;
  38. import com.mirth.connect.userutil.Response;
  39. import com.mirth.connect.userutil.ResponseMap;
  40. import com.mirth.connect.userutil.Status;
  41. import com.mirth.connect.util.PropertyLoader;
  42. public class JavaScriptScopeUtil {
  43. private static Logger logger = Logger.getLogger(JavaScriptScopeUtil.class);
  44. private static ScriptableObject sealedSharedScope = null;
  45. private static Integer rhinoOptimizationLevel = null;
  46. static {
  47. ContextFactory.initGlobal(new StoppableContextFactory());
  48. }
  49. private static void initialize() {
  50. if (rhinoOptimizationLevel == null) {
  51. rhinoOptimizationLevel = -1;
  52. /*
  53. * Checks mirth.properties for the rhino.optimizationlevel property. Setting it to -1
  54. * runs it in interpretive mode. See MIRTH-1627 for more information.
  55. */
  56. Properties properties = PropertyLoader.loadProperties("mirth");
  57. if (MapUtils.isNotEmpty(properties) && properties.containsKey("rhino.optimizationlevel")) {
  58. rhinoOptimizationLevel = Integer.valueOf(properties.getProperty("rhino.optimizationlevel")).intValue();
  59. logger.debug("set Rhino context optimization level: " + rhinoOptimizationLevel);
  60. } else {
  61. logger.debug("using defualt Rhino context optimization level (-1)");
  62. }
  63. }
  64. }
  65. /*
  66. * Retrieves the Context for the current Thread; only initializes the shared scope if necessary.
  67. * The context must be cleaned up with Context.exit() when it is no longer needed.
  68. */
  69. protected static Context getContext() {
  70. initialize();
  71. Context context = ContextFactory.getGlobal().enterContext();
  72. context.setOptimizationLevel(rhinoOptimizationLevel);
  73. if (sealedSharedScope == null) {
  74. sealedSharedScope = new ImporterTopLevel(context);
  75. Script script = JavaScriptUtil.getCompiledGlobalSealedScript(context);
  76. script.exec(context, sealedSharedScope);
  77. sealedSharedScope.sealObject();
  78. }
  79. return context;
  80. }
  81. // Creates a new global scope within the current Context
  82. private static Scriptable getScope(Context context) {
  83. Scriptable scope = context.newObject(sealedSharedScope);
  84. scope.setPrototype(sealedSharedScope);
  85. scope.setParentScope(null);
  86. return scope;
  87. }
  88. /*
  89. * Private Scope Builders
  90. */
  91. private static void add(String name, Scriptable scope, Object object) {
  92. scope.put(name, scope, Context.javaToJS(object, scope));
  93. }
  94. // Raw Message String Builder
  95. private static void addRawMessage(Scriptable scope, String message) {
  96. add("message", scope, message);
  97. }
  98. // Message Builder
  99. private static void addMessage(Scriptable scope, Message message) {
  100. ImmutableMessage immutableMessage = new ImmutableMessage(message);
  101. add("message", scope, immutableMessage);
  102. // TODO: Deprecated, Remove in 3.1
  103. add("messageObject", scope, new MessageObject(immutableMessage.getConnectorMessages().get(0)));
  104. ConnectorMessage mergedConnectorMessage = message.getMergedConnectorMessage();
  105. ImmutableConnectorMessage immutableConnectorMessage = new ImmutableConnectorMessage(mergedConnectorMessage);
  106. add("sourceMap", scope, immutableConnectorMessage.getSourceMap());
  107. add("channelMap", scope, new ChannelMap(immutableConnectorMessage.getChannelMap(), immutableConnectorMessage.getSourceMap()));
  108. add("responseMap", scope, new ResponseMap(mergedConnectorMessage.getResponseMap(), immutableMessage.getDestinationNameMap()));
  109. }
  110. // ConnectorMessage Builder
  111. private static void addConnectorMessage(Scriptable scope, ImmutableConnectorMessage message) {
  112. // TODO: Deprecated, Remove in 3.1
  113. add("messageObject", scope, new MessageObject(message));
  114. add("connectorMessage", scope, message);
  115. add("sourceMap", scope, message.getSourceMap());
  116. add("connectorMap", scope, message.getConnectorMap());
  117. add("channelMap", scope, new ChannelMap(message.getChannelMap(), message.getSourceMap()));
  118. add("responseMap", scope, new ResponseMap(message.getResponseMap(), message.getDestinationNameMap()));
  119. add("connector", scope, message.getConnectorName());
  120. add("alerts", scope, new AlertSender(message));
  121. }
  122. private static void addResponse(Scriptable scope, Response response) {
  123. add("response", scope, new ImmutableResponse(response));
  124. add("responseStatus", scope, response.getStatus());
  125. add("responseErrorMessage", scope, response.getError());
  126. add("responseStatusMessage", scope, response.getStatusMessage());
  127. }
  128. // Router Builder
  129. private static void addRouter(Scriptable scope) {
  130. add("router", scope, new VMRouter());
  131. }
  132. // Replacer
  133. private static void addReplacer(Scriptable scope) {
  134. add("replacer", scope, new TemplateValueReplacer());
  135. }
  136. // Global Map Builder
  137. private static void addGlobalMap(Scriptable scope) {
  138. add("globalMap", scope, GlobalVariableStore.getInstance());
  139. }
  140. // Configuration Map Builder
  141. private static void addConfigurationMap(Scriptable scope) {
  142. add("configurationMap", scope, ConfigurationController.getInstance().getConfigurationMap());
  143. }
  144. // Channel Builder
  145. private static void addChannel(Scriptable scope, String channelId) {
  146. add("alerts", scope, new AlertSender(channelId));
  147. add("channelId", scope, channelId);
  148. add("globalChannelMap", scope, GlobalChannelVariableStoreFactory.getInstance().get(channelId));
  149. }
  150. // Logger builder
  151. private static void addLogger(Scriptable scope, Object logger) {
  152. add("logger", scope, logger);
  153. }
  154. // Status enum builder
  155. private static void addStatusValues(Scriptable scope) {
  156. for (Status status : Status.values()) {
  157. add(status.toString(), scope, status);
  158. }
  159. }
  160. /*
  161. * Private Basic Scopes
  162. */
  163. private static Scriptable getBasicScope(Context context) {
  164. Scriptable scope = getScope(context);
  165. addRouter(scope);
  166. addReplacer(scope);
  167. addConfigurationMap(scope);
  168. addGlobalMap(scope);
  169. return scope;
  170. }
  171. private static Scriptable getBasicScope(Context context, Object logger) {
  172. Scriptable scope = getBasicScope(context);
  173. addLogger(scope, logger);
  174. return scope;
  175. }
  176. private static Scriptable getBasicScope(Context context, Object logger, String channelId) {
  177. Scriptable scope = getBasicScope(context, logger);
  178. addChannel(scope, channelId);
  179. return scope;
  180. }
  181. private static Scriptable getBasicScope(Context context, Object logger, ImmutableConnectorMessage message) {
  182. return getBasicScope(context, logger, message.getChannelId());
  183. }
  184. /*
  185. * Public Phase-specific Scopes
  186. */
  187. /**
  188. * Since this method calls getContext(), anything calling it should wrap this method in a
  189. * try-finally with Context.exit() in the finally block.
  190. */
  191. public static Scriptable getAttachmentScope(Object logger, String channelId, String message, List<Attachment> attachments) {
  192. Scriptable scope = getBasicScope(getContext(), logger, channelId);
  193. addRawMessage(scope, message);
  194. add("mirth_attachments", scope, attachments);
  195. return scope;
  196. }
  197. /**
  198. * Since this method calls getContext(), anything calling it should wrap this method in a
  199. * try-finally with Context.exit() in the finally block.
  200. */
  201. public static Scriptable getPreprocessorScope(Object logger, String channelId, String message, ImmutableConnectorMessage connectorMessage) {
  202. Scriptable scope = getBasicScope(getContext(), logger, channelId);
  203. addRawMessage(scope, message);
  204. addConnectorMessage(scope, connectorMessage);
  205. // TODO: Deprecated, Remove in 3.1
  206. add("muleContext", scope, new MuleContext(connectorMessage));
  207. return scope;
  208. }
  209. /**
  210. * Since this method calls getContext(), anything calling it should wrap this method in a
  211. * try-finally with Context.exit() in the finally block.
  212. */
  213. public static Scriptable getPostprocessorScope(Object logger, String channelId, Message message) {
  214. Scriptable scope = getBasicScope(getContext(), logger, channelId);
  215. addStatusValues(scope);
  216. addMessage(scope, message);
  217. return scope;
  218. }
  219. /**
  220. * Since this method calls getContext(), anything calling it should wrap this method in a
  221. * try-finally with Context.exit() in the finally block.
  222. */
  223. public static Scriptable getPostprocessorScope(Object logger, String channelId, Message message, Response response) {
  224. Scriptable scope = getBasicScope(getContext(), logger, channelId);
  225. addMessage(scope, message);
  226. addStatusValues(scope);
  227. add("response", scope, response);
  228. return scope;
  229. }
  230. /**
  231. * Since this method calls getContext(), anything calling it should wrap this method in a
  232. * try-finally with Context.exit() in the finally block.
  233. */
  234. public static Scriptable getFilterTransformerScope(Object logger, ImmutableConnectorMessage message, String template, Object phase) {
  235. Scriptable scope = getBasicScope(getContext(), logger, message);
  236. addConnectorMessage(scope, message);
  237. add("template", scope, template);
  238. add("phase", scope, phase);
  239. return scope;
  240. }
  241. /**
  242. * Since this method calls getContext(), anything calling it should wrap this method in a
  243. * try-finally with Context.exit() in the finally block.
  244. */
  245. public static Scriptable getResponseTransformerScope(Object logger, Response response, ImmutableConnectorMessage message, String template) {
  246. Scriptable scope = getBasicScope(getContext(), logger, message);
  247. addConnectorMessage(scope, message);
  248. addResponse(scope, response);
  249. addStatusValues(scope);
  250. add("template", scope, template);
  251. return scope;
  252. }
  253. /**
  254. * Since this method calls getContext(), anything calling it should wrap this method in a
  255. * try-finally with Context.exit() in the finally block.
  256. */
  257. public static Scriptable getDeployScope(Object logger, String channelId) {
  258. return getBasicScope(getContext(), logger, channelId);
  259. }
  260. /**
  261. * Since this method calls getContext(), anything calling it should wrap this method in a
  262. * try-finally with Context.exit() in the finally block.
  263. */
  264. public static Scriptable getDeployScope(Object logger) {
  265. return getBasicScope(getContext(), logger);
  266. }
  267. /**
  268. * Since this method calls getContext(), anything calling it should wrap this method in a
  269. * try-finally with Context.exit() in the finally block.
  270. */
  271. public static Scriptable getShutdownScope(Object logger, String channelId) {
  272. return getBasicScope(getContext(), logger, channelId);
  273. }
  274. /**
  275. * Since this method calls getContext(), anything calling it should wrap this method in a
  276. * try-finally with Context.exit() in the finally block.
  277. */
  278. public static Scriptable getShutdownScope(Object logger) {
  279. return getBasicScope(getContext(), logger);
  280. }
  281. /**
  282. * Since this method calls getContext(), anything calling it should wrap this method in a
  283. * try-finally with Context.exit() in the finally block.
  284. */
  285. public static Scriptable getMessageReceiverScope(Object logger, String channelId) {
  286. return getBasicScope(getContext(), logger, channelId);
  287. }
  288. /**
  289. * Since this method calls getContext(), anything calling it should wrap this method in a
  290. * try-finally with Context.exit() in the finally block.
  291. */
  292. public static Scriptable getMessageReceiverScope(Object logger, String channelId, ImmutableConnectorMessage message) {
  293. Scriptable scope = getBasicScope(getContext(), logger, channelId);
  294. addConnectorMessage(scope, message);
  295. return scope;
  296. }
  297. /**
  298. * Since this method calls getContext(), anything calling it should wrap this method in a
  299. * try-finally with Context.exit() in the finally block.
  300. */
  301. public static Scriptable getMessageDispatcherScope(Object logger, String channelId, ImmutableConnectorMessage message) {
  302. Scriptable scope = getBasicScope(getContext(), logger, channelId);
  303. addConnectorMessage(scope, message);
  304. addStatusValues(scope);
  305. return scope;
  306. }
  307. /**
  308. * Since this method calls getContext(), anything calling it should wrap this method in a
  309. * try-finally with Context.exit() in the finally block.
  310. */
  311. public static Scriptable getBatchProcessorScope(Object logger, String channelId, Map<String, Object> scopeObjects) {
  312. Scriptable scope = getBasicScope(getContext(), logger);
  313. for (Entry<String, Object> entry : scopeObjects.entrySet()) {
  314. add(entry.getKey(), scope, entry.getValue());
  315. }
  316. if (channelId != null)
  317. addChannel(scope, channelId);
  318. return scope;
  319. }
  320. /*
  321. * Functions to get variables back out of the scope
  322. */
  323. public static String getTransformedDataFromScope(Scriptable scope, boolean hasTemplate) {
  324. String result = null;
  325. Object transformedData = null;
  326. if (hasTemplate) {
  327. transformedData = scope.get("tmp", scope);
  328. } else {
  329. transformedData = scope.get("msg", scope);
  330. }
  331. if (transformedData != Scriptable.NOT_FOUND) {
  332. result = Context.toString(transformedData);
  333. }
  334. return result;
  335. }
  336. public static void getResponseDataFromScope(Scriptable scope, Response response) {
  337. Object status = scope.get("responseStatus", scope);
  338. Object statusMessage = scope.get("responseStatusMessage", scope);
  339. Object errorMessage = scope.get("responseErrorMessage", scope);
  340. response.setStatus((Status) Context.jsToJava(status, Status.class));
  341. if (statusMessage != null && !(statusMessage instanceof Undefined)) {
  342. response.setStatusMessage(Context.toString(statusMessage));
  343. } else {
  344. response.setStatusMessage(null);
  345. }
  346. if (errorMessage != null && !(errorMessage instanceof Undefined)) {
  347. response.setError(Context.toString(errorMessage));
  348. } else {
  349. response.setError(null);
  350. }
  351. }
  352. }