Java tutorial
/* * Copyright (c) Mirth Corporation. All rights reserved. * * http://www.mirthcorp.com * * The software in this package is published under the terms of the MPL license a copy of which has * been included with this distribution in the LICENSE.txt file. */ package com.mirth.connect.client.ui.panels.connectors; import java.awt.Color; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.DefaultComboBoxModel; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.border.TitledBorder; import net.miginfocom.swing.MigLayout; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.math.NumberUtils; import com.mirth.connect.client.ui.ChannelSetup; import com.mirth.connect.client.ui.Frame; import com.mirth.connect.client.ui.LoadedExtensions; import com.mirth.connect.client.ui.PlatformUI; import com.mirth.connect.client.ui.UIConstants; import com.mirth.connect.client.ui.components.MirthComboBox; import com.mirth.connect.client.ui.components.MirthFieldConstraints; import com.mirth.connect.client.ui.components.MirthRadioButton; import com.mirth.connect.client.ui.components.MirthTextField; import com.mirth.connect.client.ui.editors.transformer.TransformerPane; import com.mirth.connect.donkey.model.channel.SourceConnectorProperties; import com.mirth.connect.donkey.model.channel.SourceConnectorPropertiesInterface; import com.mirth.connect.model.Channel; import com.mirth.connect.model.Connector; import com.mirth.connect.model.MessageStorageMode; import com.mirth.connect.model.Step; public class SourceSettingsPanel extends JPanel { /* * This regular expression uses alternation to capture either the "responseMap.put" syntax, or * the "$r('key'," syntax. Kleene closures for whitespace are used in between every method token * since it is legal JavaScript. Instead of checking ['"] once at the beginning and end, it * checks once and then uses a backreference later on. That way you can capture keys like * "Foo's Bar". It also accounts for backslashes before any subsequent backreferences so that * "Foo\"s Bar" would still be captured. In the "$r" case, the regular expression also performs * a lookahead to ensure that there is a comma after the first argument, indicating that it is * the "put" version of the method, not the "get" version. */ private final String RESULT_PATTERN = "responseMap\\s*\\.\\s*put\\s*\\(\\s*(['\"])(((?!(?<!\\\\)\\1).)*)(?<!\\\\)\\1|\\$r\\s*\\(\\s*(['\"])(((?!(?<!\\\\)\\4).)*)(?<!\\\\)\\4(?=\\s*,)"; private final static int FULL_NAME_MATCHER_INDEX = 2; private final static int SHORT_NAME_MATCHER_INDEX = 5; private Frame parent; private ChannelSetup channelSetup; private List<String> queueOnRespondFromNames; private List<Object> queueOffRespondFromNames; public SourceSettingsPanel() { parent = PlatformUI.MIRTH_FRAME; initComponents(); initLayout(); queueWarningLabel.setVisible(false); } public void setChannelSetup(ChannelSetup channelSetup) { this.channelSetup = channelSetup; } public void setProperties(SourceConnectorPropertiesInterface propertiesInterface) { SourceConnectorProperties properties = propertiesInterface.getSourceConnectorProperties(); updateResponseDropDown(propertiesInterface, true); // Set the source queue combo box if (properties.isRespondAfterProcessing()) { sourceQueueComboBox.setSelectedIndex(0); } else { sourceQueueComboBox.setSelectedIndex(1); } if (properties.getQueueBufferSize() > 0) { queueBufferSizeField.setText(String.valueOf(properties.getQueueBufferSize())); } else { queueBufferSizeField.setText(String.valueOf(channelSetup.defaultQueueBufferSize)); } processBatchLabel.setEnabled(propertiesInterface.canBatch()); processBatchYesRadio.setEnabled(propertiesInterface.canBatch()); processBatchNoRadio.setEnabled(propertiesInterface.canBatch()); if (properties.isProcessBatch()) { processBatchYesRadio.setSelected(true); } else { processBatchNoRadio.setSelected(true); } batchResponseLabel.setEnabled(propertiesInterface.canBatch() && properties.isProcessBatch()); batchResponseFirstRadio.setEnabled(propertiesInterface.canBatch() && properties.isProcessBatch()); batchResponseLastRadio.setEnabled(propertiesInterface.canBatch() && properties.isProcessBatch()); if (properties.isFirstResponse()) { batchResponseFirstRadio.setSelected(true); } else { batchResponseLastRadio.setSelected(true); } processingThreadsField.setText(String.valueOf(properties.getProcessingThreads())); } public void updateResponseDropDown(SourceConnectorPropertiesInterface propertiesInterface, boolean channelLoad) { SourceConnectorProperties properties = propertiesInterface.getSourceConnectorProperties(); boolean enabled = parent.isSaveEnabled(); Channel channel = parent.channelEditPanel.currentChannel; Set<Object> variables = new LinkedHashSet<Object>(); variables.addAll(Arrays.asList(SourceConnectorProperties.QUEUE_OFF_RESPONSES)); List<Step> stepsToCheck = new ArrayList<Step>(); stepsToCheck.addAll(channel.getSourceConnector().getTransformer().getSteps()); List<String> scripts = new ArrayList<String>(); for (Connector connector : channel.getDestinationConnectors()) { ConnectorSettingsPanel tempConnector = LoadedExtensions.getInstance().getDestinationConnectors() .get(connector.getTransportName()); scripts.addAll(tempConnector.getScripts(connector.getProperties())); /* * We add an Entry object instead of just the connector name, so that the back-end * response variable is the "d#" key, while the front-end combo box display is the full * connector name. */ variables.add(new SimpleEntry<String, String>("d" + String.valueOf(connector.getMetaDataId()), connector.getName()) { @Override public String toString() { return getValue(); } }); stepsToCheck.addAll(connector.getTransformer().getSteps()); stepsToCheck.addAll(connector.getResponseTransformer().getSteps()); } Pattern pattern = Pattern.compile(RESULT_PATTERN); for (Iterator it = stepsToCheck.iterator(); it.hasNext();) { Step step = (Step) it.next(); Map data; data = (Map) step.getData(); if (step.getType().equalsIgnoreCase(TransformerPane.JAVASCRIPT_TYPE)) { Matcher matcher = pattern.matcher(step.getScript()); while (matcher.find()) { variables.add(getMapKey(matcher)); } } else if (step.getType().equalsIgnoreCase(TransformerPane.MAPPER_TYPE)) { if (data.containsKey(UIConstants.IS_GLOBAL)) { if (((String) data.get(UIConstants.IS_GLOBAL)) .equalsIgnoreCase(UIConstants.IS_GLOBAL_RESPONSE)) { variables.add((String) data.get("Variable")); } } } } scripts.add(channel.getPreprocessingScript()); scripts.add(channel.getPostprocessingScript()); for (String script : scripts) { if (script != null && script.length() > 0) { Matcher matcher = pattern.matcher(script); while (matcher.find()) { variables.add(getMapKey(matcher)); } } } queueOnRespondFromNames = new ArrayList<String>( Arrays.asList(SourceConnectorProperties.QUEUE_ON_RESPONSES)); queueOffRespondFromNames = new ArrayList<Object>(variables); if (channelLoad) { /* * The response variable is the response map key. To ensure that the destination name * still shows up in the combo box, we use an Entry object for the selected item, rather * than a single String. */ Object selectedItem = properties.getResponseVariable(); for (Connector connector : channel.getDestinationConnectors()) { if (selectedItem.equals("d" + String.valueOf(connector.getMetaDataId()))) { selectedItem = new SimpleEntry<String, String>("d" + String.valueOf(connector.getMetaDataId()), connector.getName()) { @Override public String toString() { return getValue(); } }; break; } } responseComboBox.setModel(new DefaultComboBoxModel(variables.toArray())); setSelectedItem(selectedItem); } else { updateSelectedResponseItem(); } parent.setSaveEnabled(enabled); } private void updateSelectedResponseItem() { Object selectedItem = responseComboBox.getSelectedItem(); if (sourceQueueComboBox.getSelectedIndex() == 0) { responseComboBox.setModel(new DefaultComboBoxModel(queueOffRespondFromNames.toArray())); queueBufferSizeLabel.setEnabled(false); queueBufferSizeField.setEnabled(false); } else { responseComboBox.setModel(new DefaultComboBoxModel(queueOnRespondFromNames.toArray())); queueBufferSizeLabel.setEnabled(true); queueBufferSizeField.setEnabled(true); } setSelectedItem(selectedItem); channelSetup.saveSourcePanel(); MessageStorageMode messageStorageMode = channelSetup.getMessageStorageMode(); channelSetup.updateQueueWarning(messageStorageMode); updateQueueWarning(messageStorageMode); } public void fillProperties(SourceConnectorPropertiesInterface propertiesInterface) { SourceConnectorProperties properties = propertiesInterface.getSourceConnectorProperties(); properties.setQueueBufferSize(NumberUtils.toInt(queueBufferSizeField.getText())); if (responseComboBox.getSelectedItem() instanceof Entry) { properties.setResponseVariable(((Entry<String, String>) responseComboBox.getSelectedItem()).getKey()); } else { properties.setResponseVariable((String) responseComboBox.getSelectedItem()); } properties.setRespondAfterProcessing(sourceQueueComboBox.getSelectedIndex() == 0); properties.setProcessBatch(processBatchYesRadio.isSelected()); properties.setFirstResponse(batchResponseFirstRadio.isSelected()); properties.setProcessingThreads(NumberUtils.toInt(processingThreadsField.getText(), 0)); } public boolean checkProperties(SourceConnectorPropertiesInterface propertiesInterface, boolean highlight) { SourceConnectorProperties properties = propertiesInterface.getSourceConnectorProperties(); boolean valid = true; if (highlight && properties.getQueueBufferSize() <= 0) { queueBufferSizeField.setBackground(UIConstants.INVALID_COLOR); valid = false; } if (properties.getProcessingThreads() <= 0) { processingThreadsField.setBackground(UIConstants.INVALID_COLOR); valid = false; } return valid; } private String getMapKey(Matcher matcher) { /* * Since multiple capturing groups are used and the final key could reside on either side of * the alternation, we use two specific group indices (2 and 5), one for the full * "responseMap" case and one for the short "$r" case. We also replace JavaScript-specific * escape sequences like \', \", etc. */ String key = matcher.group(FULL_NAME_MATCHER_INDEX); if (key == null) { key = matcher.group(SHORT_NAME_MATCHER_INDEX); } return StringEscapeUtils.unescapeEcmaScript(key); } private void setSelectedItem(Object selectedItem) { DefaultComboBoxModel model = (DefaultComboBoxModel) responseComboBox.getModel(); if (selectedItem instanceof Entry) { /* * If the selected item is an Entry and the key ("d#") is the same as an Entry in the * model, then they're "the same", and that entry is selected. */ for (int i = 0; i <= model.getSize() - 1; i++) { if (model.getElementAt(i) instanceof Entry && ((Entry<String, String>) selectedItem).getKey() .equals(((Entry<String, String>) model.getElementAt(i)).getKey())) { responseComboBox.setSelectedIndex(i); return; } } } else if (model.getIndexOf(selectedItem) >= 0) { responseComboBox.setSelectedItem(selectedItem); return; } responseComboBox.setSelectedIndex(0); } public void resetInvalidProperties() { queueBufferSizeField.setBackground(null); processingThreadsField.setBackground(null); } public void updateQueueWarning(MessageStorageMode messageStorageMode) { switch (messageStorageMode) { case METADATA: case DISABLED: queueWarningLabel.setVisible(sourceQueueComboBox.getSelectedIndex() == 1); break; default: queueWarningLabel.setVisible(false); break; } } private void initComponents() { setBackground(UIConstants.BACKGROUND_COLOR); setBorder(BorderFactory.createTitledBorder( BorderFactory.createMatteBorder(1, 0, 0, 0, new Color(204, 204, 204)), "Source Settings", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, new Font("Tahoma", 1, 11))); sourceQueueLabel = new JLabel("Source Queue:"); sourceQueueComboBox = new MirthComboBox(); sourceQueueComboBox.setModel(new DefaultComboBoxModel( new String[] { "OFF (Respond after processing)", "ON (Respond before processing)" })); sourceQueueComboBox.setToolTipText( "<html>Selecting OFF will process the message before sending the response (can use response from destinations)<br>Selecting ON will queue messages and immediately send a response (cannot use response from destinations)</html>"); sourceQueueComboBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { sourceQueueComboBoxActionPerformed(evt); } }); queueWarningLabel = new JLabel("Queuing is not supported by the current message storage mode."); queueWarningLabel.setForeground(Color.RED); queueBufferSizeLabel = new JLabel("Queue Buffer Size:"); queueBufferSizeField = new JTextField(); queueBufferSizeField.setDocument(new MirthFieldConstraints(0, false, false, true)); queueBufferSizeField.setToolTipText( "<html>The buffer size for the source queue.<br/>Up to this many connector messages may<br/>be held in memory at once when queuing.</html>"); responseLabel = new JLabel("Response:"); responseComboBox = new MirthComboBox(); responseComboBox .setModel(new DefaultComboBoxModel(new String[] { "Auto-generate (After source transformer)", "None", "Auto-generate (Before processing)", "Auto-generate (After source transformer)", "Auto-generate (Destinations completed)", "Post-processor", "Destination 1" })); responseComboBox.setToolTipText( "<html>Select a destination's response, the postprocessor return value, or a response map variable.<br/>Select <b>\"Auto-generate\"</b> to send a response generated by the inbound data type using the raw message:<br/> - <b>Before processing:</b> Response generated before the channel processes the message (SENT status)<br/> - <b>After source transformer:</b> Response generated after the channel processes the message (source status)<br/> - <b>Destinations completed:</b> Response generated after the channel processes the message, with a status<br/> based on the destination statuses, using a precedence of ERROR, QUEUED, SENT, FILTERED<br/></html>"); processBatchLabel = new JLabel("Process Batch:"); ButtonGroup processBatchButtonGroup = new ButtonGroup(); processBatchYesRadio = new MirthRadioButton("Yes"); processBatchYesRadio.setBackground(getBackground()); processBatchYesRadio.setToolTipText( "<html>Select Yes to enable batch processing. Batch messages are only supported if<br>the source connector's inbound properties contains a <b>Batch</b> section.</html>"); processBatchYesRadio.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { processBatchYesRadioActionPerformed(evt); } }); processBatchButtonGroup.add(processBatchYesRadio); processBatchNoRadio = new MirthRadioButton("No"); processBatchNoRadio.setBackground(getBackground()); processBatchNoRadio.setToolTipText( "<html>Select Yes to enable batch processing. Batch messages are only supported if<br>the source connector's inbound properties contains a <b>Batch</b> section.</html>"); processBatchNoRadio.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { processBatchNoRadioActionPerformed(evt); } }); processBatchButtonGroup.add(processBatchNoRadio); batchResponseLabel = new JLabel("Batch Response:"); ButtonGroup batchResponseButtonGroup = new ButtonGroup(); batchResponseFirstRadio = new MirthRadioButton("First"); batchResponseFirstRadio.setBackground(getBackground()); batchResponseFirstRadio.setToolTipText( "<html>Each message in the batch contains its own response that is generated via the method selected above.<br> Select either the response from the first or last message in the batch to be sent back to the originating system.</html>"); batchResponseButtonGroup.add(batchResponseFirstRadio); batchResponseLastRadio = new MirthRadioButton("Last"); batchResponseLastRadio.setBackground(getBackground()); batchResponseLastRadio.setToolTipText( "<html>Each message in the batch contains its own response that is generated via the method selected above.<br> Select either the response from the first or last message in the batch to be sent back to the originating system.</html>"); batchResponseButtonGroup.add(batchResponseLastRadio); processingThreadsLabel = new JLabel("Max Processing Threads:"); processingThreadsField = new MirthTextField(); processingThreadsField.setDocument(new MirthFieldConstraints(0, false, false, true)); processingThreadsField.setToolTipText( "<html>The maximum number of messages that can process through<br/>the channel simultaneously. Note that when this value<br/>is greater than 1, message order is NOT guaranteed.</html>"); } private void initLayout() { setLayout(new MigLayout("novisualpadding, hidemode 3, insets 0, gap 6 4", "[]12[]")); add(sourceQueueLabel, "right"); add(sourceQueueComboBox, "split"); add(queueWarningLabel, "gapbefore 12"); add(queueBufferSizeLabel, "newline, right"); add(queueBufferSizeField, "w 50!"); add(responseLabel, "newline, right"); add(responseComboBox, "w 226:"); add(processBatchLabel, "newline, right"); add(processBatchYesRadio, "split"); add(processBatchNoRadio); add(batchResponseLabel, "newline, right"); add(batchResponseFirstRadio, "split"); add(batchResponseLastRadio); add(processingThreadsLabel, "newline, right"); add(processingThreadsField, "w 50!"); } private void sourceQueueComboBoxActionPerformed(ActionEvent evt) { updateSelectedResponseItem(); } private void processBatchYesRadioActionPerformed(ActionEvent evt) { batchResponseLabel.setEnabled(true); batchResponseFirstRadio.setEnabled(true); batchResponseLastRadio.setEnabled(true); } private void processBatchNoRadioActionPerformed(ActionEvent evt) { batchResponseLabel.setEnabled(false); batchResponseFirstRadio.setEnabled(false); batchResponseLastRadio.setEnabled(false); } private JLabel sourceQueueLabel; private MirthComboBox sourceQueueComboBox; private JLabel queueWarningLabel; private JLabel queueBufferSizeLabel; private JTextField queueBufferSizeField; private JLabel responseLabel; private MirthComboBox responseComboBox; private JLabel processBatchLabel; private MirthRadioButton processBatchYesRadio; private MirthRadioButton processBatchNoRadio; private JLabel batchResponseLabel; private MirthRadioButton batchResponseFirstRadio; private MirthRadioButton batchResponseLastRadio; private JLabel processingThreadsLabel; private MirthTextField processingThreadsField; }