diff --git a/build.properties b/build.properties index b59f201bb64..7178340b66c 100644 --- a/build.properties +++ b/build.properties @@ -407,7 +407,7 @@ jdom.md5 = 140bfed13341fe2039eee0f26a16d705 # Optional for use by checkstyle checkstyle-all.version = 6.13 checkstyle-all.jar = checkstyle-${checkstyle-all.version}-all.jar -checkstyle-all.loc = http://downloads.sourceforge.net/checkstyle/checkstyle/${checkstyle-all.version}/checkstyle-${checkstyle-all.version}-all.jar?ts=${EPOCHSECONDS}&use_mirror=autoselect +checkstyle-all.loc = https://downloads.sourceforge.net/checkstyle/checkstyle/${checkstyle-all.version}/checkstyle-${checkstyle-all.version}-all.jar?ts=${EPOCHSECONDS}&use_mirror=autoselect checkstyle-all.md5 = ac6e1e81d09bcaf4c0c22181e9bda1d9 # Optional for use by rat diff --git a/src/jorphan/org/apache/jorphan/gui/JLabeledChoice.java b/src/jorphan/org/apache/jorphan/gui/JLabeledChoice.java index 44a20d1dfd3..4c1e8535850 100644 --- a/src/jorphan/org/apache/jorphan/gui/JLabeledChoice.java +++ b/src/jorphan/org/apache/jorphan/gui/JLabeledChoice.java @@ -173,6 +173,10 @@ public void itemStateChanged(ItemEvent e) { } + public void setChoiceListEnabled(boolean enabled) { + choiceList.setEnabled(enabled); + } + /** * Set the text displayed in the label. * diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/control/gui/JMSPublisherGui.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/control/gui/JMSPublisherGui.java index e68370b0453..f4dfa7ed9c5 100644 --- a/src/protocol/jms/org/apache/jmeter/protocol/jms/control/gui/JMSPublisherGui.java +++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/control/gui/JMSPublisherGui.java @@ -39,6 +39,7 @@ import org.apache.jmeter.samplers.gui.AbstractSamplerGui; import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.gui.JLabeledChoice; import org.apache.jorphan.gui.JLabeledPasswordField; import org.apache.jorphan.gui.JLabeledTextField; @@ -56,7 +57,7 @@ public class JMSPublisherGui extends AbstractSamplerGui implements ChangeListene /** Take source from a random file */ public static final String USE_RANDOM_RSC = "jms_use_random_file"; //$NON-NLS-1$ /** Take source from the text area */ - private static final String USE_TEXT_RSC = "jms_use_text"; //$NON-NLS-1$ + public static final String USE_TEXT_RSC = "jms_use_text"; //$NON-NLS-1$ /** Create a TextMessage */ public static final String TEXT_MSG_RSC = "jms_text_message"; //$NON-NLS-1$ @@ -111,6 +112,8 @@ public class JMSPublisherGui extends AbstractSamplerGui implements ChangeListene private final JLabeledRadioI18N msgChoice = new JLabeledRadioI18N("jms_message_type", MSGTYPES_ITEMS, TEXT_MSG_RSC); //$NON-NLS-1$ + private JLabeledChoice fileEncoding; + private final JCheckBox useNonPersistentDelivery = new JCheckBox(JMeterUtils.getResString("jms_use_non_persistent_delivery"),false); //$NON-NLS-1$ // These are the names of properties used to define the labels @@ -180,6 +183,7 @@ private void setupSamplerProperties(final PublisherSampler sampler) { sampler.setInputFile(messageFile.getFilename()); sampler.setRandomPath(randomFile.getFilename()); sampler.setConfigChoice(configChoice.getText()); + sampler.setFileEncoding(fileEncoding.getText()); sampler.setMessageChoice(msgChoice.getText()); sampler.setIterations(iterations.getText()); sampler.setUseAuth(useAuth.isSelected()); @@ -218,6 +222,13 @@ private void init() { // WARNING: called from ctor so must not be overridden (i. mainPanel.add(configChoice); msgChoice.setLayout(new BoxLayout(msgChoice, BoxLayout.X_AXIS)); mainPanel.add(msgChoice); + + fileEncoding = new JLabeledChoice(JMeterUtils.getResString("content_encoding") + "\u00A0\u00A0", // $NON-NLS-1$ + PublisherSampler.getSupportedEncodings(), true, false); + fileEncoding.setLayout(new BoxLayout(fileEncoding, BoxLayout.X_AXIS)); + fileEncoding.add(Box.createHorizontalGlue()); + mainPanel.add(fileEncoding); + mainPanel.add(messageFile); mainPanel.add(randomFile); @@ -249,6 +260,7 @@ public void clearGui(){ messageFile.setFilename(""); // $NON-NLS-1$ randomFile.setFilename(""); // $NON-NLS-1$ msgChoice.setText(""); // $NON-NLS-1$ + fileEncoding.setSelectedIndex(0); configChoice.setText(USE_TEXT_RSC); updateConfig(USE_TEXT_RSC); msgChoice.setText(TEXT_MSG_RSC); @@ -281,6 +293,7 @@ public void configure(TestElement el) { randomFile.setFilename(sampler.getRandomPath()); configChoice.setText(sampler.getConfigChoice()); msgChoice.setText(sampler.getMessageChoice()); + fileEncoding.setText(sampler.getFileEncoding()); iterations.setText(sampler.getIterations()); expiration.setText(sampler.getExpiration()); jmsErrorReconnectOnCodes.setText(sampler.getReconnectionErrorCodes()); @@ -315,6 +328,12 @@ public void stateChanged(ChangeEvent event) { jmsPwd.setEnabled(useAuth.isSelected() && useAuth.isEnabled()); } } + + private void updateFileEncoding() { + boolean isTextMode = USE_TEXT_RSC.equals(configChoice.getText()); + boolean isObjectType = OBJECT_MSG_RSC.equals(msgChoice.getText()); + fileEncoding.setChoiceListEnabled(!isTextMode && !isObjectType); + } /** * Update choice contains the actual logic for hiding or showing Textarea if Bytes message * is selected @@ -333,6 +352,7 @@ private void updateChoice(String command) { configChoice.resetButtons(CONFIG_ITEMS, oldChoice); textMessage.setEnabled(true); } + updateFileEncoding(); validate(); } /** @@ -355,6 +375,7 @@ private void updateConfig(String command) { messageFile.enableFile(true); randomFile.enableFile(false); } + updateFileEncoding(); } /** diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/PublisherSampler.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/PublisherSampler.java index 267e9c40db4..49d8541a07b 100644 --- a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/PublisherSampler.java +++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/PublisherSampler.java @@ -17,19 +17,20 @@ package org.apache.jmeter.protocol.jms.sampler; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; import java.io.PrintWriter; import java.io.Serializable; import java.io.StringWriter; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.HashMap; +import java.lang.reflect.Modifier; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Map; -import java.util.Objects; import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import javax.jms.DeliveryMode; import javax.jms.JMSException; @@ -43,23 +44,53 @@ import org.apache.jmeter.protocol.jms.client.InitialContextFactory; import org.apache.jmeter.protocol.jms.client.Publisher; import org.apache.jmeter.protocol.jms.control.gui.JMSPublisherGui; +import org.apache.jmeter.protocol.jms.sampler.render.MessageRenderer; +import org.apache.jmeter.protocol.jms.sampler.render.Renderers; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.services.FileServer; import org.apache.jmeter.testelement.TestStateListener; import org.apache.jmeter.testelement.property.TestElementProperty; import org.apache.jmeter.util.JMeterUtils; -import org.apache.jorphan.io.TextFile; -import org.apache.jorphan.util.JOrphanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.thoughtworks.xstream.XStream; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; /** * This class implements the JMS Publisher sampler. */ public class PublisherSampler extends BaseJMSSampler implements TestStateListener { + /** Encoding value to sent data as is (no variabilisation) **/ + public static final String RAW_DATA = ""; + /** Encoding value to sent parsed data but read with default system encoding **/ + public static final String DEFAULT_ENCODING = ""; + + /** Constant for system default encodings **/ + public static final Set NO_ENCODING = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(RAW_DATA, DEFAULT_ENCODING))); + + /** Init available encoding using constants, then JVM standard ones **/ + public static String[] getSupportedEncodings() { + // Only get JVM standard charsets + return Stream.concat( + NO_ENCODING.stream(), + Arrays + .stream(StandardCharsets.class.getDeclaredFields()) + .filter(f -> Modifier.isStatic(f.getModifiers()) && Modifier.isPublic(f.getModifiers()) && f.getType() == Charset.class) + .map(f -> { + try { + return (Charset) f.get(null); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new RuntimeException(e); + } + }) + .map(Charset::displayName) + .sorted() + ) + .toArray(size -> new String[size]); + } + private static final long serialVersionUID = 233L; private static final Logger log = LoggerFactory.getLogger(PublisherSampler.class); @@ -74,15 +105,22 @@ public class PublisherSampler extends BaseJMSSampler implements TestStateListene private static final String CONFIG_CHOICE = "jms.config_choice"; //$NON-NLS-1$ private static final String MESSAGE_CHOICE = "jms.config_msg_type"; //$NON-NLS-1$ - + private static final String NON_PERSISTENT_DELIVERY = "jms.non_persistent"; //$NON-NLS-1$ - + private static final String JMS_PROPERTIES = "jms.jmsProperties"; // $NON-NLS-1$ private static final String JMS_PRIORITY = "jms.priority"; // $NON-NLS-1$ private static final String JMS_EXPIRATION = "jms.expiration"; // $NON-NLS-1$ + private static final String JMS_FILE_ENCODING = "jms.file_encoding"; // $NON-NLS-1$ + + /** File extensions for text files **/ + private static final String[] EXT_FILE_TEXT = { ".txt", ".obj" }; + /** File extensions for binary files **/ + private static final String[] EXT_FILE_BIN = { ".dat" }; + //-- // Does not need to be synch. because it is only accessed from the sampler thread @@ -91,19 +129,8 @@ public class PublisherSampler extends BaseJMSSampler implements TestStateListene private static final FileServer FSERVER = FileServer.getFileServer(); - // Cache for file. Only used by sample() in a single thread - private String file_contents = null; - // Cache for object-message, only used when parsing from a file because in text-area - // property replacement might have been used - private Serializable object_msg_file_contents = null; - // Cache for bytes-message, only used when parsing from a file - private byte[] bytes_msg_file_contents = null; - - // Cached file name - private String cachedFileName; - - public PublisherSampler() { - } + /** File cache handler **/ + private Cache fileCache = null; /** * the implementation calls testStarted() without any parameters. @@ -137,11 +164,12 @@ public void testStarted() { /** * initialize the Publisher client. - * @throws JMSException - * @throws NamingException + * @throws JMSException + * @throws NamingException * */ private void initClient() throws JMSException, NamingException { + configureIsReconnectErrorCode(); publisher = new Publisher(getUseJNDIPropertiesAsBoolean(), getJNDIInitialContextFactory(), getProviderUrl(), getConnectionFactory(), getDestination(), isUseAuth(), getUsername(), @@ -158,6 +186,12 @@ private void initClient() throws JMSException, NamingException { */ @Override public SampleResult sample() { + String configChoice = getConfigChoice(); + if (fileCache == null) { + Cache newCache = buildCache(configChoice); + fileCache = newCache; + } + SampleResult result = new SampleResult(); result.setSampleLabel(getName()); result.setSuccessful(false); // Assume it will fail @@ -175,34 +209,34 @@ public SampleResult sample() { int loop = getIterationCount(); result.sampleStart(); String type = getMessageChoice(); - + try { Map msgProperties = getJMSProperties().getJmsPropertysAsMap(); - int deliveryMode = getUseNonPersistentDelivery() ? DeliveryMode.NON_PERSISTENT : DeliveryMode.PERSISTENT; + int deliveryMode = getUseNonPersistentDelivery() ? DeliveryMode.NON_PERSISTENT : DeliveryMode.PERSISTENT; int priority = Integer.parseInt(getPriority()); long expiration = Long.parseLong(getExpiration()); - + for (int idx = 0; idx < loop; idx++) { + Message msg; if (JMSPublisherGui.TEXT_MSG_RSC.equals(type)){ - String tmsg = getMessageContent(); - Message msg = publisher.publish(tmsg, getDestination(), msgProperties, deliveryMode, priority, expiration); + String tmsg = getRenderedContent(String.class, EXT_FILE_TEXT); + msg = publisher.publish(tmsg, getDestination(), msgProperties, deliveryMode, priority, expiration); buffer.append(tmsg); - Utils.messageProperties(propBuffer, msg); } else if (JMSPublisherGui.MAP_MSG_RSC.equals(type)){ - Map m = getMapContent(); - Message msg = publisher.publish(m, getDestination(), msgProperties, deliveryMode, priority, expiration); - Utils.messageProperties(propBuffer, msg); + @SuppressWarnings("unchecked") + Map map = getRenderedContent(Map.class, EXT_FILE_TEXT); + Map m = map; + msg = publisher.publish(m, getDestination(), msgProperties, deliveryMode, priority, expiration); } else if (JMSPublisherGui.OBJECT_MSG_RSC.equals(type)){ - Serializable omsg = getObjectContent(); - Message msg = publisher.publish(omsg, getDestination(), msgProperties, deliveryMode, priority, expiration); - Utils.messageProperties(propBuffer, msg); + Serializable omsg = getRenderedContent(Serializable.class, EXT_FILE_TEXT); + msg = publisher.publish(omsg, getDestination(), msgProperties, deliveryMode, priority, expiration); } else if (JMSPublisherGui.BYTES_MSG_RSC.equals(type)){ - byte[] bmsg = getBytesContent(); - Message msg = publisher.publish(bmsg, getDestination(), msgProperties, deliveryMode, priority, expiration); - Utils.messageProperties(propBuffer, msg); + byte[] bmsg = getRenderedContent(byte[].class, EXT_FILE_BIN); + msg = publisher.publish(bmsg, getDestination(), msgProperties, deliveryMode, priority, expiration); } else { - throw new JMSException(type+ " is not recognised"); + throw new JMSException(type+ " is not recognised"); } + Utils.messageProperties(propBuffer, msg); } result.setResponseCodeOK(); result.setResponseMessage(loop + " messages published"); @@ -215,7 +249,7 @@ public SampleResult sample() { } catch (Exception e) { handleError(result, e, false); } finally { - result.sampleEnd(); + result.sampleEnd(); } return result; } @@ -250,198 +284,54 @@ && getIsReconnectErrorCode().test(errorCode)) { result.setResponseData(writer.toString(), "UTF-8"); } - private Map getMapContent() throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { - Map m = new HashMap<>(); - String text = getMessageContent(); - String[] lines = text.split("\n"); - for (String line : lines){ - String[] parts = line.split(",",3); - if (parts.length != 3) { - throw new IllegalArgumentException("line must have 3 parts: "+line); - } - String name = parts[0]; - String type = parts[1]; - if (!type.contains(".")){// Allow shorthand names - type = "java.lang."+type; - } - String value = parts[2]; - Object obj; - if (type.equals("java.lang.String")){ - obj = value; - } else { - Class clazz = Class.forName(type); - Method method = clazz.getMethod("valueOf", String.class); - obj = method.invoke(clazz, value); - } - m.put(name, obj); + protected static Cache buildCache(String configChoice) { + Caffeine cacheBuilder = Caffeine.newBuilder(); + switch (configChoice) { + case JMSPublisherGui.USE_FILE_RSC: + cacheBuilder.maximumSize(1); + break; + default: + cacheBuilder.expireAfterWrite(0, TimeUnit.MILLISECONDS).maximumSize(0); } - return m; - } - - /** - * Method will check the setting and get the contents for the message. - * - * @return the contents for the message - */ - private String getMessageContent() { - if (getConfigChoice().equals(JMSPublisherGui.USE_FILE_RSC)) { - // in the case the test uses a file, we set it locally and - // prevent loading the file repeatedly - // if the file name changes we reload it - if (file_contents == null || !Objects.equals(cachedFileName, getInputFile())) { - cachedFileName = getInputFile(); - file_contents = getFileContent(getInputFile()); - } - return file_contents; - } else if (getConfigChoice().equals(JMSPublisherGui.USE_RANDOM_RSC)) { - // Maybe we should consider creating a global cache for the - // random files to make JMeter more efficient. - String fname = FSERVER.getRandomFile(getRandomPath(), new String[] { ".txt", ".obj" }) - .getAbsolutePath(); - return getFileContent(fname); - } else { - return getTextMessage(); + Cache newCache = cacheBuilder.build(); + return newCache; + } + + /** Gets file path to use **/ + private String getFilePath(String... ext) { + switch (getConfigChoice()) { + case JMSPublisherGui.USE_FILE_RSC: + return getInputFile(); + case JMSPublisherGui.USE_RANDOM_RSC: + String fname = FSERVER.getRandomFile(getRandomPath(), ext).getAbsolutePath(); + return fname; + default: + throw new IllegalArgumentException("Type of input not handled:" + getConfigChoice()); } } - /** - * The implementation uses TextFile to load the contents of the file and - * returns a string. + /** Look-up renderer and get appropriate value * - * @param path path to the file to read in - * @return the contents of the file - */ - public String getFileContent(String path) { - TextFile tf = new TextFile(path); - return tf.getText(); - } - - /** - * This method will load the contents for the JMS Object Message. - * The contents are either loaded from file (might be cached), random file - * or from the GUI text-area. - * - * @return Serialized object as loaded from the specified input file - */ - private Serializable getObjectContent() { - if (getConfigChoice().equals(JMSPublisherGui.USE_FILE_RSC)) { - // in the case the test uses a file, we set it locally and - // prevent loading the file repeatedly - // if the file name changes we reload it - if (object_msg_file_contents == null || !Objects.equals(cachedFileName, getInputFile())) { - cachedFileName = getInputFile(); - object_msg_file_contents = getFileObjectContent(getInputFile()); - } - - return object_msg_file_contents; - } else if (getConfigChoice().equals(JMSPublisherGui.USE_RANDOM_RSC)) { - // Maybe we should consider creating a global cache for the - // random files to make JMeter more efficient. - final String fname = FSERVER.getRandomFile(getRandomPath(), new String[] {".txt", ".obj"}) - .getAbsolutePath(); - - return getFileObjectContent(fname); + * @param type Message type to render + * @param fileExts File extensions for directory mode. + **/ + private T getRenderedContent(Class type, String[] fileExts) { + MessageRenderer renderer = Renderers.getInstance(type); + if (getConfigChoice().equals(JMSPublisherGui.USE_TEXT_RSC)) { + return renderer.getValueFromText(getTextMessage()); } else { - final String xmlMessage = getTextMessage(); - return transformXmlToObjectMessage(xmlMessage); + return renderer.getValueFromFile(getFilePath(fileExts), getFileEncoding(), !isRaw(), fileCache); } } - - /** - * This method will load the contents for the JMS BytesMessage. - * The contents are either loaded from file (might be cached), random file - * - * @return byte[] as loaded from the specified input file - * @since 2.9 - */ - private byte[] getBytesContent() { - if (getConfigChoice().equals(JMSPublisherGui.USE_FILE_RSC)) { - // in the case the test uses a file, we set it locally and - // prevent loading the file repeatedly - // if the file name changes we reload it - if (bytes_msg_file_contents == null || !Objects.equals(cachedFileName, getInputFile())) { - cachedFileName = getInputFile(); - bytes_msg_file_contents = getFileBytesContent(getInputFile()); - } - return bytes_msg_file_contents; - } else if (getConfigChoice().equals(JMSPublisherGui.USE_RANDOM_RSC)) { - final String fname = FSERVER.getRandomFile(getRandomPath(), new String[] {".dat"}) - .getAbsolutePath(); - - return getFileBytesContent(fname); - } else { - throw new IllegalArgumentException("Type of input not handled:" + getConfigChoice()); - } - } - /** - * Try to load an object from a provided file, so that it can be used as body - * for a JMS message. - * An {@link IllegalStateException} will be thrown if loading the object fails. - * - * @param path Path to the file that will be serialized - * @return byte[] instance - * @since 2.9 + * Specified if value must be parsed or not. + * @return true if value must be sent as-is. */ - private static byte[] getFileBytesContent(final String path) { - InputStream inputStream = null; - try { - File file = new File(path); - inputStream = new BufferedInputStream(new FileInputStream(file)); - return IOUtils.toByteArray(inputStream, (int)file.length()); - } catch (Exception e) { - log.error(e.getLocalizedMessage(), e); - throw new IllegalStateException("Unable to load file:'"+path+"'", e); - } finally { - JOrphanUtils.closeQuietly(inputStream); - } + private boolean isRaw() { + return RAW_DATA.equals(getFileEncoding()); } - - /** - * Try to load an object from a provided file, so that it can be used as body - * for a JMS message. - * An {@link IllegalStateException} will be thrown if loading the object fails. - * - * @param path Path to the file that will be serialized - * @return Serialized object instance - */ - private static Serializable getFileObjectContent(final String path) { - Serializable readObject = null; - InputStream inputStream = null; - try { - inputStream = new BufferedInputStream(new FileInputStream(path)); - XStream xstream = new XStream(); - readObject = (Serializable) xstream.fromXML(inputStream, readObject); - } catch (Exception e) { - log.error(e.getLocalizedMessage(), e); - throw new IllegalStateException("Unable to load object instance from file:'"+path+"'", e); - } finally { - JOrphanUtils.closeQuietly(inputStream); - } - return readObject; - } - - /** - * Try to load an object via XStream from XML text, so that it can be used as body - * for a JMS message. - * An {@link IllegalStateException} will be thrown if transforming the XML to an object fails. - * - * @param xmlMessage String containing XML text as input for the transformation - * @return Serialized object instance - */ - private static Serializable transformXmlToObjectMessage(final String xmlMessage) { - Serializable readObject = null; - try { - XStream xstream = new XStream(); - readObject = (Serializable) xstream.fromXML(xmlMessage, readObject); - } catch (Exception e) { - log.error(e.getLocalizedMessage(), e); - throw new IllegalStateException("Unable to load object instance from text", e); - } - return readObject; - } - + // ------------- get/set properties ----------------------// /** * set the source of the message @@ -469,7 +359,7 @@ public void setConfigChoice(String choice) { public String getConfigChoice() { // Allow for the old JMX file which used the local language string String config = getPropertyAsString(CONFIG_CHOICE); - if (config.equals(USE_FILE_LOCALNAME) + if (config.equals(USE_FILE_LOCALNAME) || config.equals(JMSPublisherGui.USE_FILE_RSC)){ return JMSPublisherGui.USE_FILE_RSC; } @@ -565,7 +455,7 @@ public String getPriority() { return priority; } } - + public void setPriority(String s) { // Bug 59173 if (Utils.DEFAULT_PRIORITY_4.equals(s)) { @@ -573,7 +463,7 @@ public void setPriority(String s) { } setProperty(JMS_PRIORITY, s); // always need to save the field } - + public void setExpiration(String s) { // Bug 59173 if (Utils.DEFAULT_NO_EXPIRY.equals(s)) { @@ -581,14 +471,14 @@ public void setExpiration(String s) { } setProperty(JMS_EXPIRATION, s); // always need to save the field } - + /** * @param value boolean use NON_PERSISTENT */ public void setUseNonPersistentDelivery(boolean value) { setProperty(NON_PERSISTENT_DELIVERY, value, false); } - + /** * @return true if NON_PERSISTENT delivery must be used */ @@ -596,7 +486,7 @@ public boolean getUseNonPersistentDelivery() { return getPropertyAsBoolean(NON_PERSISTENT_DELIVERY, false); } - /** + /** * @return {@link JMSProperties} JMS Properties */ public JMSProperties getJMSProperties() { @@ -614,11 +504,33 @@ public JMSProperties getJMSProperties() { } return jmsProperties; } - + /** * @param jmsProperties JMS Properties */ public void setJMSProperties(JMSProperties jmsProperties) { setProperty(new TestElementProperty(JMS_PROPERTIES, jmsProperties)); } + + /** + * Gets file encoding to use. If {@value #RAW_DATA}, content isn't parsed. + * @return File encoding. + * @see #RAW_DATA + * @see #DEFAULT_ENCODING + * @see #getSupportedEncodings() + */ + public String getFileEncoding() { + return getPropertyAsString(JMS_FILE_ENCODING, RAW_DATA); + } + + /** + * Sets file encoding to use. If {@value #RAW_DATA}, content isn't parsed. + * @param fileEncoding File encoding. + * @see #RAW_DATA + * @see #DEFAULT_ENCODING + * @see #getSupportedEncodings() + */ + public void setFileEncoding(String fileEncoding) { + setProperty(JMS_FILE_ENCODING, fileEncoding, RAW_DATA); + } } diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/BinaryMessageRenderer.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/BinaryMessageRenderer.java new file mode 100644 index 00000000000..d2e57dbc478 --- /dev/null +++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/BinaryMessageRenderer.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import static java.lang.String.format; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import org.apache.jmeter.protocol.jms.control.gui.JMSPublisherGui; + +import com.github.benmanes.caffeine.cache.Cache; + +class BinaryMessageRenderer implements MessageRenderer { + + private TextMessageRenderer delegate; + + public BinaryMessageRenderer(TextMessageRenderer delegate) { + this.delegate = delegate; + } + + @Override + public byte[] getValueFromText(String text) { + throw new UnsupportedOperationException(format("Type of input not handled: %s", JMSPublisherGui.USE_TEXT_RSC)); + } + + @Override + public byte[] getValueFromFile(String filename, String encoding, boolean hasVariable, Cache cache) { + byte[] bytes; + + if (hasVariable) { + String stringValue = delegate.getValueFromFile(filename, encoding, hasVariable, cache); + try { + bytes = stringValue.getBytes(encoding); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } else { + bytes = (byte[]) cache.get(filename, _p -> getContent(filename)); + } + + return bytes; + } + + byte[] getContent(String filename) { + try { + return Files.readAllBytes(Paths.get(filename)); + } catch (IOException e) { + throw new RuntimeException(format("Can't read content of %s", filename), e); + } + } +} diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/FileKey.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/FileKey.java new file mode 100644 index 00000000000..b7a62cff68d --- /dev/null +++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/FileKey.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import java.util.Objects; + +/** File specification for {@link TextMessageRenderer} **/ +class FileKey { + + private final String filename; + private final String encoding; + + public FileKey(String filename, String encoding) { + this.filename = filename; + this.encoding = encoding; + } + + public String getFilename() { + return filename; + } + + public String getEncoding() { + return encoding; + } + + @Override + public int hashCode() { + return Objects.hashCode(filename); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof FileKey)) { + return false; + } + + FileKey that = (FileKey) obj; + + return Objects.equals(this.filename, that.filename); + + } +} diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/MapMessageRenderer.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/MapMessageRenderer.java new file mode 100644 index 00000000000..ddfb08e14fe --- /dev/null +++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/MapMessageRenderer.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import com.github.benmanes.caffeine.cache.Cache; + +public class MapMessageRenderer implements MessageRenderer> { + + private TextMessageRenderer delegate; + + public MapMessageRenderer(TextMessageRenderer delegate) { + this.delegate = delegate; + } + + @Override + public Map getValueFromText(String text) { + Map m = new HashMap<>(); + String[] lines = text.split("\n"); + for (String line : lines){ + String[] parts = line.split(",",3); + if (parts.length != 3) { + throw new IllegalArgumentException("line must have 3 parts: "+line); + } + String name = parts[0]; + String type = parts[1]; + if (!type.contains(".")){// Allow shorthand names + type = "java.lang."+type; + } + String value = parts[2]; + Object obj; + if (type.equals("java.lang.String")){ + obj = value; + } else { + try { + Class clazz = Class.forName(type); + Method method = clazz.getMethod("valueOf", new Class[]{String.class}); + obj = method.invoke(clazz, value); + } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException e) { + throw new RuntimeException(String.format("Can't convert %s to object", line), e); + } + } + m.put(name, obj); + } + return m; + } + + @Override + public Map getValueFromFile(String filename, String encoding, boolean hasVariable, Cache cache) { + String text = delegate.getValueFromFile(filename, encoding, hasVariable, cache); + return getValueFromText(text); + } +} diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/MessageRenderer.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/MessageRenderer.java new file mode 100644 index 00000000000..a19e7452a17 --- /dev/null +++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/MessageRenderer.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import com.github.benmanes.caffeine.cache.Cache; + +public interface MessageRenderer { + /** Convert text to expected type **/ + T getValueFromText(String text); + /** Read text from file, eventually replace variables, then convert it. Cached content depends if variabilisation is active or not. **/ + T getValueFromFile(String filename, String encoding, boolean hasVariable, Cache cache); +} diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/ObjectMessageRenderer.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/ObjectMessageRenderer.java new file mode 100644 index 00000000000..e6c31d9e5d7 --- /dev/null +++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/ObjectMessageRenderer.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import static java.lang.String.format; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.Serializable; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.apache.jmeter.protocol.jms.sampler.PublisherSampler; + +import com.github.benmanes.caffeine.cache.Cache; +import com.thoughtworks.xstream.XStream; + +class ObjectMessageRenderer implements MessageRenderer { + + TextMessageRenderer delegate; + + public ObjectMessageRenderer(TextMessageRenderer delegate) { + this.delegate = delegate; + } + + @Override + public Serializable getValueFromFile(String filename, String encoding, boolean hasVariable, Cache cache) { + Serializable value; + if (hasVariable) { + value = getInterpretedContent(filename, encoding, hasVariable, cache); + } else { + value = (Serializable) cache.get(filename, _p -> getContent(filename)); + } + + return value; + } + + /** + * Try to load an object via XStream from XML text, so that it can be used as body + * for a JMS message. + * An {@link IllegalStateException} will be thrown if transforming the XML to an object fails. + * + * @param xmlMessage String containing XML text as input for the transformation + * @return Serialized object instance + */ + @Override + public Serializable getValueFromText(final String xmlMessage) { + Serializable readObject = null; + try { + XStream xstream = new XStream(); + readObject = (Serializable) xstream.fromXML(xmlMessage, readObject); + } catch (Exception e) { + throw new IllegalStateException("Unable to load object instance from text", e); + } + return readObject; + } + + /** + *

Gets content with variable replaced.

+ *

If encoding {@link PublisherSampler#DEFAULT_ENCODING isn't provided}, try to find it.

+ *

Only raw text is cached, neither interpreted text, neither parsed object.

+ */ + protected Serializable getInterpretedContent(String filename, String encoding, boolean hasVariable, Cache cache) { + Serializable value; + if (PublisherSampler.DEFAULT_ENCODING.equals(encoding)) { + encoding = findEncoding(filename); + } + String stringValue = delegate.getValueFromFile(filename, encoding, hasVariable, cache); + value = (Serializable) new XStream().fromXML(stringValue); + return value; + } + + /** Try to determine encoding based on XML prolog, if none null is returned. **/ + protected String findEncoding(String filename) { + XMLInputFactory factory = XMLInputFactory.newFactory(); + try (FileInputStream input = new FileInputStream(filename)) { + XMLStreamReader reader = factory.createXMLStreamReader(input); + return reader.getEncoding(); + } catch (IOException|XMLStreamException e) { + throw new RuntimeException(format("Unable to read %s", filename), e); + } + } + + protected Serializable getContent(String filename) { + Serializable object = (Serializable) new XStream().fromXML(new File(filename)); + return object; + } +} diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/RendererFactory.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/RendererFactory.java new file mode 100644 index 00000000000..e8ba1288c89 --- /dev/null +++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/RendererFactory.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import java.io.Serializable; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Renderer singleton. + */ +enum RendererFactory { + + INSTANCE; + + public static RendererFactory getInstance() { + return INSTANCE; + } + + private TextMessageRenderer text = new TextMessageRenderer(); + private BinaryMessageRenderer binary = new BinaryMessageRenderer(text); + private ObjectMessageRenderer object = new ObjectMessageRenderer(text); + private MapMessageRenderer map = new MapMessageRenderer(text); + + /** Registred renderers **/ + private Map, MessageRenderer> renderers; + { + Map, MessageRenderer> writable = new LinkedHashMap<>(); + writable.put(String.class, text); + writable.put(byte[].class, binary); + writable.put(Serializable.class, object); + writable.put(Map.class, map); + renderers = Collections.unmodifiableMap(writable); + } + + public TextMessageRenderer getText() { + return text; + } + + public BinaryMessageRenderer getBinary() { + return binary; + } + + public ObjectMessageRenderer getObject() { + return object; + } + + public MapMessageRenderer getMap() { + return map; + } + + @SuppressWarnings("unchecked") + public MessageRenderer getInstance(Class type) { + return (MessageRenderer) renderers.get(type); + } +} diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/Renderers.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/Renderers.java new file mode 100644 index 00000000000..7fd328ecd8e --- /dev/null +++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/Renderers.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import java.io.Serializable; +import java.util.Map; + +/** + * Renderer API entry point. + */ +public interface Renderers { + + public static MessageRenderer getText() { + return RendererFactory.getInstance().getText(); + } + + public static MessageRenderer getBinary() { + return RendererFactory.getInstance().getBinary(); + } + + public static MessageRenderer getObject() { + return RendererFactory.getInstance().getObject(); + } + + public static MessageRenderer> getMap() { + return RendererFactory.getInstance().getMap(); + } + + public static MessageRenderer getInstance(Class type) { + return RendererFactory.getInstance().getInstance(type); + } +} diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/TextMessageRenderer.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/TextMessageRenderer.java new file mode 100644 index 00000000000..332dde23235 --- /dev/null +++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/TextMessageRenderer.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import org.apache.jmeter.engine.util.CompoundVariable; +import org.apache.jorphan.io.TextFile; + +import com.github.benmanes.caffeine.cache.Cache; + +class TextMessageRenderer implements MessageRenderer { + + @Override + public String getValueFromText(String text) { + return text; + } + + @Override + public String getValueFromFile(String filename, String encoding, boolean hasVariable, Cache cache) { + String text = (String) cache.get(new FileKey(filename, encoding), key -> getContent((FileKey)key)); + if (hasVariable) { + text = new CompoundVariable(text).execute(); + } + + return text; + } + + String getContent(FileKey key) { + return new TextFile(key.getFilename(), key.getEncoding()).getText(); + } + + +} diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/package-info.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/package-info.java new file mode 100644 index 00000000000..dd61060de2c --- /dev/null +++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/render/package-info.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Package contains all renderer for JMS publisher. + * @see Renderers + * @see MessageRenderer + */ +package org.apache.jmeter.protocol.jms.sampler.render; diff --git a/test/resources/org/apache/jmeter/protocol/jms/sampler/render/cp1252.txt b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/cp1252.txt new file mode 100644 index 00000000000..33d642f7ca3 --- /dev/null +++ b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/cp1252.txt @@ -0,0 +1 @@ +éè€ \ No newline at end of file diff --git a/test/resources/org/apache/jmeter/protocol/jms/sampler/render/noVar.txt b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/noVar.txt new file mode 100644 index 00000000000..98c541790c0 --- /dev/null +++ b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/noVar.txt @@ -0,0 +1 @@ +noVar \ No newline at end of file diff --git a/test/resources/org/apache/jmeter/protocol/jms/sampler/render/object_cp1252.xml b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/object_cp1252.xml new file mode 100644 index 00000000000..df8b3749a05 --- /dev/null +++ b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/object_cp1252.xml @@ -0,0 +1 @@ +eéè€ \ No newline at end of file diff --git a/test/resources/org/apache/jmeter/protocol/jms/sampler/render/object_doe.xml b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/object_doe.xml new file mode 100644 index 00000000000..ef6eeda1e7a --- /dev/null +++ b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/object_doe.xml @@ -0,0 +1 @@ +Doe \ No newline at end of file diff --git a/test/resources/org/apache/jmeter/protocol/jms/sampler/render/object_prolog_cp1252.xml b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/object_prolog_cp1252.xml new file mode 100644 index 00000000000..83997f05fb5 --- /dev/null +++ b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/object_prolog_cp1252.xml @@ -0,0 +1,2 @@ + +eéè€ \ No newline at end of file diff --git a/test/resources/org/apache/jmeter/protocol/jms/sampler/render/object_utf8.xml b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/object_utf8.xml new file mode 100644 index 00000000000..7a1d21b7464 --- /dev/null +++ b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/object_utf8.xml @@ -0,0 +1 @@ +eéè€ \ No newline at end of file diff --git a/test/resources/org/apache/jmeter/protocol/jms/sampler/render/oneVar.txt b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/oneVar.txt new file mode 100644 index 00000000000..88fa4155b49 --- /dev/null +++ b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/oneVar.txt @@ -0,0 +1 @@ +${oneVar} \ No newline at end of file diff --git a/test/resources/org/apache/jmeter/protocol/jms/sampler/render/utf8.txt b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/utf8.txt new file mode 100644 index 00000000000..179993d71b3 --- /dev/null +++ b/test/resources/org/apache/jmeter/protocol/jms/sampler/render/utf8.txt @@ -0,0 +1 @@ +éè€ \ No newline at end of file diff --git a/test/src/org/apache/jmeter/protocol/jms/sampler/PublisherSamplerTest.java b/test/src/org/apache/jmeter/protocol/jms/sampler/PublisherSamplerTest.java new file mode 100644 index 00000000000..feb4072cb9b --- /dev/null +++ b/test/src/org/apache/jmeter/protocol/jms/sampler/PublisherSamplerTest.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +import java.util.Locale; + +import org.apache.jmeter.protocol.jms.control.gui.JMSPublisherGui; +import org.apache.jmeter.util.JMeterUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.github.benmanes.caffeine.cache.Cache; + +/** + * + */ +public class PublisherSamplerTest { + + @Before + public void initJMeter() { + JMeterUtils.setLocale(new Locale("ignoreResources")); + } + + @After + public void resetJMeter() { + JMeterUtils.setLocale(Locale.ENGLISH); + } + + @Test + public void noopCache() { + Cache noopCache = PublisherSampler.buildCache(JMSPublisherGui.USE_RANDOM_RSC); + assertEquals(0, noopCache.estimatedSize()); + + String key = "key"; + String val1 = "1st time"; + assertSame(val1, noopCache.get(key, k -> val1)); + + String val2 = "2nd call"; + assertSame(val2, noopCache.get(key, k -> val2)); + } +} diff --git a/test/src/org/apache/jmeter/protocol/jms/sampler/render/BinaryMessageRendererTest.java b/test/src/org/apache/jmeter/protocol/jms/sampler/render/BinaryMessageRendererTest.java new file mode 100644 index 00000000000..29005fb1cb3 --- /dev/null +++ b/test/src/org/apache/jmeter/protocol/jms/sampler/render/BinaryMessageRendererTest.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import static java.lang.String.format; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class BinaryMessageRendererTest extends MessageRendererTest { + + @Rule + public ExpectedException error = ExpectedException.none(); + + BinaryMessageRenderer render = RendererFactory.getInstance().getBinary(); + @Override + protected MessageRenderer getRenderer() { + return render; + } + + @Test(expected=UnsupportedOperationException.class) + public void getValueFromText() { + render.getValueFromText(""); + } + + @Test + public void readUTF8File() { + assertContent("utf8.txt", "UTF-8"); + } + + @Test + public void readCP1252File() { + assertContent("cp1252.txt", "Cp1252"); + } + + private void assertContent(String resource, String encoding) { + String filename = getResourceFile(resource); + byte[] actual = render.getContent(filename); + try { + assertArrayEquals("éè€".getBytes(encoding), actual); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + @Test + public void readNonExistingContent() { + error.expect(RuntimeException.class); + error.expectCause(instanceOf(IOException.class)); + error.expectMessage("Can't read content of __file_that_may_not_exists_else_it_will_fail"); + render.getContent("__file_that_may_not_exists_else_it_will_fail"); + } + + @Test + public void getValueFromFile_withNoVar() { + String text = format("éè€%n"); + assertValueFromFile(text, "utf8.txt", true); + assertCacheContentInString(text); + + } + + @Test + public void getValueFromFile_withOneVar() { + String value = "éè€"; + jmeterCtxService.get().getVariables().put("oneVar", value); + assertValueFromFile(format("%s%n", value), "oneVar.txt", true); + assertCacheContentInString(format("${oneVar}%n")); + } + + + + @Test + public void getValueFromFile_withInvalidEncoding() { + error.expect(RuntimeException.class); + error.expectCause(instanceOf(UnsupportedEncodingException.class)); + + render.getValueFromFile(getResourceFile("utf8.txt"), "banana", true, cache); + } + + @Test + public void getValueFromFile_inRawMode() { + String text = "${oneVar}"; + assertValueFromFile(text, "oneVar.txt", false); + assertCacheContentInBytes(text); + } + + protected void assertValueFromFile(String expected, String resource, boolean hasVariable) { + assertValueFromFile(actual -> assertBytesEquals(expected, actual), resource, hasVariable); + } + + protected void assertCacheContentInBytes(String expected) { + assertBytesEquals(expected, (byte[]) getFirstCachedValue()); + } + protected void assertCacheContentInString(String expected) { + assertEquals(expected, getFirstCachedValue()); + } + protected void assertBytesEquals(String expected, byte[] actual) { + assertArrayEquals(expected.getBytes(StandardCharsets.UTF_8), actual); + } +} diff --git a/test/src/org/apache/jmeter/protocol/jms/sampler/render/MessageRendererTest.java b/test/src/org/apache/jmeter/protocol/jms/sampler/render/MessageRendererTest.java new file mode 100644 index 00000000000..e3ab48e724e --- /dev/null +++ b/test/src/org/apache/jmeter/protocol/jms/sampler/render/MessageRendererTest.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import java.util.function.Consumer; + +import org.apache.jmeter.test.ResourceLocator; +import org.apache.jmeter.threads.JMeterContext; +import org.apache.jmeter.threads.JMeterContextServiceHelper; +import org.apache.jmeter.threads.JMeterVariables; +import org.junit.Rule; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +/** + * + */ +public abstract class MessageRendererTest { + + protected Cache cache = Caffeine.newBuilder().build(); + + protected Object getFirstCachedValue() { + return cache.asMap().values().stream().findFirst().get(); + } + + @Rule + public JMeterContextServiceHelper jmeterCtxService = new JMeterContextServiceHelper() { + @Override + protected void initContext(JMeterContext jMeterContext) { + jMeterContext.setVariables(new JMeterVariables()); + } + }; + + protected abstract MessageRenderer getRenderer(); + + protected String getResourceFile(String resource) { + return ResourceLocator.getResource(this, resource); + } + + protected void assertValueFromFile(Consumer assertion, String resource, boolean hasVariable) { + String filename = getResourceFile(resource); + T actual = getRenderer().getValueFromFile(filename, "UTF-8", hasVariable, cache); + assertion.accept(actual); + } + +} diff --git a/test/src/org/apache/jmeter/protocol/jms/sampler/render/ObjectMessageRendererTest.java b/test/src/org/apache/jmeter/protocol/jms/sampler/render/ObjectMessageRendererTest.java new file mode 100644 index 00000000000..635314631e7 --- /dev/null +++ b/test/src/org/apache/jmeter/protocol/jms/sampler/render/ObjectMessageRendererTest.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import static java.lang.String.format; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +import java.io.Serializable; + +import org.apache.jmeter.protocol.jms.sampler.PublisherSampler; +import org.junit.Test; + +/** + * + */ +public class ObjectMessageRendererTest extends MessageRendererTest { + + private ObjectMessageRenderer render = RendererFactory.getInstance().getObject(); + @Override + public ObjectMessageRenderer getRenderer() { + return render; + } + + @Test + public void getValueFromText() { + Serializable object = render.getValueFromText(getDoeContent()); + assertObject(object, "Doe"); + } + + private void assertObject(Serializable object, String name) { + assertNotNull("object", object); + assertEquals("object.class", Person.class, object.getClass()); + Person p = (Person) object; + assertEquals("object.name", name, p.getName()); + } + + @Test + public void getValueFromFile_inRawMode() { + assertValueFromFile(object -> { + assertObject(object, "Doe"); + Person p = (Person) object; + assertSame("cache", p, getFirstCachedValue()); + }, "object_doe.xml", false); + } + + @Test + public void getValueFromFile_withForcedEncoding() { + String filename = getResourceFile("object_cp1252.xml"); + Serializable object = getRenderer().getValueFromFile(filename, "Cp1252", true, cache); + assertObject(object, "eéè€"); + assertEquals("cache", format("%s%n", getUnicodeContent()), getFirstCachedValue()); + + } + + @Test + public void getValueFromFile_withDefaultEncodingAndNoProlog() { + String filename = getResourceFile("object_utf8.xml"); + Serializable object = getRenderer().getValueFromFile(filename, PublisherSampler.DEFAULT_ENCODING, true, cache); + assertObject(object, "eéè€"); + assertEquals("cache", format("%s%n", getUnicodeContent()), getFirstCachedValue()); + + } + + @Test + public void getValueFromFile_withDefaultEncodingAndProlog() { + String filename = getResourceFile("object_prolog_cp1252.xml"); + Serializable object = getRenderer().getValueFromFile(filename, PublisherSampler.DEFAULT_ENCODING, true, cache); + assertObject(object, "eéè€"); + Person p = (Person) object; + assertEquals("object.name", "eéè€", p.getName()); + assertEquals("cache", format("%n%s%n", getUnicodeContent()), getFirstCachedValue()); + + } + + private String getUnicodeContent() { + return "eéè€"; + } + + private String getDoeContent() { + return "Doe"; + } +} diff --git a/test/src/org/apache/jmeter/protocol/jms/sampler/render/Person.java b/test/src/org/apache/jmeter/protocol/jms/sampler/render/Person.java new file mode 100644 index 00000000000..12076f4eb47 --- /dev/null +++ b/test/src/org/apache/jmeter/protocol/jms/sampler/render/Person.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import java.io.Serializable; + +class Person implements Serializable { + private static final long serialVersionUID = 1L; + + private String name; + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/test/src/org/apache/jmeter/protocol/jms/sampler/render/TextMessageRendererTest.java b/test/src/org/apache/jmeter/protocol/jms/sampler/render/TextMessageRendererTest.java new file mode 100644 index 00000000000..9c7417a3b27 --- /dev/null +++ b/test/src/org/apache/jmeter/protocol/jms/sampler/render/TextMessageRendererTest.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.jms.sampler.render; + +import static java.lang.String.format; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +import java.util.Arrays; + +import org.junit.Test; + +/** + * + */ +public class TextMessageRendererTest extends MessageRendererTest { + + private TextMessageRenderer render = RendererFactory.getInstance().getText(); + @Override + protected MessageRenderer getRenderer() { + return render; + } + + @Test + public void readUTF8File() { + assertContent("utf8.txt", "UTF-8"); + } + + @Test + public void readCP1252File() { + assertContent("cp1252.txt", "Cp1252"); + } + + private void assertContent(String resource, String encoding) { + String filename = getResourceFile(resource); + String actual = render.getContent(new FileKey(filename, encoding)); + assertEquals(format("éè€%n"), actual); + } + + + @Test + public void getValueFromFileWithNoVar() { + assertValueFromFile(format("noVar%n"), "noVar.txt", true); + } + + @Test + public void getValueFromFileWithOneVar() { + jmeterCtxService.get().getVariables().put("oneVar", "foobar"); + assertValueFromFile(format("foobar%n"), "oneVar.txt", true); + } + + @Test + public void checkCache() { + jmeterCtxService.get().getVariables().put("oneVar", "foo"); + assertValueFromFile(format("foo%n"), "oneVar.txt", true); + assertEquals(format("${oneVar}%n"), getFirstCachedValue()); + + jmeterCtxService.get().getVariables().put("oneVar", "bar"); + assertValueFromFile(format("bar%n"), "oneVar.txt", true); + assertEquals(format("${oneVar}%n"), getFirstCachedValue()); + } + + @Test + public void checkNoVariable() { + jmeterCtxService.get().getVariables().put("oneVar", "RAW"); + assertValueFromFile(format("${oneVar}%n"), "oneVar.txt", false); + } + + @Test + public void getValueFromText() { + for (String text : Arrays.asList("a", null, "b", "")) { + assertSame(text, render.getValueFromText(text)); + } + } + + protected void assertValueFromFile(String expected, String resource, boolean hasVariable) { + assertValueFromFile(actual -> assertEquals(expected, actual), resource, hasVariable); + } +} diff --git a/test/src/org/apache/jmeter/test/ResourceLocator.java b/test/src/org/apache/jmeter/test/ResourceLocator.java new file mode 100644 index 00000000000..a39e7ab4afe --- /dev/null +++ b/test/src/org/apache/jmeter/test/ResourceLocator.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.test; + +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; + +public interface ResourceLocator { + + public static String getResource(Object instance, String path) { + return getResource(instance.getClass(), path); + } + + public static String getResource(Class basetype, String path) { + Path nioPath = getResourcePath(basetype, path); + return nioPath.toString(); + } + + public static Path getResourcePath(Object instance, String path) { + return getResourcePath(instance.getClass(), path); + } + + public static Path getResourcePath(Class basetype, String path) { + URL url = basetype.getResource(path); + if (url == null) return null; + Path nioPath; + try { + nioPath = Paths.get(url.toURI()); + } catch (URISyntaxException e) { + throw new IllegalStateException(e); + } + return nioPath; + } +} diff --git a/test/src/org/apache/jmeter/threads/JMeterContextServiceHelper.java b/test/src/org/apache/jmeter/threads/JMeterContextServiceHelper.java new file mode 100644 index 00000000000..316142c0d28 --- /dev/null +++ b/test/src/org/apache/jmeter/threads/JMeterContextServiceHelper.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.threads; + +import org.junit.rules.ExternalResource; + +public class JMeterContextServiceHelper extends ExternalResource { + + @Override + protected void after() { + JMeterContextService.removeContext(); + } + + private JMeterContext instance; + public JMeterContext get() { + if (instance == null) { + JMeterContext jMeterContext = new JMeterContext(); + JMeterContextService.replaceContext(jMeterContext); + initContext(jMeterContext); + instance = jMeterContext; + } + return instance; + } + + protected void initContext(JMeterContext jMeterContext) {} +}