diff --git a/activemq-broker/src/main/java/org/apache/activemq/network/NetworkBridgeListener.java b/activemq-broker/src/main/java/org/apache/activemq/network/NetworkBridgeListener.java index c30a9996d5..a08064cdc8 100644 --- a/activemq-broker/src/main/java/org/apache/activemq/network/NetworkBridgeListener.java +++ b/activemq-broker/src/main/java/org/apache/activemq/network/NetworkBridgeListener.java @@ -1,57 +1,57 @@ -/** - * 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.activemq.network; - -import org.apache.activemq.command.Message; - -/** - * called when a bridge fails - * - * - */ -public interface NetworkBridgeListener { - - /** - * called when the transport fails - */ - void bridgeFailed(); - - /** - * called after the bridge is started. - */ - void onStart(NetworkBridge bridge); - - /** - * called before the bridge is stopped. - */ - void onStop(NetworkBridge bridge); - - /** - * Called when message forwarded over the network - * @param bridge - * @param message - */ - void onOutboundMessage (NetworkBridge bridge,Message message); - - /** - * Called for when a message arrives over the network - * @param bridge - * @param message - */ - void onInboundMessage (NetworkBridge bridge,Message message); - -} +/** + * 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.activemq.network; + +import org.apache.activemq.command.Message; + +/** + * called when a bridge fails + * + * + */ +public interface NetworkBridgeListener { + + /** + * called when the transport fails + */ + void bridgeFailed(); + + /** + * called after the bridge is started. + */ + void onStart(NetworkBridge bridge); + + /** + * called before the bridge is stopped. + */ + void onStop(NetworkBridge bridge); + + /** + * Called when message forwarded over the network + * @param bridge + * @param message + */ + void onOutboundMessage (NetworkBridge bridge,Message message); + + /** + * Called for when a message arrives over the network + * @param bridge + * @param message + */ + void onInboundMessage (NetworkBridge bridge,Message message); + +} diff --git a/activemq-broker/src/main/java/org/apache/activemq/util/LockFile.java b/activemq-broker/src/main/java/org/apache/activemq/util/LockFile.java index 8fe512ab2e..7fc4648259 100644 --- a/activemq-broker/src/main/java/org/apache/activemq/util/LockFile.java +++ b/activemq-broker/src/main/java/org/apache/activemq/util/LockFile.java @@ -1,146 +1,146 @@ -/** - * 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.activemq.util; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.channels.FileLock; -import java.nio.channels.OverlappingFileLockException; -import java.util.Date; - -/** - * Used to lock a File. - * - * @author chirino - */ -public class LockFile { - - private static final boolean DISABLE_FILE_LOCK = Boolean.getBoolean("java.nio.channels.FileLock.broken"); - final private File file; - - private FileLock lock; - private RandomAccessFile readFile; - private int lockCounter; - private final boolean deleteOnUnlock; - - public LockFile(File file, boolean deleteOnUnlock) { - this.file = file; - this.deleteOnUnlock = deleteOnUnlock; - } - - /** - * @throws IOException - */ - synchronized public void lock() throws IOException { - if (DISABLE_FILE_LOCK) { - return; - } - - if (lockCounter > 0) { - return; - } - - IOHelper.mkdirs(file.getParentFile()); - synchronized (LockFile.class) { - if (System.getProperty(getVmLockKey()) != null) { - throw new IOException("File '" + file + "' could not be locked as lock is already held for this jvm."); - } - System.setProperty(getVmLockKey(), new Date().toString()); - } - try { - if (lock == null) { - readFile = new RandomAccessFile(file, "rw"); - IOException reason = null; - try { - lock = readFile.getChannel().tryLock(0, Math.max(1, readFile.getChannel().size()), false); - } catch (OverlappingFileLockException e) { - reason = IOExceptionSupport.create("File '" + file + "' could not be locked.", e); - } catch (IOException ioe) { - reason = ioe; - } - if (lock != null) { - lockCounter++; - System.setProperty(getVmLockKey(), new Date().toString()); - } else { - // new read file for next attempt - closeReadFile(); - if (reason != null) { - throw reason; - } - throw new IOException("File '" + file + "' could not be locked."); - } - - } - } finally { - synchronized (LockFile.class) { - if (lock == null) { - System.getProperties().remove(getVmLockKey()); - } - } - } - } - - /** - */ - public void unlock() { - if (DISABLE_FILE_LOCK) { - return; - } - - lockCounter--; - if (lockCounter != 0) { - return; - } - - // release the lock.. - if (lock != null) { - try { - lock.release(); - System.getProperties().remove(getVmLockKey()); - } catch (Throwable ignore) { - } - lock = null; - } - closeReadFile(); - - if (deleteOnUnlock) { - file.delete(); - } - } - - private String getVmLockKey() throws IOException { - return getClass().getName() + ".lock." + file.getCanonicalPath(); - } - - private void closeReadFile() { - // close the file. - if (readFile != null) { - try { - readFile.close(); - } catch (Throwable ignore) { - } - readFile = null; - } - - } - - public boolean keepAlive() { - return lock != null && lock.isValid() && file.exists(); - } - -} +/** + * 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.activemq.util; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; +import java.util.Date; + +/** + * Used to lock a File. + * + * @author chirino + */ +public class LockFile { + + private static final boolean DISABLE_FILE_LOCK = Boolean.getBoolean("java.nio.channels.FileLock.broken"); + final private File file; + + private FileLock lock; + private RandomAccessFile readFile; + private int lockCounter; + private final boolean deleteOnUnlock; + + public LockFile(File file, boolean deleteOnUnlock) { + this.file = file; + this.deleteOnUnlock = deleteOnUnlock; + } + + /** + * @throws IOException + */ + synchronized public void lock() throws IOException { + if (DISABLE_FILE_LOCK) { + return; + } + + if (lockCounter > 0) { + return; + } + + IOHelper.mkdirs(file.getParentFile()); + synchronized (LockFile.class) { + if (System.getProperty(getVmLockKey()) != null) { + throw new IOException("File '" + file + "' could not be locked as lock is already held for this jvm."); + } + System.setProperty(getVmLockKey(), new Date().toString()); + } + try { + if (lock == null) { + readFile = new RandomAccessFile(file, "rw"); + IOException reason = null; + try { + lock = readFile.getChannel().tryLock(0, Math.max(1, readFile.getChannel().size()), false); + } catch (OverlappingFileLockException e) { + reason = IOExceptionSupport.create("File '" + file + "' could not be locked.", e); + } catch (IOException ioe) { + reason = ioe; + } + if (lock != null) { + lockCounter++; + System.setProperty(getVmLockKey(), new Date().toString()); + } else { + // new read file for next attempt + closeReadFile(); + if (reason != null) { + throw reason; + } + throw new IOException("File '" + file + "' could not be locked."); + } + + } + } finally { + synchronized (LockFile.class) { + if (lock == null) { + System.getProperties().remove(getVmLockKey()); + } + } + } + } + + /** + */ + public void unlock() { + if (DISABLE_FILE_LOCK) { + return; + } + + lockCounter--; + if (lockCounter != 0) { + return; + } + + // release the lock.. + if (lock != null) { + try { + lock.release(); + System.getProperties().remove(getVmLockKey()); + } catch (Throwable ignore) { + } + lock = null; + } + closeReadFile(); + + if (deleteOnUnlock) { + file.delete(); + } + } + + private String getVmLockKey() throws IOException { + return getClass().getName() + ".lock." + file.getCanonicalPath(); + } + + private void closeReadFile() { + // close the file. + if (readFile != null) { + try { + readFile.close(); + } catch (Throwable ignore) { + } + readFile = null; + } + + } + + public boolean keepAlive() { + return lock != null && lock.isValid() && file.exists(); + } + +} diff --git a/activemq-console/src/main/java/org/apache/activemq/console/command/CreateCommand.java b/activemq-console/src/main/java/org/apache/activemq/console/command/CreateCommand.java index c2e6a099db..ae878372bb 100644 --- a/activemq-console/src/main/java/org/apache/activemq/console/command/CreateCommand.java +++ b/activemq-console/src/main/java/org/apache/activemq/console/command/CreateCommand.java @@ -1,296 +1,296 @@ -/* - * 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.activemq.console.command; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.xml.sax.SAXException; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.*; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; -import java.io.*; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.util.List; - -public class CreateCommand extends AbstractCommand { - - protected final String[] helpFile = new String[] { - "Task Usage: Main create path/to/brokerA [create-options]", - "Description: Creates a runnable broker instance in the specified path.", - "", - "List Options:", - " --amqconf Path to ActiveMQ conf file that will be used in the broker instance. Default is: conf/activemq.xml", - " --version Display the version information.", - " -h,-?,--help Display the create broker help information.", - "" - }; - - protected final String DEFAULT_TARGET_ACTIVEMQ_CONF = "conf/activemq.xml"; // default activemq conf to create in the new broker instance - protected final String DEFAULT_BROKERNAME_XPATH = "/beans/broker/@brokerName"; // default broker name xpath to change the broker name - - protected final String[] BASE_SUB_DIRS = { "bin", "conf" }; // default sub directories that will be created - protected final String BROKER_NAME_REGEX = "[$][{]brokerName[}]"; // use to replace broker name property holders - - protected String amqConf = "conf/activemq.xml"; // default conf if no conf is specified via --amqconf - - // default files to create - protected String[][] fileWriteMap = { - { "winActivemq", "bin/${brokerName}.bat" }, - { "unixActivemq", "bin/${brokerName}" } - }; - - - protected String brokerName; - protected File amqHome; - protected File targetAmqBase; - - @Override - public String getName() { - return "create"; - } - - @Override - public String getOneLineDescription() { - return "Creates a runnable broker instance in the specified path."; - } - - protected void runTask(List tokens) throws Exception { - context.print("Running create broker task..."); - amqHome = new File(System.getProperty("activemq.home")); - for (String token : tokens) { - - targetAmqBase = new File(token); - brokerName = targetAmqBase.getName(); - - - if (targetAmqBase.exists()) { - BufferedReader console = new BufferedReader(new InputStreamReader(System.in)); - String resp; - while (true) { - context.print("Target directory (" + targetAmqBase.getCanonicalPath() + ") already exists. Overwrite (y/n): "); - resp = console.readLine(); - if (resp.equalsIgnoreCase("y") || resp.equalsIgnoreCase("yes")) { - break; - } else if (resp.equalsIgnoreCase("n") || resp.equalsIgnoreCase("no")) { - return; - } - } - } - - context.print("Creating directory: " + targetAmqBase.getCanonicalPath()); - targetAmqBase.mkdirs(); - createSubDirs(targetAmqBase, BASE_SUB_DIRS); - writeFileMapping(targetAmqBase, fileWriteMap); - copyActivemqConf(amqHome, targetAmqBase, amqConf); - copyConfDirectory(new File(amqHome, "conf"), new File(targetAmqBase, "conf")); - } - } - - /** - * Handle the --amqconf options. - * - * @param token - option token to handle - * @param tokens - succeeding command arguments - * @throws Exception - */ - protected void handleOption(String token, List tokens) throws Exception { - if (token.startsWith("--amqconf")) { - // If no amqconf specified, or next token is a new option - if (tokens.isEmpty() || tokens.get(0).startsWith("-")) { - context.printException(new IllegalArgumentException("Attributes to amqconf not specified")); - return; - } - - amqConf = tokens.remove(0); - } else { - // Let super class handle unknown option - super.handleOption(token, tokens); - } - } - - protected void createSubDirs(File target, String[] subDirs) throws IOException { - File subDirFile; - for (String subDir : BASE_SUB_DIRS) { - subDirFile = new File(target, subDir); - context.print("Creating directory: " + subDirFile.getCanonicalPath()); - subDirFile.mkdirs(); - } - } - - protected void writeFileMapping(File targetBase, String[][] fileWriteMapping) throws IOException { - for (String[] fileWrite : fileWriteMapping) { - File dest = new File(targetBase, resolveParam(BROKER_NAME_REGEX, brokerName, fileWrite[1])); - context.print("Creating new file: " + dest.getCanonicalPath()); - writeFile(fileWrite[0], dest); - } - } - - protected void copyActivemqConf(File srcBase, File targetBase, String activemqConf) throws IOException, ParserConfigurationException, SAXException, TransformerException, XPathExpressionException { - File src = new File(srcBase, activemqConf); - - if (!src.exists()) { - throw new FileNotFoundException("File: " + src.getCanonicalPath() + " not found."); - } - - File dest = new File(targetBase, DEFAULT_TARGET_ACTIVEMQ_CONF); - context.print("Copying from: " + src.getCanonicalPath() + "\n to: " + dest.getCanonicalPath()); - - DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - Element docElem = builder.parse(src).getDocumentElement(); - - XPath xpath = XPathFactory.newInstance().newXPath(); - Attr brokerNameAttr = (Attr) xpath.evaluate(DEFAULT_BROKERNAME_XPATH, docElem, XPathConstants.NODE); - brokerNameAttr.setValue(brokerName); - - writeToFile(new DOMSource(docElem), dest); - } - - protected void printHelp() { - context.printHelp(helpFile); - } - - // write the default files to create (i.e. script files) - private void writeFile(String typeName, File dest) throws IOException { - String data; - if (typeName.equals("winActivemq")) { - data = winActivemqData; - data = resolveParam("[$][{]activemq.home[}]", amqHome.getCanonicalPath().replaceAll("[\\\\]", "/"), data); - data = resolveParam("[$][{]activemq.base[}]", targetAmqBase.getCanonicalPath().replaceAll("[\\\\]", "/"), data); - } else if (typeName.equals("unixActivemq")) { - data = getUnixActivemqData(); - data = resolveParam("[$][{]activemq.home[}]", amqHome.getCanonicalPath().replaceAll("[\\\\]", "/"), data); - data = resolveParam("[$][{]activemq.base[}]", targetAmqBase.getCanonicalPath().replaceAll("[\\\\]", "/"), data); - } else { - throw new IllegalStateException("Unknown file type: " + typeName); - } - - ByteBuffer buf = ByteBuffer.allocate(data.length()); - buf.put(data.getBytes()); - buf.flip(); - - FileChannel destinationChannel = new FileOutputStream(dest).getChannel(); - destinationChannel.write(buf); - destinationChannel.close(); - - // Set file permissions available for Java 6.0 only - dest.setExecutable(true); - dest.setReadable(true); - dest.setWritable(true); - } - - // utlity method to write an xml source to file - private void writeToFile(Source src, File file) throws TransformerException { - TransformerFactory tFactory = TransformerFactory.newInstance(); - Transformer fileTransformer = tFactory.newTransformer(); - - Result res = new StreamResult(file); - fileTransformer.transform(src, res); - } - - // utility method to copy one file to another - private void copyFile(File from, File dest) throws IOException { - if (!from.exists()) { - return; - } - FileChannel sourceChannel = new FileInputStream(from).getChannel(); - FileChannel destinationChannel = new FileOutputStream(dest).getChannel(); - sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel); - sourceChannel.close(); - destinationChannel.close(); - } - - private void copyConfDirectory(File from, File dest) throws IOException { - if (from.isDirectory()) { - String files[] = from.list(); - - for (String file : files) { - File srcFile = new File(from, file); - if (srcFile.isFile() && !srcFile.getName().equals("activemq.xml")) { - File destFile = new File(dest, file); - context.print("Copying from: " + srcFile.getCanonicalPath() + "\n to: " + destFile.getCanonicalPath()); - copyFile(srcFile, destFile); - } - } - } else { - throw new IOException(from + " is not a directory"); - } - } - - // replace a property place holder (paramName) with the paramValue - private String resolveParam(String paramName, String paramValue, String target) { - return target.replaceAll(paramName, paramValue); - } - - // Embedded windows script data - private static final String winActivemqData = - "@echo off\n" - + "set ACTIVEMQ_HOME=\"${activemq.home}\"\n" - + "set ACTIVEMQ_BASE=\"${activemq.base}\"\n" - + "\n" - + "set PARAM=%1\n" - + ":getParam\n" - + "shift\n" - + "if \"%1\"==\"\" goto end\n" - + "set PARAM=%PARAM% %1\n" - + "goto getParam\n" - + ":end\n" - + "\n" - + "%ACTIVEMQ_HOME%/bin/activemq %PARAM%"; - - - private String getUnixActivemqData() { - StringBuffer res = new StringBuffer(); - res.append("## Figure out the ACTIVEMQ_BASE from the directory this script was run from\n"); - res.append("PRG=\"$0\"\n"); - res.append("progname=`basename \"$0\"`\n"); - res.append("saveddir=`pwd`\n"); - res.append("# need this for relative symlinks\n"); - res.append("dirname_prg=`dirname \"$PRG\"`\n"); - res.append("cd \"$dirname_prg\"\n"); - res.append("while [ -h \"$PRG\" ] ; do\n"); - res.append(" ls=`ls -ld \"$PRG\"`\n"); - res.append(" link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n"); - res.append(" if expr \"$link\" : '.*/.*' > /dev/null; then\n"); - res.append(" PRG=\"$link\"\n"); - res.append(" else\n"); - res.append(" PRG=`dirname \"$PRG\"`\"/$link\"\n"); - res.append(" fi\n"); - res.append("done\n"); - res.append("ACTIVEMQ_BASE=`dirname \"$PRG\"`/..\n"); - res.append("cd \"$saveddir\"\n\n"); - res.append("ACTIVEMQ_BASE=`cd \"$ACTIVEMQ_BASE\" && pwd`\n\n"); - res.append("## Enable remote debugging\n"); - res.append("#export ACTIVEMQ_DEBUG_OPTS=\"-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005\"\n\n"); - res.append("## Add system properties for this instance here (if needed), e.g\n"); - res.append("#export ACTIVEMQ_OPTS_MEMORY=\"-Xms256M -Xmx1G\"\n"); - res.append("#export ACTIVEMQ_OPTS=\"$ACTIVEMQ_OPTS_MEMORY -Dorg.apache.activemq.UseDedicatedTaskRunner=true -Djava.util.logging.config.file=logging.properties\"\n\n"); - res.append("export ACTIVEMQ_HOME=${activemq.home}\n"); - res.append("export ACTIVEMQ_BASE=$ACTIVEMQ_BASE\n\n"); - res.append("${ACTIVEMQ_HOME}/bin/activemq \"$@\""); - return res.toString(); - } - -} +/* + * 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.activemq.console.command; + +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.*; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.List; + +public class CreateCommand extends AbstractCommand { + + protected final String[] helpFile = new String[] { + "Task Usage: Main create path/to/brokerA [create-options]", + "Description: Creates a runnable broker instance in the specified path.", + "", + "List Options:", + " --amqconf Path to ActiveMQ conf file that will be used in the broker instance. Default is: conf/activemq.xml", + " --version Display the version information.", + " -h,-?,--help Display the create broker help information.", + "" + }; + + protected final String DEFAULT_TARGET_ACTIVEMQ_CONF = "conf/activemq.xml"; // default activemq conf to create in the new broker instance + protected final String DEFAULT_BROKERNAME_XPATH = "/beans/broker/@brokerName"; // default broker name xpath to change the broker name + + protected final String[] BASE_SUB_DIRS = { "bin", "conf" }; // default sub directories that will be created + protected final String BROKER_NAME_REGEX = "[$][{]brokerName[}]"; // use to replace broker name property holders + + protected String amqConf = "conf/activemq.xml"; // default conf if no conf is specified via --amqconf + + // default files to create + protected String[][] fileWriteMap = { + { "winActivemq", "bin/${brokerName}.bat" }, + { "unixActivemq", "bin/${brokerName}" } + }; + + + protected String brokerName; + protected File amqHome; + protected File targetAmqBase; + + @Override + public String getName() { + return "create"; + } + + @Override + public String getOneLineDescription() { + return "Creates a runnable broker instance in the specified path."; + } + + protected void runTask(List tokens) throws Exception { + context.print("Running create broker task..."); + amqHome = new File(System.getProperty("activemq.home")); + for (String token : tokens) { + + targetAmqBase = new File(token); + brokerName = targetAmqBase.getName(); + + + if (targetAmqBase.exists()) { + BufferedReader console = new BufferedReader(new InputStreamReader(System.in)); + String resp; + while (true) { + context.print("Target directory (" + targetAmqBase.getCanonicalPath() + ") already exists. Overwrite (y/n): "); + resp = console.readLine(); + if (resp.equalsIgnoreCase("y") || resp.equalsIgnoreCase("yes")) { + break; + } else if (resp.equalsIgnoreCase("n") || resp.equalsIgnoreCase("no")) { + return; + } + } + } + + context.print("Creating directory: " + targetAmqBase.getCanonicalPath()); + targetAmqBase.mkdirs(); + createSubDirs(targetAmqBase, BASE_SUB_DIRS); + writeFileMapping(targetAmqBase, fileWriteMap); + copyActivemqConf(amqHome, targetAmqBase, amqConf); + copyConfDirectory(new File(amqHome, "conf"), new File(targetAmqBase, "conf")); + } + } + + /** + * Handle the --amqconf options. + * + * @param token - option token to handle + * @param tokens - succeeding command arguments + * @throws Exception + */ + protected void handleOption(String token, List tokens) throws Exception { + if (token.startsWith("--amqconf")) { + // If no amqconf specified, or next token is a new option + if (tokens.isEmpty() || tokens.get(0).startsWith("-")) { + context.printException(new IllegalArgumentException("Attributes to amqconf not specified")); + return; + } + + amqConf = tokens.remove(0); + } else { + // Let super class handle unknown option + super.handleOption(token, tokens); + } + } + + protected void createSubDirs(File target, String[] subDirs) throws IOException { + File subDirFile; + for (String subDir : BASE_SUB_DIRS) { + subDirFile = new File(target, subDir); + context.print("Creating directory: " + subDirFile.getCanonicalPath()); + subDirFile.mkdirs(); + } + } + + protected void writeFileMapping(File targetBase, String[][] fileWriteMapping) throws IOException { + for (String[] fileWrite : fileWriteMapping) { + File dest = new File(targetBase, resolveParam(BROKER_NAME_REGEX, brokerName, fileWrite[1])); + context.print("Creating new file: " + dest.getCanonicalPath()); + writeFile(fileWrite[0], dest); + } + } + + protected void copyActivemqConf(File srcBase, File targetBase, String activemqConf) throws IOException, ParserConfigurationException, SAXException, TransformerException, XPathExpressionException { + File src = new File(srcBase, activemqConf); + + if (!src.exists()) { + throw new FileNotFoundException("File: " + src.getCanonicalPath() + " not found."); + } + + File dest = new File(targetBase, DEFAULT_TARGET_ACTIVEMQ_CONF); + context.print("Copying from: " + src.getCanonicalPath() + "\n to: " + dest.getCanonicalPath()); + + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Element docElem = builder.parse(src).getDocumentElement(); + + XPath xpath = XPathFactory.newInstance().newXPath(); + Attr brokerNameAttr = (Attr) xpath.evaluate(DEFAULT_BROKERNAME_XPATH, docElem, XPathConstants.NODE); + brokerNameAttr.setValue(brokerName); + + writeToFile(new DOMSource(docElem), dest); + } + + protected void printHelp() { + context.printHelp(helpFile); + } + + // write the default files to create (i.e. script files) + private void writeFile(String typeName, File dest) throws IOException { + String data; + if (typeName.equals("winActivemq")) { + data = winActivemqData; + data = resolveParam("[$][{]activemq.home[}]", amqHome.getCanonicalPath().replaceAll("[\\\\]", "/"), data); + data = resolveParam("[$][{]activemq.base[}]", targetAmqBase.getCanonicalPath().replaceAll("[\\\\]", "/"), data); + } else if (typeName.equals("unixActivemq")) { + data = getUnixActivemqData(); + data = resolveParam("[$][{]activemq.home[}]", amqHome.getCanonicalPath().replaceAll("[\\\\]", "/"), data); + data = resolveParam("[$][{]activemq.base[}]", targetAmqBase.getCanonicalPath().replaceAll("[\\\\]", "/"), data); + } else { + throw new IllegalStateException("Unknown file type: " + typeName); + } + + ByteBuffer buf = ByteBuffer.allocate(data.length()); + buf.put(data.getBytes()); + buf.flip(); + + FileChannel destinationChannel = new FileOutputStream(dest).getChannel(); + destinationChannel.write(buf); + destinationChannel.close(); + + // Set file permissions available for Java 6.0 only + dest.setExecutable(true); + dest.setReadable(true); + dest.setWritable(true); + } + + // utlity method to write an xml source to file + private void writeToFile(Source src, File file) throws TransformerException { + TransformerFactory tFactory = TransformerFactory.newInstance(); + Transformer fileTransformer = tFactory.newTransformer(); + + Result res = new StreamResult(file); + fileTransformer.transform(src, res); + } + + // utility method to copy one file to another + private void copyFile(File from, File dest) throws IOException { + if (!from.exists()) { + return; + } + FileChannel sourceChannel = new FileInputStream(from).getChannel(); + FileChannel destinationChannel = new FileOutputStream(dest).getChannel(); + sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel); + sourceChannel.close(); + destinationChannel.close(); + } + + private void copyConfDirectory(File from, File dest) throws IOException { + if (from.isDirectory()) { + String files[] = from.list(); + + for (String file : files) { + File srcFile = new File(from, file); + if (srcFile.isFile() && !srcFile.getName().equals("activemq.xml")) { + File destFile = new File(dest, file); + context.print("Copying from: " + srcFile.getCanonicalPath() + "\n to: " + destFile.getCanonicalPath()); + copyFile(srcFile, destFile); + } + } + } else { + throw new IOException(from + " is not a directory"); + } + } + + // replace a property place holder (paramName) with the paramValue + private String resolveParam(String paramName, String paramValue, String target) { + return target.replaceAll(paramName, paramValue); + } + + // Embedded windows script data + private static final String winActivemqData = + "@echo off\n" + + "set ACTIVEMQ_HOME=\"${activemq.home}\"\n" + + "set ACTIVEMQ_BASE=\"${activemq.base}\"\n" + + "\n" + + "set PARAM=%1\n" + + ":getParam\n" + + "shift\n" + + "if \"%1\"==\"\" goto end\n" + + "set PARAM=%PARAM% %1\n" + + "goto getParam\n" + + ":end\n" + + "\n" + + "%ACTIVEMQ_HOME%/bin/activemq %PARAM%"; + + + private String getUnixActivemqData() { + StringBuffer res = new StringBuffer(); + res.append("## Figure out the ACTIVEMQ_BASE from the directory this script was run from\n"); + res.append("PRG=\"$0\"\n"); + res.append("progname=`basename \"$0\"`\n"); + res.append("saveddir=`pwd`\n"); + res.append("# need this for relative symlinks\n"); + res.append("dirname_prg=`dirname \"$PRG\"`\n"); + res.append("cd \"$dirname_prg\"\n"); + res.append("while [ -h \"$PRG\" ] ; do\n"); + res.append(" ls=`ls -ld \"$PRG\"`\n"); + res.append(" link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n"); + res.append(" if expr \"$link\" : '.*/.*' > /dev/null; then\n"); + res.append(" PRG=\"$link\"\n"); + res.append(" else\n"); + res.append(" PRG=`dirname \"$PRG\"`\"/$link\"\n"); + res.append(" fi\n"); + res.append("done\n"); + res.append("ACTIVEMQ_BASE=`dirname \"$PRG\"`/..\n"); + res.append("cd \"$saveddir\"\n\n"); + res.append("ACTIVEMQ_BASE=`cd \"$ACTIVEMQ_BASE\" && pwd`\n\n"); + res.append("## Enable remote debugging\n"); + res.append("#export ACTIVEMQ_DEBUG_OPTS=\"-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005\"\n\n"); + res.append("## Add system properties for this instance here (if needed), e.g\n"); + res.append("#export ACTIVEMQ_OPTS_MEMORY=\"-Xms256M -Xmx1G\"\n"); + res.append("#export ACTIVEMQ_OPTS=\"$ACTIVEMQ_OPTS_MEMORY -Dorg.apache.activemq.UseDedicatedTaskRunner=true -Djava.util.logging.config.file=logging.properties\"\n\n"); + res.append("export ACTIVEMQ_HOME=${activemq.home}\n"); + res.append("export ACTIVEMQ_BASE=$ACTIVEMQ_BASE\n\n"); + res.append("${ACTIVEMQ_HOME}/bin/activemq \"$@\""); + return res.toString(); + } + +} diff --git a/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarBuffer.java b/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarBuffer.java index 6bde68fa52..46f91109c5 100644 --- a/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarBuffer.java +++ b/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarBuffer.java @@ -1,462 +1,462 @@ -/* - * 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. - * - */ - -/* - * This package is based on the work done by Timothy Gerard Endres - * (time@ice.com) to whom the Ant project is very grateful for his great code. - */ - -package org.apache.activemq.console.command.store.tar; - -import java.io.InputStream; -import java.io.OutputStream; -import java.io.IOException; -import java.util.Arrays; - -/** - * The TarBuffer class implements the tar archive concept - * of a buffered input stream. This concept goes back to the - * days of blocked tape drives and special io devices. In the - * Java universe, the only real function that this class - * performs is to ensure that files have the correct "block" - * size, or other tars will complain. - *

- * You should never have a need to access this class directly. - * TarBuffers are created by Tar IO Streams. - * - */ - -public class TarBuffer { - - /** Default record size */ - public static final int DEFAULT_RCDSIZE = (512); - - /** Default block size */ - public static final int DEFAULT_BLKSIZE = (DEFAULT_RCDSIZE * 20); - - private InputStream inStream; - private OutputStream outStream; - private byte[] blockBuffer; - private int currBlkIdx; - private int currRecIdx; - private int blockSize; - private int recordSize; - private int recsPerBlock; - private boolean debug; - - /** - * Constructor for a TarBuffer on an input stream. - * @param inStream the input stream to use - */ - public TarBuffer(InputStream inStream) { - this(inStream, TarBuffer.DEFAULT_BLKSIZE); - } - - /** - * Constructor for a TarBuffer on an input stream. - * @param inStream the input stream to use - * @param blockSize the block size to use - */ - public TarBuffer(InputStream inStream, int blockSize) { - this(inStream, blockSize, TarBuffer.DEFAULT_RCDSIZE); - } - - /** - * Constructor for a TarBuffer on an input stream. - * @param inStream the input stream to use - * @param blockSize the block size to use - * @param recordSize the record size to use - */ - public TarBuffer(InputStream inStream, int blockSize, int recordSize) { - this.inStream = inStream; - this.outStream = null; - - this.initialize(blockSize, recordSize); - } - - /** - * Constructor for a TarBuffer on an output stream. - * @param outStream the output stream to use - */ - public TarBuffer(OutputStream outStream) { - this(outStream, TarBuffer.DEFAULT_BLKSIZE); - } - - /** - * Constructor for a TarBuffer on an output stream. - * @param outStream the output stream to use - * @param blockSize the block size to use - */ - public TarBuffer(OutputStream outStream, int blockSize) { - this(outStream, blockSize, TarBuffer.DEFAULT_RCDSIZE); - } - - /** - * Constructor for a TarBuffer on an output stream. - * @param outStream the output stream to use - * @param blockSize the block size to use - * @param recordSize the record size to use - */ - public TarBuffer(OutputStream outStream, int blockSize, int recordSize) { - this.inStream = null; - this.outStream = outStream; - - this.initialize(blockSize, recordSize); - } - - /** - * Initialization common to all constructors. - */ - private void initialize(int blockSize, int recordSize) { - this.debug = false; - this.blockSize = blockSize; - this.recordSize = recordSize; - this.recsPerBlock = (this.blockSize / this.recordSize); - this.blockBuffer = new byte[this.blockSize]; - - if (this.inStream != null) { - this.currBlkIdx = -1; - this.currRecIdx = this.recsPerBlock; - } else { - this.currBlkIdx = 0; - this.currRecIdx = 0; - } - } - - /** - * Get the TAR Buffer's block size. Blocks consist of multiple records. - * @return the block size - */ - public int getBlockSize() { - return this.blockSize; - } - - /** - * Get the TAR Buffer's record size. - * @return the record size - */ - public int getRecordSize() { - return this.recordSize; - } - - /** - * Set the debugging flag for the buffer. - * - * @param debug If true, print debugging output. - */ - public void setDebug(boolean debug) { - this.debug = debug; - } - - /** - * Determine if an archive record indicate End of Archive. End of - * archive is indicated by a record that consists entirely of null bytes. - * - * @param record The record data to check. - * @return true if the record data is an End of Archive - */ - public boolean isEOFRecord(byte[] record) { - for (int i = 0, sz = getRecordSize(); i < sz; ++i) { - if (record[i] != 0) { - return false; - } - } - - return true; - } - - /** - * Skip over a record on the input stream. - * @throws IOException on error - */ - public void skipRecord() throws IOException { - if (debug) { - System.err.println("SkipRecord: recIdx = " + currRecIdx - + " blkIdx = " + currBlkIdx); - } - - if (inStream == null) { - throw new IOException("reading (via skip) from an output buffer"); - } - - if (currRecIdx >= recsPerBlock) { - if (!readBlock()) { - return; // UNDONE - } - } - - currRecIdx++; - } - - /** - * Read a record from the input stream and return the data. - * - * @return The record data. - * @throws IOException on error - */ - public byte[] readRecord() throws IOException { - if (debug) { - System.err.println("ReadRecord: recIdx = " + currRecIdx - + " blkIdx = " + currBlkIdx); - } - - if (inStream == null) { - throw new IOException("reading from an output buffer"); - } - - if (currRecIdx >= recsPerBlock) { - if (!readBlock()) { - return null; - } - } - - byte[] result = new byte[recordSize]; - - System.arraycopy(blockBuffer, - (currRecIdx * recordSize), result, 0, - recordSize); - - currRecIdx++; - - return result; - } - - /** - * @return false if End-Of-File, else true - */ - private boolean readBlock() throws IOException { - if (debug) { - System.err.println("ReadBlock: blkIdx = " + currBlkIdx); - } - - if (inStream == null) { - throw new IOException("reading from an output buffer"); - } - - currRecIdx = 0; - - int offset = 0; - int bytesNeeded = blockSize; - - while (bytesNeeded > 0) { - long numBytes = inStream.read(blockBuffer, offset, - bytesNeeded); - - // - // NOTE - // We have fit EOF, and the block is not full! - // - // This is a broken archive. It does not follow the standard - // blocking algorithm. However, because we are generous, and - // it requires little effort, we will simply ignore the error - // and continue as if the entire block were read. This does - // not appear to break anything upstream. We used to return - // false in this case. - // - // Thanks to 'Yohann.Roussel@alcatel.fr' for this fix. - // - if (numBytes == -1) { - if (offset == 0) { - // Ensure that we do not read gigabytes of zeros - // for a corrupt tar file. - // See http://issues.apache.org/bugzilla/show_bug.cgi?id=39924 - return false; - } - // However, just leaving the unread portion of the buffer dirty does - // cause problems in some cases. This problem is described in - // http://issues.apache.org/bugzilla/show_bug.cgi?id=29877 - // - // The solution is to fill the unused portion of the buffer with zeros. - - Arrays.fill(blockBuffer, offset, offset + bytesNeeded, (byte) 0); - - break; - } - - offset += numBytes; - bytesNeeded -= numBytes; - - if (numBytes != blockSize) { - if (debug) { - System.err.println("ReadBlock: INCOMPLETE READ " - + numBytes + " of " + blockSize - + " bytes read."); - } - } - } - - currBlkIdx++; - - return true; - } - - /** - * Get the current block number, zero based. - * - * @return The current zero based block number. - */ - public int getCurrentBlockNum() { - return currBlkIdx; - } - - /** - * Get the current record number, within the current block, zero based. - * Thus, current offset = (currentBlockNum * recsPerBlk) + currentRecNum. - * - * @return The current zero based record number. - */ - public int getCurrentRecordNum() { - return currRecIdx - 1; - } - - /** - * Write an archive record to the archive. - * - * @param record The record data to write to the archive. - * @throws IOException on error - */ - public void writeRecord(byte[] record) throws IOException { - if (debug) { - System.err.println("WriteRecord: recIdx = " + currRecIdx - + " blkIdx = " + currBlkIdx); - } - - if (outStream == null) { - throw new IOException("writing to an input buffer"); - } - - if (record.length != recordSize) { - throw new IOException("record to write has length '" - + record.length - + "' which is not the record size of '" - + recordSize + "'"); - } - - if (currRecIdx >= recsPerBlock) { - writeBlock(); - } - - System.arraycopy(record, 0, blockBuffer, - (currRecIdx * recordSize), - recordSize); - - currRecIdx++; - } - - /** - * Write an archive record to the archive, where the record may be - * inside of a larger array buffer. The buffer must be "offset plus - * record size" long. - * - * @param buf The buffer containing the record data to write. - * @param offset The offset of the record data within buf. - * @throws IOException on error - */ - public void writeRecord(byte[] buf, int offset) throws IOException { - if (debug) { - System.err.println("WriteRecord: recIdx = " + currRecIdx - + " blkIdx = " + currBlkIdx); - } - - if (outStream == null) { - throw new IOException("writing to an input buffer"); - } - - if ((offset + recordSize) > buf.length) { - throw new IOException("record has length '" + buf.length - + "' with offset '" + offset - + "' which is less than the record size of '" - + recordSize + "'"); - } - - if (currRecIdx >= recsPerBlock) { - writeBlock(); - } - - System.arraycopy(buf, offset, blockBuffer, - (currRecIdx * recordSize), - recordSize); - - currRecIdx++; - } - - /** - * Write a TarBuffer block to the archive. - */ - private void writeBlock() throws IOException { - if (debug) { - System.err.println("WriteBlock: blkIdx = " + currBlkIdx); - } - - if (outStream == null) { - throw new IOException("writing to an input buffer"); - } - - outStream.write(blockBuffer, 0, blockSize); - outStream.flush(); - - currRecIdx = 0; - currBlkIdx++; - Arrays.fill(blockBuffer, (byte) 0); - } - - /** - * Flush the current data block if it has any data in it. - */ - void flushBlock() throws IOException { - if (debug) { - System.err.println("TarBuffer.flushBlock() called."); - } - - if (outStream == null) { - throw new IOException("writing to an input buffer"); - } - - if (currRecIdx > 0) { - writeBlock(); - } - } - - /** - * Close the TarBuffer. If this is an output buffer, also flush the - * current block before closing. - * @throws IOException on error - */ - public void close() throws IOException { - if (debug) { - System.err.println("TarBuffer.closeBuffer()."); - } - - if (outStream != null) { - flushBlock(); - - if (outStream != System.out - && outStream != System.err) { - outStream.close(); - - outStream = null; - } - } else if (inStream != null) { - if (inStream != System.in) { - inStream.close(); - - inStream = null; - } - } - } -} +/* + * 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. + * + */ + +/* + * This package is based on the work done by Timothy Gerard Endres + * (time@ice.com) to whom the Ant project is very grateful for his great code. + */ + +package org.apache.activemq.console.command.store.tar; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * The TarBuffer class implements the tar archive concept + * of a buffered input stream. This concept goes back to the + * days of blocked tape drives and special io devices. In the + * Java universe, the only real function that this class + * performs is to ensure that files have the correct "block" + * size, or other tars will complain. + *

+ * You should never have a need to access this class directly. + * TarBuffers are created by Tar IO Streams. + * + */ + +public class TarBuffer { + + /** Default record size */ + public static final int DEFAULT_RCDSIZE = (512); + + /** Default block size */ + public static final int DEFAULT_BLKSIZE = (DEFAULT_RCDSIZE * 20); + + private InputStream inStream; + private OutputStream outStream; + private byte[] blockBuffer; + private int currBlkIdx; + private int currRecIdx; + private int blockSize; + private int recordSize; + private int recsPerBlock; + private boolean debug; + + /** + * Constructor for a TarBuffer on an input stream. + * @param inStream the input stream to use + */ + public TarBuffer(InputStream inStream) { + this(inStream, TarBuffer.DEFAULT_BLKSIZE); + } + + /** + * Constructor for a TarBuffer on an input stream. + * @param inStream the input stream to use + * @param blockSize the block size to use + */ + public TarBuffer(InputStream inStream, int blockSize) { + this(inStream, blockSize, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for a TarBuffer on an input stream. + * @param inStream the input stream to use + * @param blockSize the block size to use + * @param recordSize the record size to use + */ + public TarBuffer(InputStream inStream, int blockSize, int recordSize) { + this.inStream = inStream; + this.outStream = null; + + this.initialize(blockSize, recordSize); + } + + /** + * Constructor for a TarBuffer on an output stream. + * @param outStream the output stream to use + */ + public TarBuffer(OutputStream outStream) { + this(outStream, TarBuffer.DEFAULT_BLKSIZE); + } + + /** + * Constructor for a TarBuffer on an output stream. + * @param outStream the output stream to use + * @param blockSize the block size to use + */ + public TarBuffer(OutputStream outStream, int blockSize) { + this(outStream, blockSize, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for a TarBuffer on an output stream. + * @param outStream the output stream to use + * @param blockSize the block size to use + * @param recordSize the record size to use + */ + public TarBuffer(OutputStream outStream, int blockSize, int recordSize) { + this.inStream = null; + this.outStream = outStream; + + this.initialize(blockSize, recordSize); + } + + /** + * Initialization common to all constructors. + */ + private void initialize(int blockSize, int recordSize) { + this.debug = false; + this.blockSize = blockSize; + this.recordSize = recordSize; + this.recsPerBlock = (this.blockSize / this.recordSize); + this.blockBuffer = new byte[this.blockSize]; + + if (this.inStream != null) { + this.currBlkIdx = -1; + this.currRecIdx = this.recsPerBlock; + } else { + this.currBlkIdx = 0; + this.currRecIdx = 0; + } + } + + /** + * Get the TAR Buffer's block size. Blocks consist of multiple records. + * @return the block size + */ + public int getBlockSize() { + return this.blockSize; + } + + /** + * Get the TAR Buffer's record size. + * @return the record size + */ + public int getRecordSize() { + return this.recordSize; + } + + /** + * Set the debugging flag for the buffer. + * + * @param debug If true, print debugging output. + */ + public void setDebug(boolean debug) { + this.debug = debug; + } + + /** + * Determine if an archive record indicate End of Archive. End of + * archive is indicated by a record that consists entirely of null bytes. + * + * @param record The record data to check. + * @return true if the record data is an End of Archive + */ + public boolean isEOFRecord(byte[] record) { + for (int i = 0, sz = getRecordSize(); i < sz; ++i) { + if (record[i] != 0) { + return false; + } + } + + return true; + } + + /** + * Skip over a record on the input stream. + * @throws IOException on error + */ + public void skipRecord() throws IOException { + if (debug) { + System.err.println("SkipRecord: recIdx = " + currRecIdx + + " blkIdx = " + currBlkIdx); + } + + if (inStream == null) { + throw new IOException("reading (via skip) from an output buffer"); + } + + if (currRecIdx >= recsPerBlock) { + if (!readBlock()) { + return; // UNDONE + } + } + + currRecIdx++; + } + + /** + * Read a record from the input stream and return the data. + * + * @return The record data. + * @throws IOException on error + */ + public byte[] readRecord() throws IOException { + if (debug) { + System.err.println("ReadRecord: recIdx = " + currRecIdx + + " blkIdx = " + currBlkIdx); + } + + if (inStream == null) { + throw new IOException("reading from an output buffer"); + } + + if (currRecIdx >= recsPerBlock) { + if (!readBlock()) { + return null; + } + } + + byte[] result = new byte[recordSize]; + + System.arraycopy(blockBuffer, + (currRecIdx * recordSize), result, 0, + recordSize); + + currRecIdx++; + + return result; + } + + /** + * @return false if End-Of-File, else true + */ + private boolean readBlock() throws IOException { + if (debug) { + System.err.println("ReadBlock: blkIdx = " + currBlkIdx); + } + + if (inStream == null) { + throw new IOException("reading from an output buffer"); + } + + currRecIdx = 0; + + int offset = 0; + int bytesNeeded = blockSize; + + while (bytesNeeded > 0) { + long numBytes = inStream.read(blockBuffer, offset, + bytesNeeded); + + // + // NOTE + // We have fit EOF, and the block is not full! + // + // This is a broken archive. It does not follow the standard + // blocking algorithm. However, because we are generous, and + // it requires little effort, we will simply ignore the error + // and continue as if the entire block were read. This does + // not appear to break anything upstream. We used to return + // false in this case. + // + // Thanks to 'Yohann.Roussel@alcatel.fr' for this fix. + // + if (numBytes == -1) { + if (offset == 0) { + // Ensure that we do not read gigabytes of zeros + // for a corrupt tar file. + // See http://issues.apache.org/bugzilla/show_bug.cgi?id=39924 + return false; + } + // However, just leaving the unread portion of the buffer dirty does + // cause problems in some cases. This problem is described in + // http://issues.apache.org/bugzilla/show_bug.cgi?id=29877 + // + // The solution is to fill the unused portion of the buffer with zeros. + + Arrays.fill(blockBuffer, offset, offset + bytesNeeded, (byte) 0); + + break; + } + + offset += numBytes; + bytesNeeded -= numBytes; + + if (numBytes != blockSize) { + if (debug) { + System.err.println("ReadBlock: INCOMPLETE READ " + + numBytes + " of " + blockSize + + " bytes read."); + } + } + } + + currBlkIdx++; + + return true; + } + + /** + * Get the current block number, zero based. + * + * @return The current zero based block number. + */ + public int getCurrentBlockNum() { + return currBlkIdx; + } + + /** + * Get the current record number, within the current block, zero based. + * Thus, current offset = (currentBlockNum * recsPerBlk) + currentRecNum. + * + * @return The current zero based record number. + */ + public int getCurrentRecordNum() { + return currRecIdx - 1; + } + + /** + * Write an archive record to the archive. + * + * @param record The record data to write to the archive. + * @throws IOException on error + */ + public void writeRecord(byte[] record) throws IOException { + if (debug) { + System.err.println("WriteRecord: recIdx = " + currRecIdx + + " blkIdx = " + currBlkIdx); + } + + if (outStream == null) { + throw new IOException("writing to an input buffer"); + } + + if (record.length != recordSize) { + throw new IOException("record to write has length '" + + record.length + + "' which is not the record size of '" + + recordSize + "'"); + } + + if (currRecIdx >= recsPerBlock) { + writeBlock(); + } + + System.arraycopy(record, 0, blockBuffer, + (currRecIdx * recordSize), + recordSize); + + currRecIdx++; + } + + /** + * Write an archive record to the archive, where the record may be + * inside of a larger array buffer. The buffer must be "offset plus + * record size" long. + * + * @param buf The buffer containing the record data to write. + * @param offset The offset of the record data within buf. + * @throws IOException on error + */ + public void writeRecord(byte[] buf, int offset) throws IOException { + if (debug) { + System.err.println("WriteRecord: recIdx = " + currRecIdx + + " blkIdx = " + currBlkIdx); + } + + if (outStream == null) { + throw new IOException("writing to an input buffer"); + } + + if ((offset + recordSize) > buf.length) { + throw new IOException("record has length '" + buf.length + + "' with offset '" + offset + + "' which is less than the record size of '" + + recordSize + "'"); + } + + if (currRecIdx >= recsPerBlock) { + writeBlock(); + } + + System.arraycopy(buf, offset, blockBuffer, + (currRecIdx * recordSize), + recordSize); + + currRecIdx++; + } + + /** + * Write a TarBuffer block to the archive. + */ + private void writeBlock() throws IOException { + if (debug) { + System.err.println("WriteBlock: blkIdx = " + currBlkIdx); + } + + if (outStream == null) { + throw new IOException("writing to an input buffer"); + } + + outStream.write(blockBuffer, 0, blockSize); + outStream.flush(); + + currRecIdx = 0; + currBlkIdx++; + Arrays.fill(blockBuffer, (byte) 0); + } + + /** + * Flush the current data block if it has any data in it. + */ + void flushBlock() throws IOException { + if (debug) { + System.err.println("TarBuffer.flushBlock() called."); + } + + if (outStream == null) { + throw new IOException("writing to an input buffer"); + } + + if (currRecIdx > 0) { + writeBlock(); + } + } + + /** + * Close the TarBuffer. If this is an output buffer, also flush the + * current block before closing. + * @throws IOException on error + */ + public void close() throws IOException { + if (debug) { + System.err.println("TarBuffer.closeBuffer()."); + } + + if (outStream != null) { + flushBlock(); + + if (outStream != System.out + && outStream != System.err) { + outStream.close(); + + outStream = null; + } + } else if (inStream != null) { + if (inStream != System.in) { + inStream.close(); + + inStream = null; + } + } + } +} diff --git a/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarConstants.java b/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarConstants.java index 940dc46654..6088e82c67 100644 --- a/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarConstants.java +++ b/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarConstants.java @@ -1,158 +1,158 @@ -/* - * 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. - * - */ - -/* - * This package is based on the work done by Timothy Gerard Endres - * (time@ice.com) to whom the Ant project is very grateful for his great code. - */ - -package org.apache.activemq.console.command.store.tar; - -/** - * This interface contains all the definitions used in the package. - * - */ -// CheckStyle:InterfaceIsTypeCheck OFF (bc) -public interface TarConstants { - - /** - * The length of the name field in a header buffer. - */ - int NAMELEN = 100; - - /** - * The length of the mode field in a header buffer. - */ - int MODELEN = 8; - - /** - * The length of the user id field in a header buffer. - */ - int UIDLEN = 8; - - /** - * The length of the group id field in a header buffer. - */ - int GIDLEN = 8; - - /** - * The length of the checksum field in a header buffer. - */ - int CHKSUMLEN = 8; - - /** - * The length of the size field in a header buffer. - */ - int SIZELEN = 12; - - /** - * The maximum size of a file in a tar archive (That's 11 sevens, octal). - */ - long MAXSIZE = 077777777777L; - - /** - * The length of the magic field in a header buffer. - */ - int MAGICLEN = 8; - - /** - * The length of the modification time field in a header buffer. - */ - int MODTIMELEN = 12; - - /** - * The length of the user name field in a header buffer. - */ - int UNAMELEN = 32; - - /** - * The length of the group name field in a header buffer. - */ - int GNAMELEN = 32; - - /** - * The length of the devices field in a header buffer. - */ - int DEVLEN = 8; - - /** - * LF_ constants represent the "link flag" of an entry, or more commonly, - * the "entry type". This is the "old way" of indicating a normal file. - */ - byte LF_OLDNORM = 0; - - /** - * Normal file type. - */ - byte LF_NORMAL = (byte) '0'; - - /** - * Link file type. - */ - byte LF_LINK = (byte) '1'; - - /** - * Symbolic link file type. - */ - byte LF_SYMLINK = (byte) '2'; - - /** - * Character device file type. - */ - byte LF_CHR = (byte) '3'; - - /** - * Block device file type. - */ - byte LF_BLK = (byte) '4'; - - /** - * Directory file type. - */ - byte LF_DIR = (byte) '5'; - - /** - * FIFO (pipe) file type. - */ - byte LF_FIFO = (byte) '6'; - - /** - * Contiguous file type. - */ - byte LF_CONTIG = (byte) '7'; - - /** - * The magic tag representing a POSIX tar archive. - */ - String TMAGIC = "ustar"; - - /** - * The magic tag representing a GNU tar archive. - */ - String GNU_TMAGIC = "ustar "; - - /** - * The namr of the GNU tar entry which contains a long name. - */ - String GNU_LONGLINK = "././@LongLink"; - - /** - * Identifies the *next* file on the tape as having a long name. - */ - byte LF_GNUTYPE_LONGNAME = (byte) 'L'; -} +/* + * 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. + * + */ + +/* + * This package is based on the work done by Timothy Gerard Endres + * (time@ice.com) to whom the Ant project is very grateful for his great code. + */ + +package org.apache.activemq.console.command.store.tar; + +/** + * This interface contains all the definitions used in the package. + * + */ +// CheckStyle:InterfaceIsTypeCheck OFF (bc) +public interface TarConstants { + + /** + * The length of the name field in a header buffer. + */ + int NAMELEN = 100; + + /** + * The length of the mode field in a header buffer. + */ + int MODELEN = 8; + + /** + * The length of the user id field in a header buffer. + */ + int UIDLEN = 8; + + /** + * The length of the group id field in a header buffer. + */ + int GIDLEN = 8; + + /** + * The length of the checksum field in a header buffer. + */ + int CHKSUMLEN = 8; + + /** + * The length of the size field in a header buffer. + */ + int SIZELEN = 12; + + /** + * The maximum size of a file in a tar archive (That's 11 sevens, octal). + */ + long MAXSIZE = 077777777777L; + + /** + * The length of the magic field in a header buffer. + */ + int MAGICLEN = 8; + + /** + * The length of the modification time field in a header buffer. + */ + int MODTIMELEN = 12; + + /** + * The length of the user name field in a header buffer. + */ + int UNAMELEN = 32; + + /** + * The length of the group name field in a header buffer. + */ + int GNAMELEN = 32; + + /** + * The length of the devices field in a header buffer. + */ + int DEVLEN = 8; + + /** + * LF_ constants represent the "link flag" of an entry, or more commonly, + * the "entry type". This is the "old way" of indicating a normal file. + */ + byte LF_OLDNORM = 0; + + /** + * Normal file type. + */ + byte LF_NORMAL = (byte) '0'; + + /** + * Link file type. + */ + byte LF_LINK = (byte) '1'; + + /** + * Symbolic link file type. + */ + byte LF_SYMLINK = (byte) '2'; + + /** + * Character device file type. + */ + byte LF_CHR = (byte) '3'; + + /** + * Block device file type. + */ + byte LF_BLK = (byte) '4'; + + /** + * Directory file type. + */ + byte LF_DIR = (byte) '5'; + + /** + * FIFO (pipe) file type. + */ + byte LF_FIFO = (byte) '6'; + + /** + * Contiguous file type. + */ + byte LF_CONTIG = (byte) '7'; + + /** + * The magic tag representing a POSIX tar archive. + */ + String TMAGIC = "ustar"; + + /** + * The magic tag representing a GNU tar archive. + */ + String GNU_TMAGIC = "ustar "; + + /** + * The namr of the GNU tar entry which contains a long name. + */ + String GNU_LONGLINK = "././@LongLink"; + + /** + * Identifies the *next* file on the tape as having a long name. + */ + byte LF_GNUTYPE_LONGNAME = (byte) 'L'; +} diff --git a/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarEntry.java b/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarEntry.java index 5c7afa9fb3..c3887079c5 100644 --- a/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarEntry.java +++ b/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarEntry.java @@ -1,664 +1,664 @@ -/* - * 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. - * - */ - -/* - * This package is based on the work done by Timothy Gerard Endres - * (time@ice.com) to whom the Ant project is very grateful for his great code. - */ - -package org.apache.activemq.console.command.store.tar; - -import java.io.File; -import java.util.Date; -import java.util.Locale; - -/** - * This class represents an entry in a Tar archive. It consists - * of the entry's header, as well as the entry's File. Entries - * can be instantiated in one of three ways, depending on how - * they are to be used. - *

- * TarEntries that are created from the header bytes read from - * an archive are instantiated with the TarEntry( byte[] ) - * constructor. These entries will be used when extracting from - * or listing the contents of an archive. These entries have their - * header filled in using the header bytes. They also set the File - * to null, since they reference an archive entry not a file. - *

- * TarEntries that are created from Files that are to be written - * into an archive are instantiated with the TarEntry( File ) - * constructor. These entries have their header filled in using - * the File's information. They also keep a reference to the File - * for convenience when writing entries. - *

- * Finally, TarEntries can be constructed from nothing but a name. - * This allows the programmer to construct the entry by hand, for - * instance when only an InputStream is available for writing to - * the archive, and the header information is constructed from - * other information. In this case the header fields are set to - * defaults and the File is set to null. - * - *

- * The C structure for a Tar Entry's header is: - *

- * struct header {
- * char name[NAMSIZ];
- * char mode[8];
- * char uid[8];
- * char gid[8];
- * char size[12];
- * char mtime[12];
- * char chksum[8];
- * char linkflag;
- * char linkname[NAMSIZ];
- * char magic[8];
- * char uname[TUNMLEN];
- * char gname[TGNMLEN];
- * char devmajor[8];
- * char devminor[8];
- * } header;
- * 
- * - */ - -public class TarEntry implements TarConstants { - /** The entry's name. */ - private StringBuffer name; - - /** The entry's permission mode. */ - private int mode; - - /** The entry's user id. */ - private int userId; - - /** The entry's group id. */ - private int groupId; - - /** The entry's size. */ - private long size; - - /** The entry's modification time. */ - private long modTime; - - /** The entry's link flag. */ - private byte linkFlag; - - /** The entry's link name. */ - private StringBuffer linkName; - - /** The entry's magic tag. */ - private StringBuffer magic; - - /** The entry's user name. */ - private StringBuffer userName; - - /** The entry's group name. */ - private StringBuffer groupName; - - /** The entry's major device number. */ - private int devMajor; - - /** The entry's minor device number. */ - private int devMinor; - - /** The entry's file reference */ - private File file; - - /** Maximum length of a user's name in the tar file */ - public static final int MAX_NAMELEN = 31; - - /** Default permissions bits for directories */ - public static final int DEFAULT_DIR_MODE = 040755; - - /** Default permissions bits for files */ - public static final int DEFAULT_FILE_MODE = 0100644; - - /** Convert millis to seconds */ - public static final int MILLIS_PER_SECOND = 1000; - - /** - * Construct an empty entry and prepares the header values. - */ - private TarEntry () { - this.magic = new StringBuffer(TMAGIC); - this.name = new StringBuffer(); - this.linkName = new StringBuffer(); - - String user = System.getProperty("user.name", ""); - - if (user.length() > MAX_NAMELEN) { - user = user.substring(0, MAX_NAMELEN); - } - - this.userId = 0; - this.groupId = 0; - this.userName = new StringBuffer(user); - this.groupName = new StringBuffer(""); - this.file = null; - } - - /** - * Construct an entry with only a name. This allows the programmer - * to construct the entry's header "by hand". File is set to null. - * - * @param name the entry name - */ - public TarEntry(String name) { - this(name, false); - } - - /** - * Construct an entry with only a name. This allows the programmer - * to construct the entry's header "by hand". File is set to null. - * - * @param name the entry name - * @param preserveLeadingSlashes whether to allow leading slashes - * in the name. - */ - public TarEntry(String name, boolean preserveLeadingSlashes) { - this(); - - name = normalizeFileName(name, preserveLeadingSlashes); - boolean isDir = name.endsWith("/"); - - this.devMajor = 0; - this.devMinor = 0; - this.name = new StringBuffer(name); - this.mode = isDir ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE; - this.linkFlag = isDir ? LF_DIR : LF_NORMAL; - this.userId = 0; - this.groupId = 0; - this.size = 0; - this.modTime = (new Date()).getTime() / MILLIS_PER_SECOND; - this.linkName = new StringBuffer(""); - this.userName = new StringBuffer(""); - this.groupName = new StringBuffer(""); - this.devMajor = 0; - this.devMinor = 0; - - } - - /** - * Construct an entry with a name and a link flag. - * - * @param name the entry name - * @param linkFlag the entry link flag. - */ - public TarEntry(String name, byte linkFlag) { - this(name); - this.linkFlag = linkFlag; - if (linkFlag == LF_GNUTYPE_LONGNAME) { - magic = new StringBuffer(GNU_TMAGIC); - } - } - - /** - * Construct an entry for a file. File is set to file, and the - * header is constructed from information from the file. - * - * @param file The file that the entry represents. - */ - public TarEntry(File file) { - this(); - - this.file = file; - - String fileName = normalizeFileName(file.getPath(), false); - this.linkName = new StringBuffer(""); - this.name = new StringBuffer(fileName); - - if (file.isDirectory()) { - this.mode = DEFAULT_DIR_MODE; - this.linkFlag = LF_DIR; - - int nameLength = name.length(); - if (nameLength == 0 || name.charAt(nameLength - 1) != '/') { - this.name.append("/"); - } - this.size = 0; - } else { - this.mode = DEFAULT_FILE_MODE; - this.linkFlag = LF_NORMAL; - this.size = file.length(); - } - - this.modTime = file.lastModified() / MILLIS_PER_SECOND; - this.devMajor = 0; - this.devMinor = 0; - } - - /** - * Construct an entry from an archive's header bytes. File is set - * to null. - * - * @param headerBuf The header bytes from a tar archive entry. - */ - public TarEntry(byte[] headerBuf) { - this(); - parseTarHeader(headerBuf); - } - - /** - * Determine if the two entries are equal. Equality is determined - * by the header names being equal. - * - * @param it Entry to be checked for equality. - * @return True if the entries are equal. - */ - public boolean equals(TarEntry it) { - return getName().equals(it.getName()); - } - - /** - * Determine if the two entries are equal. Equality is determined - * by the header names being equal. - * - * @param it Entry to be checked for equality. - * @return True if the entries are equal. - */ - public boolean equals(Object it) { - if (it == null || getClass() != it.getClass()) { - return false; - } - return equals((TarEntry) it); - } - - /** - * Hashcodes are based on entry names. - * - * @return the entry hashcode - */ - public int hashCode() { - return getName().hashCode(); - } - - /** - * Determine if the given entry is a descendant of this entry. - * Descendancy is determined by the name of the descendant - * starting with this entry's name. - * - * @param desc Entry to be checked as a descendent of this. - * @return True if entry is a descendant of this. - */ - public boolean isDescendent(TarEntry desc) { - return desc.getName().startsWith(getName()); - } - - /** - * Get this entry's name. - * - * @return This entry's name. - */ - public String getName() { - return name.toString(); - } - - /** - * Set this entry's name. - * - * @param name This entry's new name. - */ - public void setName(String name) { - this.name = new StringBuffer(normalizeFileName(name, false)); - } - - /** - * Set the mode for this entry - * - * @param mode the mode for this entry - */ - public void setMode(int mode) { - this.mode = mode; - } - - /** - * Get this entry's link name. - * - * @return This entry's link name. - */ - public String getLinkName() { - return linkName.toString(); - } - - /** - * Get this entry's user id. - * - * @return This entry's user id. - */ - public int getUserId() { - return userId; - } - - /** - * Set this entry's user id. - * - * @param userId This entry's new user id. - */ - public void setUserId(int userId) { - this.userId = userId; - } - - /** - * Get this entry's group id. - * - * @return This entry's group id. - */ - public int getGroupId() { - return groupId; - } - - /** - * Set this entry's group id. - * - * @param groupId This entry's new group id. - */ - public void setGroupId(int groupId) { - this.groupId = groupId; - } - - /** - * Get this entry's user name. - * - * @return This entry's user name. - */ - public String getUserName() { - return userName.toString(); - } - - /** - * Set this entry's user name. - * - * @param userName This entry's new user name. - */ - public void setUserName(String userName) { - this.userName = new StringBuffer(userName); - } - - /** - * Get this entry's group name. - * - * @return This entry's group name. - */ - public String getGroupName() { - return groupName.toString(); - } - - /** - * Set this entry's group name. - * - * @param groupName This entry's new group name. - */ - public void setGroupName(String groupName) { - this.groupName = new StringBuffer(groupName); - } - - /** - * Convenience method to set this entry's group and user ids. - * - * @param userId This entry's new user id. - * @param groupId This entry's new group id. - */ - public void setIds(int userId, int groupId) { - setUserId(userId); - setGroupId(groupId); - } - - /** - * Convenience method to set this entry's group and user names. - * - * @param userName This entry's new user name. - * @param groupName This entry's new group name. - */ - public void setNames(String userName, String groupName) { - setUserName(userName); - setGroupName(groupName); - } - - /** - * Set this entry's modification time. The parameter passed - * to this method is in "Java time". - * - * @param time This entry's new modification time. - */ - public void setModTime(long time) { - modTime = time / MILLIS_PER_SECOND; - } - - /** - * Set this entry's modification time. - * - * @param time This entry's new modification time. - */ - public void setModTime(Date time) { - modTime = time.getTime() / MILLIS_PER_SECOND; - } - - /** - * Set this entry's modification time. - * - * @return time This entry's new modification time. - */ - public Date getModTime() { - return new Date(modTime * MILLIS_PER_SECOND); - } - - /** - * Get this entry's file. - * - * @return This entry's file. - */ - public File getFile() { - return file; - } - - /** - * Get this entry's mode. - * - * @return This entry's mode. - */ - public int getMode() { - return mode; - } - - /** - * Get this entry's file size. - * - * @return This entry's file size. - */ - public long getSize() { - return size; - } - - /** - * Set this entry's file size. - * - * @param size This entry's new file size. - */ - public void setSize(long size) { - this.size = size; - } - - - /** - * Indicate if this entry is a GNU long name block - * - * @return true if this is a long name extension provided by GNU tar - */ - public boolean isGNULongNameEntry() { - return linkFlag == LF_GNUTYPE_LONGNAME - && name.toString().equals(GNU_LONGLINK); - } - - /** - * Return whether or not this entry represents a directory. - * - * @return True if this entry is a directory. - */ - public boolean isDirectory() { - if (file != null) { - return file.isDirectory(); - } - - if (linkFlag == LF_DIR) { - return true; - } - - if (getName().endsWith("/")) { - return true; - } - - return false; - } - - /** - * If this entry represents a file, and the file is a directory, return - * an array of TarEntries for this entry's children. - * - * @return An array of TarEntry's for this entry's children. - */ - public TarEntry[] getDirectoryEntries() { - if (file == null || !file.isDirectory()) { - return new TarEntry[0]; - } - - String[] list = file.list(); - TarEntry[] result = new TarEntry[list.length]; - - for (int i = 0; i < list.length; ++i) { - result[i] = new TarEntry(new File(file, list[i])); - } - - return result; - } - - /** - * Write an entry's header information to a header buffer. - * - * @param outbuf The tar entry header buffer to fill in. - */ - public void writeEntryHeader(byte[] outbuf) { - int offset = 0; - - offset = TarUtils.getNameBytes(name, outbuf, offset, NAMELEN); - offset = TarUtils.getOctalBytes(mode, outbuf, offset, MODELEN); - offset = TarUtils.getOctalBytes(userId, outbuf, offset, UIDLEN); - offset = TarUtils.getOctalBytes(groupId, outbuf, offset, GIDLEN); - offset = TarUtils.getLongOctalBytes(size, outbuf, offset, SIZELEN); - offset = TarUtils.getLongOctalBytes(modTime, outbuf, offset, MODTIMELEN); - - int csOffset = offset; - - for (int c = 0; c < CHKSUMLEN; ++c) { - outbuf[offset++] = (byte) ' '; - } - - outbuf[offset++] = linkFlag; - offset = TarUtils.getNameBytes(linkName, outbuf, offset, NAMELEN); - offset = TarUtils.getNameBytes(magic, outbuf, offset, MAGICLEN); - offset = TarUtils.getNameBytes(userName, outbuf, offset, UNAMELEN); - offset = TarUtils.getNameBytes(groupName, outbuf, offset, GNAMELEN); - offset = TarUtils.getOctalBytes(devMajor, outbuf, offset, DEVLEN); - offset = TarUtils.getOctalBytes(devMinor, outbuf, offset, DEVLEN); - - while (offset < outbuf.length) { - outbuf[offset++] = 0; - } - - long chk = TarUtils.computeCheckSum(outbuf); - - TarUtils.getCheckSumOctalBytes(chk, outbuf, csOffset, CHKSUMLEN); - } - - /** - * Parse an entry's header information from a header buffer. - * - * @param header The tar entry header buffer to get information from. - */ - public void parseTarHeader(byte[] header) { - int offset = 0; - - name = TarUtils.parseName(header, offset, NAMELEN); - offset += NAMELEN; - mode = (int) TarUtils.parseOctal(header, offset, MODELEN); - offset += MODELEN; - userId = (int) TarUtils.parseOctal(header, offset, UIDLEN); - offset += UIDLEN; - groupId = (int) TarUtils.parseOctal(header, offset, GIDLEN); - offset += GIDLEN; - size = TarUtils.parseOctal(header, offset, SIZELEN); - offset += SIZELEN; - modTime = TarUtils.parseOctal(header, offset, MODTIMELEN); - offset += MODTIMELEN; - offset += CHKSUMLEN; - linkFlag = header[offset++]; - linkName = TarUtils.parseName(header, offset, NAMELEN); - offset += NAMELEN; - magic = TarUtils.parseName(header, offset, MAGICLEN); - offset += MAGICLEN; - userName = TarUtils.parseName(header, offset, UNAMELEN); - offset += UNAMELEN; - groupName = TarUtils.parseName(header, offset, GNAMELEN); - offset += GNAMELEN; - devMajor = (int) TarUtils.parseOctal(header, offset, DEVLEN); - offset += DEVLEN; - devMinor = (int) TarUtils.parseOctal(header, offset, DEVLEN); - } - - /** - * Strips Windows' drive letter as well as any leading slashes, - * turns path separators into forward slahes. - */ - private static String normalizeFileName(String fileName, - boolean preserveLeadingSlashes) { - String osname = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); - - if (osname != null) { - - // Strip off drive letters! - // REVIEW Would a better check be "(File.separator == '\')"? - - if (osname.startsWith("windows")) { - if (fileName.length() > 2) { - char ch1 = fileName.charAt(0); - char ch2 = fileName.charAt(1); - - if (ch2 == ':' - && ((ch1 >= 'a' && ch1 <= 'z') - || (ch1 >= 'A' && ch1 <= 'Z'))) { - fileName = fileName.substring(2); - } - } - } else if (osname.indexOf("netware") > -1) { - int colon = fileName.indexOf(':'); - if (colon != -1) { - fileName = fileName.substring(colon + 1); - } - } - } - - fileName = fileName.replace(File.separatorChar, '/'); - - // No absolute pathnames - // Windows (and Posix?) paths can start with "\\NetworkDrive\", - // so we loop on starting /'s. - while (!preserveLeadingSlashes && fileName.startsWith("/")) { - fileName = fileName.substring(1); - } - return fileName; - } -} +/* + * 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. + * + */ + +/* + * This package is based on the work done by Timothy Gerard Endres + * (time@ice.com) to whom the Ant project is very grateful for his great code. + */ + +package org.apache.activemq.console.command.store.tar; + +import java.io.File; +import java.util.Date; +import java.util.Locale; + +/** + * This class represents an entry in a Tar archive. It consists + * of the entry's header, as well as the entry's File. Entries + * can be instantiated in one of three ways, depending on how + * they are to be used. + *

+ * TarEntries that are created from the header bytes read from + * an archive are instantiated with the TarEntry( byte[] ) + * constructor. These entries will be used when extracting from + * or listing the contents of an archive. These entries have their + * header filled in using the header bytes. They also set the File + * to null, since they reference an archive entry not a file. + *

+ * TarEntries that are created from Files that are to be written + * into an archive are instantiated with the TarEntry( File ) + * constructor. These entries have their header filled in using + * the File's information. They also keep a reference to the File + * for convenience when writing entries. + *

+ * Finally, TarEntries can be constructed from nothing but a name. + * This allows the programmer to construct the entry by hand, for + * instance when only an InputStream is available for writing to + * the archive, and the header information is constructed from + * other information. In this case the header fields are set to + * defaults and the File is set to null. + * + *

+ * The C structure for a Tar Entry's header is: + *

+ * struct header {
+ * char name[NAMSIZ];
+ * char mode[8];
+ * char uid[8];
+ * char gid[8];
+ * char size[12];
+ * char mtime[12];
+ * char chksum[8];
+ * char linkflag;
+ * char linkname[NAMSIZ];
+ * char magic[8];
+ * char uname[TUNMLEN];
+ * char gname[TGNMLEN];
+ * char devmajor[8];
+ * char devminor[8];
+ * } header;
+ * 
+ * + */ + +public class TarEntry implements TarConstants { + /** The entry's name. */ + private StringBuffer name; + + /** The entry's permission mode. */ + private int mode; + + /** The entry's user id. */ + private int userId; + + /** The entry's group id. */ + private int groupId; + + /** The entry's size. */ + private long size; + + /** The entry's modification time. */ + private long modTime; + + /** The entry's link flag. */ + private byte linkFlag; + + /** The entry's link name. */ + private StringBuffer linkName; + + /** The entry's magic tag. */ + private StringBuffer magic; + + /** The entry's user name. */ + private StringBuffer userName; + + /** The entry's group name. */ + private StringBuffer groupName; + + /** The entry's major device number. */ + private int devMajor; + + /** The entry's minor device number. */ + private int devMinor; + + /** The entry's file reference */ + private File file; + + /** Maximum length of a user's name in the tar file */ + public static final int MAX_NAMELEN = 31; + + /** Default permissions bits for directories */ + public static final int DEFAULT_DIR_MODE = 040755; + + /** Default permissions bits for files */ + public static final int DEFAULT_FILE_MODE = 0100644; + + /** Convert millis to seconds */ + public static final int MILLIS_PER_SECOND = 1000; + + /** + * Construct an empty entry and prepares the header values. + */ + private TarEntry () { + this.magic = new StringBuffer(TMAGIC); + this.name = new StringBuffer(); + this.linkName = new StringBuffer(); + + String user = System.getProperty("user.name", ""); + + if (user.length() > MAX_NAMELEN) { + user = user.substring(0, MAX_NAMELEN); + } + + this.userId = 0; + this.groupId = 0; + this.userName = new StringBuffer(user); + this.groupName = new StringBuffer(""); + this.file = null; + } + + /** + * Construct an entry with only a name. This allows the programmer + * to construct the entry's header "by hand". File is set to null. + * + * @param name the entry name + */ + public TarEntry(String name) { + this(name, false); + } + + /** + * Construct an entry with only a name. This allows the programmer + * to construct the entry's header "by hand". File is set to null. + * + * @param name the entry name + * @param preserveLeadingSlashes whether to allow leading slashes + * in the name. + */ + public TarEntry(String name, boolean preserveLeadingSlashes) { + this(); + + name = normalizeFileName(name, preserveLeadingSlashes); + boolean isDir = name.endsWith("/"); + + this.devMajor = 0; + this.devMinor = 0; + this.name = new StringBuffer(name); + this.mode = isDir ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE; + this.linkFlag = isDir ? LF_DIR : LF_NORMAL; + this.userId = 0; + this.groupId = 0; + this.size = 0; + this.modTime = (new Date()).getTime() / MILLIS_PER_SECOND; + this.linkName = new StringBuffer(""); + this.userName = new StringBuffer(""); + this.groupName = new StringBuffer(""); + this.devMajor = 0; + this.devMinor = 0; + + } + + /** + * Construct an entry with a name and a link flag. + * + * @param name the entry name + * @param linkFlag the entry link flag. + */ + public TarEntry(String name, byte linkFlag) { + this(name); + this.linkFlag = linkFlag; + if (linkFlag == LF_GNUTYPE_LONGNAME) { + magic = new StringBuffer(GNU_TMAGIC); + } + } + + /** + * Construct an entry for a file. File is set to file, and the + * header is constructed from information from the file. + * + * @param file The file that the entry represents. + */ + public TarEntry(File file) { + this(); + + this.file = file; + + String fileName = normalizeFileName(file.getPath(), false); + this.linkName = new StringBuffer(""); + this.name = new StringBuffer(fileName); + + if (file.isDirectory()) { + this.mode = DEFAULT_DIR_MODE; + this.linkFlag = LF_DIR; + + int nameLength = name.length(); + if (nameLength == 0 || name.charAt(nameLength - 1) != '/') { + this.name.append("/"); + } + this.size = 0; + } else { + this.mode = DEFAULT_FILE_MODE; + this.linkFlag = LF_NORMAL; + this.size = file.length(); + } + + this.modTime = file.lastModified() / MILLIS_PER_SECOND; + this.devMajor = 0; + this.devMinor = 0; + } + + /** + * Construct an entry from an archive's header bytes. File is set + * to null. + * + * @param headerBuf The header bytes from a tar archive entry. + */ + public TarEntry(byte[] headerBuf) { + this(); + parseTarHeader(headerBuf); + } + + /** + * Determine if the two entries are equal. Equality is determined + * by the header names being equal. + * + * @param it Entry to be checked for equality. + * @return True if the entries are equal. + */ + public boolean equals(TarEntry it) { + return getName().equals(it.getName()); + } + + /** + * Determine if the two entries are equal. Equality is determined + * by the header names being equal. + * + * @param it Entry to be checked for equality. + * @return True if the entries are equal. + */ + public boolean equals(Object it) { + if (it == null || getClass() != it.getClass()) { + return false; + } + return equals((TarEntry) it); + } + + /** + * Hashcodes are based on entry names. + * + * @return the entry hashcode + */ + public int hashCode() { + return getName().hashCode(); + } + + /** + * Determine if the given entry is a descendant of this entry. + * Descendancy is determined by the name of the descendant + * starting with this entry's name. + * + * @param desc Entry to be checked as a descendent of this. + * @return True if entry is a descendant of this. + */ + public boolean isDescendent(TarEntry desc) { + return desc.getName().startsWith(getName()); + } + + /** + * Get this entry's name. + * + * @return This entry's name. + */ + public String getName() { + return name.toString(); + } + + /** + * Set this entry's name. + * + * @param name This entry's new name. + */ + public void setName(String name) { + this.name = new StringBuffer(normalizeFileName(name, false)); + } + + /** + * Set the mode for this entry + * + * @param mode the mode for this entry + */ + public void setMode(int mode) { + this.mode = mode; + } + + /** + * Get this entry's link name. + * + * @return This entry's link name. + */ + public String getLinkName() { + return linkName.toString(); + } + + /** + * Get this entry's user id. + * + * @return This entry's user id. + */ + public int getUserId() { + return userId; + } + + /** + * Set this entry's user id. + * + * @param userId This entry's new user id. + */ + public void setUserId(int userId) { + this.userId = userId; + } + + /** + * Get this entry's group id. + * + * @return This entry's group id. + */ + public int getGroupId() { + return groupId; + } + + /** + * Set this entry's group id. + * + * @param groupId This entry's new group id. + */ + public void setGroupId(int groupId) { + this.groupId = groupId; + } + + /** + * Get this entry's user name. + * + * @return This entry's user name. + */ + public String getUserName() { + return userName.toString(); + } + + /** + * Set this entry's user name. + * + * @param userName This entry's new user name. + */ + public void setUserName(String userName) { + this.userName = new StringBuffer(userName); + } + + /** + * Get this entry's group name. + * + * @return This entry's group name. + */ + public String getGroupName() { + return groupName.toString(); + } + + /** + * Set this entry's group name. + * + * @param groupName This entry's new group name. + */ + public void setGroupName(String groupName) { + this.groupName = new StringBuffer(groupName); + } + + /** + * Convenience method to set this entry's group and user ids. + * + * @param userId This entry's new user id. + * @param groupId This entry's new group id. + */ + public void setIds(int userId, int groupId) { + setUserId(userId); + setGroupId(groupId); + } + + /** + * Convenience method to set this entry's group and user names. + * + * @param userName This entry's new user name. + * @param groupName This entry's new group name. + */ + public void setNames(String userName, String groupName) { + setUserName(userName); + setGroupName(groupName); + } + + /** + * Set this entry's modification time. The parameter passed + * to this method is in "Java time". + * + * @param time This entry's new modification time. + */ + public void setModTime(long time) { + modTime = time / MILLIS_PER_SECOND; + } + + /** + * Set this entry's modification time. + * + * @param time This entry's new modification time. + */ + public void setModTime(Date time) { + modTime = time.getTime() / MILLIS_PER_SECOND; + } + + /** + * Set this entry's modification time. + * + * @return time This entry's new modification time. + */ + public Date getModTime() { + return new Date(modTime * MILLIS_PER_SECOND); + } + + /** + * Get this entry's file. + * + * @return This entry's file. + */ + public File getFile() { + return file; + } + + /** + * Get this entry's mode. + * + * @return This entry's mode. + */ + public int getMode() { + return mode; + } + + /** + * Get this entry's file size. + * + * @return This entry's file size. + */ + public long getSize() { + return size; + } + + /** + * Set this entry's file size. + * + * @param size This entry's new file size. + */ + public void setSize(long size) { + this.size = size; + } + + + /** + * Indicate if this entry is a GNU long name block + * + * @return true if this is a long name extension provided by GNU tar + */ + public boolean isGNULongNameEntry() { + return linkFlag == LF_GNUTYPE_LONGNAME + && name.toString().equals(GNU_LONGLINK); + } + + /** + * Return whether or not this entry represents a directory. + * + * @return True if this entry is a directory. + */ + public boolean isDirectory() { + if (file != null) { + return file.isDirectory(); + } + + if (linkFlag == LF_DIR) { + return true; + } + + if (getName().endsWith("/")) { + return true; + } + + return false; + } + + /** + * If this entry represents a file, and the file is a directory, return + * an array of TarEntries for this entry's children. + * + * @return An array of TarEntry's for this entry's children. + */ + public TarEntry[] getDirectoryEntries() { + if (file == null || !file.isDirectory()) { + return new TarEntry[0]; + } + + String[] list = file.list(); + TarEntry[] result = new TarEntry[list.length]; + + for (int i = 0; i < list.length; ++i) { + result[i] = new TarEntry(new File(file, list[i])); + } + + return result; + } + + /** + * Write an entry's header information to a header buffer. + * + * @param outbuf The tar entry header buffer to fill in. + */ + public void writeEntryHeader(byte[] outbuf) { + int offset = 0; + + offset = TarUtils.getNameBytes(name, outbuf, offset, NAMELEN); + offset = TarUtils.getOctalBytes(mode, outbuf, offset, MODELEN); + offset = TarUtils.getOctalBytes(userId, outbuf, offset, UIDLEN); + offset = TarUtils.getOctalBytes(groupId, outbuf, offset, GIDLEN); + offset = TarUtils.getLongOctalBytes(size, outbuf, offset, SIZELEN); + offset = TarUtils.getLongOctalBytes(modTime, outbuf, offset, MODTIMELEN); + + int csOffset = offset; + + for (int c = 0; c < CHKSUMLEN; ++c) { + outbuf[offset++] = (byte) ' '; + } + + outbuf[offset++] = linkFlag; + offset = TarUtils.getNameBytes(linkName, outbuf, offset, NAMELEN); + offset = TarUtils.getNameBytes(magic, outbuf, offset, MAGICLEN); + offset = TarUtils.getNameBytes(userName, outbuf, offset, UNAMELEN); + offset = TarUtils.getNameBytes(groupName, outbuf, offset, GNAMELEN); + offset = TarUtils.getOctalBytes(devMajor, outbuf, offset, DEVLEN); + offset = TarUtils.getOctalBytes(devMinor, outbuf, offset, DEVLEN); + + while (offset < outbuf.length) { + outbuf[offset++] = 0; + } + + long chk = TarUtils.computeCheckSum(outbuf); + + TarUtils.getCheckSumOctalBytes(chk, outbuf, csOffset, CHKSUMLEN); + } + + /** + * Parse an entry's header information from a header buffer. + * + * @param header The tar entry header buffer to get information from. + */ + public void parseTarHeader(byte[] header) { + int offset = 0; + + name = TarUtils.parseName(header, offset, NAMELEN); + offset += NAMELEN; + mode = (int) TarUtils.parseOctal(header, offset, MODELEN); + offset += MODELEN; + userId = (int) TarUtils.parseOctal(header, offset, UIDLEN); + offset += UIDLEN; + groupId = (int) TarUtils.parseOctal(header, offset, GIDLEN); + offset += GIDLEN; + size = TarUtils.parseOctal(header, offset, SIZELEN); + offset += SIZELEN; + modTime = TarUtils.parseOctal(header, offset, MODTIMELEN); + offset += MODTIMELEN; + offset += CHKSUMLEN; + linkFlag = header[offset++]; + linkName = TarUtils.parseName(header, offset, NAMELEN); + offset += NAMELEN; + magic = TarUtils.parseName(header, offset, MAGICLEN); + offset += MAGICLEN; + userName = TarUtils.parseName(header, offset, UNAMELEN); + offset += UNAMELEN; + groupName = TarUtils.parseName(header, offset, GNAMELEN); + offset += GNAMELEN; + devMajor = (int) TarUtils.parseOctal(header, offset, DEVLEN); + offset += DEVLEN; + devMinor = (int) TarUtils.parseOctal(header, offset, DEVLEN); + } + + /** + * Strips Windows' drive letter as well as any leading slashes, + * turns path separators into forward slahes. + */ + private static String normalizeFileName(String fileName, + boolean preserveLeadingSlashes) { + String osname = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); + + if (osname != null) { + + // Strip off drive letters! + // REVIEW Would a better check be "(File.separator == '\')"? + + if (osname.startsWith("windows")) { + if (fileName.length() > 2) { + char ch1 = fileName.charAt(0); + char ch2 = fileName.charAt(1); + + if (ch2 == ':' + && ((ch1 >= 'a' && ch1 <= 'z') + || (ch1 >= 'A' && ch1 <= 'Z'))) { + fileName = fileName.substring(2); + } + } + } else if (osname.indexOf("netware") > -1) { + int colon = fileName.indexOf(':'); + if (colon != -1) { + fileName = fileName.substring(colon + 1); + } + } + } + + fileName = fileName.replace(File.separatorChar, '/'); + + // No absolute pathnames + // Windows (and Posix?) paths can start with "\\NetworkDrive\", + // so we loop on starting /'s. + while (!preserveLeadingSlashes && fileName.startsWith("/")) { + fileName = fileName.substring(1); + } + return fileName; + } +} diff --git a/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarInputStream.java b/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarInputStream.java index 53c114de6d..f0a75a9d45 100644 --- a/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarInputStream.java +++ b/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarInputStream.java @@ -1,402 +1,402 @@ -/* - * 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. - * - */ - -/* - * This package is based on the work done by Timothy Gerard Endres - * (time@ice.com) to whom the Ant project is very grateful for his great code. - */ - -package org.apache.activemq.console.command.store.tar; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * The TarInputStream reads a UNIX tar archive as an InputStream. - * methods are provided to position at each successive entry in - * the archive, and the read each entry as a normal input stream - * using read(). - * - */ -public class TarInputStream extends FilterInputStream { - private static final int SMALL_BUFFER_SIZE = 256; - private static final int BUFFER_SIZE = 8 * 1024; - private static final int LARGE_BUFFER_SIZE = 32 * 1024; - private static final int BYTE_MASK = 0xFF; - - // CheckStyle:VisibilityModifier OFF - bc - protected boolean debug; - protected boolean hasHitEOF; - protected long entrySize; - protected long entryOffset; - protected byte[] readBuf; - protected TarBuffer buffer; - protected TarEntry currEntry; - - /** - * This contents of this array is not used at all in this class, - * it is only here to avoid repreated object creation during calls - * to the no-arg read method. - */ - protected byte[] oneBuf; - - // CheckStyle:VisibilityModifier ON - - /** - * Constructor for TarInputStream. - * @param is the input stream to use - */ - public TarInputStream(InputStream is) { - this(is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE); - } - - /** - * Constructor for TarInputStream. - * @param is the input stream to use - * @param blockSize the block size to use - */ - public TarInputStream(InputStream is, int blockSize) { - this(is, blockSize, TarBuffer.DEFAULT_RCDSIZE); - } - - /** - * Constructor for TarInputStream. - * @param is the input stream to use - * @param blockSize the block size to use - * @param recordSize the record size to use - */ - public TarInputStream(InputStream is, int blockSize, int recordSize) { - super(is); - - this.buffer = new TarBuffer(is, blockSize, recordSize); - this.readBuf = null; - this.oneBuf = new byte[1]; - this.debug = false; - this.hasHitEOF = false; - } - - /** - * Sets the debugging flag. - * - * @param debug True to turn on debugging. - */ - public void setDebug(boolean debug) { - this.debug = debug; - buffer.setDebug(debug); - } - - /** - * Closes this stream. Calls the TarBuffer's close() method. - * @throws IOException on error - */ - public void close() throws IOException { - buffer.close(); - } - - /** - * Get the record size being used by this stream's TarBuffer. - * - * @return The TarBuffer record size. - */ - public int getRecordSize() { - return buffer.getRecordSize(); - } - - /** - * Get the available data that can be read from the current - * entry in the archive. This does not indicate how much data - * is left in the entire archive, only in the current entry. - * This value is determined from the entry's size header field - * and the amount of data already read from the current entry. - * Integer.MAX_VALUE is returen in case more than Integer.MAX_VALUE - * bytes are left in the current entry in the archive. - * - * @return The number of available bytes for the current entry. - * @throws IOException for signature - */ - public int available() throws IOException { - if (entrySize - entryOffset > Integer.MAX_VALUE) { - return Integer.MAX_VALUE; - } - return (int) (entrySize - entryOffset); - } - - /** - * Skip bytes in the input buffer. This skips bytes in the - * current entry's data, not the entire archive, and will - * stop at the end of the current entry's data if the number - * to skip extends beyond that point. - * - * @param numToSkip The number of bytes to skip. - * @return the number actually skipped - * @throws IOException on error - */ - public long skip(long numToSkip) throws IOException { - // REVIEW - // This is horribly inefficient, but it ensures that we - // properly skip over bytes via the TarBuffer... - // - byte[] skipBuf = new byte[BUFFER_SIZE]; - long skip = numToSkip; - while (skip > 0) { - int realSkip = (int) (skip > skipBuf.length ? skipBuf.length : skip); - int numRead = read(skipBuf, 0, realSkip); - if (numRead == -1) { - break; - } - skip -= numRead; - } - return (numToSkip - skip); - } - - /** - * Since we do not support marking just yet, we return false. - * - * @return False. - */ - public boolean markSupported() { - return false; - } - - /** - * Since we do not support marking just yet, we do nothing. - * - * @param markLimit The limit to mark. - */ - public void mark(int markLimit) { - } - - /** - * Since we do not support marking just yet, we do nothing. - */ - public void reset() { - } - - /** - * Get the next entry in this tar archive. This will skip - * over any remaining data in the current entry, if there - * is one, and place the input stream at the header of the - * next entry, and read the header and instantiate a new - * TarEntry from the header bytes and return that entry. - * If there are no more entries in the archive, null will - * be returned to indicate that the end of the archive has - * been reached. - * - * @return The next TarEntry in the archive, or null. - * @throws IOException on error - */ - public TarEntry getNextEntry() throws IOException { - if (hasHitEOF) { - return null; - } - - if (currEntry != null) { - long numToSkip = entrySize - entryOffset; - - if (debug) { - System.err.println("TarInputStream: SKIP currENTRY '" - + currEntry.getName() + "' SZ " - + entrySize + " OFF " - + entryOffset + " skipping " - + numToSkip + " bytes"); - } - - while (numToSkip > 0) { - long skipped = skip(numToSkip); - if (skipped <= 0) { - throw new RuntimeException("failed to skip current tar" - + " entry"); - } - numToSkip -= skipped; - } - - readBuf = null; - } - - byte[] headerBuf = buffer.readRecord(); - - if (headerBuf == null) { - if (debug) { - System.err.println("READ NULL RECORD"); - } - hasHitEOF = true; - } else if (buffer.isEOFRecord(headerBuf)) { - if (debug) { - System.err.println("READ EOF RECORD"); - } - hasHitEOF = true; - } - - if (hasHitEOF) { - currEntry = null; - } else { - currEntry = new TarEntry(headerBuf); - - if (debug) { - System.err.println("TarInputStream: SET CURRENTRY '" - + currEntry.getName() - + "' size = " - + currEntry.getSize()); - } - - entryOffset = 0; - - entrySize = currEntry.getSize(); - } - - if (currEntry != null && currEntry.isGNULongNameEntry()) { - // read in the name - StringBuffer longName = new StringBuffer(); - byte[] buf = new byte[SMALL_BUFFER_SIZE]; - int length = 0; - while ((length = read(buf)) >= 0) { - longName.append(new String(buf, 0, length)); - } - getNextEntry(); - if (currEntry == null) { - // Bugzilla: 40334 - // Malformed tar file - long entry name not followed by entry - return null; - } - // remove trailing null terminator - if (longName.length() > 0 - && longName.charAt(longName.length() - 1) == 0) { - longName.deleteCharAt(longName.length() - 1); - } - currEntry.setName(longName.toString()); - } - - return currEntry; - } - - /** - * Reads a byte from the current tar archive entry. - * - * This method simply calls read( byte[], int, int ). - * - * @return The byte read, or -1 at EOF. - * @throws IOException on error - */ - public int read() throws IOException { - int num = read(oneBuf, 0, 1); - return num == -1 ? -1 : ((int) oneBuf[0]) & BYTE_MASK; - } - - /** - * Reads bytes from the current tar archive entry. - * - * This method is aware of the boundaries of the current - * entry in the archive and will deal with them as if they - * were this stream's start and EOF. - * - * @param buf The buffer into which to place bytes read. - * @param offset The offset at which to place bytes read. - * @param numToRead The number of bytes to read. - * @return The number of bytes read, or -1 at EOF. - * @throws IOException on error - */ - public int read(byte[] buf, int offset, int numToRead) throws IOException { - int totalRead = 0; - - if (entryOffset >= entrySize) { - return -1; - } - - if ((numToRead + entryOffset) > entrySize) { - numToRead = (int) (entrySize - entryOffset); - } - - if (readBuf != null) { - int sz = (numToRead > readBuf.length) ? readBuf.length - : numToRead; - - System.arraycopy(readBuf, 0, buf, offset, sz); - - if (sz >= readBuf.length) { - readBuf = null; - } else { - int newLen = readBuf.length - sz; - byte[] newBuf = new byte[newLen]; - - System.arraycopy(readBuf, sz, newBuf, 0, newLen); - - readBuf = newBuf; - } - - totalRead += sz; - numToRead -= sz; - offset += sz; - } - - while (numToRead > 0) { - byte[] rec = buffer.readRecord(); - - if (rec == null) { - // Unexpected EOF! - throw new IOException("unexpected EOF with " + numToRead - + " bytes unread"); - } - - int sz = numToRead; - int recLen = rec.length; - - if (recLen > sz) { - System.arraycopy(rec, 0, buf, offset, sz); - - readBuf = new byte[recLen - sz]; - - System.arraycopy(rec, sz, readBuf, 0, recLen - sz); - } else { - sz = recLen; - - System.arraycopy(rec, 0, buf, offset, recLen); - } - - totalRead += sz; - numToRead -= sz; - offset += sz; - } - - entryOffset += totalRead; - - return totalRead; - } - - /** - * Copies the contents of the current tar archive entry directly into - * an output stream. - * - * @param out The OutputStream into which to write the entry's data. - * @throws IOException on error - */ - public void copyEntryContents(OutputStream out) throws IOException { - byte[] buf = new byte[LARGE_BUFFER_SIZE]; - - while (true) { - int numRead = read(buf, 0, buf.length); - - if (numRead == -1) { - break; - } - - out.write(buf, 0, numRead); - } - } -} +/* + * 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. + * + */ + +/* + * This package is based on the work done by Timothy Gerard Endres + * (time@ice.com) to whom the Ant project is very grateful for his great code. + */ + +package org.apache.activemq.console.command.store.tar; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * The TarInputStream reads a UNIX tar archive as an InputStream. + * methods are provided to position at each successive entry in + * the archive, and the read each entry as a normal input stream + * using read(). + * + */ +public class TarInputStream extends FilterInputStream { + private static final int SMALL_BUFFER_SIZE = 256; + private static final int BUFFER_SIZE = 8 * 1024; + private static final int LARGE_BUFFER_SIZE = 32 * 1024; + private static final int BYTE_MASK = 0xFF; + + // CheckStyle:VisibilityModifier OFF - bc + protected boolean debug; + protected boolean hasHitEOF; + protected long entrySize; + protected long entryOffset; + protected byte[] readBuf; + protected TarBuffer buffer; + protected TarEntry currEntry; + + /** + * This contents of this array is not used at all in this class, + * it is only here to avoid repreated object creation during calls + * to the no-arg read method. + */ + protected byte[] oneBuf; + + // CheckStyle:VisibilityModifier ON + + /** + * Constructor for TarInputStream. + * @param is the input stream to use + */ + public TarInputStream(InputStream is) { + this(is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for TarInputStream. + * @param is the input stream to use + * @param blockSize the block size to use + */ + public TarInputStream(InputStream is, int blockSize) { + this(is, blockSize, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for TarInputStream. + * @param is the input stream to use + * @param blockSize the block size to use + * @param recordSize the record size to use + */ + public TarInputStream(InputStream is, int blockSize, int recordSize) { + super(is); + + this.buffer = new TarBuffer(is, blockSize, recordSize); + this.readBuf = null; + this.oneBuf = new byte[1]; + this.debug = false; + this.hasHitEOF = false; + } + + /** + * Sets the debugging flag. + * + * @param debug True to turn on debugging. + */ + public void setDebug(boolean debug) { + this.debug = debug; + buffer.setDebug(debug); + } + + /** + * Closes this stream. Calls the TarBuffer's close() method. + * @throws IOException on error + */ + public void close() throws IOException { + buffer.close(); + } + + /** + * Get the record size being used by this stream's TarBuffer. + * + * @return The TarBuffer record size. + */ + public int getRecordSize() { + return buffer.getRecordSize(); + } + + /** + * Get the available data that can be read from the current + * entry in the archive. This does not indicate how much data + * is left in the entire archive, only in the current entry. + * This value is determined from the entry's size header field + * and the amount of data already read from the current entry. + * Integer.MAX_VALUE is returen in case more than Integer.MAX_VALUE + * bytes are left in the current entry in the archive. + * + * @return The number of available bytes for the current entry. + * @throws IOException for signature + */ + public int available() throws IOException { + if (entrySize - entryOffset > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + return (int) (entrySize - entryOffset); + } + + /** + * Skip bytes in the input buffer. This skips bytes in the + * current entry's data, not the entire archive, and will + * stop at the end of the current entry's data if the number + * to skip extends beyond that point. + * + * @param numToSkip The number of bytes to skip. + * @return the number actually skipped + * @throws IOException on error + */ + public long skip(long numToSkip) throws IOException { + // REVIEW + // This is horribly inefficient, but it ensures that we + // properly skip over bytes via the TarBuffer... + // + byte[] skipBuf = new byte[BUFFER_SIZE]; + long skip = numToSkip; + while (skip > 0) { + int realSkip = (int) (skip > skipBuf.length ? skipBuf.length : skip); + int numRead = read(skipBuf, 0, realSkip); + if (numRead == -1) { + break; + } + skip -= numRead; + } + return (numToSkip - skip); + } + + /** + * Since we do not support marking just yet, we return false. + * + * @return False. + */ + public boolean markSupported() { + return false; + } + + /** + * Since we do not support marking just yet, we do nothing. + * + * @param markLimit The limit to mark. + */ + public void mark(int markLimit) { + } + + /** + * Since we do not support marking just yet, we do nothing. + */ + public void reset() { + } + + /** + * Get the next entry in this tar archive. This will skip + * over any remaining data in the current entry, if there + * is one, and place the input stream at the header of the + * next entry, and read the header and instantiate a new + * TarEntry from the header bytes and return that entry. + * If there are no more entries in the archive, null will + * be returned to indicate that the end of the archive has + * been reached. + * + * @return The next TarEntry in the archive, or null. + * @throws IOException on error + */ + public TarEntry getNextEntry() throws IOException { + if (hasHitEOF) { + return null; + } + + if (currEntry != null) { + long numToSkip = entrySize - entryOffset; + + if (debug) { + System.err.println("TarInputStream: SKIP currENTRY '" + + currEntry.getName() + "' SZ " + + entrySize + " OFF " + + entryOffset + " skipping " + + numToSkip + " bytes"); + } + + while (numToSkip > 0) { + long skipped = skip(numToSkip); + if (skipped <= 0) { + throw new RuntimeException("failed to skip current tar" + + " entry"); + } + numToSkip -= skipped; + } + + readBuf = null; + } + + byte[] headerBuf = buffer.readRecord(); + + if (headerBuf == null) { + if (debug) { + System.err.println("READ NULL RECORD"); + } + hasHitEOF = true; + } else if (buffer.isEOFRecord(headerBuf)) { + if (debug) { + System.err.println("READ EOF RECORD"); + } + hasHitEOF = true; + } + + if (hasHitEOF) { + currEntry = null; + } else { + currEntry = new TarEntry(headerBuf); + + if (debug) { + System.err.println("TarInputStream: SET CURRENTRY '" + + currEntry.getName() + + "' size = " + + currEntry.getSize()); + } + + entryOffset = 0; + + entrySize = currEntry.getSize(); + } + + if (currEntry != null && currEntry.isGNULongNameEntry()) { + // read in the name + StringBuffer longName = new StringBuffer(); + byte[] buf = new byte[SMALL_BUFFER_SIZE]; + int length = 0; + while ((length = read(buf)) >= 0) { + longName.append(new String(buf, 0, length)); + } + getNextEntry(); + if (currEntry == null) { + // Bugzilla: 40334 + // Malformed tar file - long entry name not followed by entry + return null; + } + // remove trailing null terminator + if (longName.length() > 0 + && longName.charAt(longName.length() - 1) == 0) { + longName.deleteCharAt(longName.length() - 1); + } + currEntry.setName(longName.toString()); + } + + return currEntry; + } + + /** + * Reads a byte from the current tar archive entry. + * + * This method simply calls read( byte[], int, int ). + * + * @return The byte read, or -1 at EOF. + * @throws IOException on error + */ + public int read() throws IOException { + int num = read(oneBuf, 0, 1); + return num == -1 ? -1 : ((int) oneBuf[0]) & BYTE_MASK; + } + + /** + * Reads bytes from the current tar archive entry. + * + * This method is aware of the boundaries of the current + * entry in the archive and will deal with them as if they + * were this stream's start and EOF. + * + * @param buf The buffer into which to place bytes read. + * @param offset The offset at which to place bytes read. + * @param numToRead The number of bytes to read. + * @return The number of bytes read, or -1 at EOF. + * @throws IOException on error + */ + public int read(byte[] buf, int offset, int numToRead) throws IOException { + int totalRead = 0; + + if (entryOffset >= entrySize) { + return -1; + } + + if ((numToRead + entryOffset) > entrySize) { + numToRead = (int) (entrySize - entryOffset); + } + + if (readBuf != null) { + int sz = (numToRead > readBuf.length) ? readBuf.length + : numToRead; + + System.arraycopy(readBuf, 0, buf, offset, sz); + + if (sz >= readBuf.length) { + readBuf = null; + } else { + int newLen = readBuf.length - sz; + byte[] newBuf = new byte[newLen]; + + System.arraycopy(readBuf, sz, newBuf, 0, newLen); + + readBuf = newBuf; + } + + totalRead += sz; + numToRead -= sz; + offset += sz; + } + + while (numToRead > 0) { + byte[] rec = buffer.readRecord(); + + if (rec == null) { + // Unexpected EOF! + throw new IOException("unexpected EOF with " + numToRead + + " bytes unread"); + } + + int sz = numToRead; + int recLen = rec.length; + + if (recLen > sz) { + System.arraycopy(rec, 0, buf, offset, sz); + + readBuf = new byte[recLen - sz]; + + System.arraycopy(rec, sz, readBuf, 0, recLen - sz); + } else { + sz = recLen; + + System.arraycopy(rec, 0, buf, offset, recLen); + } + + totalRead += sz; + numToRead -= sz; + offset += sz; + } + + entryOffset += totalRead; + + return totalRead; + } + + /** + * Copies the contents of the current tar archive entry directly into + * an output stream. + * + * @param out The OutputStream into which to write the entry's data. + * @throws IOException on error + */ + public void copyEntryContents(OutputStream out) throws IOException { + byte[] buf = new byte[LARGE_BUFFER_SIZE]; + + while (true) { + int numRead = read(buf, 0, buf.length); + + if (numRead == -1) { + break; + } + + out.write(buf, 0, numRead); + } + } +} diff --git a/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarOutputStream.java b/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarOutputStream.java index 26de93f7b9..ddd288d78b 100644 --- a/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarOutputStream.java +++ b/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarOutputStream.java @@ -1,356 +1,356 @@ -/* - * 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. - * - */ - -/* - * This package is based on the work done by Timothy Gerard Endres - * (time@ice.com) to whom the Ant project is very grateful for his great code. - */ - -package org.apache.activemq.console.command.store.tar; - -import java.io.FilterOutputStream; -import java.io.OutputStream; -import java.io.IOException; - -/** - * The TarOutputStream writes a UNIX tar archive as an OutputStream. - * Methods are provided to put entries, and then write their contents - * by writing to this stream using write(). - * - */ -public class TarOutputStream extends FilterOutputStream { - /** Fail if a long file name is required in the archive. */ - public static final int LONGFILE_ERROR = 0; - - /** Long paths will be truncated in the archive. */ - public static final int LONGFILE_TRUNCATE = 1; - - /** GNU tar extensions are used to store long file names in the archive. */ - public static final int LONGFILE_GNU = 2; - - // CheckStyle:VisibilityModifier OFF - bc - protected boolean debug; - protected long currSize; - protected String currName; - protected long currBytes; - protected byte[] oneBuf; - protected byte[] recordBuf; - protected int assemLen; - protected byte[] assemBuf; - protected TarBuffer buffer; - protected int longFileMode = LONGFILE_ERROR; - // CheckStyle:VisibilityModifier ON - - private boolean closed = false; - - /** - * Constructor for TarInputStream. - * @param os the output stream to use - */ - public TarOutputStream(OutputStream os) { - this(os, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE); - } - - /** - * Constructor for TarInputStream. - * @param os the output stream to use - * @param blockSize the block size to use - */ - public TarOutputStream(OutputStream os, int blockSize) { - this(os, blockSize, TarBuffer.DEFAULT_RCDSIZE); - } - - /** - * Constructor for TarInputStream. - * @param os the output stream to use - * @param blockSize the block size to use - * @param recordSize the record size to use - */ - public TarOutputStream(OutputStream os, int blockSize, int recordSize) { - super(os); - - this.buffer = new TarBuffer(os, blockSize, recordSize); - this.debug = false; - this.assemLen = 0; - this.assemBuf = new byte[recordSize]; - this.recordBuf = new byte[recordSize]; - this.oneBuf = new byte[1]; - } - - /** - * Set the long file mode. - * This can be LONGFILE_ERROR(0), LONGFILE_TRUNCATE(1) or LONGFILE_GNU(2). - * This specifies the treatment of long file names (names >= TarConstants.NAMELEN). - * Default is LONGFILE_ERROR. - * @param longFileMode the mode to use - */ - public void setLongFileMode(int longFileMode) { - this.longFileMode = longFileMode; - } - - - /** - * Sets the debugging flag. - * - * @param debugF True to turn on debugging. - */ - public void setDebug(boolean debugF) { - this.debug = debugF; - } - - /** - * Sets the debugging flag in this stream's TarBuffer. - * - * @param debug True to turn on debugging. - */ - public void setBufferDebug(boolean debug) { - buffer.setDebug(debug); - } - - /** - * Ends the TAR archive without closing the underlying OutputStream. - * The result is that the two EOF records of nulls are written. - * @throws IOException on error - */ - public void finish() throws IOException { - // See Bugzilla 28776 for a discussion on this - // http://issues.apache.org/bugzilla/show_bug.cgi?id=28776 - writeEOFRecord(); - writeEOFRecord(); - buffer.flushBlock(); - } - - /** - * Ends the TAR archive and closes the underlying OutputStream. - * This means that finish() is called followed by calling the - * TarBuffer's close(). - * @throws IOException on error - */ - public void close() throws IOException { - if (!closed) { - finish(); - buffer.close(); - out.close(); - closed = true; - } - } - - /** - * Get the record size being used by this stream's TarBuffer. - * - * @return The TarBuffer record size. - */ - public int getRecordSize() { - return buffer.getRecordSize(); - } - - /** - * Put an entry on the output stream. This writes the entry's - * header record and positions the output stream for writing - * the contents of the entry. Once this method is called, the - * stream is ready for calls to write() to write the entry's - * contents. Once the contents are written, closeEntry() - * MUST be called to ensure that all buffered data - * is completely written to the output stream. - * - * @param entry The TarEntry to be written to the archive. - * @throws IOException on error - */ - public void putNextEntry(TarEntry entry) throws IOException { - if (entry.getName().length() >= TarConstants.NAMELEN) { - - if (longFileMode == LONGFILE_GNU) { - // create a TarEntry for the LongLink, the contents - // of which are the entry's name - TarEntry longLinkEntry = new TarEntry(TarConstants.GNU_LONGLINK, - TarConstants.LF_GNUTYPE_LONGNAME); - - longLinkEntry.setSize(entry.getName().length() + 1); - putNextEntry(longLinkEntry); - write(entry.getName().getBytes()); - write(0); - closeEntry(); - } else if (longFileMode != LONGFILE_TRUNCATE) { - throw new RuntimeException("file name '" + entry.getName() - + "' is too long ( > " - + TarConstants.NAMELEN + " bytes)"); - } - } - - entry.writeEntryHeader(recordBuf); - buffer.writeRecord(recordBuf); - - currBytes = 0; - - if (entry.isDirectory()) { - currSize = 0; - } else { - currSize = entry.getSize(); - } - currName = entry.getName(); - } - - /** - * Close an entry. This method MUST be called for all file - * entries that contain data. The reason is that we must - * buffer data written to the stream in order to satisfy - * the buffer's record based writes. Thus, there may be - * data fragments still being assembled that must be written - * to the output stream before this entry is closed and the - * next entry written. - * @throws IOException on error - */ - public void closeEntry() throws IOException { - if (assemLen > 0) { - for (int i = assemLen; i < assemBuf.length; ++i) { - assemBuf[i] = 0; - } - - buffer.writeRecord(assemBuf); - - currBytes += assemLen; - assemLen = 0; - } - - if (currBytes < currSize) { - throw new IOException("entry '" + currName + "' closed at '" - + currBytes - + "' before the '" + currSize - + "' bytes specified in the header were written"); - } - } - - /** - * Writes a byte to the current tar archive entry. - * - * This method simply calls read( byte[], int, int ). - * - * @param b The byte written. - * @throws IOException on error - */ - public void write(int b) throws IOException { - oneBuf[0] = (byte) b; - - write(oneBuf, 0, 1); - } - - /** - * Writes bytes to the current tar archive entry. - * - * This method simply calls write( byte[], int, int ). - * - * @param wBuf The buffer to write to the archive. - * @throws IOException on error - */ - public void write(byte[] wBuf) throws IOException { - write(wBuf, 0, wBuf.length); - } - - /** - * Writes bytes to the current tar archive entry. This method - * is aware of the current entry and will throw an exception if - * you attempt to write bytes past the length specified for the - * current entry. The method is also (painfully) aware of the - * record buffering required by TarBuffer, and manages buffers - * that are not a multiple of recordsize in length, including - * assembling records from small buffers. - * - * @param wBuf The buffer to write to the archive. - * @param wOffset The offset in the buffer from which to get bytes. - * @param numToWrite The number of bytes to write. - * @throws IOException on error - */ - public void write(byte[] wBuf, int wOffset, int numToWrite) throws IOException { - if ((currBytes + numToWrite) > currSize) { - throw new IOException("request to write '" + numToWrite - + "' bytes exceeds size in header of '" - + currSize + "' bytes for entry '" - + currName + "'"); - - // - // We have to deal with assembly!!! - // The programmer can be writing little 32 byte chunks for all - // we know, and we must assemble complete records for writing. - // REVIEW Maybe this should be in TarBuffer? Could that help to - // eliminate some of the buffer copying. - // - } - - if (assemLen > 0) { - if ((assemLen + numToWrite) >= recordBuf.length) { - int aLen = recordBuf.length - assemLen; - - System.arraycopy(assemBuf, 0, recordBuf, 0, - assemLen); - System.arraycopy(wBuf, wOffset, recordBuf, - assemLen, aLen); - buffer.writeRecord(recordBuf); - - currBytes += recordBuf.length; - wOffset += aLen; - numToWrite -= aLen; - assemLen = 0; - } else { - System.arraycopy(wBuf, wOffset, assemBuf, assemLen, - numToWrite); - - wOffset += numToWrite; - assemLen += numToWrite; - numToWrite = 0; - } - } - - // - // When we get here we have EITHER: - // o An empty "assemble" buffer. - // o No bytes to write (numToWrite == 0) - // - while (numToWrite > 0) { - if (numToWrite < recordBuf.length) { - System.arraycopy(wBuf, wOffset, assemBuf, assemLen, - numToWrite); - - assemLen += numToWrite; - - break; - } - - buffer.writeRecord(wBuf, wOffset); - - int num = recordBuf.length; - - currBytes += num; - numToWrite -= num; - wOffset += num; - } - } - - /** - * Write an EOF (end of archive) record to the tar archive. - * An EOF record consists of a record of all zeros. - */ - private void writeEOFRecord() throws IOException { - for (int i = 0; i < recordBuf.length; ++i) { - recordBuf[i] = 0; - } - - buffer.writeRecord(recordBuf); - } -} - - +/* + * 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. + * + */ + +/* + * This package is based on the work done by Timothy Gerard Endres + * (time@ice.com) to whom the Ant project is very grateful for his great code. + */ + +package org.apache.activemq.console.command.store.tar; + +import java.io.FilterOutputStream; +import java.io.OutputStream; +import java.io.IOException; + +/** + * The TarOutputStream writes a UNIX tar archive as an OutputStream. + * Methods are provided to put entries, and then write their contents + * by writing to this stream using write(). + * + */ +public class TarOutputStream extends FilterOutputStream { + /** Fail if a long file name is required in the archive. */ + public static final int LONGFILE_ERROR = 0; + + /** Long paths will be truncated in the archive. */ + public static final int LONGFILE_TRUNCATE = 1; + + /** GNU tar extensions are used to store long file names in the archive. */ + public static final int LONGFILE_GNU = 2; + + // CheckStyle:VisibilityModifier OFF - bc + protected boolean debug; + protected long currSize; + protected String currName; + protected long currBytes; + protected byte[] oneBuf; + protected byte[] recordBuf; + protected int assemLen; + protected byte[] assemBuf; + protected TarBuffer buffer; + protected int longFileMode = LONGFILE_ERROR; + // CheckStyle:VisibilityModifier ON + + private boolean closed = false; + + /** + * Constructor for TarInputStream. + * @param os the output stream to use + */ + public TarOutputStream(OutputStream os) { + this(os, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for TarInputStream. + * @param os the output stream to use + * @param blockSize the block size to use + */ + public TarOutputStream(OutputStream os, int blockSize) { + this(os, blockSize, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for TarInputStream. + * @param os the output stream to use + * @param blockSize the block size to use + * @param recordSize the record size to use + */ + public TarOutputStream(OutputStream os, int blockSize, int recordSize) { + super(os); + + this.buffer = new TarBuffer(os, blockSize, recordSize); + this.debug = false; + this.assemLen = 0; + this.assemBuf = new byte[recordSize]; + this.recordBuf = new byte[recordSize]; + this.oneBuf = new byte[1]; + } + + /** + * Set the long file mode. + * This can be LONGFILE_ERROR(0), LONGFILE_TRUNCATE(1) or LONGFILE_GNU(2). + * This specifies the treatment of long file names (names >= TarConstants.NAMELEN). + * Default is LONGFILE_ERROR. + * @param longFileMode the mode to use + */ + public void setLongFileMode(int longFileMode) { + this.longFileMode = longFileMode; + } + + + /** + * Sets the debugging flag. + * + * @param debugF True to turn on debugging. + */ + public void setDebug(boolean debugF) { + this.debug = debugF; + } + + /** + * Sets the debugging flag in this stream's TarBuffer. + * + * @param debug True to turn on debugging. + */ + public void setBufferDebug(boolean debug) { + buffer.setDebug(debug); + } + + /** + * Ends the TAR archive without closing the underlying OutputStream. + * The result is that the two EOF records of nulls are written. + * @throws IOException on error + */ + public void finish() throws IOException { + // See Bugzilla 28776 for a discussion on this + // http://issues.apache.org/bugzilla/show_bug.cgi?id=28776 + writeEOFRecord(); + writeEOFRecord(); + buffer.flushBlock(); + } + + /** + * Ends the TAR archive and closes the underlying OutputStream. + * This means that finish() is called followed by calling the + * TarBuffer's close(). + * @throws IOException on error + */ + public void close() throws IOException { + if (!closed) { + finish(); + buffer.close(); + out.close(); + closed = true; + } + } + + /** + * Get the record size being used by this stream's TarBuffer. + * + * @return The TarBuffer record size. + */ + public int getRecordSize() { + return buffer.getRecordSize(); + } + + /** + * Put an entry on the output stream. This writes the entry's + * header record and positions the output stream for writing + * the contents of the entry. Once this method is called, the + * stream is ready for calls to write() to write the entry's + * contents. Once the contents are written, closeEntry() + * MUST be called to ensure that all buffered data + * is completely written to the output stream. + * + * @param entry The TarEntry to be written to the archive. + * @throws IOException on error + */ + public void putNextEntry(TarEntry entry) throws IOException { + if (entry.getName().length() >= TarConstants.NAMELEN) { + + if (longFileMode == LONGFILE_GNU) { + // create a TarEntry for the LongLink, the contents + // of which are the entry's name + TarEntry longLinkEntry = new TarEntry(TarConstants.GNU_LONGLINK, + TarConstants.LF_GNUTYPE_LONGNAME); + + longLinkEntry.setSize(entry.getName().length() + 1); + putNextEntry(longLinkEntry); + write(entry.getName().getBytes()); + write(0); + closeEntry(); + } else if (longFileMode != LONGFILE_TRUNCATE) { + throw new RuntimeException("file name '" + entry.getName() + + "' is too long ( > " + + TarConstants.NAMELEN + " bytes)"); + } + } + + entry.writeEntryHeader(recordBuf); + buffer.writeRecord(recordBuf); + + currBytes = 0; + + if (entry.isDirectory()) { + currSize = 0; + } else { + currSize = entry.getSize(); + } + currName = entry.getName(); + } + + /** + * Close an entry. This method MUST be called for all file + * entries that contain data. The reason is that we must + * buffer data written to the stream in order to satisfy + * the buffer's record based writes. Thus, there may be + * data fragments still being assembled that must be written + * to the output stream before this entry is closed and the + * next entry written. + * @throws IOException on error + */ + public void closeEntry() throws IOException { + if (assemLen > 0) { + for (int i = assemLen; i < assemBuf.length; ++i) { + assemBuf[i] = 0; + } + + buffer.writeRecord(assemBuf); + + currBytes += assemLen; + assemLen = 0; + } + + if (currBytes < currSize) { + throw new IOException("entry '" + currName + "' closed at '" + + currBytes + + "' before the '" + currSize + + "' bytes specified in the header were written"); + } + } + + /** + * Writes a byte to the current tar archive entry. + * + * This method simply calls read( byte[], int, int ). + * + * @param b The byte written. + * @throws IOException on error + */ + public void write(int b) throws IOException { + oneBuf[0] = (byte) b; + + write(oneBuf, 0, 1); + } + + /** + * Writes bytes to the current tar archive entry. + * + * This method simply calls write( byte[], int, int ). + * + * @param wBuf The buffer to write to the archive. + * @throws IOException on error + */ + public void write(byte[] wBuf) throws IOException { + write(wBuf, 0, wBuf.length); + } + + /** + * Writes bytes to the current tar archive entry. This method + * is aware of the current entry and will throw an exception if + * you attempt to write bytes past the length specified for the + * current entry. The method is also (painfully) aware of the + * record buffering required by TarBuffer, and manages buffers + * that are not a multiple of recordsize in length, including + * assembling records from small buffers. + * + * @param wBuf The buffer to write to the archive. + * @param wOffset The offset in the buffer from which to get bytes. + * @param numToWrite The number of bytes to write. + * @throws IOException on error + */ + public void write(byte[] wBuf, int wOffset, int numToWrite) throws IOException { + if ((currBytes + numToWrite) > currSize) { + throw new IOException("request to write '" + numToWrite + + "' bytes exceeds size in header of '" + + currSize + "' bytes for entry '" + + currName + "'"); + + // + // We have to deal with assembly!!! + // The programmer can be writing little 32 byte chunks for all + // we know, and we must assemble complete records for writing. + // REVIEW Maybe this should be in TarBuffer? Could that help to + // eliminate some of the buffer copying. + // + } + + if (assemLen > 0) { + if ((assemLen + numToWrite) >= recordBuf.length) { + int aLen = recordBuf.length - assemLen; + + System.arraycopy(assemBuf, 0, recordBuf, 0, + assemLen); + System.arraycopy(wBuf, wOffset, recordBuf, + assemLen, aLen); + buffer.writeRecord(recordBuf); + + currBytes += recordBuf.length; + wOffset += aLen; + numToWrite -= aLen; + assemLen = 0; + } else { + System.arraycopy(wBuf, wOffset, assemBuf, assemLen, + numToWrite); + + wOffset += numToWrite; + assemLen += numToWrite; + numToWrite = 0; + } + } + + // + // When we get here we have EITHER: + // o An empty "assemble" buffer. + // o No bytes to write (numToWrite == 0) + // + while (numToWrite > 0) { + if (numToWrite < recordBuf.length) { + System.arraycopy(wBuf, wOffset, assemBuf, assemLen, + numToWrite); + + assemLen += numToWrite; + + break; + } + + buffer.writeRecord(wBuf, wOffset); + + int num = recordBuf.length; + + currBytes += num; + numToWrite -= num; + wOffset += num; + } + } + + /** + * Write an EOF (end of archive) record to the tar archive. + * An EOF record consists of a record of all zeros. + */ + private void writeEOFRecord() throws IOException { + for (int i = 0; i < recordBuf.length; ++i) { + recordBuf[i] = 0; + } + + buffer.writeRecord(recordBuf); + } +} + + diff --git a/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarUtils.java b/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarUtils.java index a89caec557..94e1e1ca19 100644 --- a/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarUtils.java +++ b/activemq-console/src/main/java/org/apache/activemq/console/command/store/tar/TarUtils.java @@ -1,206 +1,206 @@ -/* - * 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. - * - */ - -/* - * This package is based on the work done by Timothy Gerard Endres - * (time@ice.com) to whom the Ant project is very grateful for his great code. - */ - -package org.apache.activemq.console.command.store.tar; - -/** - * This class provides static utility methods to work with byte streams. - * - */ -// CheckStyle:HideUtilityClassConstructorCheck OFF (bc) -public class TarUtils { - - private static final int BYTE_MASK = 255; - - /** - * Parse an octal string from a header buffer. This is used for the - * file permission mode value. - * - * @param header The header buffer from which to parse. - * @param offset The offset into the buffer from which to parse. - * @param length The number of header bytes to parse. - * @return The long value of the octal string. - */ - public static long parseOctal(byte[] header, int offset, int length) { - long result = 0; - boolean stillPadding = true; - int end = offset + length; - - for (int i = offset; i < end; ++i) { - if (header[i] == 0) { - break; - } - - if (header[i] == (byte) ' ' || header[i] == '0') { - if (stillPadding) { - continue; - } - - if (header[i] == (byte) ' ') { - break; - } - } - - stillPadding = false; - // CheckStyle:MagicNumber OFF - result = (result << 3) + (header[i] - '0'); - // CheckStyle:MagicNumber ON - } - - return result; - } - - /** - * Parse an entry name from a header buffer. - * - * @param header The header buffer from which to parse. - * @param offset The offset into the buffer from which to parse. - * @param length The number of header bytes to parse. - * @return The header's entry name. - */ - public static StringBuffer parseName(byte[] header, int offset, int length) { - StringBuffer result = new StringBuffer(length); - int end = offset + length; - - for (int i = offset; i < end; ++i) { - if (header[i] == 0) { - break; - } - - result.append((char) header[i]); - } - - return result; - } - - /** - * Determine the number of bytes in an entry name. - * - * @param name The header name from which to parse. - * @param buf The buffer from which to parse. - * @param offset The offset into the buffer from which to parse. - * @param length The number of header bytes to parse. - * @return The number of bytes in a header's entry name. - */ - public static int getNameBytes(StringBuffer name, byte[] buf, int offset, int length) { - int i; - - for (i = 0; i < length && i < name.length(); ++i) { - buf[offset + i] = (byte) name.charAt(i); - } - - for (; i < length; ++i) { - buf[offset + i] = 0; - } - - return offset + length; - } - - /** - * Parse an octal integer from a header buffer. - * - * @param value The header value - * @param buf The buffer from which to parse. - * @param offset The offset into the buffer from which to parse. - * @param length The number of header bytes to parse. - * @return The integer value of the octal bytes. - */ - public static int getOctalBytes(long value, byte[] buf, int offset, int length) { - int idx = length - 1; - - buf[offset + idx] = 0; - --idx; - buf[offset + idx] = (byte) ' '; - --idx; - - if (value == 0) { - buf[offset + idx] = (byte) '0'; - --idx; - } else { - for (long val = value; idx >= 0 && val > 0; --idx) { - // CheckStyle:MagicNumber OFF - buf[offset + idx] = (byte) ((byte) '0' + (byte) (val & 7)); - val = val >> 3; - // CheckStyle:MagicNumber ON - } - } - - for (; idx >= 0; --idx) { - buf[offset + idx] = (byte) ' '; - } - - return offset + length; - } - - /** - * Parse an octal long integer from a header buffer. - * - * @param value The header value - * @param buf The buffer from which to parse. - * @param offset The offset into the buffer from which to parse. - * @param length The number of header bytes to parse. - * @return The long value of the octal bytes. - */ - public static int getLongOctalBytes(long value, byte[] buf, int offset, int length) { - byte[] temp = new byte[length + 1]; - - getOctalBytes(value, temp, 0, length + 1); - System.arraycopy(temp, 0, buf, offset, length); - - return offset + length; - } - - /** - * Parse the checksum octal integer from a header buffer. - * - * @param value The header value - * @param buf The buffer from which to parse. - * @param offset The offset into the buffer from which to parse. - * @param length The number of header bytes to parse. - * @return The integer value of the entry's checksum. - */ - public static int getCheckSumOctalBytes(long value, byte[] buf, int offset, int length) { - getOctalBytes(value, buf, offset, length); - - buf[offset + length - 1] = (byte) ' '; - buf[offset + length - 2] = 0; - - return offset + length; - } - - /** - * Compute the checksum of a tar entry header. - * - * @param buf The tar entry's header buffer. - * @return The computed checksum. - */ - public static long computeCheckSum(byte[] buf) { - long sum = 0; - - for (int i = 0; i < buf.length; ++i) { - sum += BYTE_MASK & buf[i]; - } - - return sum; - } -} +/* + * 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. + * + */ + +/* + * This package is based on the work done by Timothy Gerard Endres + * (time@ice.com) to whom the Ant project is very grateful for his great code. + */ + +package org.apache.activemq.console.command.store.tar; + +/** + * This class provides static utility methods to work with byte streams. + * + */ +// CheckStyle:HideUtilityClassConstructorCheck OFF (bc) +public class TarUtils { + + private static final int BYTE_MASK = 255; + + /** + * Parse an octal string from a header buffer. This is used for the + * file permission mode value. + * + * @param header The header buffer from which to parse. + * @param offset The offset into the buffer from which to parse. + * @param length The number of header bytes to parse. + * @return The long value of the octal string. + */ + public static long parseOctal(byte[] header, int offset, int length) { + long result = 0; + boolean stillPadding = true; + int end = offset + length; + + for (int i = offset; i < end; ++i) { + if (header[i] == 0) { + break; + } + + if (header[i] == (byte) ' ' || header[i] == '0') { + if (stillPadding) { + continue; + } + + if (header[i] == (byte) ' ') { + break; + } + } + + stillPadding = false; + // CheckStyle:MagicNumber OFF + result = (result << 3) + (header[i] - '0'); + // CheckStyle:MagicNumber ON + } + + return result; + } + + /** + * Parse an entry name from a header buffer. + * + * @param header The header buffer from which to parse. + * @param offset The offset into the buffer from which to parse. + * @param length The number of header bytes to parse. + * @return The header's entry name. + */ + public static StringBuffer parseName(byte[] header, int offset, int length) { + StringBuffer result = new StringBuffer(length); + int end = offset + length; + + for (int i = offset; i < end; ++i) { + if (header[i] == 0) { + break; + } + + result.append((char) header[i]); + } + + return result; + } + + /** + * Determine the number of bytes in an entry name. + * + * @param name The header name from which to parse. + * @param buf The buffer from which to parse. + * @param offset The offset into the buffer from which to parse. + * @param length The number of header bytes to parse. + * @return The number of bytes in a header's entry name. + */ + public static int getNameBytes(StringBuffer name, byte[] buf, int offset, int length) { + int i; + + for (i = 0; i < length && i < name.length(); ++i) { + buf[offset + i] = (byte) name.charAt(i); + } + + for (; i < length; ++i) { + buf[offset + i] = 0; + } + + return offset + length; + } + + /** + * Parse an octal integer from a header buffer. + * + * @param value The header value + * @param buf The buffer from which to parse. + * @param offset The offset into the buffer from which to parse. + * @param length The number of header bytes to parse. + * @return The integer value of the octal bytes. + */ + public static int getOctalBytes(long value, byte[] buf, int offset, int length) { + int idx = length - 1; + + buf[offset + idx] = 0; + --idx; + buf[offset + idx] = (byte) ' '; + --idx; + + if (value == 0) { + buf[offset + idx] = (byte) '0'; + --idx; + } else { + for (long val = value; idx >= 0 && val > 0; --idx) { + // CheckStyle:MagicNumber OFF + buf[offset + idx] = (byte) ((byte) '0' + (byte) (val & 7)); + val = val >> 3; + // CheckStyle:MagicNumber ON + } + } + + for (; idx >= 0; --idx) { + buf[offset + idx] = (byte) ' '; + } + + return offset + length; + } + + /** + * Parse an octal long integer from a header buffer. + * + * @param value The header value + * @param buf The buffer from which to parse. + * @param offset The offset into the buffer from which to parse. + * @param length The number of header bytes to parse. + * @return The long value of the octal bytes. + */ + public static int getLongOctalBytes(long value, byte[] buf, int offset, int length) { + byte[] temp = new byte[length + 1]; + + getOctalBytes(value, temp, 0, length + 1); + System.arraycopy(temp, 0, buf, offset, length); + + return offset + length; + } + + /** + * Parse the checksum octal integer from a header buffer. + * + * @param value The header value + * @param buf The buffer from which to parse. + * @param offset The offset into the buffer from which to parse. + * @param length The number of header bytes to parse. + * @return The integer value of the entry's checksum. + */ + public static int getCheckSumOctalBytes(long value, byte[] buf, int offset, int length) { + getOctalBytes(value, buf, offset, length); + + buf[offset + length - 1] = (byte) ' '; + buf[offset + length - 2] = 0; + + return offset + length; + } + + /** + * Compute the checksum of a tar entry header. + * + * @param buf The tar entry's header buffer. + * @return The computed checksum. + */ + public static long computeCheckSum(byte[] buf) { + long sum = 0; + + for (int i = 0; i < buf.length; ++i) { + sum += BYTE_MASK & buf[i]; + } + + return sum; + } +} diff --git a/activemq-jdbc-store/src/main/java/org/apache/activemq/store/jdbc/adapter/TransactJDBCAdapter.java b/activemq-jdbc-store/src/main/java/org/apache/activemq/store/jdbc/adapter/TransactJDBCAdapter.java index 0ab985e364..41879792cf 100644 --- a/activemq-jdbc-store/src/main/java/org/apache/activemq/store/jdbc/adapter/TransactJDBCAdapter.java +++ b/activemq-jdbc-store/src/main/java/org/apache/activemq/store/jdbc/adapter/TransactJDBCAdapter.java @@ -28,12 +28,12 @@ import org.apache.activemq.store.jdbc.Statements; public class TransactJDBCAdapter extends ImageBasedJDBCAdaptor { @Override public void setStatements(Statements statements) { - String lockCreateStatement = "SELECT * FROM " + statements.getFullLockTableName() + " WITH (UPDLOCK, ROWLOCK)"; + String lockCreateStatement = "SELECT * FROM " + statements.getFullLockTableName() + " WITH (UPDLOCK, ROWLOCK)"; if (statements.isUseLockCreateWhereClause()) { lockCreateStatement += " WHERE ID = 1"; } - + statements.setLockCreateStatement(lockCreateStatement); super.setStatements(statements); diff --git a/activemq-kahadb-store/src/main/java/org/apache/activemq/store/kahadb/disk/journal/ReplicationTarget.java b/activemq-kahadb-store/src/main/java/org/apache/activemq/store/kahadb/disk/journal/ReplicationTarget.java index fe228f72c3..dae59dacdc 100644 --- a/activemq-kahadb-store/src/main/java/org/apache/activemq/store/kahadb/disk/journal/ReplicationTarget.java +++ b/activemq-kahadb-store/src/main/java/org/apache/activemq/store/kahadb/disk/journal/ReplicationTarget.java @@ -1,25 +1,25 @@ -/** - * 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.activemq.store.kahadb.disk.journal; - -import org.apache.activemq.util.ByteSequence; - -public interface ReplicationTarget { - - void replicate(Location location, ByteSequence sequence, boolean sync); - -} +/** + * 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.activemq.store.kahadb.disk.journal; + +import org.apache.activemq.util.ByteSequence; + +public interface ReplicationTarget { + + void replicate(Location location, ByteSequence sequence, boolean sync); + +} diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/ExclusiveConsumerTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/ExclusiveConsumerTest.java index 757fceb678..53ef28fc5a 100644 --- a/activemq-unit-tests/src/test/java/org/apache/activemq/ExclusiveConsumerTest.java +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/ExclusiveConsumerTest.java @@ -1,357 +1,357 @@ -/** - * 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.activemq; - -import javax.jms.Connection; -import javax.jms.ConnectionFactory; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageProducer; -import javax.jms.Session; - -import junit.framework.TestCase; - -import org.apache.activemq.command.ActiveMQQueue; - -public class ExclusiveConsumerTest extends TestCase { - - private static final String VM_BROKER_URL = "vm://localhost?broker.persistent=false&broker.useJmx=true"; - - public ExclusiveConsumerTest(String name) { - super(name); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - } - - private Connection createConnection(final boolean start) throws JMSException { - ConnectionFactory cf = new ActiveMQConnectionFactory(VM_BROKER_URL); - Connection conn = cf.createConnection(); - if (start) { - conn.start(); - } - return conn; - } - - public void testExclusiveConsumerSelectedCreatedFirst() throws JMSException, InterruptedException { - Connection conn = createConnection(true); - - Session exclusiveSession = null; - Session fallbackSession = null; - Session senderSession = null; - - try { - - exclusiveSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - fallbackSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - senderSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - ActiveMQQueue exclusiveQueue = new ActiveMQQueue("TEST.QUEUE1?consumer.exclusive=true"); - MessageConsumer exclusiveConsumer = exclusiveSession.createConsumer(exclusiveQueue); - - ActiveMQQueue fallbackQueue = new ActiveMQQueue("TEST.QUEUE1"); - MessageConsumer fallbackConsumer = fallbackSession.createConsumer(fallbackQueue); - - ActiveMQQueue senderQueue = new ActiveMQQueue("TEST.QUEUE1"); - - MessageProducer producer = senderSession.createProducer(senderQueue); - - Message msg = senderSession.createTextMessage("test"); - producer.send(msg); - // TODO need two send a 2nd message - bug AMQ-1024 - // producer.send(msg); - Thread.sleep(100); - - // Verify exclusive consumer receives the message. - assertNotNull(exclusiveConsumer.receive(100)); - assertNull(fallbackConsumer.receive(100)); - - } finally { - fallbackSession.close(); - senderSession.close(); - conn.close(); - } - - } - - public void testExclusiveConsumerSelectedCreatedAfter() throws JMSException, InterruptedException { - Connection conn = createConnection(true); - - Session exclusiveSession = null; - Session fallbackSession = null; - Session senderSession = null; - - try { - - exclusiveSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - fallbackSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - senderSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - ActiveMQQueue fallbackQueue = new ActiveMQQueue("TEST.QUEUE5"); - MessageConsumer fallbackConsumer = fallbackSession.createConsumer(fallbackQueue); - - ActiveMQQueue exclusiveQueue = new ActiveMQQueue("TEST.QUEUE5?consumer.exclusive=true"); - MessageConsumer exclusiveConsumer = exclusiveSession.createConsumer(exclusiveQueue); - - ActiveMQQueue senderQueue = new ActiveMQQueue("TEST.QUEUE5"); - - MessageProducer producer = senderSession.createProducer(senderQueue); - - Message msg = senderSession.createTextMessage("test"); - producer.send(msg); - Thread.sleep(100); - - // Verify exclusive consumer receives the message. - assertNotNull(exclusiveConsumer.receive(100)); - assertNull(fallbackConsumer.receive(100)); - - } finally { - fallbackSession.close(); - senderSession.close(); - conn.close(); - } - - } - - public void testFailoverToAnotherExclusiveConsumerCreatedFirst() throws JMSException, - InterruptedException { - Connection conn = createConnection(true); - - Session exclusiveSession1 = null; - Session exclusiveSession2 = null; - Session fallbackSession = null; - Session senderSession = null; - - try { - - exclusiveSession1 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - exclusiveSession2 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - fallbackSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - senderSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // This creates the exclusive consumer first which avoids AMQ-1024 - // bug. - ActiveMQQueue exclusiveQueue = new ActiveMQQueue("TEST.QUEUE2?consumer.exclusive=true"); - MessageConsumer exclusiveConsumer1 = exclusiveSession1.createConsumer(exclusiveQueue); - MessageConsumer exclusiveConsumer2 = exclusiveSession2.createConsumer(exclusiveQueue); - - ActiveMQQueue fallbackQueue = new ActiveMQQueue("TEST.QUEUE2"); - MessageConsumer fallbackConsumer = fallbackSession.createConsumer(fallbackQueue); - - ActiveMQQueue senderQueue = new ActiveMQQueue("TEST.QUEUE2"); - - MessageProducer producer = senderSession.createProducer(senderQueue); - - Message msg = senderSession.createTextMessage("test"); - producer.send(msg); - Thread.sleep(100); - - // Verify exclusive consumer receives the message. - assertNotNull(exclusiveConsumer1.receive(100)); - assertNull(exclusiveConsumer2.receive(100)); - assertNull(fallbackConsumer.receive(100)); - - // Close the exclusive consumer to verify the non-exclusive consumer - // takes over - exclusiveConsumer1.close(); - - producer.send(msg); - producer.send(msg); - - assertNotNull(exclusiveConsumer2.receive(100)); - assertNull(fallbackConsumer.receive(100)); - - } finally { - fallbackSession.close(); - senderSession.close(); - conn.close(); - } - - } - - public void testFailoverToAnotherExclusiveConsumerCreatedAfter() throws JMSException, - InterruptedException { - Connection conn = createConnection(true); - - Session exclusiveSession1 = null; - Session exclusiveSession2 = null; - Session fallbackSession = null; - Session senderSession = null; - - try { - - exclusiveSession1 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - exclusiveSession2 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - fallbackSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - senderSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // This creates the exclusive consumer first which avoids AMQ-1024 - // bug. - ActiveMQQueue exclusiveQueue = new ActiveMQQueue("TEST.QUEUE6?consumer.exclusive=true"); - MessageConsumer exclusiveConsumer1 = exclusiveSession1.createConsumer(exclusiveQueue); - - ActiveMQQueue fallbackQueue = new ActiveMQQueue("TEST.QUEUE6"); - MessageConsumer fallbackConsumer = fallbackSession.createConsumer(fallbackQueue); - - MessageConsumer exclusiveConsumer2 = exclusiveSession2.createConsumer(exclusiveQueue); - - ActiveMQQueue senderQueue = new ActiveMQQueue("TEST.QUEUE6"); - - MessageProducer producer = senderSession.createProducer(senderQueue); - - Message msg = senderSession.createTextMessage("test"); - producer.send(msg); - Thread.sleep(100); - - // Verify exclusive consumer receives the message. - assertNotNull(exclusiveConsumer1.receive(100)); - assertNull(exclusiveConsumer2.receive(100)); - assertNull(fallbackConsumer.receive(100)); - - // Close the exclusive consumer to verify the non-exclusive consumer - // takes over - exclusiveConsumer1.close(); - - producer.send(msg); - producer.send(msg); - - assertNotNull(exclusiveConsumer2.receive(1000)); - assertNull(fallbackConsumer.receive(100)); - - } finally { - fallbackSession.close(); - senderSession.close(); - conn.close(); - } - - } - - public void testFailoverToNonExclusiveConsumer() throws JMSException, InterruptedException { - Connection conn = createConnection(true); - - Session exclusiveSession = null; - Session fallbackSession = null; - Session senderSession = null; - - try { - - exclusiveSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - fallbackSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - senderSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // This creates the exclusive consumer first which avoids AMQ-1024 - // bug. - ActiveMQQueue exclusiveQueue = new ActiveMQQueue("TEST.QUEUE3?consumer.exclusive=true"); - MessageConsumer exclusiveConsumer = exclusiveSession.createConsumer(exclusiveQueue); - - ActiveMQQueue fallbackQueue = new ActiveMQQueue("TEST.QUEUE3"); - MessageConsumer fallbackConsumer = fallbackSession.createConsumer(fallbackQueue); - - ActiveMQQueue senderQueue = new ActiveMQQueue("TEST.QUEUE3"); - - MessageProducer producer = senderSession.createProducer(senderQueue); - - Message msg = senderSession.createTextMessage("test"); - producer.send(msg); - Thread.sleep(100); - - // Verify exclusive consumer receives the message. - assertNotNull(exclusiveConsumer.receive(100)); - assertNull(fallbackConsumer.receive(100)); - - // Close the exclusive consumer to verify the non-exclusive consumer - // takes over - exclusiveConsumer.close(); - - producer.send(msg); - - assertNotNull(fallbackConsumer.receive(100)); - - } finally { - fallbackSession.close(); - senderSession.close(); - conn.close(); - } - - } - - public void testFallbackToExclusiveConsumer() throws JMSException, InterruptedException { - Connection conn = createConnection(true); - - Session exclusiveSession = null; - Session fallbackSession = null; - Session senderSession = null; - - try { - - exclusiveSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - fallbackSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - senderSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // This creates the exclusive consumer first which avoids AMQ-1024 - // bug. - ActiveMQQueue exclusiveQueue = new ActiveMQQueue("TEST.QUEUE4?consumer.exclusive=true"); - MessageConsumer exclusiveConsumer = exclusiveSession.createConsumer(exclusiveQueue); - - ActiveMQQueue fallbackQueue = new ActiveMQQueue("TEST.QUEUE4"); - MessageConsumer fallbackConsumer = fallbackSession.createConsumer(fallbackQueue); - - ActiveMQQueue senderQueue = new ActiveMQQueue("TEST.QUEUE4"); - - MessageProducer producer = senderSession.createProducer(senderQueue); - - Message msg = senderSession.createTextMessage("test"); - producer.send(msg); - Thread.sleep(100); - - // Verify exclusive consumer receives the message. - assertNotNull(exclusiveConsumer.receive(100)); - assertNull(fallbackConsumer.receive(100)); - - // Close the exclusive consumer to verify the non-exclusive consumer - // takes over - exclusiveConsumer.close(); - - producer.send(msg); - - // Verify other non-exclusive consumer receices the message. - assertNotNull(fallbackConsumer.receive(100)); - - // Create exclusive consumer to determine if it will start receiving - // the messages. - exclusiveConsumer = exclusiveSession.createConsumer(exclusiveQueue); - - producer.send(msg); - assertNotNull(exclusiveConsumer.receive(100)); - assertNull(fallbackConsumer.receive(100)); - - } finally { - fallbackSession.close(); - senderSession.close(); - conn.close(); - } - - } -} +/** + * 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.activemq; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import junit.framework.TestCase; + +import org.apache.activemq.command.ActiveMQQueue; + +public class ExclusiveConsumerTest extends TestCase { + + private static final String VM_BROKER_URL = "vm://localhost?broker.persistent=false&broker.useJmx=true"; + + public ExclusiveConsumerTest(String name) { + super(name); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + private Connection createConnection(final boolean start) throws JMSException { + ConnectionFactory cf = new ActiveMQConnectionFactory(VM_BROKER_URL); + Connection conn = cf.createConnection(); + if (start) { + conn.start(); + } + return conn; + } + + public void testExclusiveConsumerSelectedCreatedFirst() throws JMSException, InterruptedException { + Connection conn = createConnection(true); + + Session exclusiveSession = null; + Session fallbackSession = null; + Session senderSession = null; + + try { + + exclusiveSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + fallbackSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + senderSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + + ActiveMQQueue exclusiveQueue = new ActiveMQQueue("TEST.QUEUE1?consumer.exclusive=true"); + MessageConsumer exclusiveConsumer = exclusiveSession.createConsumer(exclusiveQueue); + + ActiveMQQueue fallbackQueue = new ActiveMQQueue("TEST.QUEUE1"); + MessageConsumer fallbackConsumer = fallbackSession.createConsumer(fallbackQueue); + + ActiveMQQueue senderQueue = new ActiveMQQueue("TEST.QUEUE1"); + + MessageProducer producer = senderSession.createProducer(senderQueue); + + Message msg = senderSession.createTextMessage("test"); + producer.send(msg); + // TODO need two send a 2nd message - bug AMQ-1024 + // producer.send(msg); + Thread.sleep(100); + + // Verify exclusive consumer receives the message. + assertNotNull(exclusiveConsumer.receive(100)); + assertNull(fallbackConsumer.receive(100)); + + } finally { + fallbackSession.close(); + senderSession.close(); + conn.close(); + } + + } + + public void testExclusiveConsumerSelectedCreatedAfter() throws JMSException, InterruptedException { + Connection conn = createConnection(true); + + Session exclusiveSession = null; + Session fallbackSession = null; + Session senderSession = null; + + try { + + exclusiveSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + fallbackSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + senderSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + + ActiveMQQueue fallbackQueue = new ActiveMQQueue("TEST.QUEUE5"); + MessageConsumer fallbackConsumer = fallbackSession.createConsumer(fallbackQueue); + + ActiveMQQueue exclusiveQueue = new ActiveMQQueue("TEST.QUEUE5?consumer.exclusive=true"); + MessageConsumer exclusiveConsumer = exclusiveSession.createConsumer(exclusiveQueue); + + ActiveMQQueue senderQueue = new ActiveMQQueue("TEST.QUEUE5"); + + MessageProducer producer = senderSession.createProducer(senderQueue); + + Message msg = senderSession.createTextMessage("test"); + producer.send(msg); + Thread.sleep(100); + + // Verify exclusive consumer receives the message. + assertNotNull(exclusiveConsumer.receive(100)); + assertNull(fallbackConsumer.receive(100)); + + } finally { + fallbackSession.close(); + senderSession.close(); + conn.close(); + } + + } + + public void testFailoverToAnotherExclusiveConsumerCreatedFirst() throws JMSException, + InterruptedException { + Connection conn = createConnection(true); + + Session exclusiveSession1 = null; + Session exclusiveSession2 = null; + Session fallbackSession = null; + Session senderSession = null; + + try { + + exclusiveSession1 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + exclusiveSession2 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + fallbackSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + senderSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // This creates the exclusive consumer first which avoids AMQ-1024 + // bug. + ActiveMQQueue exclusiveQueue = new ActiveMQQueue("TEST.QUEUE2?consumer.exclusive=true"); + MessageConsumer exclusiveConsumer1 = exclusiveSession1.createConsumer(exclusiveQueue); + MessageConsumer exclusiveConsumer2 = exclusiveSession2.createConsumer(exclusiveQueue); + + ActiveMQQueue fallbackQueue = new ActiveMQQueue("TEST.QUEUE2"); + MessageConsumer fallbackConsumer = fallbackSession.createConsumer(fallbackQueue); + + ActiveMQQueue senderQueue = new ActiveMQQueue("TEST.QUEUE2"); + + MessageProducer producer = senderSession.createProducer(senderQueue); + + Message msg = senderSession.createTextMessage("test"); + producer.send(msg); + Thread.sleep(100); + + // Verify exclusive consumer receives the message. + assertNotNull(exclusiveConsumer1.receive(100)); + assertNull(exclusiveConsumer2.receive(100)); + assertNull(fallbackConsumer.receive(100)); + + // Close the exclusive consumer to verify the non-exclusive consumer + // takes over + exclusiveConsumer1.close(); + + producer.send(msg); + producer.send(msg); + + assertNotNull(exclusiveConsumer2.receive(100)); + assertNull(fallbackConsumer.receive(100)); + + } finally { + fallbackSession.close(); + senderSession.close(); + conn.close(); + } + + } + + public void testFailoverToAnotherExclusiveConsumerCreatedAfter() throws JMSException, + InterruptedException { + Connection conn = createConnection(true); + + Session exclusiveSession1 = null; + Session exclusiveSession2 = null; + Session fallbackSession = null; + Session senderSession = null; + + try { + + exclusiveSession1 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + exclusiveSession2 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + fallbackSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + senderSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // This creates the exclusive consumer first which avoids AMQ-1024 + // bug. + ActiveMQQueue exclusiveQueue = new ActiveMQQueue("TEST.QUEUE6?consumer.exclusive=true"); + MessageConsumer exclusiveConsumer1 = exclusiveSession1.createConsumer(exclusiveQueue); + + ActiveMQQueue fallbackQueue = new ActiveMQQueue("TEST.QUEUE6"); + MessageConsumer fallbackConsumer = fallbackSession.createConsumer(fallbackQueue); + + MessageConsumer exclusiveConsumer2 = exclusiveSession2.createConsumer(exclusiveQueue); + + ActiveMQQueue senderQueue = new ActiveMQQueue("TEST.QUEUE6"); + + MessageProducer producer = senderSession.createProducer(senderQueue); + + Message msg = senderSession.createTextMessage("test"); + producer.send(msg); + Thread.sleep(100); + + // Verify exclusive consumer receives the message. + assertNotNull(exclusiveConsumer1.receive(100)); + assertNull(exclusiveConsumer2.receive(100)); + assertNull(fallbackConsumer.receive(100)); + + // Close the exclusive consumer to verify the non-exclusive consumer + // takes over + exclusiveConsumer1.close(); + + producer.send(msg); + producer.send(msg); + + assertNotNull(exclusiveConsumer2.receive(1000)); + assertNull(fallbackConsumer.receive(100)); + + } finally { + fallbackSession.close(); + senderSession.close(); + conn.close(); + } + + } + + public void testFailoverToNonExclusiveConsumer() throws JMSException, InterruptedException { + Connection conn = createConnection(true); + + Session exclusiveSession = null; + Session fallbackSession = null; + Session senderSession = null; + + try { + + exclusiveSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + fallbackSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + senderSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // This creates the exclusive consumer first which avoids AMQ-1024 + // bug. + ActiveMQQueue exclusiveQueue = new ActiveMQQueue("TEST.QUEUE3?consumer.exclusive=true"); + MessageConsumer exclusiveConsumer = exclusiveSession.createConsumer(exclusiveQueue); + + ActiveMQQueue fallbackQueue = new ActiveMQQueue("TEST.QUEUE3"); + MessageConsumer fallbackConsumer = fallbackSession.createConsumer(fallbackQueue); + + ActiveMQQueue senderQueue = new ActiveMQQueue("TEST.QUEUE3"); + + MessageProducer producer = senderSession.createProducer(senderQueue); + + Message msg = senderSession.createTextMessage("test"); + producer.send(msg); + Thread.sleep(100); + + // Verify exclusive consumer receives the message. + assertNotNull(exclusiveConsumer.receive(100)); + assertNull(fallbackConsumer.receive(100)); + + // Close the exclusive consumer to verify the non-exclusive consumer + // takes over + exclusiveConsumer.close(); + + producer.send(msg); + + assertNotNull(fallbackConsumer.receive(100)); + + } finally { + fallbackSession.close(); + senderSession.close(); + conn.close(); + } + + } + + public void testFallbackToExclusiveConsumer() throws JMSException, InterruptedException { + Connection conn = createConnection(true); + + Session exclusiveSession = null; + Session fallbackSession = null; + Session senderSession = null; + + try { + + exclusiveSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + fallbackSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + senderSession = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // This creates the exclusive consumer first which avoids AMQ-1024 + // bug. + ActiveMQQueue exclusiveQueue = new ActiveMQQueue("TEST.QUEUE4?consumer.exclusive=true"); + MessageConsumer exclusiveConsumer = exclusiveSession.createConsumer(exclusiveQueue); + + ActiveMQQueue fallbackQueue = new ActiveMQQueue("TEST.QUEUE4"); + MessageConsumer fallbackConsumer = fallbackSession.createConsumer(fallbackQueue); + + ActiveMQQueue senderQueue = new ActiveMQQueue("TEST.QUEUE4"); + + MessageProducer producer = senderSession.createProducer(senderQueue); + + Message msg = senderSession.createTextMessage("test"); + producer.send(msg); + Thread.sleep(100); + + // Verify exclusive consumer receives the message. + assertNotNull(exclusiveConsumer.receive(100)); + assertNull(fallbackConsumer.receive(100)); + + // Close the exclusive consumer to verify the non-exclusive consumer + // takes over + exclusiveConsumer.close(); + + producer.send(msg); + + // Verify other non-exclusive consumer receices the message. + assertNotNull(fallbackConsumer.receive(100)); + + // Create exclusive consumer to determine if it will start receiving + // the messages. + exclusiveConsumer = exclusiveSession.createConsumer(exclusiveQueue); + + producer.send(msg); + assertNotNull(exclusiveConsumer.receive(100)); + assertNull(fallbackConsumer.receive(100)); + + } finally { + fallbackSession.close(); + senderSession.close(); + conn.close(); + } + + } +} diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/bugs/AMQ4893Test.java b/activemq-unit-tests/src/test/java/org/apache/activemq/bugs/AMQ4893Test.java index bd4b7246cc..026a4be42d 100644 --- a/activemq-unit-tests/src/test/java/org/apache/activemq/bugs/AMQ4893Test.java +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/bugs/AMQ4893Test.java @@ -1,86 +1,86 @@ -/** - * 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.activemq.bugs; - -import java.io.IOException; -import java.util.Map; - -import javax.jms.JMSException; - -import org.apache.activemq.command.ActiveMQObjectMessage; -import org.apache.activemq.openwire.OpenWireFormat; -import org.apache.activemq.util.ByteSequence; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class AMQ4893Test { - - private static final transient Logger LOG = LoggerFactory.getLogger(AMQ4893Test.class); - - @Test - public void testPropertiesInt() throws Exception { - ActiveMQObjectMessage message = new ActiveMQObjectMessage(); - message.setIntProperty("TestProp", 333); - fakeUnmarshal(message); - roundTripProperties(message); - } - - @Test - public void testPropertiesString() throws Exception { - ActiveMQObjectMessage message = new ActiveMQObjectMessage(); - message.setStringProperty("TestProp", "Value"); - fakeUnmarshal(message); - roundTripProperties(message); - } - - @Test - public void testPropertiesObject() throws Exception { - ActiveMQObjectMessage message = new ActiveMQObjectMessage(); - message.setObjectProperty("TestProp", "Value"); - fakeUnmarshal(message); - roundTripProperties(message); - } - - @Test - public void testPropertiesObjectNoMarshalling() throws Exception { - ActiveMQObjectMessage message = new ActiveMQObjectMessage(); - message.setObjectProperty("TestProp", "Value"); - roundTripProperties(message); - } - - private void roundTripProperties(ActiveMQObjectMessage message) throws IOException, JMSException { - ActiveMQObjectMessage copy = new ActiveMQObjectMessage(); - for (Map.Entry prop : message.getProperties().entrySet()) { - LOG.debug("{} -> {}", prop.getKey(), prop.getValue().getClass()); - copy.setObjectProperty(prop.getKey(), prop.getValue()); - } - } - - private void fakeUnmarshal(ActiveMQObjectMessage message) throws IOException { - // we need to force the unmarshalled property field to be set so it - // gives us a hawtbuffer for the string - OpenWireFormat format = new OpenWireFormat(); - message.beforeMarshall(format); - message.afterMarshall(format); - - ByteSequence seq = message.getMarshalledProperties(); - message.clearProperties(); - message.setMarshalledProperties(seq); - } +/** + * 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.activemq.bugs; + +import java.io.IOException; +import java.util.Map; + +import javax.jms.JMSException; + +import org.apache.activemq.command.ActiveMQObjectMessage; +import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.util.ByteSequence; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AMQ4893Test { + + private static final transient Logger LOG = LoggerFactory.getLogger(AMQ4893Test.class); + + @Test + public void testPropertiesInt() throws Exception { + ActiveMQObjectMessage message = new ActiveMQObjectMessage(); + message.setIntProperty("TestProp", 333); + fakeUnmarshal(message); + roundTripProperties(message); + } + + @Test + public void testPropertiesString() throws Exception { + ActiveMQObjectMessage message = new ActiveMQObjectMessage(); + message.setStringProperty("TestProp", "Value"); + fakeUnmarshal(message); + roundTripProperties(message); + } + + @Test + public void testPropertiesObject() throws Exception { + ActiveMQObjectMessage message = new ActiveMQObjectMessage(); + message.setObjectProperty("TestProp", "Value"); + fakeUnmarshal(message); + roundTripProperties(message); + } + + @Test + public void testPropertiesObjectNoMarshalling() throws Exception { + ActiveMQObjectMessage message = new ActiveMQObjectMessage(); + message.setObjectProperty("TestProp", "Value"); + roundTripProperties(message); + } + + private void roundTripProperties(ActiveMQObjectMessage message) throws IOException, JMSException { + ActiveMQObjectMessage copy = new ActiveMQObjectMessage(); + for (Map.Entry prop : message.getProperties().entrySet()) { + LOG.debug("{} -> {}", prop.getKey(), prop.getValue().getClass()); + copy.setObjectProperty(prop.getKey(), prop.getValue()); + } + } + + private void fakeUnmarshal(ActiveMQObjectMessage message) throws IOException { + // we need to force the unmarshalled property field to be set so it + // gives us a hawtbuffer for the string + OpenWireFormat format = new OpenWireFormat(); + message.beforeMarshall(format); + message.afterMarshall(format); + + ByteSequence seq = message.getMarshalledProperties(); + message.clearProperties(); + message.setMarshalledProperties(seq); + } } \ No newline at end of file diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/bugs/Receiver.java b/activemq-unit-tests/src/test/java/org/apache/activemq/bugs/Receiver.java index b1872df208..65f30e3337 100644 --- a/activemq-unit-tests/src/test/java/org/apache/activemq/bugs/Receiver.java +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/bugs/Receiver.java @@ -1,21 +1,21 @@ -/** - * 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.activemq.bugs; - -public interface Receiver { - void receive(String s) throws Exception; -} +/** + * 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.activemq.bugs; + +public interface Receiver { + void receive(String s) throws Exception; +} diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/store/kahadb/PBMesssagesTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/store/kahadb/PBMesssagesTest.java index 8d2c1ffbd7..8d1ed683b4 100644 --- a/activemq-unit-tests/src/test/java/org/apache/activemq/store/kahadb/PBMesssagesTest.java +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/store/kahadb/PBMesssagesTest.java @@ -1,56 +1,56 @@ -/** - * 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.activemq.store.kahadb; - -import java.io.IOException; - -import junit.framework.TestCase; - -import org.apache.activemq.protobuf.Buffer; -import org.apache.activemq.store.kahadb.data.KahaAddMessageCommand; -import org.apache.activemq.store.kahadb.data.KahaDestination; -import org.apache.activemq.store.kahadb.data.KahaDestination.DestinationType; -import org.apache.activemq.store.kahadb.data.KahaEntryType; -import org.apache.activemq.util.ByteSequence; -import org.apache.activemq.util.DataByteArrayInputStream; -import org.apache.activemq.util.DataByteArrayOutputStream; - -public class PBMesssagesTest extends TestCase { - - @SuppressWarnings("rawtypes") - public void testKahaAddMessageCommand() throws IOException { - - KahaAddMessageCommand expected = new KahaAddMessageCommand(); - expected.setDestination(new KahaDestination().setName("Foo").setType(DestinationType.QUEUE)); - expected.setMessage(new Buffer(new byte[] {1,2,3,4,5,6} )); - expected.setMessageId("Hello World"); - - int size = expected.serializedSizeFramed(); - DataByteArrayOutputStream os = new DataByteArrayOutputStream(size + 1); - os.writeByte(expected.type().getNumber()); - expected.writeFramed(os); - ByteSequence seq = os.toByteSequence(); - - DataByteArrayInputStream is = new DataByteArrayInputStream(seq); - KahaEntryType type = KahaEntryType.valueOf(is.readByte()); - JournalCommand message = (JournalCommand)type.createMessage(); - message.mergeFramed(is); - - assertEquals(expected, message); - } - -} +/** + * 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.activemq.store.kahadb; + +import java.io.IOException; + +import junit.framework.TestCase; + +import org.apache.activemq.protobuf.Buffer; +import org.apache.activemq.store.kahadb.data.KahaAddMessageCommand; +import org.apache.activemq.store.kahadb.data.KahaDestination; +import org.apache.activemq.store.kahadb.data.KahaDestination.DestinationType; +import org.apache.activemq.store.kahadb.data.KahaEntryType; +import org.apache.activemq.util.ByteSequence; +import org.apache.activemq.util.DataByteArrayInputStream; +import org.apache.activemq.util.DataByteArrayOutputStream; + +public class PBMesssagesTest extends TestCase { + + @SuppressWarnings("rawtypes") + public void testKahaAddMessageCommand() throws IOException { + + KahaAddMessageCommand expected = new KahaAddMessageCommand(); + expected.setDestination(new KahaDestination().setName("Foo").setType(DestinationType.QUEUE)); + expected.setMessage(new Buffer(new byte[] {1,2,3,4,5,6} )); + expected.setMessageId("Hello World"); + + int size = expected.serializedSizeFramed(); + DataByteArrayOutputStream os = new DataByteArrayOutputStream(size + 1); + os.writeByte(expected.type().getNumber()); + expected.writeFramed(os); + ByteSequence seq = os.toByteSequence(); + + DataByteArrayInputStream is = new DataByteArrayInputStream(seq); + KahaEntryType type = KahaEntryType.valueOf(is.readByte()); + JournalCommand message = (JournalCommand)type.createMessage(); + message.mergeFramed(is); + + assertEquals(expected, message); + } + +} diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/transport/failover/FailoverUriTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/transport/failover/FailoverUriTest.java index 20a33ff453..01a8966e0c 100644 --- a/activemq-unit-tests/src/test/java/org/apache/activemq/transport/failover/FailoverUriTest.java +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/transport/failover/FailoverUriTest.java @@ -1,48 +1,48 @@ -/** - * 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.activemq.transport.failover; - -import junit.framework.Test; - -import org.apache.activemq.transport.tcp.TransportUriTest; - -public class FailoverUriTest extends TransportUriTest { - - @Override - public void initCombosForTestUriOptionsWork() { - addCombinationValues("prefix", new Object[]{"failover:(", "failover://("}); - addCombinationValues("postfix", new Object[] {")?initialReconnectDelay=1000&maxReconnectDelay=1000" - , "?wireFormat.tightEncodingEnabled=false)?jms.useAsyncSend=true&jms.copyMessageOnSend=false" - , "?wireFormat.maxInactivityDuration=0&keepAlive=true)?jms.prefetchPolicy.all=500&initialReconnectDelay=10000&useExponentialBackOff=false&maxReconnectAttempts=0&randomize=false"}); - } - - @Override - public void initCombosForTestBadVersionNumberDoesNotWork() { - addCombinationValues("prefix", new Object[]{"failover:("}); - addCombinationValues("postfix", new Object[] {")?initialReconnectDelay=1000&maxReconnectDelay=1000"}); - } - - @Override - public void initCombosForTestBadPropertyNameFails() { - addCombinationValues("prefix", new Object[]{"failover:("}); - addCombinationValues("postfix", new Object[] {")?initialReconnectDelay=1000&maxReconnectDelay=1000"}); - } - - public static Test suite() { - return suite(FailoverUriTest.class); - } -} +/** + * 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.activemq.transport.failover; + +import junit.framework.Test; + +import org.apache.activemq.transport.tcp.TransportUriTest; + +public class FailoverUriTest extends TransportUriTest { + + @Override + public void initCombosForTestUriOptionsWork() { + addCombinationValues("prefix", new Object[]{"failover:(", "failover://("}); + addCombinationValues("postfix", new Object[] {")?initialReconnectDelay=1000&maxReconnectDelay=1000" + , "?wireFormat.tightEncodingEnabled=false)?jms.useAsyncSend=true&jms.copyMessageOnSend=false" + , "?wireFormat.maxInactivityDuration=0&keepAlive=true)?jms.prefetchPolicy.all=500&initialReconnectDelay=10000&useExponentialBackOff=false&maxReconnectAttempts=0&randomize=false"}); + } + + @Override + public void initCombosForTestBadVersionNumberDoesNotWork() { + addCombinationValues("prefix", new Object[]{"failover:("}); + addCombinationValues("postfix", new Object[] {")?initialReconnectDelay=1000&maxReconnectDelay=1000"}); + } + + @Override + public void initCombosForTestBadPropertyNameFails() { + addCombinationValues("prefix", new Object[]{"failover:("}); + addCombinationValues("postfix", new Object[] {")?initialReconnectDelay=1000&maxReconnectDelay=1000"}); + } + + public static Test suite() { + return suite(FailoverUriTest.class); + } +} diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/usecases/DurableSubscriptionHangTestCase.java b/activemq-unit-tests/src/test/java/org/apache/activemq/usecases/DurableSubscriptionHangTestCase.java index bd4f64a9da..bef912a514 100644 --- a/activemq-unit-tests/src/test/java/org/apache/activemq/usecases/DurableSubscriptionHangTestCase.java +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/usecases/DurableSubscriptionHangTestCase.java @@ -1,135 +1,135 @@ -/** - * 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.activemq.usecases; - -import java.util.concurrent.TimeUnit; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageProducer; -import javax.jms.Session; -import javax.jms.TextMessage; -import javax.jms.Topic; -import javax.jms.TopicConnection; -import javax.jms.TopicSession; -import javax.jms.TopicSubscriber; -import org.apache.activemq.ActiveMQConnectionFactory; -import org.apache.activemq.broker.BrokerService; -import org.apache.activemq.broker.region.policy.PolicyEntry; -import org.apache.activemq.broker.region.policy.PolicyMap; -import org.apache.commons.lang.RandomStringUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -import static org.junit.Assert.assertNotNull; - -public class DurableSubscriptionHangTestCase { - private static final Logger LOG = LoggerFactory.getLogger(DurableSubscriptionHangTestCase.class); - final static String brokerName = "DurableSubscriptionHangTestCase"; - final static String clientID = "myId"; - private static final String topicName = "myTopic"; - private static final String durableSubName = "mySub"; - BrokerService brokerService; - - @Before - public void startBroker() throws Exception { - brokerService = new BrokerService(); - brokerService.setDeleteAllMessagesOnStartup(true); - brokerService.setBrokerName(brokerName); - PolicyMap policyMap = new PolicyMap(); - PolicyEntry defaultEntry = new PolicyEntry(); - defaultEntry.setExpireMessagesPeriod(5000); - policyMap.setDefaultEntry(defaultEntry); - brokerService.setDestinationPolicy(policyMap); - brokerService.start(); - } - - @After - public void brokerStop() throws Exception { - brokerService.stop(); - } - - @Test - public void testHanging() throws Exception - { - registerDurableSubscription(); - produceExpiredAndOneNonExpiredMessages(); - TimeUnit.SECONDS.sleep(10); // make sure messages are expired - Message message = collectMessagesFromDurableSubscriptionForOneMinute(); - LOG.info("got message:" + message); - assertNotNull("Unable to read unexpired message", message); - } - - private void produceExpiredAndOneNonExpiredMessages() throws JMSException { - ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://" + brokerName); - TopicConnection connection = connectionFactory.createTopicConnection(); - TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); - Topic topic = session.createTopic(topicName); - MessageProducer producer = session.createProducer(topic); - producer.setTimeToLive(TimeUnit.SECONDS.toMillis(1)); - for(int i=0; i<40000; i++) - { - sendRandomMessage(session, producer); - } - producer.setTimeToLive(TimeUnit.DAYS.toMillis(1)); - sendRandomMessage(session, producer); - connection.close(); - LOG.info("produceExpiredAndOneNonExpiredMessages done"); - } - - private void registerDurableSubscription() throws JMSException - { - ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://" + brokerName); - TopicConnection connection = connectionFactory.createTopicConnection(); - connection.setClientID(clientID); - TopicSession topicSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); - Topic topic = topicSession.createTopic(topicName); - TopicSubscriber durableSubscriber = topicSession.createDurableSubscriber(topic, durableSubName); - connection.start(); - durableSubscriber.close(); - connection.close(); - LOG.info("Durable Sub Registered"); - } - - private Message collectMessagesFromDurableSubscriptionForOneMinute() throws Exception - { - ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://" + brokerName); - TopicConnection connection = connectionFactory.createTopicConnection(); - - connection.setClientID(clientID); - TopicSession topicSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); - Topic topic = topicSession.createTopic(topicName); - connection.start(); - TopicSubscriber subscriber = topicSession.createDurableSubscriber(topic, durableSubName); - LOG.info("About to receive messages"); - Message message = subscriber.receive(120000); - subscriber.close(); - connection.close(); - LOG.info("collectMessagesFromDurableSubscriptionForOneMinute done"); - - return message; - } - - private void sendRandomMessage(TopicSession session, MessageProducer producer) throws JMSException { - TextMessage textMessage = session.createTextMessage(); - textMessage.setText(RandomStringUtils.random(500, "abcdefghijklmnopqrstuvwxyz")); - producer.send(textMessage); - } -} +/** + * 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.activemq.usecases; + +import java.util.concurrent.TimeUnit; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicConnection; +import javax.jms.TopicSession; +import javax.jms.TopicSubscriber; +import org.apache.activemq.ActiveMQConnectionFactory; +import org.apache.activemq.broker.BrokerService; +import org.apache.activemq.broker.region.policy.PolicyEntry; +import org.apache.activemq.broker.region.policy.PolicyMap; +import org.apache.commons.lang.RandomStringUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +import static org.junit.Assert.assertNotNull; + +public class DurableSubscriptionHangTestCase { + private static final Logger LOG = LoggerFactory.getLogger(DurableSubscriptionHangTestCase.class); + final static String brokerName = "DurableSubscriptionHangTestCase"; + final static String clientID = "myId"; + private static final String topicName = "myTopic"; + private static final String durableSubName = "mySub"; + BrokerService brokerService; + + @Before + public void startBroker() throws Exception { + brokerService = new BrokerService(); + brokerService.setDeleteAllMessagesOnStartup(true); + brokerService.setBrokerName(brokerName); + PolicyMap policyMap = new PolicyMap(); + PolicyEntry defaultEntry = new PolicyEntry(); + defaultEntry.setExpireMessagesPeriod(5000); + policyMap.setDefaultEntry(defaultEntry); + brokerService.setDestinationPolicy(policyMap); + brokerService.start(); + } + + @After + public void brokerStop() throws Exception { + brokerService.stop(); + } + + @Test + public void testHanging() throws Exception + { + registerDurableSubscription(); + produceExpiredAndOneNonExpiredMessages(); + TimeUnit.SECONDS.sleep(10); // make sure messages are expired + Message message = collectMessagesFromDurableSubscriptionForOneMinute(); + LOG.info("got message:" + message); + assertNotNull("Unable to read unexpired message", message); + } + + private void produceExpiredAndOneNonExpiredMessages() throws JMSException { + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://" + brokerName); + TopicConnection connection = connectionFactory.createTopicConnection(); + TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + Topic topic = session.createTopic(topicName); + MessageProducer producer = session.createProducer(topic); + producer.setTimeToLive(TimeUnit.SECONDS.toMillis(1)); + for(int i=0; i<40000; i++) + { + sendRandomMessage(session, producer); + } + producer.setTimeToLive(TimeUnit.DAYS.toMillis(1)); + sendRandomMessage(session, producer); + connection.close(); + LOG.info("produceExpiredAndOneNonExpiredMessages done"); + } + + private void registerDurableSubscription() throws JMSException + { + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://" + brokerName); + TopicConnection connection = connectionFactory.createTopicConnection(); + connection.setClientID(clientID); + TopicSession topicSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + Topic topic = topicSession.createTopic(topicName); + TopicSubscriber durableSubscriber = topicSession.createDurableSubscriber(topic, durableSubName); + connection.start(); + durableSubscriber.close(); + connection.close(); + LOG.info("Durable Sub Registered"); + } + + private Message collectMessagesFromDurableSubscriptionForOneMinute() throws Exception + { + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://" + brokerName); + TopicConnection connection = connectionFactory.createTopicConnection(); + + connection.setClientID(clientID); + TopicSession topicSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + Topic topic = topicSession.createTopic(topicName); + connection.start(); + TopicSubscriber subscriber = topicSession.createDurableSubscriber(topic, durableSubName); + LOG.info("About to receive messages"); + Message message = subscriber.receive(120000); + subscriber.close(); + connection.close(); + LOG.info("collectMessagesFromDurableSubscriptionForOneMinute done"); + + return message; + } + + private void sendRandomMessage(TopicSession session, MessageProducer producer) throws JMSException { + TextMessage textMessage = session.createTextMessage(); + textMessage.setText(RandomStringUtils.random(500, "abcdefghijklmnopqrstuvwxyz")); + producer.send(textMessage); + } +} diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/usecases/TestBrokerConnectionDuplexExcludedDestinations.java b/activemq-unit-tests/src/test/java/org/apache/activemq/usecases/TestBrokerConnectionDuplexExcludedDestinations.java index ac6b3146bd..2d3b64ceb1 100644 --- a/activemq-unit-tests/src/test/java/org/apache/activemq/usecases/TestBrokerConnectionDuplexExcludedDestinations.java +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/usecases/TestBrokerConnectionDuplexExcludedDestinations.java @@ -1,171 +1,171 @@ -/** - * 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.activemq.usecases; - -import java.net.URI; - -import javax.jms.Connection; -import javax.jms.ConnectionFactory; -import javax.jms.DeliveryMode; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageProducer; -import javax.jms.Queue; -import javax.jms.Session; -import javax.jms.TextMessage; - -import junit.framework.TestCase; - -import org.apache.activemq.ActiveMQConnectionFactory; -import org.apache.activemq.broker.BrokerFactory; -import org.apache.activemq.broker.BrokerService; - -public class TestBrokerConnectionDuplexExcludedDestinations extends TestCase { - - BrokerService receiverBroker; - BrokerService senderBroker; - - Connection hubConnection; - Session hubSession; - - Connection spokeConnection; - Session spokeSession; - - @Override - public void setUp() throws Exception { - // Hub broker - String configFileName = "org/apache/activemq/usecases/receiver-duplex.xml"; - URI uri = new URI("xbean:" + configFileName); - receiverBroker = BrokerFactory.createBroker(uri); - receiverBroker.setPersistent(false); - receiverBroker.setBrokerName("Hub"); - - // Spoke broker - configFileName = "org/apache/activemq/usecases/sender-duplex.xml"; - uri = new URI("xbean:" + configFileName); - senderBroker = BrokerFactory.createBroker(uri); - senderBroker.setPersistent(false); - senderBroker.setBrokerName("Spoke"); - - // Start both Hub and Spoke broker - receiverBroker.start(); - senderBroker.start(); - - // create hub session - ConnectionFactory cfHub = new ActiveMQConnectionFactory("tcp://localhost:62002"); - - hubConnection = cfHub.createConnection(); - hubConnection.start(); - hubSession = hubConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // create spoke session - ConnectionFactory cfSpoke = new ActiveMQConnectionFactory("tcp://localhost:62001"); - spokeConnection = cfSpoke.createConnection(); - spokeConnection.start(); - spokeSession = spokeConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); - } - - @Override - public void tearDown() throws Exception { - hubSession.close(); - hubConnection.stop(); - hubConnection.close(); - - spokeSession.close(); - spokeConnection.stop(); - spokeConnection.close(); - - senderBroker.stop(); - receiverBroker.stop(); - } - - public void testDuplexSendFromHubToSpoke() throws Exception { - - //create hub producer - MessageProducer hubProducer = hubSession.createProducer(null); - hubProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); - hubProducer.setDisableMessageID(true); - hubProducer.setDisableMessageTimestamp(true); - - //create spoke producer - MessageProducer spokeProducer = hubSession.createProducer(null); - spokeProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); - spokeProducer.setDisableMessageID(true); - spokeProducer.setDisableMessageTimestamp(true); - - Queue excludedQueueHub = hubSession.createQueue("exclude.test.foo"); - TextMessage excludedMsgHub = hubSession.createTextMessage(); - excludedMsgHub.setText(excludedQueueHub.toString()); - - Queue includedQueueHub = hubSession.createQueue("include.test.foo"); - - TextMessage includedMsgHub = hubSession.createTextMessage(); - includedMsgHub.setText(includedQueueHub.toString()); - - Queue alwaysIncludedQueueHub = hubSession.createQueue("always.include.test.foo"); - - TextMessage alwaysIncludedMsgHub = hubSession.createTextMessage(); - alwaysIncludedMsgHub.setText(alwaysIncludedQueueHub.toString()); - - // Sending from Hub queue - hubProducer.send(excludedQueueHub, excludedMsgHub); - hubProducer.send(includedQueueHub, includedMsgHub); - hubProducer.send(alwaysIncludedQueueHub, alwaysIncludedMsgHub); - - Queue excludedQueueSpoke = spokeSession.createQueue("exclude.test.foo"); - MessageConsumer excludedConsumerSpoke = spokeSession.createConsumer(excludedQueueSpoke); - - Thread.sleep(100); - - Queue includedQueueSpoke = spokeSession.createQueue("include.test.foo"); - MessageConsumer includedConsumerSpoke = spokeSession.createConsumer(includedQueueSpoke); - - Thread.sleep(100); - - Queue alwaysIncludedQueueSpoke = spokeSession.createQueue("always.include.test.foo"); - MessageConsumer alwaysIncludedConsumerSpoke = spokeSession.createConsumer(alwaysIncludedQueueHub); - - Thread.sleep(100); - TextMessage alwaysIncludedMsgSpoke = spokeSession.createTextMessage(); - alwaysIncludedMsgSpoke.setText(alwaysIncludedQueueSpoke.toString()); - spokeProducer.send(alwaysIncludedQueueSpoke, alwaysIncludedMsgSpoke); - - MessageConsumer alwaysIncludedConsumerHub = spokeSession.createConsumer(alwaysIncludedQueueHub); - assertNotNull(alwaysIncludedConsumerHub); - - // Receiving from excluded Spoke queue - Message msg = excludedConsumerSpoke.receive(200); - assertNull(msg); - - // Receiving from included Spoke queue - msg = includedConsumerSpoke.receive(200); - assertEquals(includedMsgHub, msg); - - // Receiving from included Spoke queue - msg = alwaysIncludedConsumerSpoke.receive(200); - assertEquals(alwaysIncludedMsgHub, msg); - - // we should be able to receive excluded queue message on Hub - MessageConsumer excludedConsumerHub = hubSession.createConsumer(excludedQueueHub); - msg = excludedConsumerHub.receive(200);; - assertEquals(excludedMsgHub, msg); - - hubProducer.close(); - excludedConsumerSpoke.close(); - } -} +/** + * 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.activemq.usecases; + +import java.net.URI; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; + +import junit.framework.TestCase; + +import org.apache.activemq.ActiveMQConnectionFactory; +import org.apache.activemq.broker.BrokerFactory; +import org.apache.activemq.broker.BrokerService; + +public class TestBrokerConnectionDuplexExcludedDestinations extends TestCase { + + BrokerService receiverBroker; + BrokerService senderBroker; + + Connection hubConnection; + Session hubSession; + + Connection spokeConnection; + Session spokeSession; + + @Override + public void setUp() throws Exception { + // Hub broker + String configFileName = "org/apache/activemq/usecases/receiver-duplex.xml"; + URI uri = new URI("xbean:" + configFileName); + receiverBroker = BrokerFactory.createBroker(uri); + receiverBroker.setPersistent(false); + receiverBroker.setBrokerName("Hub"); + + // Spoke broker + configFileName = "org/apache/activemq/usecases/sender-duplex.xml"; + uri = new URI("xbean:" + configFileName); + senderBroker = BrokerFactory.createBroker(uri); + senderBroker.setPersistent(false); + senderBroker.setBrokerName("Spoke"); + + // Start both Hub and Spoke broker + receiverBroker.start(); + senderBroker.start(); + + // create hub session + ConnectionFactory cfHub = new ActiveMQConnectionFactory("tcp://localhost:62002"); + + hubConnection = cfHub.createConnection(); + hubConnection.start(); + hubSession = hubConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // create spoke session + ConnectionFactory cfSpoke = new ActiveMQConnectionFactory("tcp://localhost:62001"); + spokeConnection = cfSpoke.createConnection(); + spokeConnection.start(); + spokeSession = spokeConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + } + + @Override + public void tearDown() throws Exception { + hubSession.close(); + hubConnection.stop(); + hubConnection.close(); + + spokeSession.close(); + spokeConnection.stop(); + spokeConnection.close(); + + senderBroker.stop(); + receiverBroker.stop(); + } + + public void testDuplexSendFromHubToSpoke() throws Exception { + + //create hub producer + MessageProducer hubProducer = hubSession.createProducer(null); + hubProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + hubProducer.setDisableMessageID(true); + hubProducer.setDisableMessageTimestamp(true); + + //create spoke producer + MessageProducer spokeProducer = hubSession.createProducer(null); + spokeProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + spokeProducer.setDisableMessageID(true); + spokeProducer.setDisableMessageTimestamp(true); + + Queue excludedQueueHub = hubSession.createQueue("exclude.test.foo"); + TextMessage excludedMsgHub = hubSession.createTextMessage(); + excludedMsgHub.setText(excludedQueueHub.toString()); + + Queue includedQueueHub = hubSession.createQueue("include.test.foo"); + + TextMessage includedMsgHub = hubSession.createTextMessage(); + includedMsgHub.setText(includedQueueHub.toString()); + + Queue alwaysIncludedQueueHub = hubSession.createQueue("always.include.test.foo"); + + TextMessage alwaysIncludedMsgHub = hubSession.createTextMessage(); + alwaysIncludedMsgHub.setText(alwaysIncludedQueueHub.toString()); + + // Sending from Hub queue + hubProducer.send(excludedQueueHub, excludedMsgHub); + hubProducer.send(includedQueueHub, includedMsgHub); + hubProducer.send(alwaysIncludedQueueHub, alwaysIncludedMsgHub); + + Queue excludedQueueSpoke = spokeSession.createQueue("exclude.test.foo"); + MessageConsumer excludedConsumerSpoke = spokeSession.createConsumer(excludedQueueSpoke); + + Thread.sleep(100); + + Queue includedQueueSpoke = spokeSession.createQueue("include.test.foo"); + MessageConsumer includedConsumerSpoke = spokeSession.createConsumer(includedQueueSpoke); + + Thread.sleep(100); + + Queue alwaysIncludedQueueSpoke = spokeSession.createQueue("always.include.test.foo"); + MessageConsumer alwaysIncludedConsumerSpoke = spokeSession.createConsumer(alwaysIncludedQueueHub); + + Thread.sleep(100); + TextMessage alwaysIncludedMsgSpoke = spokeSession.createTextMessage(); + alwaysIncludedMsgSpoke.setText(alwaysIncludedQueueSpoke.toString()); + spokeProducer.send(alwaysIncludedQueueSpoke, alwaysIncludedMsgSpoke); + + MessageConsumer alwaysIncludedConsumerHub = spokeSession.createConsumer(alwaysIncludedQueueHub); + assertNotNull(alwaysIncludedConsumerHub); + + // Receiving from excluded Spoke queue + Message msg = excludedConsumerSpoke.receive(200); + assertNull(msg); + + // Receiving from included Spoke queue + msg = includedConsumerSpoke.receive(200); + assertEquals(includedMsgHub, msg); + + // Receiving from included Spoke queue + msg = alwaysIncludedConsumerSpoke.receive(200); + assertEquals(alwaysIncludedMsgHub, msg); + + // we should be able to receive excluded queue message on Hub + MessageConsumer excludedConsumerHub = hubSession.createConsumer(excludedQueueHub); + msg = excludedConsumerHub.receive(200);; + assertEquals(excludedMsgHub, msg); + + hubProducer.close(); + excludedConsumerSpoke.close(); + } +} diff --git a/activemq-unit-tests/src/test/resources/org/apache/activemq/usecases/sender-duplex.xml b/activemq-unit-tests/src/test/resources/org/apache/activemq/usecases/sender-duplex.xml index d13c9a090b..a8195c0745 100644 --- a/activemq-unit-tests/src/test/resources/org/apache/activemq/usecases/sender-duplex.xml +++ b/activemq-unit-tests/src/test/resources/org/apache/activemq/usecases/sender-duplex.xml @@ -1,63 +1,63 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-unit-tests/src/test/resources/spring-embedded-xbean-local.xml b/activemq-unit-tests/src/test/resources/spring-embedded-xbean-local.xml index ae3652ca4a..584faea2b6 100644 --- a/activemq-unit-tests/src/test/resources/spring-embedded-xbean-local.xml +++ b/activemq-unit-tests/src/test/resources/spring-embedded-xbean-local.xml @@ -1,80 +1,80 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 10 - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + + + + + + + + + + + + diff --git a/activemq-web-console/src/main/webapp/WEB-INF/tags/jms/formatTimestamp.tag b/activemq-web-console/src/main/webapp/WEB-INF/tags/jms/formatTimestamp.tag index 1a4a54bd8e..f8b9cdcae6 100644 --- a/activemq-web-console/src/main/webapp/WEB-INF/tags/jms/formatTimestamp.tag +++ b/activemq-web-console/src/main/webapp/WEB-INF/tags/jms/formatTimestamp.tag @@ -1,29 +1,29 @@ -<%-- - 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. ---%> -<%@ attribute name="timestamp" type="java.lang.String"%> -<%@ tag import="java.util.Date" %> -<%@ tag import="java.text.SimpleDateFormat" %> -<% - if (timestamp != null) { - long time = Long.parseLong(timestamp); - Date date = new Date(time); - - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS z"); - - out.println(formatter.format(date)); - } -%> +<%-- + 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. +--%> +<%@ attribute name="timestamp" type="java.lang.String"%> +<%@ tag import="java.util.Date" %> +<%@ tag import="java.text.SimpleDateFormat" %> +<% + if (timestamp != null) { + long time = Long.parseLong(timestamp); + Date date = new Date(time); + + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS z"); + + out.println(formatter.format(date)); + } +%> diff --git a/activemq-web-demo/src/main/webapp/js/amq.js b/activemq-web-demo/src/main/webapp/js/amq.js index 2d7cf6bc16..773719b552 100644 --- a/activemq-web-demo/src/main/webapp/js/amq.js +++ b/activemq-web-demo/src/main/webapp/js/amq.js @@ -1,307 +1,307 @@ -/** - * - * 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. - */ - -// AMQ Ajax handler -// This class provides the main API for using the Ajax features of AMQ. It -// allows JMS messages to be sent and received from javascript when used -// with the org.apache.activemq.web.MessageListenerServlet. -// -// This version of the file provides an adapter interface for the jquery library -// and a namespace for the Javascript file, private/public variables and -// methods, and other scripting niceties. -- jim cook 2007/08/28 - -var org = org || {}; -org.activemq = org.activemq || {}; - -org.activemq.Amq = function() { - var connectStatusHandler; - - // Just a shortcut to eliminate some redundant typing. - var adapter = org.activemq.AmqAdapter; - - if (typeof adapter == 'undefined') { - throw 'An org.activemq.AmqAdapter must be declared before the amq.js script file.' - } - - // The URI of the AjaxServlet. - var uri; - - // The number of seconds that the long-polling socket will stay connected. - // Best to keep this to a value less than one minute. - var timeout; - - // A session should not be considered initialized until the JSESSIONID is returned - // from the initial GET request. Otherwise subscription POSTS may register the - // subscription with the wrong session. - var sessionInitialized = false; - - // This callback will be called after the first GET request returns. - var sessionInitializedCallback; - - // Poll delay. if set to positive integer, this is the time to wait in ms - // before sending the next poll after the last completes. - var pollDelay; - - // Inidicates whether logging is active or not. Not by default. - var logging = false; - - // 5 second delay if an error occurs during poll. This could be due to - // server capacity problems or a timeout condition. - var pollErrorDelay = 5000; - - // Map of handlers that will respond to message receipts. The id used during - // addListener(id, destination, handler) is used to key the callback - // handler. - var messageHandlers = {}; - - // Indicates whether an AJAX post call is in progress. - var batchInProgress = false; - - // A collection of pending messages that accumulate when an AJAX call is in - // progress. These messages will be delivered as soon as the current call - // completes. The array contains objects in the format { destination, - // message, messageType }. - var messageQueue = []; - - // String to distinguish this client from others sharing the same session. - // This can occur when multiple browser windows or tabs using amq.js simultaneously. - // All windows share the same JESSIONID, but need to consume messages independently. - var clientId = null; - - /** - * Iterate over the returned XML and for each message in the response, - * invoke the handler with the matching id. - */ - var messageHandler = function(data) { - var response = data.getElementsByTagName("ajax-response"); - if (response != null && response.length == 1) { - connectStatusHandler(true); - var responses = response[0].childNodes; // - for (var i = 0; i < responses.length; i++) { - var responseElement = responses[i]; - - // only process nodes of type element..... - if (responseElement.nodeType != 1) continue; - - var id = responseElement.getAttribute('id'); - - var handler = messageHandlers[id]; - - if (logging && handler == null) { - adapter.log('No handler found to match message with id = ' + id); - continue; - } - - // Loop thru and handle each - for (var j = 0; j < responseElement.childNodes.length; j++) { - handler(responseElement.childNodes[j]); - } - } - } - }; - - var errorHandler = function(xhr, status, ex) { - connectStatusHandler(false); - if (logging) adapter.log('Error occurred in ajax call. HTTP result: ' + - xhr.status + ', status: ' + status); - } - - var pollErrorHandler = function(xhr, status, ex) { - connectStatusHandler(false); - if (status === 'error' && xhr.status === 0) { - if (logging) adapter.log('Server connection dropped.'); - setTimeout(function() { sendPoll(); }, pollErrorDelay); - return; - } - if (logging) adapter.log('Error occurred in poll. HTTP result: ' + - xhr.status + ', status: ' + status); - setTimeout(function() { sendPoll(); }, pollErrorDelay); - } - - var pollHandler = function(data) { - try { - messageHandler(data); - } catch(e) { - if (logging) adapter.log('Exception in the poll handler: ' + data, e); - throw(e); - } finally { - setTimeout(sendPoll, pollDelay); - } - }; - - var initHandler = function(data) { - sessionInitialized = true; - if(sessionInitializedCallback) { - sessionInitializedCallback(); - } - pollHandler(data); - } - - var sendPoll = function() { - // Workaround IE6 bug where it caches the response - // Generate a unique query string with date and random - var now = new Date(); - var timeoutArg = sessionInitialized ? timeout : 0.001; - var data = 'timeout=' + timeoutArg * 1000 - + '&d=' + now.getTime() - + '&r=' + Math.random(); - var successCallback = sessionInitialized ? pollHandler : initHandler; - - var options = { method: 'get', - data: addClientId( data ), - success: successCallback, - error: pollErrorHandler}; - adapter.ajax(uri, options); - }; - - var sendJmsMessage = function(destination, message, type, headers) { - var message = { - destination: destination, - message: message, - messageType: type - }; - // Add message to outbound queue - if (batchInProgress) { - messageQueue[messageQueue.length] = {message:message, headers:headers}; - } else { - org.activemq.Amq.startBatch(); - adapter.ajax(uri, { method: 'post', - data: addClientId( buildParams( [message] ) ), - error: errorHandler, - headers: headers, - success: org.activemq.Amq.endBatch}); - } - }; - - var buildParams = function(msgs) { - var s = []; - for (var i = 0, c = msgs.length; i < c; i++) { - if (i != 0) s[s.length] = '&'; - s[s.length] = ((i == 0) ? 'destination' : 'd' + i); - s[s.length] = '='; - s[s.length] = msgs[i].destination; - s[s.length] = ((i == 0) ? '&message' : '&m' + i); - s[s.length] = '='; - s[s.length] = msgs[i].message; - s[s.length] = ((i == 0) ? '&type' : '&t' + i); - s[s.length] = '='; - s[s.length] = msgs[i].messageType; - } - return s.join(''); - } - - // add clientId to data if it exists, before passing data to ajax connection adapter. - var addClientId = function( data ) { - var output = data || ''; - if( clientId ) { - if( output.length > 0 ) { - output += '&'; - } - output += 'clientId='+clientId; - } - return output; - } - - return { - // optional clientId can be supplied to allow multiple clients (browser windows) within the same session. - init : function(options) { - connectStatusHandler = options.connectStatusHandler || function(connected){}; - uri = options.uri || '/amq'; - pollDelay = typeof options.pollDelay == 'number' ? options.pollDelay : 0; - timeout = typeof options.timeout == 'number' ? options.timeout : 25; - logging = options.logging; - sessionInitializedCallback = options.sessionInitializedCallback - clientId = options.clientId; - adapter.init(options); - sendPoll(); - - }, - - startBatch : function() { - batchInProgress = true; - }, - - endBatch : function() { - if (messageQueue.length > 0) { - var messagesToSend = []; - var messagesToQueue = []; - var outgoingHeaders = null; - - // we need to ensure that messages which set headers are sent by themselves. - // if 2 'listen' messages were sent together, and a 'selector' header were added to one of them, - // AMQ would add the selector to both 'listen' commands. - for(i=0;i + for (var i = 0; i < responses.length; i++) { + var responseElement = responses[i]; + + // only process nodes of type element..... + if (responseElement.nodeType != 1) continue; + + var id = responseElement.getAttribute('id'); + + var handler = messageHandlers[id]; + + if (logging && handler == null) { + adapter.log('No handler found to match message with id = ' + id); + continue; + } + + // Loop thru and handle each + for (var j = 0; j < responseElement.childNodes.length; j++) { + handler(responseElement.childNodes[j]); + } + } + } + }; + + var errorHandler = function(xhr, status, ex) { + connectStatusHandler(false); + if (logging) adapter.log('Error occurred in ajax call. HTTP result: ' + + xhr.status + ', status: ' + status); + } + + var pollErrorHandler = function(xhr, status, ex) { + connectStatusHandler(false); + if (status === 'error' && xhr.status === 0) { + if (logging) adapter.log('Server connection dropped.'); + setTimeout(function() { sendPoll(); }, pollErrorDelay); + return; + } + if (logging) adapter.log('Error occurred in poll. HTTP result: ' + + xhr.status + ', status: ' + status); + setTimeout(function() { sendPoll(); }, pollErrorDelay); + } + + var pollHandler = function(data) { + try { + messageHandler(data); + } catch(e) { + if (logging) adapter.log('Exception in the poll handler: ' + data, e); + throw(e); + } finally { + setTimeout(sendPoll, pollDelay); + } + }; + + var initHandler = function(data) { + sessionInitialized = true; + if(sessionInitializedCallback) { + sessionInitializedCallback(); + } + pollHandler(data); + } + + var sendPoll = function() { + // Workaround IE6 bug where it caches the response + // Generate a unique query string with date and random + var now = new Date(); + var timeoutArg = sessionInitialized ? timeout : 0.001; + var data = 'timeout=' + timeoutArg * 1000 + + '&d=' + now.getTime() + + '&r=' + Math.random(); + var successCallback = sessionInitialized ? pollHandler : initHandler; + + var options = { method: 'get', + data: addClientId( data ), + success: successCallback, + error: pollErrorHandler}; + adapter.ajax(uri, options); + }; + + var sendJmsMessage = function(destination, message, type, headers) { + var message = { + destination: destination, + message: message, + messageType: type + }; + // Add message to outbound queue + if (batchInProgress) { + messageQueue[messageQueue.length] = {message:message, headers:headers}; + } else { + org.activemq.Amq.startBatch(); + adapter.ajax(uri, { method: 'post', + data: addClientId( buildParams( [message] ) ), + error: errorHandler, + headers: headers, + success: org.activemq.Amq.endBatch}); + } + }; + + var buildParams = function(msgs) { + var s = []; + for (var i = 0, c = msgs.length; i < c; i++) { + if (i != 0) s[s.length] = '&'; + s[s.length] = ((i == 0) ? 'destination' : 'd' + i); + s[s.length] = '='; + s[s.length] = msgs[i].destination; + s[s.length] = ((i == 0) ? '&message' : '&m' + i); + s[s.length] = '='; + s[s.length] = msgs[i].message; + s[s.length] = ((i == 0) ? '&type' : '&t' + i); + s[s.length] = '='; + s[s.length] = msgs[i].messageType; + } + return s.join(''); + } + + // add clientId to data if it exists, before passing data to ajax connection adapter. + var addClientId = function( data ) { + var output = data || ''; + if( clientId ) { + if( output.length > 0 ) { + output += '&'; + } + output += 'clientId='+clientId; + } + return output; + } + + return { + // optional clientId can be supplied to allow multiple clients (browser windows) within the same session. + init : function(options) { + connectStatusHandler = options.connectStatusHandler || function(connected){}; + uri = options.uri || '/amq'; + pollDelay = typeof options.pollDelay == 'number' ? options.pollDelay : 0; + timeout = typeof options.timeout == 'number' ? options.timeout : 25; + logging = options.logging; + sessionInitializedCallback = options.sessionInitializedCallback + clientId = options.clientId; + adapter.init(options); + sendPoll(); + + }, + + startBatch : function() { + batchInProgress = true; + }, + + endBatch : function() { + if (messageQueue.length > 0) { + var messagesToSend = []; + var messagesToQueue = []; + var outgoingHeaders = null; + + // we need to ensure that messages which set headers are sent by themselves. + // if 2 'listen' messages were sent together, and a 'selector' header were added to one of them, + // AMQ would add the selector to both 'listen' commands. + for(i=0;i= 0; - var data = xml ? xhr.responseXML : xhr.responseText; - options.success(data); - } - } : function() {}, - onFailure: options.error || function() { - }, - onException: options.error || function() { - } - } - - if( options.headers ) { - request.requestHeaders = options.headers; - } - - if (options.method == 'post') { - request.postBody = options.data; - } else { - request.parameters = options.data; - request.method = 'get'; - } - - new Ajax.Request( uri, request ); - }, - - log: function(message, exception) { - if (typeof console != 'undefined' && console.log) console.log(message); - } - -}; +/** + * + * 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. + */ + +// AMQ Ajax Adapter for Prototype +// This class provides an adapter interface for the prototype library to perform +// some of the library-dependent tasks...namely logging and ajax. + +var org = org || {}; +org.activemq = org.activemq || {}; + +org.activemq.AmqAdapter = { + + init: function(options) { + }, + +/** + * Implement this method to make an AJAX call to the AjaxServlet. An + * options object will accompany this class and will contain the properties + * that describe the details of the AJAX call. The options object will + * have the following properties: + * + * - method: 'get' or 'post' + * - data: query data to accompany the post or get. + * - success: A callback function that is invoked upon successful + * completion of the AJAX call. The parameter is: + * - data: The result of the AJAX call. In the case of XML + * data should resolve to a Document element. + * - error: A callback when some type of error occurs. The callback + * function's parameters should be: + * - xhr: The XmlHttpRequest object. + * - status: A text string of the status. + * - ex: The exception that caused the error. + * - headers: An object containing additional headers for the ajax request. + */ + ajax: function(uri, options) { + request = { + onSuccess: options.success ? function(xhr, header) { + if (options.success) { + var ct = xhr.getResponseHeader("content-type"); + var xml = ct && ct.indexOf("xml") >= 0; + var data = xml ? xhr.responseXML : xhr.responseText; + options.success(data); + } + } : function() {}, + onFailure: options.error || function() { + }, + onException: options.error || function() { + } + } + + if( options.headers ) { + request.requestHeaders = options.headers; + } + + if (options.method == 'post') { + request.postBody = options.data; + } else { + request.parameters = options.data; + request.method = 'get'; + } + + new Ajax.Request( uri, request ); + }, + + log: function(message, exception) { + if (typeof console != 'undefined' && console.log) console.log(message); + } + +}; diff --git a/activemq-web-demo/src/main/webapp/js/chat.js b/activemq-web-demo/src/main/webapp/js/chat.js index ed6d8aa640..f616add5f5 100755 --- a/activemq-web-demo/src/main/webapp/js/chat.js +++ b/activemq-web-demo/src/main/webapp/js/chat.js @@ -1,209 +1,209 @@ -/** - * - * 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. - */ - -var amq = org.activemq.Amq; - -org.activemq.Chat = function() { - var last = ''; - - var user = null; - - var chatTopic = 'topic://CHAT.DEMO'; - - var chat, join, joined, phrase, members, username = null; - - var chatHandler = function(message) { - var type = message.getAttribute('type'); - var from = message.getAttribute('from'); - - switch (type) { - // Incoming chat message - case 'chat' : { - var text = message.childNodes[0].data; - - if (from == last) from = '...'; - else { - last = from; - from += ':'; - } - - chat.innerHTML += '' + from + ' ' + text + '
'; - break; - } - - // Incoming ping request, add the person's name to your list. - case 'ping' : { - members.innerHTML += '' + from + '
'; - break; - } - - // someone new joined the chatroom, clear your list and - // broadcast your name to all users. - case 'join' : { - members.innerHTML = ''; - if (user != null) - amq.sendMessage(chatTopic, ''); - chat.innerHTML += '' + from + ' has joined the room!
'; - break; - } - - // Screw you guys, I'm going home... - // When I (and everyone else) receive a leave message, we broadcast - // our own names in a ping in order to update everyone's list. - // todo: Make this more efficient by simply removing the person's name from the list. - case 'leave': { - members.innerHTML = ''; - chat.innerHTML += '' + from + ' has left the room!
'; - - // If we are the one that is leaving... - if (from == user) { - // switch the input form - join.className = ''; - joined.className = 'hidden'; - username.focus(); - - user = null; - amq.removeListener('chat', chatTopic); - } - if (user != null) - amq.sendMessage(chatTopic, ''); - break; - } - } - - chat.scrollTop = chat.scrollHeight - chat.clientHeight; - }; - - var getKeyCode = function (ev) { - var keyc; - if (window.event) keyc = window.event.keyCode; - else keyc = ev.keyCode; - return keyc; - }; - - var addEvent = function(obj, type, fn) { - if (obj.addEventListener) - obj.addEventListener(type, fn, false); - else if (obj.attachEvent) { - obj["e"+type+fn] = fn; - obj[type+fn] = function() { obj["e"+type+fn]( window.event ); } - obj.attachEvent( "on"+type, obj[type+fn] ); - } - }; - - var initEventHandlers = function() { - addEvent(username, 'keyup', function(ev) { - var keyc = getKeyCode(ev); - if (keyc == 13 || keyc == 10) { - org.activemq.Chat.join(); - return false; - } - return true; - }); - - addEvent(document.getElementById('joinB'), 'click', function() { - org.activemq.Chat.join(); - return true; - }); - - addEvent(phrase, 'keyup', function(ev) { - var keyc = getKeyCode(ev); - - if (keyc == 13 || keyc == 10) { - var text = phrase.value; - phrase.value = ''; - org.activemq.Chat.chat(text); - return false; - } - return true; - }); - - addEvent(document.getElementById('sendB'), 'click', function() { - var text = phrase.value; - phrase.value = ''; - org.activemq.Chat.chat(text); - }); - - addEvent(document.getElementById('leaveB'), 'click', function() { - org.activemq.Chat.leave(); - return false; - }); - }; - - return { - join: function() { - var name = username.value; - if (name == null || name.length == 0) { - alert('Please enter a username!'); - } else { - user = name; - - amq.addListener('chat', chatTopic, chatHandler); - join.className = 'hidden'; - joined.className = ''; - phrase.focus(); - - amq.sendMessage(chatTopic, ''); - } - }, - - leave: function() { - amq.sendMessage(chatTopic, ''); - }, - - chat: function(text) { - if (text != null && text.length > 0) { - // TODO more encoding? - text = text.replace('<', '<'); - text = text.replace('>', '>'); - - amq.sendMessage(chatTopic, '' + text + ''); - } - }, - - init: function() { - join = document.getElementById('join'); - joined = document.getElementById('joined'); - chat = document.getElementById('chat'); - members = document.getElementById('members'); - username = document.getElementById('username'); - phrase = document.getElementById('phrase'); - - if (join.className == 'hidden' && joined.className == 'hidden') { - join.className = ''; - joined.className = 'hidden'; - username.focus(); - } - - initEventHandlers(); - } - } -}(); - - - - - - - - - - - - - +/** + * + * 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. + */ + +var amq = org.activemq.Amq; + +org.activemq.Chat = function() { + var last = ''; + + var user = null; + + var chatTopic = 'topic://CHAT.DEMO'; + + var chat, join, joined, phrase, members, username = null; + + var chatHandler = function(message) { + var type = message.getAttribute('type'); + var from = message.getAttribute('from'); + + switch (type) { + // Incoming chat message + case 'chat' : { + var text = message.childNodes[0].data; + + if (from == last) from = '...'; + else { + last = from; + from += ':'; + } + + chat.innerHTML += '' + from + ' ' + text + '
'; + break; + } + + // Incoming ping request, add the person's name to your list. + case 'ping' : { + members.innerHTML += '' + from + '
'; + break; + } + + // someone new joined the chatroom, clear your list and + // broadcast your name to all users. + case 'join' : { + members.innerHTML = ''; + if (user != null) + amq.sendMessage(chatTopic, ''); + chat.innerHTML += '' + from + ' has joined the room!
'; + break; + } + + // Screw you guys, I'm going home... + // When I (and everyone else) receive a leave message, we broadcast + // our own names in a ping in order to update everyone's list. + // todo: Make this more efficient by simply removing the person's name from the list. + case 'leave': { + members.innerHTML = ''; + chat.innerHTML += '' + from + ' has left the room!
'; + + // If we are the one that is leaving... + if (from == user) { + // switch the input form + join.className = ''; + joined.className = 'hidden'; + username.focus(); + + user = null; + amq.removeListener('chat', chatTopic); + } + if (user != null) + amq.sendMessage(chatTopic, ''); + break; + } + } + + chat.scrollTop = chat.scrollHeight - chat.clientHeight; + }; + + var getKeyCode = function (ev) { + var keyc; + if (window.event) keyc = window.event.keyCode; + else keyc = ev.keyCode; + return keyc; + }; + + var addEvent = function(obj, type, fn) { + if (obj.addEventListener) + obj.addEventListener(type, fn, false); + else if (obj.attachEvent) { + obj["e"+type+fn] = fn; + obj[type+fn] = function() { obj["e"+type+fn]( window.event ); } + obj.attachEvent( "on"+type, obj[type+fn] ); + } + }; + + var initEventHandlers = function() { + addEvent(username, 'keyup', function(ev) { + var keyc = getKeyCode(ev); + if (keyc == 13 || keyc == 10) { + org.activemq.Chat.join(); + return false; + } + return true; + }); + + addEvent(document.getElementById('joinB'), 'click', function() { + org.activemq.Chat.join(); + return true; + }); + + addEvent(phrase, 'keyup', function(ev) { + var keyc = getKeyCode(ev); + + if (keyc == 13 || keyc == 10) { + var text = phrase.value; + phrase.value = ''; + org.activemq.Chat.chat(text); + return false; + } + return true; + }); + + addEvent(document.getElementById('sendB'), 'click', function() { + var text = phrase.value; + phrase.value = ''; + org.activemq.Chat.chat(text); + }); + + addEvent(document.getElementById('leaveB'), 'click', function() { + org.activemq.Chat.leave(); + return false; + }); + }; + + return { + join: function() { + var name = username.value; + if (name == null || name.length == 0) { + alert('Please enter a username!'); + } else { + user = name; + + amq.addListener('chat', chatTopic, chatHandler); + join.className = 'hidden'; + joined.className = ''; + phrase.focus(); + + amq.sendMessage(chatTopic, ''); + } + }, + + leave: function() { + amq.sendMessage(chatTopic, ''); + }, + + chat: function(text) { + if (text != null && text.length > 0) { + // TODO more encoding? + text = text.replace('<', '<'); + text = text.replace('>', '>'); + + amq.sendMessage(chatTopic, '' + text + ''); + } + }, + + init: function() { + join = document.getElementById('join'); + joined = document.getElementById('joined'); + chat = document.getElementById('chat'); + members = document.getElementById('members'); + username = document.getElementById('username'); + phrase = document.getElementById('phrase'); + + if (join.className == 'hidden' && joined.className == 'hidden') { + join.className = ''; + joined.className = 'hidden'; + username.focus(); + } + + initEventHandlers(); + } + } +}(); + + + + + + + + + + + + + diff --git a/activemq-web-demo/src/main/webapp/mqtt/mqttws31.js b/activemq-web-demo/src/main/webapp/mqtt/mqttws31.js index 5c2f3e3dc2..26cfaace3b 100644 --- a/activemq-web-demo/src/main/webapp/mqtt/mqttws31.js +++ b/activemq-web-demo/src/main/webapp/mqtt/mqttws31.js @@ -1,529 +1,529 @@ -/******************************************************************************* - * Copyright (c) 2013 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Andrew Banks - initial API and implementation and initial documentation - *******************************************************************************/ - - -// Only expose a single object name in the global namespace. -// Everything must go through this module. Global Messaging module -// only has a single public function, client, which returns -// a Messaging client object given connection details. - -/** - * @namespace Messaging - * Send and receive messages using web browsers. - *

- * This programming interface lets a JavaScript client application use the MQTT V3.1 protocol to - * connect to an MQTT-supporting messaging server. - * - * The function supported includes: - *

    - *
  1. Connecting to and disconnecting from a server. The server is identified by its host name and port number. - *
  2. Specifying options that relate to the communications link with the server, - * for example the frequency of keep-alive heartbeats, and whether SSL/TLS is required. - *
  3. Subscribing to and receiving messages from MQTT Topics. - *
  4. Publishing messages to MQTT Topics. - *
- *

- *

The API consists of two main objects:

- * The Messaging.Client object. This contains methods that provide the functionality of the API, - * including provision of callbacks that notify the application when a message arrives from or is delivered to the messaging server, - * or when the status of its connection to the messaging server changes. - *

- * The Messaging.Message object. This encapsulates the payload of the message along with various attributes - * associated with its delivery, in particular the destination to which it has been (or is about to be) sent. - *

- * The programming interface validates parameters passed to it, and will throw an Error containing an error message - * intended for developer use, if it detects an error with any parameter. - *

- * Example: - * - *

-client = new Messaging.Client(location.hostname, Number(location.port), "clientId");
-client.onConnectionLost = onConnectionLost;
-client.onMessageArrived = onMessageArrived;
-client.connect({onSuccess:onConnect});
-
-function onConnect() {
-  // Once a connection has been made, make a subscription and send a message.
-  console.log("onConnect");
-  client.subscribe("/World");
-  message = new Messaging.Message("Hello");
-  message.destinationName = "/World";
-  client.send(message); 
-};
-function onConnectionLost(responseObject) {
-  if (responseObject.errorCode !== 0)
-    console.log("onConnectionLost:"+responseObject.errorMessage);
-};
-function onMessageArrived(message) {
-  console.log("onMessageArrived:"+message.payloadString);
-  client.disconnect(); 
-};	
- * 
- *

- * Other programming languages, - * Java, - * C. - */ -Messaging = (function (global) { - - // Private variables below, these are only visible inside the function closure - // which is used to define the module. - - var version = "0.0.0.0"; - var buildLevel = "@BUILDLEVEL@"; - - /** - * Unique message type identifiers, with associated - * associated integer values. - * @private - */ - var MESSAGE_TYPE = { - CONNECT: 1, - CONNACK: 2, - PUBLISH: 3, - PUBACK: 4, - PUBREC: 5, - PUBREL: 6, - PUBCOMP: 7, - SUBSCRIBE: 8, - SUBACK: 9, - UNSUBSCRIBE: 10, - UNSUBACK: 11, - PINGREQ: 12, - PINGRESP: 13, - DISCONNECT: 14 - }; - - // Collection of utility methods used to simplify module code - // and promote the DRY pattern. - - /** - * Validate an object's parameter names to ensure they - * match a list of expected variables name for this option - * type. Used to ensure option object passed into the API don't - * contain erroneous parameters. - * @param {Object} obj User options object - * @param {key:type, key2:type, ...} valid keys and types that may exist in obj. - * @throws {Error} Invalid option parameter found. - * @private - */ - var validate = function(obj, keys) { - for(key in obj) { - if (obj.hasOwnProperty(key)) { - if (keys.hasOwnProperty(key)) { - if (typeof obj[key] !== keys[key]) - throw new Error(format(ERROR.INVALID_TYPE, [typeof obj[key], key])); - } else { - var errorStr = "Unknown property, " + key + ". Valid properties are:"; - for (key in keys) - if (keys.hasOwnProperty(key)) - errorStr = errorStr+" "+key; - throw new Error(errorStr); - } - } - } - }; - - /** - * Return a new function which runs the user function bound - * to a fixed scope. - * @param {function} User function - * @param {object} Function scope - * @return {function} User function bound to another scope - * @private - */ - var scope = function (f, scope) { - return function () { - return f.apply(scope, arguments); - }; - }; - - /** - * Unique message type identifiers, with associated - * associated integer values. - * @private - */ - var ERROR = { - OK: {code:0, text:"AMQJSC0000I OK."}, - CONNECT_TIMEOUT: {code:1, text:"AMQJSC0001E Connect timed out."}, - SUBSCRIBE_TIMEOUT: {code:2, text:"AMQJS0002E Subscribe timed out."}, - UNSUBSCRIBE_TIMEOUT: {code:3, text:"AMQJS0003E Unsubscribe timed out."}, - PING_TIMEOUT: {code:4, text:"AMQJS0004E Ping timed out."}, - INTERNAL_ERROR: {code:5, text:"AMQJS0005E Internal error."}, - CONNACK_RETURNCODE: {code:6, text:"AMQJS0006E Bad Connack return code:{0} {1}."}, - SOCKET_ERROR: {code:7, text:"AMQJS0007E Socket error:{0}."}, - SOCKET_CLOSE: {code:8, text:"AMQJS0008I Socket closed."}, - MALFORMED_UTF: {code:9, text:"AMQJS0009E Malformed UTF data:{0} {1} {2}."}, - UNSUPPORTED: {code:10, text:"AMQJS0010E {0} is not supported by this browser."}, - INVALID_STATE: {code:11, text:"AMQJS0011E Invalid state {0}."}, - INVALID_TYPE: {code:12, text:"AMQJS0012E Invalid type {0} for {1}."}, - INVALID_ARGUMENT: {code:13, text:"AMQJS0013E Invalid argument {0} for {1}."}, - UNSUPPORTED_OPERATION: {code:14, text:"AMQJS0014E Unsupported operation."}, - INVALID_STORED_DATA: {code:15, text:"AMQJS0015E Invalid data in local storage key={0} value={1}."}, - INVALID_MQTT_MESSAGE_TYPE: {code:16, text:"AMQJS0016E Invalid MQTT message type {0}."}, - MALFORMED_UNICODE: {code:17, text:"AMQJS0017E Malformed Unicode string:{0} {1}."}, - }; - - /** CONNACK RC Meaning. */ - var CONNACK_RC = { - 0:"Connection Accepted", - 1:"Connection Refused: unacceptable protocol version", - 2:"Connection Refused: identifier rejected", - 3:"Connection Refused: server unavailable", - 4:"Connection Refused: bad user name or password", - 5:"Connection Refused: not authorized" - }; - - /** - * Format an error message text. - * @private - * @param {error} ERROR.KEY value above. - * @param {substitutions} [array] substituted into the text. - * @return the text with the substitutions made. - */ - var format = function(error, substitutions) { - var text = error.text; - if (substitutions) { - for (var i=0; i 0) { - var part1 = text.substring(0,start); - var part2 = text.substring(start+field.length); - text = part1+substitutions[i]+part2; - } - } - } - return text; - }; - - //MQTT protocol and version 6 M Q I s d p 3 - var MqttProtoIdentifier = [0x00,0x06,0x4d,0x51,0x49,0x73,0x64,0x70,0x03]; - - /** - * @ignore - * Construct an MQTT wire protocol message. - * @param type MQTT packet type. - * @param options optional wire message attributes. - * - * Optional properties - * - * messageIdentifier: message ID in the range [0..65535] - * payloadMessage: Application Message - PUBLISH only - * connectStrings: array of 0 or more Strings to be put into the CONNECT payload - * topics: array of strings (SUBSCRIBE, UNSUBSCRIBE) - * requestQoS: array of QoS values [0..2] - * - * "Flag" properties - * cleanSession: true if present / false if absent (CONNECT) - * willMessage: true if present / false if absent (CONNECT) - * isRetained: true if present / false if absent (CONNECT) - * userName: true if present / false if absent (CONNECT) - * password: true if present / false if absent (CONNECT) - * keepAliveInterval: integer [0..65535] (CONNECT) - * - * @private - */ - var WireMessage = function (type, options) { - this.type = type; - for(name in options) { - if (options.hasOwnProperty(name)) { - this[name] = options[name]; - } - } - }; - - WireMessage.prototype.encode = function() { - // Compute the first byte of the fixed header - var first = ((this.type & 0x0f) << 4); - - /* - * Now calculate the length of the variable header + payload by adding up the lengths - * of all the component parts - */ - - remLength = 0; - topicStrLength = new Array(); - - // if the message contains a messageIdentifier then we need two bytes for that - if (this.messageIdentifier != undefined) - remLength += 2; - - switch(this.type) { - // If this a Connect then we need to include 12 bytes for its header - case MESSAGE_TYPE.CONNECT: - remLength += MqttProtoIdentifier.length + 3; - remLength += UTF8Length(this.clientId) + 2; - if (this.willMessage != undefined) { - remLength += UTF8Length(this.willMessage.destinationName) + 2; - // Will message is always a string, sent as UTF-8 characters with a preceding length. - var willMessagePayloadBytes = this.willMessage.payloadBytes; - if (!(willMessagePayloadBytes instanceof Uint8Array)) - willMessagePayloadBytes = new Uint8Array(payloadBytes); - remLength += willMessagePayloadBytes.byteLength +2; - } - if (this.userName != undefined) - remLength += UTF8Length(this.userName) + 2; - if (this.password != undefined) - remLength += UTF8Length(this.password) + 2; - break; - - // Subscribe, Unsubscribe can both contain topic strings - case MESSAGE_TYPE.SUBSCRIBE: - first |= 0x02; // Qos = 1; - for ( var i = 0; i < this.topics.length; i++) { - topicStrLength[i] = UTF8Length(this.topics[i]); - remLength += topicStrLength[i] + 2; - } - remLength += this.requestedQos.length; // 1 byte for each topic's Qos - // QoS on Subscribe only - break; - - case MESSAGE_TYPE.UNSUBSCRIBE: - first |= 0x02; // Qos = 1; - for ( var i = 0; i < this.topics.length; i++) { - topicStrLength[i] = UTF8Length(this.topics[i]); - remLength += topicStrLength[i] + 2; - } - break; - - case MESSAGE_TYPE.PUBLISH: - if (this.payloadMessage.duplicate) first |= 0x08; - first = first |= (this.payloadMessage.qos << 1); - if (this.payloadMessage.retained) first |= 0x01; - destinationNameLength = UTF8Length(this.payloadMessage.destinationName); - remLength += destinationNameLength + 2; - var payloadBytes = this.payloadMessage.payloadBytes; - remLength += payloadBytes.byteLength; - if (payloadBytes instanceof ArrayBuffer) - payloadBytes = new Uint8Array(payloadBytes); - else if (!(payloadBytes instanceof Uint8Array)) - payloadBytes = new Uint8Array(payloadBytes.buffer); - break; - - case MESSAGE_TYPE.DISCONNECT: - break; - - default: - ; - } - - // Now we can allocate a buffer for the message - - var mbi = encodeMBI(remLength); // Convert the length to MQTT MBI format - var pos = mbi.length + 1; // Offset of start of variable header - var buffer = new ArrayBuffer(remLength + pos); - var byteStream = new Uint8Array(buffer); // view it as a sequence of bytes - - //Write the fixed header into the buffer - byteStream[0] = first; - byteStream.set(mbi,1); - - // If this is a PUBLISH then the variable header starts with a topic - if (this.type == MESSAGE_TYPE.PUBLISH) - pos = writeString(this.payloadMessage.destinationName, destinationNameLength, byteStream, pos); - // If this is a CONNECT then the variable header contains the protocol name/version, flags and keepalive time - - else if (this.type == MESSAGE_TYPE.CONNECT) { - byteStream.set(MqttProtoIdentifier, pos); - pos += MqttProtoIdentifier.length; - var connectFlags = 0; - if (this.cleanSession) - connectFlags = 0x02; - if (this.willMessage != undefined ) { - connectFlags |= 0x04; - connectFlags |= (this.willMessage.qos<<3); - if (this.willMessage.retained) { - connectFlags |= 0x20; - } - } - if (this.userName != undefined) - connectFlags |= 0x80; - if (this.password != undefined) - connectFlags |= 0x40; - byteStream[pos++] = connectFlags; - pos = writeUint16 (this.keepAliveInterval, byteStream, pos); - } - - // Output the messageIdentifier - if there is one - if (this.messageIdentifier != undefined) - pos = writeUint16 (this.messageIdentifier, byteStream, pos); - - switch(this.type) { - case MESSAGE_TYPE.CONNECT: - pos = writeString(this.clientId, UTF8Length(this.clientId), byteStream, pos); - if (this.willMessage != undefined) { - pos = writeString(this.willMessage.destinationName, UTF8Length(this.willMessage.destinationName), byteStream, pos); - pos = writeUint16(willMessagePayloadBytes.byteLength, byteStream, pos); - byteStream.set(willMessagePayloadBytes, pos); - pos += willMessagePayloadBytes.byteLength; - - } - if (this.userName != undefined) - pos = writeString(this.userName, UTF8Length(this.userName), byteStream, pos); - if (this.password != undefined) - pos = writeString(this.password, UTF8Length(this.password), byteStream, pos); - break; - - case MESSAGE_TYPE.PUBLISH: - // PUBLISH has a text or binary payload, if text do not add a 2 byte length field, just the UTF characters. - byteStream.set(payloadBytes, pos); - - break; - -// case MESSAGE_TYPE.PUBREC: -// case MESSAGE_TYPE.PUBREL: -// case MESSAGE_TYPE.PUBCOMP: -// break; - - case MESSAGE_TYPE.SUBSCRIBE: - // SUBSCRIBE has a list of topic strings and request QoS - for (var i=0; i> 4; - var messageInfo = first &= 0x0f; - var pos = 1; - - - // Decode the remaining length (MBI format) - - var digit; - var remLength = 0; - var multiplier = 1; - do { - digit = input[pos++]; - remLength += ((digit & 0x7F) * multiplier); - multiplier *= 128; - } while ((digit & 0x80) != 0); - - var wireMessage = new WireMessage(type); - switch(type) { - case MESSAGE_TYPE.CONNACK: - wireMessage.topicNameCompressionResponse = input[pos++]; - wireMessage.returnCode = input[pos++]; - break; - - case MESSAGE_TYPE.PUBLISH: - var qos = (messageInfo >> 1) & 0x03; - - var len = readUint16(input, pos); - pos += 2; - var topicName = parseUTF8(input, pos, len); - pos += len; - // If QoS 1 or 2 there will be a messageIdentifier - if (qos > 0) { - wireMessage.messageIdentifier = readUint16(input, pos); - pos += 2; - } - - var message = new Messaging.Message(input.subarray(pos)); - if ((messageInfo & 0x01) == 0x01) - message.retained = true; - if ((messageInfo & 0x08) == 0x08) - message.duplicate = true; - message.qos = qos; - message.destinationName = topicName; - wireMessage.payloadMessage = message; - break; - - case MESSAGE_TYPE.PUBACK: - case MESSAGE_TYPE.PUBREC: - case MESSAGE_TYPE.PUBREL: - case MESSAGE_TYPE.PUBCOMP: - case MESSAGE_TYPE.UNSUBACK: - wireMessage.messageIdentifier = readUint16(input, pos); - break; - - case MESSAGE_TYPE.SUBACK: - wireMessage.messageIdentifier = readUint16(input, pos); - pos += 2; - wireMessage.grantedQos = input.subarray(pos); - break; - - default: - ; - } - - return wireMessage; - } - - function writeUint16(input, buffer, offset) { - buffer[offset++] = input >> 8; //MSB - buffer[offset++] = input % 256; //LSB - return offset; - } - - function writeString(input, utf8Length, buffer, offset) { - offset = writeUint16(utf8Length, buffer, offset); - stringToUTF8(input, buffer, offset); - return offset + utf8Length; - } - - function readUint16(buffer, offset) { - return 256*buffer[offset] + buffer[offset+1]; - } - - /** - * Encodes an MQTT Multi-Byte Integer - * @private - */ - function encodeMBI(number) { - var output = new Array(1); - var numBytes = 0; - - do { - var digit = number % 128; - number = number >> 7; - if (number > 0) { - digit |= 0x80; - } - output[numBytes++] = digit; - } while ( (number > 0) && (numBytes<4) ); - - return output; - } - - /** - * Takes a String and calculates its length in bytes when encoded in UTF8. - * @private - */ - function UTF8Length(input) { - var output = 0; - for (var i = 0; i + * This programming interface lets a JavaScript client application use the MQTT V3.1 protocol to + * connect to an MQTT-supporting messaging server. + * + * The function supported includes: + *

    + *
  1. Connecting to and disconnecting from a server. The server is identified by its host name and port number. + *
  2. Specifying options that relate to the communications link with the server, + * for example the frequency of keep-alive heartbeats, and whether SSL/TLS is required. + *
  3. Subscribing to and receiving messages from MQTT Topics. + *
  4. Publishing messages to MQTT Topics. + *
+ *

+ *

The API consists of two main objects:

+ * The Messaging.Client object. This contains methods that provide the functionality of the API, + * including provision of callbacks that notify the application when a message arrives from or is delivered to the messaging server, + * or when the status of its connection to the messaging server changes. + *

+ * The Messaging.Message object. This encapsulates the payload of the message along with various attributes + * associated with its delivery, in particular the destination to which it has been (or is about to be) sent. + *

+ * The programming interface validates parameters passed to it, and will throw an Error containing an error message + * intended for developer use, if it detects an error with any parameter. + *

+ * Example: + * + *

+client = new Messaging.Client(location.hostname, Number(location.port), "clientId");
+client.onConnectionLost = onConnectionLost;
+client.onMessageArrived = onMessageArrived;
+client.connect({onSuccess:onConnect});
+
+function onConnect() {
+  // Once a connection has been made, make a subscription and send a message.
+  console.log("onConnect");
+  client.subscribe("/World");
+  message = new Messaging.Message("Hello");
+  message.destinationName = "/World";
+  client.send(message); 
+};
+function onConnectionLost(responseObject) {
+  if (responseObject.errorCode !== 0)
+    console.log("onConnectionLost:"+responseObject.errorMessage);
+};
+function onMessageArrived(message) {
+  console.log("onMessageArrived:"+message.payloadString);
+  client.disconnect(); 
+};	
+ * 
+ *

+ * Other programming languages, + * Java, + * C. + */ +Messaging = (function (global) { + + // Private variables below, these are only visible inside the function closure + // which is used to define the module. + + var version = "0.0.0.0"; + var buildLevel = "@BUILDLEVEL@"; + + /** + * Unique message type identifiers, with associated + * associated integer values. + * @private + */ + var MESSAGE_TYPE = { + CONNECT: 1, + CONNACK: 2, + PUBLISH: 3, + PUBACK: 4, + PUBREC: 5, + PUBREL: 6, + PUBCOMP: 7, + SUBSCRIBE: 8, + SUBACK: 9, + UNSUBSCRIBE: 10, + UNSUBACK: 11, + PINGREQ: 12, + PINGRESP: 13, + DISCONNECT: 14 + }; + + // Collection of utility methods used to simplify module code + // and promote the DRY pattern. + + /** + * Validate an object's parameter names to ensure they + * match a list of expected variables name for this option + * type. Used to ensure option object passed into the API don't + * contain erroneous parameters. + * @param {Object} obj User options object + * @param {key:type, key2:type, ...} valid keys and types that may exist in obj. + * @throws {Error} Invalid option parameter found. + * @private + */ + var validate = function(obj, keys) { + for(key in obj) { + if (obj.hasOwnProperty(key)) { + if (keys.hasOwnProperty(key)) { + if (typeof obj[key] !== keys[key]) + throw new Error(format(ERROR.INVALID_TYPE, [typeof obj[key], key])); + } else { + var errorStr = "Unknown property, " + key + ". Valid properties are:"; + for (key in keys) + if (keys.hasOwnProperty(key)) + errorStr = errorStr+" "+key; + throw new Error(errorStr); + } + } + } + }; + + /** + * Return a new function which runs the user function bound + * to a fixed scope. + * @param {function} User function + * @param {object} Function scope + * @return {function} User function bound to another scope + * @private + */ + var scope = function (f, scope) { + return function () { + return f.apply(scope, arguments); + }; + }; + + /** + * Unique message type identifiers, with associated + * associated integer values. + * @private + */ + var ERROR = { + OK: {code:0, text:"AMQJSC0000I OK."}, + CONNECT_TIMEOUT: {code:1, text:"AMQJSC0001E Connect timed out."}, + SUBSCRIBE_TIMEOUT: {code:2, text:"AMQJS0002E Subscribe timed out."}, + UNSUBSCRIBE_TIMEOUT: {code:3, text:"AMQJS0003E Unsubscribe timed out."}, + PING_TIMEOUT: {code:4, text:"AMQJS0004E Ping timed out."}, + INTERNAL_ERROR: {code:5, text:"AMQJS0005E Internal error."}, + CONNACK_RETURNCODE: {code:6, text:"AMQJS0006E Bad Connack return code:{0} {1}."}, + SOCKET_ERROR: {code:7, text:"AMQJS0007E Socket error:{0}."}, + SOCKET_CLOSE: {code:8, text:"AMQJS0008I Socket closed."}, + MALFORMED_UTF: {code:9, text:"AMQJS0009E Malformed UTF data:{0} {1} {2}."}, + UNSUPPORTED: {code:10, text:"AMQJS0010E {0} is not supported by this browser."}, + INVALID_STATE: {code:11, text:"AMQJS0011E Invalid state {0}."}, + INVALID_TYPE: {code:12, text:"AMQJS0012E Invalid type {0} for {1}."}, + INVALID_ARGUMENT: {code:13, text:"AMQJS0013E Invalid argument {0} for {1}."}, + UNSUPPORTED_OPERATION: {code:14, text:"AMQJS0014E Unsupported operation."}, + INVALID_STORED_DATA: {code:15, text:"AMQJS0015E Invalid data in local storage key={0} value={1}."}, + INVALID_MQTT_MESSAGE_TYPE: {code:16, text:"AMQJS0016E Invalid MQTT message type {0}."}, + MALFORMED_UNICODE: {code:17, text:"AMQJS0017E Malformed Unicode string:{0} {1}."}, + }; + + /** CONNACK RC Meaning. */ + var CONNACK_RC = { + 0:"Connection Accepted", + 1:"Connection Refused: unacceptable protocol version", + 2:"Connection Refused: identifier rejected", + 3:"Connection Refused: server unavailable", + 4:"Connection Refused: bad user name or password", + 5:"Connection Refused: not authorized" + }; + + /** + * Format an error message text. + * @private + * @param {error} ERROR.KEY value above. + * @param {substitutions} [array] substituted into the text. + * @return the text with the substitutions made. + */ + var format = function(error, substitutions) { + var text = error.text; + if (substitutions) { + for (var i=0; i 0) { + var part1 = text.substring(0,start); + var part2 = text.substring(start+field.length); + text = part1+substitutions[i]+part2; + } + } + } + return text; + }; + + //MQTT protocol and version 6 M Q I s d p 3 + var MqttProtoIdentifier = [0x00,0x06,0x4d,0x51,0x49,0x73,0x64,0x70,0x03]; + + /** + * @ignore + * Construct an MQTT wire protocol message. + * @param type MQTT packet type. + * @param options optional wire message attributes. + * + * Optional properties + * + * messageIdentifier: message ID in the range [0..65535] + * payloadMessage: Application Message - PUBLISH only + * connectStrings: array of 0 or more Strings to be put into the CONNECT payload + * topics: array of strings (SUBSCRIBE, UNSUBSCRIBE) + * requestQoS: array of QoS values [0..2] + * + * "Flag" properties + * cleanSession: true if present / false if absent (CONNECT) + * willMessage: true if present / false if absent (CONNECT) + * isRetained: true if present / false if absent (CONNECT) + * userName: true if present / false if absent (CONNECT) + * password: true if present / false if absent (CONNECT) + * keepAliveInterval: integer [0..65535] (CONNECT) + * + * @private + */ + var WireMessage = function (type, options) { + this.type = type; + for(name in options) { + if (options.hasOwnProperty(name)) { + this[name] = options[name]; + } + } + }; + + WireMessage.prototype.encode = function() { + // Compute the first byte of the fixed header + var first = ((this.type & 0x0f) << 4); + + /* + * Now calculate the length of the variable header + payload by adding up the lengths + * of all the component parts + */ + + remLength = 0; + topicStrLength = new Array(); + + // if the message contains a messageIdentifier then we need two bytes for that + if (this.messageIdentifier != undefined) + remLength += 2; + + switch(this.type) { + // If this a Connect then we need to include 12 bytes for its header + case MESSAGE_TYPE.CONNECT: + remLength += MqttProtoIdentifier.length + 3; + remLength += UTF8Length(this.clientId) + 2; + if (this.willMessage != undefined) { + remLength += UTF8Length(this.willMessage.destinationName) + 2; + // Will message is always a string, sent as UTF-8 characters with a preceding length. + var willMessagePayloadBytes = this.willMessage.payloadBytes; + if (!(willMessagePayloadBytes instanceof Uint8Array)) + willMessagePayloadBytes = new Uint8Array(payloadBytes); + remLength += willMessagePayloadBytes.byteLength +2; + } + if (this.userName != undefined) + remLength += UTF8Length(this.userName) + 2; + if (this.password != undefined) + remLength += UTF8Length(this.password) + 2; + break; + + // Subscribe, Unsubscribe can both contain topic strings + case MESSAGE_TYPE.SUBSCRIBE: + first |= 0x02; // Qos = 1; + for ( var i = 0; i < this.topics.length; i++) { + topicStrLength[i] = UTF8Length(this.topics[i]); + remLength += topicStrLength[i] + 2; + } + remLength += this.requestedQos.length; // 1 byte for each topic's Qos + // QoS on Subscribe only + break; + + case MESSAGE_TYPE.UNSUBSCRIBE: + first |= 0x02; // Qos = 1; + for ( var i = 0; i < this.topics.length; i++) { + topicStrLength[i] = UTF8Length(this.topics[i]); + remLength += topicStrLength[i] + 2; + } + break; + + case MESSAGE_TYPE.PUBLISH: + if (this.payloadMessage.duplicate) first |= 0x08; + first = first |= (this.payloadMessage.qos << 1); + if (this.payloadMessage.retained) first |= 0x01; + destinationNameLength = UTF8Length(this.payloadMessage.destinationName); + remLength += destinationNameLength + 2; + var payloadBytes = this.payloadMessage.payloadBytes; + remLength += payloadBytes.byteLength; + if (payloadBytes instanceof ArrayBuffer) + payloadBytes = new Uint8Array(payloadBytes); + else if (!(payloadBytes instanceof Uint8Array)) + payloadBytes = new Uint8Array(payloadBytes.buffer); + break; + + case MESSAGE_TYPE.DISCONNECT: + break; + + default: + ; + } + + // Now we can allocate a buffer for the message + + var mbi = encodeMBI(remLength); // Convert the length to MQTT MBI format + var pos = mbi.length + 1; // Offset of start of variable header + var buffer = new ArrayBuffer(remLength + pos); + var byteStream = new Uint8Array(buffer); // view it as a sequence of bytes + + //Write the fixed header into the buffer + byteStream[0] = first; + byteStream.set(mbi,1); + + // If this is a PUBLISH then the variable header starts with a topic + if (this.type == MESSAGE_TYPE.PUBLISH) + pos = writeString(this.payloadMessage.destinationName, destinationNameLength, byteStream, pos); + // If this is a CONNECT then the variable header contains the protocol name/version, flags and keepalive time + + else if (this.type == MESSAGE_TYPE.CONNECT) { + byteStream.set(MqttProtoIdentifier, pos); + pos += MqttProtoIdentifier.length; + var connectFlags = 0; + if (this.cleanSession) + connectFlags = 0x02; + if (this.willMessage != undefined ) { + connectFlags |= 0x04; + connectFlags |= (this.willMessage.qos<<3); + if (this.willMessage.retained) { + connectFlags |= 0x20; + } + } + if (this.userName != undefined) + connectFlags |= 0x80; + if (this.password != undefined) + connectFlags |= 0x40; + byteStream[pos++] = connectFlags; + pos = writeUint16 (this.keepAliveInterval, byteStream, pos); + } + + // Output the messageIdentifier - if there is one + if (this.messageIdentifier != undefined) + pos = writeUint16 (this.messageIdentifier, byteStream, pos); + + switch(this.type) { + case MESSAGE_TYPE.CONNECT: + pos = writeString(this.clientId, UTF8Length(this.clientId), byteStream, pos); + if (this.willMessage != undefined) { + pos = writeString(this.willMessage.destinationName, UTF8Length(this.willMessage.destinationName), byteStream, pos); + pos = writeUint16(willMessagePayloadBytes.byteLength, byteStream, pos); + byteStream.set(willMessagePayloadBytes, pos); + pos += willMessagePayloadBytes.byteLength; + + } + if (this.userName != undefined) + pos = writeString(this.userName, UTF8Length(this.userName), byteStream, pos); + if (this.password != undefined) + pos = writeString(this.password, UTF8Length(this.password), byteStream, pos); + break; + + case MESSAGE_TYPE.PUBLISH: + // PUBLISH has a text or binary payload, if text do not add a 2 byte length field, just the UTF characters. + byteStream.set(payloadBytes, pos); + + break; + +// case MESSAGE_TYPE.PUBREC: +// case MESSAGE_TYPE.PUBREL: +// case MESSAGE_TYPE.PUBCOMP: +// break; + + case MESSAGE_TYPE.SUBSCRIBE: + // SUBSCRIBE has a list of topic strings and request QoS + for (var i=0; i> 4; + var messageInfo = first &= 0x0f; + var pos = 1; + + + // Decode the remaining length (MBI format) + + var digit; + var remLength = 0; + var multiplier = 1; + do { + digit = input[pos++]; + remLength += ((digit & 0x7F) * multiplier); + multiplier *= 128; + } while ((digit & 0x80) != 0); + + var wireMessage = new WireMessage(type); + switch(type) { + case MESSAGE_TYPE.CONNACK: + wireMessage.topicNameCompressionResponse = input[pos++]; + wireMessage.returnCode = input[pos++]; + break; + + case MESSAGE_TYPE.PUBLISH: + var qos = (messageInfo >> 1) & 0x03; + + var len = readUint16(input, pos); + pos += 2; + var topicName = parseUTF8(input, pos, len); + pos += len; + // If QoS 1 or 2 there will be a messageIdentifier + if (qos > 0) { + wireMessage.messageIdentifier = readUint16(input, pos); + pos += 2; + } + + var message = new Messaging.Message(input.subarray(pos)); + if ((messageInfo & 0x01) == 0x01) + message.retained = true; + if ((messageInfo & 0x08) == 0x08) + message.duplicate = true; + message.qos = qos; + message.destinationName = topicName; + wireMessage.payloadMessage = message; + break; + + case MESSAGE_TYPE.PUBACK: + case MESSAGE_TYPE.PUBREC: + case MESSAGE_TYPE.PUBREL: + case MESSAGE_TYPE.PUBCOMP: + case MESSAGE_TYPE.UNSUBACK: + wireMessage.messageIdentifier = readUint16(input, pos); + break; + + case MESSAGE_TYPE.SUBACK: + wireMessage.messageIdentifier = readUint16(input, pos); + pos += 2; + wireMessage.grantedQos = input.subarray(pos); + break; + + default: + ; + } + + return wireMessage; + } + + function writeUint16(input, buffer, offset) { + buffer[offset++] = input >> 8; //MSB + buffer[offset++] = input % 256; //LSB + return offset; + } + + function writeString(input, utf8Length, buffer, offset) { + offset = writeUint16(utf8Length, buffer, offset); + stringToUTF8(input, buffer, offset); + return offset + utf8Length; + } + + function readUint16(buffer, offset) { + return 256*buffer[offset] + buffer[offset+1]; + } + + /** + * Encodes an MQTT Multi-Byte Integer + * @private + */ + function encodeMBI(number) { + var output = new Array(1); + var numBytes = 0; + + do { + var digit = number % 128; + number = number >> 7; + if (number > 0) { + digit |= 0x80; + } + output[numBytes++] = digit; + } while ( (number > 0) && (numBytes<4) ); + + return output; + } + + /** + * Takes a String and calculates its length in bytes when encoded in UTF8. + * @private + */ + function UTF8Length(input) { + var output = 0; + for (var i = 0; i 0x7FF) { @@ -532,90 +532,90 @@ Messaging = (function (global) { { i++; output++; - } + } output +=3; - } - else if (charCode > 0x7F) - output +=2; - else - output++; - } - return output; - } - - /** - * Takes a String and writes it into an array as UTF8 encoded bytes. - * @private - */ - function stringToUTF8(input, output, start) { - var pos = start; - for (var i = 0; i>6 & 0x1F | 0xC0; - output[pos++] = charCode & 0x3F | 0x80; - } else if (charCode <= 0xFFFF) { - output[pos++] = charCode>>12 & 0x0F | 0xE0; - output[pos++] = charCode>>6 & 0x3F | 0x80; - output[pos++] = charCode & 0x3F | 0x80; - } else { - output[pos++] = charCode>>18 & 0x07 | 0xF0; - output[pos++] = charCode>>12 & 0x3F | 0x80; - output[pos++] = charCode>>6 & 0x3F | 0x80; - output[pos++] = charCode & 0x3F | 0x80; - }; - } - return output; - } - - function parseUTF8(input, offset, length) { - var output = ""; - var utf16; - var pos = offset; - - while (pos < offset+length) - { - var byte1 = input[pos++]; - if (byte1 < 128) - utf16 = byte1; - else - { - var byte2 = input[pos++]-128; - if (byte2 < 0) - throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16),""])); - if (byte1 < 0xE0) // 2 byte character - utf16 = 64*(byte1-0xC0) + byte2; - else - { - var byte3 = input[pos++]-128; - if (byte3 < 0) - throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16)])); - if (byte1 < 0xF0) // 3 byte character + } + else if (charCode > 0x7F) + output +=2; + else + output++; + } + return output; + } + + /** + * Takes a String and writes it into an array as UTF8 encoded bytes. + * @private + */ + function stringToUTF8(input, output, start) { + var pos = start; + for (var i = 0; i>6 & 0x1F | 0xC0; + output[pos++] = charCode & 0x3F | 0x80; + } else if (charCode <= 0xFFFF) { + output[pos++] = charCode>>12 & 0x0F | 0xE0; + output[pos++] = charCode>>6 & 0x3F | 0x80; + output[pos++] = charCode & 0x3F | 0x80; + } else { + output[pos++] = charCode>>18 & 0x07 | 0xF0; + output[pos++] = charCode>>12 & 0x3F | 0x80; + output[pos++] = charCode>>6 & 0x3F | 0x80; + output[pos++] = charCode & 0x3F | 0x80; + }; + } + return output; + } + + function parseUTF8(input, offset, length) { + var output = ""; + var utf16; + var pos = offset; + + while (pos < offset+length) + { + var byte1 = input[pos++]; + if (byte1 < 128) + utf16 = byte1; + else + { + var byte2 = input[pos++]-128; + if (byte2 < 0) + throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16),""])); + if (byte1 < 0xE0) // 2 byte character + utf16 = 64*(byte1-0xC0) + byte2; + else + { + var byte3 = input[pos++]-128; + if (byte3 < 0) + throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16)])); + if (byte1 < 0xF0) // 3 byte character utf16 = 4096*(byte1-0xE0) + 64*byte2 + byte3; else { var byte4 = input[pos++]-128; - if (byte4 < 0) + if (byte4 < 0) throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)])); if (byte1 < 0xF8) // 4 byte character - utf16 = 262144*(byte1-0xF0) + 4096*byte2 + 64*byte3 + byte4; - else // longer encodings are not supported + utf16 = 262144*(byte1-0xF0) + 4096*byte2 + 64*byte3 + byte4; + else // longer encodings are not supported throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)])); - } - } + } + } } if (utf16 > 0xFFFF) // 4 byte character - express as a surrogate pair @@ -623,1254 +623,1254 @@ Messaging = (function (global) { utf16 -= 0x10000; output += String.fromCharCode(0xD800 + (utf16 >> 10)); // lead character utf16 = 0xDC00 + (utf16 & 0x3FF); // trail character - } - output += String.fromCharCode(utf16); - } - return output; - } - - /** @ignore Repeat keepalive requests, monitor responses.*/ - var Pinger = function(client, window, keepAliveInterval) { - this._client = client; - this._window = window; - this._keepAliveInterval = keepAliveInterval*1000; - this.isReset = false; - - var pingReq = new WireMessage(MESSAGE_TYPE.PINGREQ).encode(); - - var doTimeout = function (pinger) { - return function () { - return doPing.apply(pinger); - }; - }; - - /** @ignore */ - var doPing = function() { - if (!this.isReset) { - this._client._trace("Pinger.doPing", "Timed out"); - this._client._disconnected( ERROR.PING_TIMEOUT.code , format(ERROR.PING_TIMEOUT)); - } else { - this.isReset = false; - this._client._trace("Pinger.doPing", "send PINGREQ"); - this._client.socket.send(pingReq); - this.timeout = this._window.setTimeout(doTimeout(this), this._keepAliveInterval); - } - } - - this.reset = function() { - this.isReset = true; - this._window.clearTimeout(this.timeout); - if (this._keepAliveInterval > 0) - this.timeout = setTimeout(doTimeout(this), this._keepAliveInterval); - } - - this.cancel = function() { - this._window.clearTimeout(this.timeout); - } - }; - - /** @ignore Monitor request completion. */ - var Timeout = function(client, window, timeoutSeconds, action, args) { - this._window = window; - if (!timeoutSeconds) - timeoutSeconds = 30; - - var doTimeout = function (action, client, args) { - return function () { - return action.apply(client, args); - }; - }; - this.timeout = setTimeout(doTimeout(action, client, args), timeoutSeconds * 1000); - - this.cancel = function() { - this._window.clearTimeout(this.timeout); - } - }; - - /* - * Internal implementation of the Websockets MQTT V3.1 client. - * - * @name Messaging.ClientImpl @constructor - * @param {String} host the DNS nameof the webSocket host. - * @param {Number} port the port number for that host. - * @param {String} clientId the MQ client identifier. - */ - var ClientImpl = function (host, port, clientId) { - // Check dependencies are satisfied in this browser. - if (!("WebSocket" in global && global["WebSocket"] !== null)) { - throw new Error(format(ERROR.UNSUPPORTED, ["WebSocket"])); - } - if (!("localStorage" in global && global["localStorage"] !== null)) { - throw new Error(format(ERROR.UNSUPPORTED, ["localStorage"])); - } - if (!("ArrayBuffer" in global && global["ArrayBuffer"] !== null)) { - throw new Error(format(ERROR.UNSUPPORTED, ["ArrayBuffer"])); - } - - this._trace("Messaging.Client", host, port, clientId); - - this.host = host; - this.port = port; - this.clientId = clientId; - - // Local storagekeys are qualified with the following string. - this._localKey=host+":"+port+":"+clientId+":"; - - // Create private instance-only message queue - // Internal queue of messages to be sent, in sending order. - this._msg_queue = []; - - // Messages we have sent and are expecting a response for, indexed by their respective message ids. - this._sentMessages = {}; - - // Messages we have received and acknowleged and are expecting a confirm message for - // indexed by their respective message ids. - this._receivedMessages = {}; - - // Internal list of callbacks to be executed when messages - // have been successfully sent over web socket, e.g. disconnect - // when it doesn't have to wait for ACK, just message is dispatched. - this._notify_msg_sent = {}; - - // Unique identifier for SEND messages, incrementing - // counter as messages are sent. - this._message_identifier = 1; - - // Used to determine the transmission sequence of stored sent messages. - this._sequence = 0; - - - // Load the local state, if any, from the saved version, only restore state relevant to this client. - for(key in localStorage) - if ( key.indexOf("Sent:"+this._localKey) == 0 - || key.indexOf("Received:"+this._localKey) == 0) - this.restore(key); - }; - - // Messaging Client public instance members. - ClientImpl.prototype.host; - ClientImpl.prototype.port; - ClientImpl.prototype.clientId; - - // Messaging Client private instance members. - ClientImpl.prototype.socket; - /* true once we have received an acknowledgement to a CONNECT packet. */ - ClientImpl.prototype.connected = false; - /* The largest message identifier allowed, may not be larger than 2**16 but - * if set smaller reduces the maximum number of outbound messages allowed. - */ - ClientImpl.prototype.maxMessageIdentifier = 65536; - ClientImpl.prototype.connectOptions; - ClientImpl.prototype.hostIndex; - ClientImpl.prototype.onConnectionLost; - ClientImpl.prototype.onMessageDelivered; - ClientImpl.prototype.onMessageArrived; - ClientImpl.prototype._msg_queue = null; - ClientImpl.prototype._connectTimeout; - /* The sendPinger monitors how long we allow before we send data to prove to the server that we are alive. */ - ClientImpl.prototype.sendPinger = null; - /* The receivePinger monitors how long we allow before we require evidence that the server is alive. */ - ClientImpl.prototype.receivePinger = null; - - ClientImpl.prototype._traceBuffer = null; - ClientImpl.prototype._MAX_TRACE_ENTRIES = 100; - - ClientImpl.prototype.connect = function (connectOptions) { - var connectOptionsMasked = this._traceMask(connectOptions, "password"); - this._trace("Client.connect", connectOptionsMasked, this.socket, this.connected); - - if (this.connected) - throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); - if (this.socket) - throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); - - this.connectOptions = connectOptions; - - if (connectOptions.hosts) { - this.hostIndex = 0; - this._doConnect(connectOptions.hosts[0], connectOptions.ports[0]); - } else { - this._doConnect(this.host, this.port); - } - - }; - - ClientImpl.prototype.subscribe = function (filter, subscribeOptions) { - this._trace("Client.subscribe", filter, subscribeOptions); - - if (!this.connected) - throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); - - var wireMessage = new WireMessage(MESSAGE_TYPE.SUBSCRIBE); - wireMessage.topics=[filter]; - if (subscribeOptions.qos != undefined) - wireMessage.requestedQos = [subscribeOptions.qos]; - else - wireMessage.requestedQos = [0]; - - if (subscribeOptions.onSuccess) { - wireMessage.callback = function() {subscribeOptions.onSuccess({invocationContext:subscribeOptions.invocationContext});}; - } - if (subscribeOptions.timeout) { - wireMessage.timeOut = new Timeout(this, window, subscribeOptions.timeout, subscribeOptions.onFailure - , [{invocationContext:subscribeOptions.invocationContext, - errorCode:ERROR.SUBSCRIBE_TIMEOUT.code, - errorMessage:format(ERROR.SUBSCRIBE_TIMEOUT)}]); - } - - // All subscriptions return a SUBACK. - this._requires_ack(wireMessage); - this._schedule_message(wireMessage); - }; - - /** @ignore */ - ClientImpl.prototype.unsubscribe = function(filter, unsubscribeOptions) { - this._trace("Client.unsubscribe", filter, unsubscribeOptions); - - if (!this.connected) - throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); - - var wireMessage = new WireMessage(MESSAGE_TYPE.UNSUBSCRIBE); - wireMessage.topics = [filter]; - - if (unsubscribeOptions.onSuccess) { - wireMessage.callback = function() {unsubscribeOptions.onSuccess({invocationContext:unsubscribeOptions.invocationContext});}; - } - if (unsubscribeOptions.timeout) { - wireMessage.timeOut = new Timeout(this, window, unsubscribeOptions.timeout, unsubscribeOptions.onFailure - , [{invocationContext:unsubscribeOptions.invocationContext, - errorCode:ERROR.UNSUBSCRIBE_TIMEOUT.code, - errorMessage:format(ERROR.UNSUBSCRIBE_TIMEOUT)}]); - } - - // All unsubscribes return a SUBACK. - this._requires_ack(wireMessage); - this._schedule_message(wireMessage); - }; - - ClientImpl.prototype.send = function (message) { - this._trace("Client.send", message); - - if (!this.connected) - throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); - - wireMessage = new WireMessage(MESSAGE_TYPE.PUBLISH); - wireMessage.payloadMessage = message; - - if (message.qos > 0) - this._requires_ack(wireMessage); - else if (this.onMessageDelivered) - this._notify_msg_sent[wireMessage] = this.onMessageDelivered(wireMessage.payloadMessage); - this._schedule_message(wireMessage); - }; - - ClientImpl.prototype.disconnect = function () { - this._trace("Client.disconnect"); - - if (!this.socket) - throw new Error(format(ERROR.INVALID_STATE, ["not connecting or connected"])); - - wireMessage = new WireMessage(MESSAGE_TYPE.DISCONNECT); - - // Run the disconnected call back as soon as the message has been sent, - // in case of a failure later on in the disconnect processing. - // as a consequence, the _disconected call back may be run several times. - this._notify_msg_sent[wireMessage] = scope(this._disconnected, this); - - this._schedule_message(wireMessage); - }; - - ClientImpl.prototype.getTraceLog = function () { - if ( this._traceBuffer !== null ) { - this._trace("Client.getTraceLog", new Date()); - this._trace("Client.getTraceLog in flight messages", this._sentMessages.length); - for (key in this._sentMessages) - this._trace("_sentMessages ",key, this._sentMessages[key]); - for (key in this._receivedMessages) - this._trace("_receivedMessages ",key, this._receivedMessages[key]); - - return this._traceBuffer; - } - }; - - ClientImpl.prototype.startTrace = function () { - if ( this._traceBuffer === null ) { - this._traceBuffer = []; - } - this._trace("Client.startTrace", new Date(), version); - }; - - ClientImpl.prototype.stopTrace = function () { - delete this._traceBuffer; - }; - - ClientImpl.prototype._doConnect = function (host, port) { - // When the socket is open, this client will send the CONNECT WireMessage using the saved parameters. - if (this.connectOptions.useSSL) - wsurl = ["wss://", host, ":", port, "/mqtt"].join(""); - else - wsurl = ["ws://", host, ":", port, "/mqtt"].join(""); - this.connected = false; - this.socket = new WebSocket(wsurl, 'mqttv3.1'); - this.socket.binaryType = 'arraybuffer'; - this.socket.onopen = scope(this._on_socket_open, this); - this.socket.onmessage = scope(this._on_socket_message, this); - this.socket.onerror = scope(this._on_socket_error, this); - this.socket.onclose = scope(this._on_socket_close, this); - - this.sendPinger = new Pinger(this, window, this.connectOptions.keepAliveInterval); - this.receivePinger = new Pinger(this, window, this.connectOptions.keepAliveInterval); - - this._connectTimeout = new Timeout(this, window, this.connectOptions.timeout, this._disconnected, [ERROR.CONNECT_TIMEOUT.code, format(ERROR.CONNECT_TIMEOUT)]); - }; - - - // Schedule a new message to be sent over the WebSockets - // connection. CONNECT messages cause WebSocket connection - // to be started. All other messages are queued internally - // until this has happened. When WS connection starts, process - // all outstanding messages. - ClientImpl.prototype._schedule_message = function (message) { - this._msg_queue.push(message); - // Process outstanding messages in the queue if we have an open socket, and have received CONNACK. - if (this.connected) { - this._process_queue(); - } - }; - - ClientImpl.prototype.store = function(prefix, wireMessage) { - storedMessage = {type:wireMessage.type, messageIdentifier:wireMessage.messageIdentifier, version:1}; - - switch(wireMessage.type) { - case MESSAGE_TYPE.PUBLISH: - if(wireMessage.pubRecReceived) - storedMessage.pubRecReceived = true; - - // Convert the payload to a hex string. - storedMessage.payloadMessage = {}; - var hex = ""; - var messageBytes = wireMessage.payloadMessage.payloadBytes; - for (var i=0; i= 2) { - var x = parseInt(hex.substring(0, 2), 16); - hex = hex.substring(2, hex.length); - byteStream[i++] = x; - } - var payloadMessage = new Messaging.Message(byteStream); - - payloadMessage.qos = storedMessage.payloadMessage.qos; - payloadMessage.destinationName = storedMessage.payloadMessage.destinationName; - if (storedMessage.payloadMessage.duplicate) - payloadMessage.duplicate = true; - if (storedMessage.payloadMessage.retained) - payloadMessage.retained = true; - wireMessage.payloadMessage = payloadMessage; - - break; - - default: - throw Error(format(ERROR.INVALID_STORED_DATA, [key, value])); - } - - if (key.indexOf("Sent:"+this._localKey) == 0) { - this._sentMessages[wireMessage.messageIdentifier] = wireMessage; - } else if (key.indexOf("Received:"+this._localKey) == 0) { - this._receivedMessages[wireMessage.messageIdentifier] = wireMessage; - } - }; - - ClientImpl.prototype._process_queue = function () { - var message = null; - // Process messages in order they were added - var fifo = this._msg_queue.reverse(); - - // Send all queued messages down socket connection - while ((message = fifo.pop())) { - this._socket_send(message); - // Notify listeners that message was successfully sent - if (this._notify_msg_sent[message]) { - this._notify_msg_sent[message](); - delete this._notify_msg_sent[message]; - } - } - }; - - /** - * @ignore - * Expect an ACK response for this message. Add message to the set of in progress - * messages and set an unused identifier in this message. - */ - ClientImpl.prototype._requires_ack = function (wireMessage) { - var messageCount = Object.keys(this._sentMessages).length; - if (messageCount > this.maxMessageIdentifier) - throw Error ("Too many messages:"+messageCount); - - while(this._sentMessages[this._message_identifier] !== undefined) { - this._message_identifier++; - } - wireMessage.messageIdentifier = this._message_identifier; - this._sentMessages[wireMessage.messageIdentifier] = wireMessage; - if (wireMessage.type === MESSAGE_TYPE.PUBLISH) { - this.store("Sent:", wireMessage); - } - if (this._message_identifier === this.maxMessagIdentifier) { - this._message_identifier = 1; - } - }; - - /** - * @ignore - * Called when the underlying websocket has been opened. - */ - ClientImpl.prototype._on_socket_open = function () { - // Create the CONNECT message object. - var wireMessage = new WireMessage(MESSAGE_TYPE.CONNECT, this.connectOptions); - wireMessage.clientId = this.clientId; - this._socket_send(wireMessage); - }; - - /** - * @ignore - * Called when the underlying websocket has received a complete packet. - */ - ClientImpl.prototype._on_socket_message = function (event) { - this._trace("Client._on_socket_message", event.data); - - // Reset the receive ping timer, we now have evidence the server is alive. - this.receivePinger.reset(); - var byteArray = new Uint8Array(event.data); - try { - var wireMessage = decodeMessage(byteArray); - } catch (error) { - this._disconnected(ERROR.INTERNAL_ERROR.code , format(ERROR.INTERNAL_ERROR, [error.message])); - return; - } - this._trace("Client._on_socket_message", wireMessage); - - switch(wireMessage.type) { - case MESSAGE_TYPE.CONNACK: - this._connectTimeout.cancel(); - - // If we have started using clean session then clear up the local state. - if (this.connectOptions.cleanSession) { - for (key in this._sentMessages) { - var sentMessage = this._sentMessages[key]; - localStorage.removeItem("Sent:"+this._localKey+sentMessage.messageIdentifier); - } - this._sentMessages = {}; - - for (key in this._receivedMessages) { - var receivedMessage = this._receivedMessages[key]; - localStorage.removeItem("Received:"+this._localKey+receivedMessage.messageIdentifier); - } - this._receivedMessages = {}; - } - // Client connected and ready for business. - if (wireMessage.returnCode === 0) { - this.connected = true; - // Jump to the end of the list of hosts and stop looking for a good host. - if (this.connectOptions.hosts) - this.hostIndex = this.connectOptions.hosts.length; - } else { - this._disconnected(ERROR.CONNACK_RETURNCODE.code , format(ERROR.CONNACK_RETURNCODE, [wireMessage.returnCode, CONNACK_RC[wireMessage.returnCode]])); - break; - } - - // Resend messages. - var sequencedMessages = new Array(); - for (var msgId in this._sentMessages) { - if (this._sentMessages.hasOwnProperty(msgId)) - sequencedMessages.push(this._sentMessages[msgId]); - } - - // Sort sentMessages into the original sent order. - var sequencedMessages = sequencedMessages.sort(function(a,b) {return a.sequence - b.sequence;} ); - for (var i=0, len=sequencedMessages.length; i - * Other programming languages, - * Java, - * C. - *

- * Most applications will create just one Client object and then call its connect() method, - * however applications can create more than one Client object if they wish. - * In this case the combination of host, port and clientId attributes must be different for each Client object. - *

- * The send, subscribe and unsubscribe methods are implemented as asynchronous JavaScript methods - * (even though the underlying protocol exchange might be synchronous in nature). - * This means they signal their completion by calling back to the application, - * via Success or Failure callback functions provided by the application on the method in question. - * Such callbacks are called at most once per method invocation and do not persist beyond the lifetime - * of the script that made the invocation. - *

- * In contrast there are some callback functions most notably onMessageArrived - * that are defined on the Messaging.Client object. - * These may get called multiple times, and aren't directly related to specific method invocations made by the client. - * - * @name Messaging.Client - * - * @constructor - * Creates a Messaging.Client object that can be used to communicate with a Messaging server. - * - * @param {string} host the address of the messaging server, as a DNS name or dotted decimal IP address. - * @param {number} port the port number in the host to connect to. - * @param {string} clientId the Messaging client identifier, between 1 and 23 characters in length. - * - * @property {string} host read only the server's DNS hostname or dotted decimal IP address. - * @property {number} port read only the server's port. - * @property {string} clientId read only used when connecting to the server. - * @property {function} onConnectionLost called when a connection has been lost, - * after a connect() method has succeeded. - * Establish the call back used when a connection has been lost. The connection may be - * lost because the client initiates a disconnect or because the server or network - * cause the client to be disconnected. The disconnect call back may be called without - * the connectionComplete call back being invoked if, for example the client fails to - * connect. - * A single response object parameter is passed to the onConnectionLost callback containing the following fields: - *

    - *
  1. errorCode - *
  2. errorMessage - *
- * @property {function} onMessageDelivered called when a message has been delivered. - * All processing that this Client will ever do has been completed. So, for example, - * in the case of a Qos=2 message sent by this client, the PubComp flow has been received from the server - * and the message has been removed from persistent storage before this callback is invoked. - * Parameters passed to the onMessageDelivered callback are: - *
    - *
  1. Messaging.Message that was delivered. - *
- * @property {function} onMessageArrived called when a message has arrived in this Messaging.client. - * Parameters passed to the onMessageArrived callback are: - *
    - *
  1. Messaging.Message that has arrived. - *
- */ - var Client = function (host, port, clientId) { - if (typeof host !== "string") - throw new Error(format(ERROR.INVALID_TYPE, [typeof host, "host"])); - if (typeof port !== "number" || port < 0) - throw new Error(format(ERROR.INVALID_TYPE, [typeof port, "port"])); - - var clientIdLength = 0; - for (var i = 0; i 23) - throw new Error(format(ERROR.INVALID_ARGUMENT, [clientId, "clientId"])); - - var client = new ClientImpl(host, port, clientId); - this._getHost = function() { return client.host; }; - this._setHost = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; - - this._getPort = function() { return client.port; }; - this._setPort = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; - - this._getClientId = function() { return client.clientId; }; - this._setClientId = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; - - this._getOnConnectionLost = function() { return client.onConnectionLost; }; - this._setOnConnectionLost = function(newOnConnectionLost) { - if (typeof newOnConnectionLost === "function") - client.onConnectionLost = newOnConnectionLost; - else - throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnConnectionLost, "onConnectionLost"])); - }; - - this._getOnMessageDelivered = function() { return client.onMessageDelivered; }; - this._setOnMessageDelivered = function(newOnMessageDelivered) { - if (typeof newOnMessageDelivered === "function") - client.onMessageDelivered = newOnMessageDelivered; - else - throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageDelivered, "onMessageDelivered"])); - }; - - this._getOnMessageArrived = function() { return client.onMessageArrived; }; - this._setOnMessageArrived = function(newOnMessageArrived) { - if (typeof newOnMessageArrived === "function") - client.onMessageArrived = newOnMessageArrived; - else - throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageArrived, "onMessageArrived"])); - }; - - /** - * Connect this Messaging client to its server. - * - * @name Messaging.Client#connect - * @function - * @param {Object} [connectOptions] attributes used with the connection. - *

- * Properties of the connect options are: - * @config {number} [timeout] If the connect has not succeeded within this number of seconds, it is deemed to have failed. - * The default is 30 seconds. - * @config {string} [userName] Authentication username for this connection. - * @config {string} [password] Authentication password for this connection. - * @config {Messaging.Message} [willMessage] sent by the server when the client disconnects abnormally. - * @config {Number} [keepAliveInterval] the server disconnects this client if there is no activity for this - * number of seconds. The default value of 60 seconds is assumed if not set. - * @config {boolean} [cleanSession] if true(default) the client and server persistent state is deleted on successful connect. - * @config {boolean} [useSSL] if present and true, use an SSL Websocket connection. - * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. - * @config {function} [onSuccess] called when the connect acknowledgement has been received from the server. - * A single response object parameter is passed to the onSuccess callback containing the following fields: - *

    - *
  1. invocationContext as passed in to the onSuccess method in the connectOptions. - *
- * @config {function} [onFailure] called when the connect request has failed or timed out. - * A single response object parameter is passed to the onFailure callback containing the following fields: - *
    - *
  1. invocationContext as passed in to the onFailure method in the connectOptions. - *
  2. errorCode a number indicating the nature of the error. - *
  3. errorMessage text describing the error. - *
- * @config {Array} [hosts] If present this set of hostnames is tried in order in place - * of the host and port paramater on the construtor. The hosts and the matching ports are tried one at at time in order until - * one of then succeeds. - * @config {Array} [ports] If present this set of ports matching the hosts. - * @throws {InvalidState} if the client is not in disconnected state. The client must have received connectionLost - * or disconnected before calling connect for a second or subsequent time. - */ - this.connect = function (connectOptions) { - connectOptions = connectOptions || {} ; - validate(connectOptions, {timeout:"number", - userName:"string", - password:"string", - willMessage:"object", - keepAliveInterval:"number", - cleanSession:"boolean", - useSSL:"boolean", - invocationContext:"object", - onSuccess:"function", - onFailure:"function", - hosts:"object", - ports:"object"}); - - // If no keep alive interval is set, assume 60 seconds. - if (connectOptions.keepAliveInterval === undefined) - connectOptions.keepAliveInterval = 60; - - if (connectOptions.willMessage) { - if (!(connectOptions.willMessage instanceof Message)) - throw new Error(format(ERROR.INVALID_TYPE, [connectOptions.willMessage, "connectOptions.willMessage"])); - // The will message must have a payload that can be represented as a string. - // Cause the willMessage to throw an exception if this is not the case. - connectOptions.willMessage.stringPayload; - - if (typeof connectOptions.willMessage.destinationName === "undefined") - throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.willMessage.destinationName, "connectOptions.willMessage.destinationName"])); - } - if (typeof connectOptions.cleanSession === "undefined") - connectOptions.cleanSession = true; - if (connectOptions.hosts) { - if (!connectOptions.ports) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); - if (!(connectOptions.hosts instanceof Array) ) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); - if (!(connectOptions.ports instanceof Array) ) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); - if (connectOptions.hosts.length <1 ) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); - if (connectOptions.hosts.length != connectOptions.ports.length) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); - for (var i = 0; i - * @param {object} [subscribeOptions] used to control the subscription, as follows: - *

- * @config {number} [qos] the maiximum qos of any publications sent as a result of making this subscription. - * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. - * @config {function} [onSuccess] called when the subscribe acknowledgement has been received from the server. - * A single response object parameter is passed to the onSuccess callback containing the following fields: - *

    - *
  1. invocationContext if set in the subscribeOptions. - *
- * @config {function} [onFailure] called when the subscribe request has failed or timed out. - * A single response object parameter is passed to the onFailure callback containing the following fields: - *
    - *
  1. invocationContext if set in the subscribeOptions. - *
  2. errorCode a number indicating the nature of the error. - *
  3. errorMessage text describing the error. - *
- * @config {number} [timeout] which if present determines the number of seconds after which the onFailure calback is called - * the presence of a timeout does not prevent the onSuccess callback from being called when the MQTT Suback is eventually received. - * @throws {InvalidState} if the client is not in connected state. - */ - this.subscribe = function (filter, subscribeOptions) { - if (typeof filter !== "string") - throw new Error("Invalid argument:"+filter); - subscribeOptions = subscribeOptions || {} ; - validate(subscribeOptions, {qos:"number", - invocationContext:"object", - onSuccess:"function", - onFailure:"function", - timeout:"number" - }); - if (subscribeOptions.timeout && !subscribeOptions.onFailure) - throw new Error("subscribeOptions.timeout specified with no onFailure callback."); - if (typeof subscribeOptions.qos !== "undefined" - && !(subscribeOptions.qos === 0 || subscribeOptions.qos === 1 || subscribeOptions.qos === 2 )) - throw new Error(format(ERROR.INVALID_ARGUMENT, [subscribeOptions.qos, "subscribeOptions.qos"])); - client.subscribe(filter, subscribeOptions); - }; - - /** - * Unsubscribe for messages, stop receiving messages sent to destinations described by the filter. - * - * @name Messaging.Client#unsubscribe - * @function - * @param {string} filter describing the destinations to receive messages from. - * @param {object} [unsubscribeOptions] used to control the subscription, as follows: - *

- * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. - * @config {function} [onSuccess] called when the unsubscribe acknowledgement has been receive dfrom the server. - * A single response object parameter is passed to the onSuccess callback containing the following fields: - *

    - *
  1. invocationContext if set in the unsubscribeOptions. - *
- * @config {function} [onFailure] called when the unsubscribe request has failed or timed out. - * A single response object parameter is passed to the onFailure callback containing the following fields: - *
    - *
  1. invocationContext if set in the unsubscribeOptions. - *
  2. errorCode a number indicating the nature of the error. - *
  3. errorMessage text describing the error. - *
- * @config {number} [timeout] which if present determines the number of seconds after which the onFailure callback is called, the - * presence of a timeout does not prevent the onSuccess callback from being called when the MQTT UnSuback is eventually received. - * @throws {InvalidState} if the client is not in connected state. - */ - this.unsubscribe = function (filter, unsubscribeOptions) { - if (typeof filter !== "string") - throw new Error("Invalid argument:"+filter); - unsubscribeOptions = unsubscribeOptions || {} ; - validate(unsubscribeOptions, {invocationContext:"object", - onSuccess:"function", - onFailure:"function", - timeout:"number" - }); - if (unsubscribeOptions.timeout && !unsubscribeOptions.onFailure) - throw new Error("unsubscribeOptions.timeout specified with no onFailure callback."); - client.unsubscribe(filter, unsubscribeOptions); - }; - - /** - * Send a message to the consumers of the destination in the Message. - * - * @name Messaging.Client#send - * @function - * @param {Messaging.Message} message to send. - - * @throws {InvalidState} if the client is not in connected state. - */ - this.send = function (message) { - if (!(message instanceof Message)) - throw new Error("Invalid argument:"+typeof message); - if (typeof message.destinationName === "undefined") - throw new Error("Invalid parameter Message.destinationName:"+message.destinationName); - - client.send(message); - }; - - /** - * Normal disconnect of this Messaging client from its server. - * - * @name Messaging.Client#disconnect - * @function - * @throws {InvalidState} if the client is not in connected or connecting state. - */ - this.disconnect = function () { - client.disconnect(); - }; - - /** - * Get the contents of the trace log. - * - * @name Messaging.Client#getTraceLog - * @function - * @return {Object[]} tracebuffer containing the time ordered trace records. - */ - this.getTraceLog = function () { - return client.getTraceLog(); - } - - /** - * Start tracing. - * - * @name Messaging.Client#startTrace - * @function - */ - this.startTrace = function () { - client.startTrace(); - }; - - /** - * Stop tracing. - * - * @name Messaging.Client#stopTrace - * @function - */ - this.stopTrace = function () { - client.stopTrace(); - }; - }; - - Client.prototype = { - get host() { return this._getHost(); }, - set host(newHost) { this._setHost(newHost); }, - - get port() { return this._getPort(); }, - set port(newPort) { this._setPort(newPort); }, - - get clientId() { return this._getClientId(); }, - set clientId(newClientId) { this._setClientId(newClientId); }, - - get onConnectionLost() { return this._getOnConnectionLost(); }, - set onConnectionLost(newOnConnectionLost) { this._setOnConnectionLost(newOnConnectionLost); }, - - get onMessageDelivered() { return this._getOnMessageDelivered(); }, - set onMessageDelivered(newOnMessageDelivered) { this._setOnMessageDelivered(newOnMessageDelivered); }, - - get onMessageArrived() { return this._getOnMessageArrived(); }, - set onMessageArrived(newOnMessageArrived) { this._setOnMessageArrived(newOnMessageArrived); } - }; - - /** - * An application message, sent or received. - *

- * Other programming languages, - * Java, - * C. - *

- * All attributes may be null, which implies the default values. - * - * @name Messaging.Message - * @constructor - * @param {String|ArrayBuffer} payload The message data to be sent. - *

- * @property {string} payloadString read only The payload as a string if the payload consists of valid UTF-8 characters. - * @property {ArrayBuffer} payloadBytes read only The payload as an ArrayBuffer. - *

- * @property {string} destinationName mandatory The name of the destination to which the message is to be sent - * (for messages about to be sent) or the name of the destination from which the message has been received. - * (for messages received by the onMessage function). - *

- * @property {number} qos The Quality of Service used to deliver the message. - *

- *
0 Best effort (default). - *
1 At least once. - *
2 Exactly once. - *
- *

- * @property {Boolean} retained If true, the message is to be retained by the server and delivered - * to both current and future subscriptions. - * If false the server only delivers the message to current subscribers, this is the default for new Messages. - * A received message has the retained boolean set to true if the message was published - * with the retained boolean set to true - * and the subscrption was made after the message has been published. - *

- * @property {Boolean} duplicate read only If true, this message might be a duplicate of one which has already been received. - * This is only set on messages received from the server. - * - */ - var Message = function (newPayload) { - var payload; - if ( typeof newPayload === "string" - || newPayload instanceof ArrayBuffer - || newPayload instanceof Int8Array - || newPayload instanceof Uint8Array - || newPayload instanceof Int16Array - || newPayload instanceof Uint16Array - || newPayload instanceof Int32Array - || newPayload instanceof Uint32Array - || newPayload instanceof Float32Array - || newPayload instanceof Float64Array - ) { - payload = newPayload; - } else { - throw (format(ERROR.INVALID_ARGUMENT, [newPayload, "newPayload"])); - } - - this._getPayloadString = function () { - if (typeof payload === "string") - return payload; - else - return parseUTF8(payload, 0, payload.length); - }; - - this._getPayloadBytes = function() { - if (typeof payload === "string") { - var buffer = new ArrayBuffer(UTF8Length(payload)); - var byteStream = new Uint8Array(buffer); - stringToUTF8(payload, byteStream, 0); - - return byteStream; - } else { - return payload; - }; - }; - - var destinationName = undefined; - this._getDestinationName = function() { return destinationName; }; - this._setDestinationName = function(newDestinationName) { - if (typeof newDestinationName === "string") - destinationName = newDestinationName; - else - throw new Error(format(ERROR.INVALID_ARGUMENT, [newDestinationName, "newDestinationName"])); - }; - - var qos = 0; - this._getQos = function() { return qos; }; - this._setQos = function(newQos) { - if (newQos === 0 || newQos === 1 || newQos === 2 ) - qos = newQos; - else - throw new Error("Invalid argument:"+newQos); - }; - - var retained = false; - this._getRetained = function() { return retained; }; - this._setRetained = function(newRetained) { - if (typeof newRetained === "boolean") - retained = newRetained; - else - throw new Error(format(ERROR.INVALID_ARGUMENT, [newRetained, "newRetained"])); - }; - - var duplicate = false; - this._getDuplicate = function() { return duplicate; }; - this._setDuplicate = function(newDuplicate) { duplicate = newDuplicate; }; - }; - - Message.prototype = { - get payloadString() { return this._getPayloadString(); }, - get payloadBytes() { return this._getPayloadBytes(); }, - - get destinationName() { return this._getDestinationName(); }, - set destinationName(newDestinationName) { this._setDestinationName(newDestinationName); }, - - get qos() { return this._getQos(); }, - set qos(newQos) { this._setQos(newQos); }, - - get retained() { return this._getRetained(); }, - set retained(newRetained) { this._setRetained(newRetained); }, - - get duplicate() { return this._getDuplicate(); }, - set duplicate(newDuplicate) { this._setDuplicate(newDuplicate); } - }; - - // Module contents. - return { - Client: Client, - Message: Message - }; + } + output += String.fromCharCode(utf16); + } + return output; + } + + /** @ignore Repeat keepalive requests, monitor responses.*/ + var Pinger = function(client, window, keepAliveInterval) { + this._client = client; + this._window = window; + this._keepAliveInterval = keepAliveInterval*1000; + this.isReset = false; + + var pingReq = new WireMessage(MESSAGE_TYPE.PINGREQ).encode(); + + var doTimeout = function (pinger) { + return function () { + return doPing.apply(pinger); + }; + }; + + /** @ignore */ + var doPing = function() { + if (!this.isReset) { + this._client._trace("Pinger.doPing", "Timed out"); + this._client._disconnected( ERROR.PING_TIMEOUT.code , format(ERROR.PING_TIMEOUT)); + } else { + this.isReset = false; + this._client._trace("Pinger.doPing", "send PINGREQ"); + this._client.socket.send(pingReq); + this.timeout = this._window.setTimeout(doTimeout(this), this._keepAliveInterval); + } + } + + this.reset = function() { + this.isReset = true; + this._window.clearTimeout(this.timeout); + if (this._keepAliveInterval > 0) + this.timeout = setTimeout(doTimeout(this), this._keepAliveInterval); + } + + this.cancel = function() { + this._window.clearTimeout(this.timeout); + } + }; + + /** @ignore Monitor request completion. */ + var Timeout = function(client, window, timeoutSeconds, action, args) { + this._window = window; + if (!timeoutSeconds) + timeoutSeconds = 30; + + var doTimeout = function (action, client, args) { + return function () { + return action.apply(client, args); + }; + }; + this.timeout = setTimeout(doTimeout(action, client, args), timeoutSeconds * 1000); + + this.cancel = function() { + this._window.clearTimeout(this.timeout); + } + }; + + /* + * Internal implementation of the Websockets MQTT V3.1 client. + * + * @name Messaging.ClientImpl @constructor + * @param {String} host the DNS nameof the webSocket host. + * @param {Number} port the port number for that host. + * @param {String} clientId the MQ client identifier. + */ + var ClientImpl = function (host, port, clientId) { + // Check dependencies are satisfied in this browser. + if (!("WebSocket" in global && global["WebSocket"] !== null)) { + throw new Error(format(ERROR.UNSUPPORTED, ["WebSocket"])); + } + if (!("localStorage" in global && global["localStorage"] !== null)) { + throw new Error(format(ERROR.UNSUPPORTED, ["localStorage"])); + } + if (!("ArrayBuffer" in global && global["ArrayBuffer"] !== null)) { + throw new Error(format(ERROR.UNSUPPORTED, ["ArrayBuffer"])); + } + + this._trace("Messaging.Client", host, port, clientId); + + this.host = host; + this.port = port; + this.clientId = clientId; + + // Local storagekeys are qualified with the following string. + this._localKey=host+":"+port+":"+clientId+":"; + + // Create private instance-only message queue + // Internal queue of messages to be sent, in sending order. + this._msg_queue = []; + + // Messages we have sent and are expecting a response for, indexed by their respective message ids. + this._sentMessages = {}; + + // Messages we have received and acknowleged and are expecting a confirm message for + // indexed by their respective message ids. + this._receivedMessages = {}; + + // Internal list of callbacks to be executed when messages + // have been successfully sent over web socket, e.g. disconnect + // when it doesn't have to wait for ACK, just message is dispatched. + this._notify_msg_sent = {}; + + // Unique identifier for SEND messages, incrementing + // counter as messages are sent. + this._message_identifier = 1; + + // Used to determine the transmission sequence of stored sent messages. + this._sequence = 0; + + + // Load the local state, if any, from the saved version, only restore state relevant to this client. + for(key in localStorage) + if ( key.indexOf("Sent:"+this._localKey) == 0 + || key.indexOf("Received:"+this._localKey) == 0) + this.restore(key); + }; + + // Messaging Client public instance members. + ClientImpl.prototype.host; + ClientImpl.prototype.port; + ClientImpl.prototype.clientId; + + // Messaging Client private instance members. + ClientImpl.prototype.socket; + /* true once we have received an acknowledgement to a CONNECT packet. */ + ClientImpl.prototype.connected = false; + /* The largest message identifier allowed, may not be larger than 2**16 but + * if set smaller reduces the maximum number of outbound messages allowed. + */ + ClientImpl.prototype.maxMessageIdentifier = 65536; + ClientImpl.prototype.connectOptions; + ClientImpl.prototype.hostIndex; + ClientImpl.prototype.onConnectionLost; + ClientImpl.prototype.onMessageDelivered; + ClientImpl.prototype.onMessageArrived; + ClientImpl.prototype._msg_queue = null; + ClientImpl.prototype._connectTimeout; + /* The sendPinger monitors how long we allow before we send data to prove to the server that we are alive. */ + ClientImpl.prototype.sendPinger = null; + /* The receivePinger monitors how long we allow before we require evidence that the server is alive. */ + ClientImpl.prototype.receivePinger = null; + + ClientImpl.prototype._traceBuffer = null; + ClientImpl.prototype._MAX_TRACE_ENTRIES = 100; + + ClientImpl.prototype.connect = function (connectOptions) { + var connectOptionsMasked = this._traceMask(connectOptions, "password"); + this._trace("Client.connect", connectOptionsMasked, this.socket, this.connected); + + if (this.connected) + throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); + if (this.socket) + throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); + + this.connectOptions = connectOptions; + + if (connectOptions.hosts) { + this.hostIndex = 0; + this._doConnect(connectOptions.hosts[0], connectOptions.ports[0]); + } else { + this._doConnect(this.host, this.port); + } + + }; + + ClientImpl.prototype.subscribe = function (filter, subscribeOptions) { + this._trace("Client.subscribe", filter, subscribeOptions); + + if (!this.connected) + throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); + + var wireMessage = new WireMessage(MESSAGE_TYPE.SUBSCRIBE); + wireMessage.topics=[filter]; + if (subscribeOptions.qos != undefined) + wireMessage.requestedQos = [subscribeOptions.qos]; + else + wireMessage.requestedQos = [0]; + + if (subscribeOptions.onSuccess) { + wireMessage.callback = function() {subscribeOptions.onSuccess({invocationContext:subscribeOptions.invocationContext});}; + } + if (subscribeOptions.timeout) { + wireMessage.timeOut = new Timeout(this, window, subscribeOptions.timeout, subscribeOptions.onFailure + , [{invocationContext:subscribeOptions.invocationContext, + errorCode:ERROR.SUBSCRIBE_TIMEOUT.code, + errorMessage:format(ERROR.SUBSCRIBE_TIMEOUT)}]); + } + + // All subscriptions return a SUBACK. + this._requires_ack(wireMessage); + this._schedule_message(wireMessage); + }; + + /** @ignore */ + ClientImpl.prototype.unsubscribe = function(filter, unsubscribeOptions) { + this._trace("Client.unsubscribe", filter, unsubscribeOptions); + + if (!this.connected) + throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); + + var wireMessage = new WireMessage(MESSAGE_TYPE.UNSUBSCRIBE); + wireMessage.topics = [filter]; + + if (unsubscribeOptions.onSuccess) { + wireMessage.callback = function() {unsubscribeOptions.onSuccess({invocationContext:unsubscribeOptions.invocationContext});}; + } + if (unsubscribeOptions.timeout) { + wireMessage.timeOut = new Timeout(this, window, unsubscribeOptions.timeout, unsubscribeOptions.onFailure + , [{invocationContext:unsubscribeOptions.invocationContext, + errorCode:ERROR.UNSUBSCRIBE_TIMEOUT.code, + errorMessage:format(ERROR.UNSUBSCRIBE_TIMEOUT)}]); + } + + // All unsubscribes return a SUBACK. + this._requires_ack(wireMessage); + this._schedule_message(wireMessage); + }; + + ClientImpl.prototype.send = function (message) { + this._trace("Client.send", message); + + if (!this.connected) + throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); + + wireMessage = new WireMessage(MESSAGE_TYPE.PUBLISH); + wireMessage.payloadMessage = message; + + if (message.qos > 0) + this._requires_ack(wireMessage); + else if (this.onMessageDelivered) + this._notify_msg_sent[wireMessage] = this.onMessageDelivered(wireMessage.payloadMessage); + this._schedule_message(wireMessage); + }; + + ClientImpl.prototype.disconnect = function () { + this._trace("Client.disconnect"); + + if (!this.socket) + throw new Error(format(ERROR.INVALID_STATE, ["not connecting or connected"])); + + wireMessage = new WireMessage(MESSAGE_TYPE.DISCONNECT); + + // Run the disconnected call back as soon as the message has been sent, + // in case of a failure later on in the disconnect processing. + // as a consequence, the _disconected call back may be run several times. + this._notify_msg_sent[wireMessage] = scope(this._disconnected, this); + + this._schedule_message(wireMessage); + }; + + ClientImpl.prototype.getTraceLog = function () { + if ( this._traceBuffer !== null ) { + this._trace("Client.getTraceLog", new Date()); + this._trace("Client.getTraceLog in flight messages", this._sentMessages.length); + for (key in this._sentMessages) + this._trace("_sentMessages ",key, this._sentMessages[key]); + for (key in this._receivedMessages) + this._trace("_receivedMessages ",key, this._receivedMessages[key]); + + return this._traceBuffer; + } + }; + + ClientImpl.prototype.startTrace = function () { + if ( this._traceBuffer === null ) { + this._traceBuffer = []; + } + this._trace("Client.startTrace", new Date(), version); + }; + + ClientImpl.prototype.stopTrace = function () { + delete this._traceBuffer; + }; + + ClientImpl.prototype._doConnect = function (host, port) { + // When the socket is open, this client will send the CONNECT WireMessage using the saved parameters. + if (this.connectOptions.useSSL) + wsurl = ["wss://", host, ":", port, "/mqtt"].join(""); + else + wsurl = ["ws://", host, ":", port, "/mqtt"].join(""); + this.connected = false; + this.socket = new WebSocket(wsurl, 'mqttv3.1'); + this.socket.binaryType = 'arraybuffer'; + this.socket.onopen = scope(this._on_socket_open, this); + this.socket.onmessage = scope(this._on_socket_message, this); + this.socket.onerror = scope(this._on_socket_error, this); + this.socket.onclose = scope(this._on_socket_close, this); + + this.sendPinger = new Pinger(this, window, this.connectOptions.keepAliveInterval); + this.receivePinger = new Pinger(this, window, this.connectOptions.keepAliveInterval); + + this._connectTimeout = new Timeout(this, window, this.connectOptions.timeout, this._disconnected, [ERROR.CONNECT_TIMEOUT.code, format(ERROR.CONNECT_TIMEOUT)]); + }; + + + // Schedule a new message to be sent over the WebSockets + // connection. CONNECT messages cause WebSocket connection + // to be started. All other messages are queued internally + // until this has happened. When WS connection starts, process + // all outstanding messages. + ClientImpl.prototype._schedule_message = function (message) { + this._msg_queue.push(message); + // Process outstanding messages in the queue if we have an open socket, and have received CONNACK. + if (this.connected) { + this._process_queue(); + } + }; + + ClientImpl.prototype.store = function(prefix, wireMessage) { + storedMessage = {type:wireMessage.type, messageIdentifier:wireMessage.messageIdentifier, version:1}; + + switch(wireMessage.type) { + case MESSAGE_TYPE.PUBLISH: + if(wireMessage.pubRecReceived) + storedMessage.pubRecReceived = true; + + // Convert the payload to a hex string. + storedMessage.payloadMessage = {}; + var hex = ""; + var messageBytes = wireMessage.payloadMessage.payloadBytes; + for (var i=0; i= 2) { + var x = parseInt(hex.substring(0, 2), 16); + hex = hex.substring(2, hex.length); + byteStream[i++] = x; + } + var payloadMessage = new Messaging.Message(byteStream); + + payloadMessage.qos = storedMessage.payloadMessage.qos; + payloadMessage.destinationName = storedMessage.payloadMessage.destinationName; + if (storedMessage.payloadMessage.duplicate) + payloadMessage.duplicate = true; + if (storedMessage.payloadMessage.retained) + payloadMessage.retained = true; + wireMessage.payloadMessage = payloadMessage; + + break; + + default: + throw Error(format(ERROR.INVALID_STORED_DATA, [key, value])); + } + + if (key.indexOf("Sent:"+this._localKey) == 0) { + this._sentMessages[wireMessage.messageIdentifier] = wireMessage; + } else if (key.indexOf("Received:"+this._localKey) == 0) { + this._receivedMessages[wireMessage.messageIdentifier] = wireMessage; + } + }; + + ClientImpl.prototype._process_queue = function () { + var message = null; + // Process messages in order they were added + var fifo = this._msg_queue.reverse(); + + // Send all queued messages down socket connection + while ((message = fifo.pop())) { + this._socket_send(message); + // Notify listeners that message was successfully sent + if (this._notify_msg_sent[message]) { + this._notify_msg_sent[message](); + delete this._notify_msg_sent[message]; + } + } + }; + + /** + * @ignore + * Expect an ACK response for this message. Add message to the set of in progress + * messages and set an unused identifier in this message. + */ + ClientImpl.prototype._requires_ack = function (wireMessage) { + var messageCount = Object.keys(this._sentMessages).length; + if (messageCount > this.maxMessageIdentifier) + throw Error ("Too many messages:"+messageCount); + + while(this._sentMessages[this._message_identifier] !== undefined) { + this._message_identifier++; + } + wireMessage.messageIdentifier = this._message_identifier; + this._sentMessages[wireMessage.messageIdentifier] = wireMessage; + if (wireMessage.type === MESSAGE_TYPE.PUBLISH) { + this.store("Sent:", wireMessage); + } + if (this._message_identifier === this.maxMessagIdentifier) { + this._message_identifier = 1; + } + }; + + /** + * @ignore + * Called when the underlying websocket has been opened. + */ + ClientImpl.prototype._on_socket_open = function () { + // Create the CONNECT message object. + var wireMessage = new WireMessage(MESSAGE_TYPE.CONNECT, this.connectOptions); + wireMessage.clientId = this.clientId; + this._socket_send(wireMessage); + }; + + /** + * @ignore + * Called when the underlying websocket has received a complete packet. + */ + ClientImpl.prototype._on_socket_message = function (event) { + this._trace("Client._on_socket_message", event.data); + + // Reset the receive ping timer, we now have evidence the server is alive. + this.receivePinger.reset(); + var byteArray = new Uint8Array(event.data); + try { + var wireMessage = decodeMessage(byteArray); + } catch (error) { + this._disconnected(ERROR.INTERNAL_ERROR.code , format(ERROR.INTERNAL_ERROR, [error.message])); + return; + } + this._trace("Client._on_socket_message", wireMessage); + + switch(wireMessage.type) { + case MESSAGE_TYPE.CONNACK: + this._connectTimeout.cancel(); + + // If we have started using clean session then clear up the local state. + if (this.connectOptions.cleanSession) { + for (key in this._sentMessages) { + var sentMessage = this._sentMessages[key]; + localStorage.removeItem("Sent:"+this._localKey+sentMessage.messageIdentifier); + } + this._sentMessages = {}; + + for (key in this._receivedMessages) { + var receivedMessage = this._receivedMessages[key]; + localStorage.removeItem("Received:"+this._localKey+receivedMessage.messageIdentifier); + } + this._receivedMessages = {}; + } + // Client connected and ready for business. + if (wireMessage.returnCode === 0) { + this.connected = true; + // Jump to the end of the list of hosts and stop looking for a good host. + if (this.connectOptions.hosts) + this.hostIndex = this.connectOptions.hosts.length; + } else { + this._disconnected(ERROR.CONNACK_RETURNCODE.code , format(ERROR.CONNACK_RETURNCODE, [wireMessage.returnCode, CONNACK_RC[wireMessage.returnCode]])); + break; + } + + // Resend messages. + var sequencedMessages = new Array(); + for (var msgId in this._sentMessages) { + if (this._sentMessages.hasOwnProperty(msgId)) + sequencedMessages.push(this._sentMessages[msgId]); + } + + // Sort sentMessages into the original sent order. + var sequencedMessages = sequencedMessages.sort(function(a,b) {return a.sequence - b.sequence;} ); + for (var i=0, len=sequencedMessages.length; i + * Other programming languages, + * Java, + * C. + *

+ * Most applications will create just one Client object and then call its connect() method, + * however applications can create more than one Client object if they wish. + * In this case the combination of host, port and clientId attributes must be different for each Client object. + *

+ * The send, subscribe and unsubscribe methods are implemented as asynchronous JavaScript methods + * (even though the underlying protocol exchange might be synchronous in nature). + * This means they signal their completion by calling back to the application, + * via Success or Failure callback functions provided by the application on the method in question. + * Such callbacks are called at most once per method invocation and do not persist beyond the lifetime + * of the script that made the invocation. + *

+ * In contrast there are some callback functions most notably onMessageArrived + * that are defined on the Messaging.Client object. + * These may get called multiple times, and aren't directly related to specific method invocations made by the client. + * + * @name Messaging.Client + * + * @constructor + * Creates a Messaging.Client object that can be used to communicate with a Messaging server. + * + * @param {string} host the address of the messaging server, as a DNS name or dotted decimal IP address. + * @param {number} port the port number in the host to connect to. + * @param {string} clientId the Messaging client identifier, between 1 and 23 characters in length. + * + * @property {string} host read only the server's DNS hostname or dotted decimal IP address. + * @property {number} port read only the server's port. + * @property {string} clientId read only used when connecting to the server. + * @property {function} onConnectionLost called when a connection has been lost, + * after a connect() method has succeeded. + * Establish the call back used when a connection has been lost. The connection may be + * lost because the client initiates a disconnect or because the server or network + * cause the client to be disconnected. The disconnect call back may be called without + * the connectionComplete call back being invoked if, for example the client fails to + * connect. + * A single response object parameter is passed to the onConnectionLost callback containing the following fields: + *

    + *
  1. errorCode + *
  2. errorMessage + *
+ * @property {function} onMessageDelivered called when a message has been delivered. + * All processing that this Client will ever do has been completed. So, for example, + * in the case of a Qos=2 message sent by this client, the PubComp flow has been received from the server + * and the message has been removed from persistent storage before this callback is invoked. + * Parameters passed to the onMessageDelivered callback are: + *
    + *
  1. Messaging.Message that was delivered. + *
+ * @property {function} onMessageArrived called when a message has arrived in this Messaging.client. + * Parameters passed to the onMessageArrived callback are: + *
    + *
  1. Messaging.Message that has arrived. + *
+ */ + var Client = function (host, port, clientId) { + if (typeof host !== "string") + throw new Error(format(ERROR.INVALID_TYPE, [typeof host, "host"])); + if (typeof port !== "number" || port < 0) + throw new Error(format(ERROR.INVALID_TYPE, [typeof port, "port"])); + + var clientIdLength = 0; + for (var i = 0; i 23) + throw new Error(format(ERROR.INVALID_ARGUMENT, [clientId, "clientId"])); + + var client = new ClientImpl(host, port, clientId); + this._getHost = function() { return client.host; }; + this._setHost = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; + + this._getPort = function() { return client.port; }; + this._setPort = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; + + this._getClientId = function() { return client.clientId; }; + this._setClientId = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; + + this._getOnConnectionLost = function() { return client.onConnectionLost; }; + this._setOnConnectionLost = function(newOnConnectionLost) { + if (typeof newOnConnectionLost === "function") + client.onConnectionLost = newOnConnectionLost; + else + throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnConnectionLost, "onConnectionLost"])); + }; + + this._getOnMessageDelivered = function() { return client.onMessageDelivered; }; + this._setOnMessageDelivered = function(newOnMessageDelivered) { + if (typeof newOnMessageDelivered === "function") + client.onMessageDelivered = newOnMessageDelivered; + else + throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageDelivered, "onMessageDelivered"])); + }; + + this._getOnMessageArrived = function() { return client.onMessageArrived; }; + this._setOnMessageArrived = function(newOnMessageArrived) { + if (typeof newOnMessageArrived === "function") + client.onMessageArrived = newOnMessageArrived; + else + throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageArrived, "onMessageArrived"])); + }; + + /** + * Connect this Messaging client to its server. + * + * @name Messaging.Client#connect + * @function + * @param {Object} [connectOptions] attributes used with the connection. + *

+ * Properties of the connect options are: + * @config {number} [timeout] If the connect has not succeeded within this number of seconds, it is deemed to have failed. + * The default is 30 seconds. + * @config {string} [userName] Authentication username for this connection. + * @config {string} [password] Authentication password for this connection. + * @config {Messaging.Message} [willMessage] sent by the server when the client disconnects abnormally. + * @config {Number} [keepAliveInterval] the server disconnects this client if there is no activity for this + * number of seconds. The default value of 60 seconds is assumed if not set. + * @config {boolean} [cleanSession] if true(default) the client and server persistent state is deleted on successful connect. + * @config {boolean} [useSSL] if present and true, use an SSL Websocket connection. + * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. + * @config {function} [onSuccess] called when the connect acknowledgement has been received from the server. + * A single response object parameter is passed to the onSuccess callback containing the following fields: + *

    + *
  1. invocationContext as passed in to the onSuccess method in the connectOptions. + *
+ * @config {function} [onFailure] called when the connect request has failed or timed out. + * A single response object parameter is passed to the onFailure callback containing the following fields: + *
    + *
  1. invocationContext as passed in to the onFailure method in the connectOptions. + *
  2. errorCode a number indicating the nature of the error. + *
  3. errorMessage text describing the error. + *
+ * @config {Array} [hosts] If present this set of hostnames is tried in order in place + * of the host and port paramater on the construtor. The hosts and the matching ports are tried one at at time in order until + * one of then succeeds. + * @config {Array} [ports] If present this set of ports matching the hosts. + * @throws {InvalidState} if the client is not in disconnected state. The client must have received connectionLost + * or disconnected before calling connect for a second or subsequent time. + */ + this.connect = function (connectOptions) { + connectOptions = connectOptions || {} ; + validate(connectOptions, {timeout:"number", + userName:"string", + password:"string", + willMessage:"object", + keepAliveInterval:"number", + cleanSession:"boolean", + useSSL:"boolean", + invocationContext:"object", + onSuccess:"function", + onFailure:"function", + hosts:"object", + ports:"object"}); + + // If no keep alive interval is set, assume 60 seconds. + if (connectOptions.keepAliveInterval === undefined) + connectOptions.keepAliveInterval = 60; + + if (connectOptions.willMessage) { + if (!(connectOptions.willMessage instanceof Message)) + throw new Error(format(ERROR.INVALID_TYPE, [connectOptions.willMessage, "connectOptions.willMessage"])); + // The will message must have a payload that can be represented as a string. + // Cause the willMessage to throw an exception if this is not the case. + connectOptions.willMessage.stringPayload; + + if (typeof connectOptions.willMessage.destinationName === "undefined") + throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.willMessage.destinationName, "connectOptions.willMessage.destinationName"])); + } + if (typeof connectOptions.cleanSession === "undefined") + connectOptions.cleanSession = true; + if (connectOptions.hosts) { + if (!connectOptions.ports) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); + if (!(connectOptions.hosts instanceof Array) ) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); + if (!(connectOptions.ports instanceof Array) ) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); + if (connectOptions.hosts.length <1 ) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); + if (connectOptions.hosts.length != connectOptions.ports.length) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); + for (var i = 0; i + * @param {object} [subscribeOptions] used to control the subscription, as follows: + *

+ * @config {number} [qos] the maiximum qos of any publications sent as a result of making this subscription. + * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. + * @config {function} [onSuccess] called when the subscribe acknowledgement has been received from the server. + * A single response object parameter is passed to the onSuccess callback containing the following fields: + *

    + *
  1. invocationContext if set in the subscribeOptions. + *
+ * @config {function} [onFailure] called when the subscribe request has failed or timed out. + * A single response object parameter is passed to the onFailure callback containing the following fields: + *
    + *
  1. invocationContext if set in the subscribeOptions. + *
  2. errorCode a number indicating the nature of the error. + *
  3. errorMessage text describing the error. + *
+ * @config {number} [timeout] which if present determines the number of seconds after which the onFailure calback is called + * the presence of a timeout does not prevent the onSuccess callback from being called when the MQTT Suback is eventually received. + * @throws {InvalidState} if the client is not in connected state. + */ + this.subscribe = function (filter, subscribeOptions) { + if (typeof filter !== "string") + throw new Error("Invalid argument:"+filter); + subscribeOptions = subscribeOptions || {} ; + validate(subscribeOptions, {qos:"number", + invocationContext:"object", + onSuccess:"function", + onFailure:"function", + timeout:"number" + }); + if (subscribeOptions.timeout && !subscribeOptions.onFailure) + throw new Error("subscribeOptions.timeout specified with no onFailure callback."); + if (typeof subscribeOptions.qos !== "undefined" + && !(subscribeOptions.qos === 0 || subscribeOptions.qos === 1 || subscribeOptions.qos === 2 )) + throw new Error(format(ERROR.INVALID_ARGUMENT, [subscribeOptions.qos, "subscribeOptions.qos"])); + client.subscribe(filter, subscribeOptions); + }; + + /** + * Unsubscribe for messages, stop receiving messages sent to destinations described by the filter. + * + * @name Messaging.Client#unsubscribe + * @function + * @param {string} filter describing the destinations to receive messages from. + * @param {object} [unsubscribeOptions] used to control the subscription, as follows: + *

+ * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. + * @config {function} [onSuccess] called when the unsubscribe acknowledgement has been receive dfrom the server. + * A single response object parameter is passed to the onSuccess callback containing the following fields: + *

    + *
  1. invocationContext if set in the unsubscribeOptions. + *
+ * @config {function} [onFailure] called when the unsubscribe request has failed or timed out. + * A single response object parameter is passed to the onFailure callback containing the following fields: + *
    + *
  1. invocationContext if set in the unsubscribeOptions. + *
  2. errorCode a number indicating the nature of the error. + *
  3. errorMessage text describing the error. + *
+ * @config {number} [timeout] which if present determines the number of seconds after which the onFailure callback is called, the + * presence of a timeout does not prevent the onSuccess callback from being called when the MQTT UnSuback is eventually received. + * @throws {InvalidState} if the client is not in connected state. + */ + this.unsubscribe = function (filter, unsubscribeOptions) { + if (typeof filter !== "string") + throw new Error("Invalid argument:"+filter); + unsubscribeOptions = unsubscribeOptions || {} ; + validate(unsubscribeOptions, {invocationContext:"object", + onSuccess:"function", + onFailure:"function", + timeout:"number" + }); + if (unsubscribeOptions.timeout && !unsubscribeOptions.onFailure) + throw new Error("unsubscribeOptions.timeout specified with no onFailure callback."); + client.unsubscribe(filter, unsubscribeOptions); + }; + + /** + * Send a message to the consumers of the destination in the Message. + * + * @name Messaging.Client#send + * @function + * @param {Messaging.Message} message to send. + + * @throws {InvalidState} if the client is not in connected state. + */ + this.send = function (message) { + if (!(message instanceof Message)) + throw new Error("Invalid argument:"+typeof message); + if (typeof message.destinationName === "undefined") + throw new Error("Invalid parameter Message.destinationName:"+message.destinationName); + + client.send(message); + }; + + /** + * Normal disconnect of this Messaging client from its server. + * + * @name Messaging.Client#disconnect + * @function + * @throws {InvalidState} if the client is not in connected or connecting state. + */ + this.disconnect = function () { + client.disconnect(); + }; + + /** + * Get the contents of the trace log. + * + * @name Messaging.Client#getTraceLog + * @function + * @return {Object[]} tracebuffer containing the time ordered trace records. + */ + this.getTraceLog = function () { + return client.getTraceLog(); + } + + /** + * Start tracing. + * + * @name Messaging.Client#startTrace + * @function + */ + this.startTrace = function () { + client.startTrace(); + }; + + /** + * Stop tracing. + * + * @name Messaging.Client#stopTrace + * @function + */ + this.stopTrace = function () { + client.stopTrace(); + }; + }; + + Client.prototype = { + get host() { return this._getHost(); }, + set host(newHost) { this._setHost(newHost); }, + + get port() { return this._getPort(); }, + set port(newPort) { this._setPort(newPort); }, + + get clientId() { return this._getClientId(); }, + set clientId(newClientId) { this._setClientId(newClientId); }, + + get onConnectionLost() { return this._getOnConnectionLost(); }, + set onConnectionLost(newOnConnectionLost) { this._setOnConnectionLost(newOnConnectionLost); }, + + get onMessageDelivered() { return this._getOnMessageDelivered(); }, + set onMessageDelivered(newOnMessageDelivered) { this._setOnMessageDelivered(newOnMessageDelivered); }, + + get onMessageArrived() { return this._getOnMessageArrived(); }, + set onMessageArrived(newOnMessageArrived) { this._setOnMessageArrived(newOnMessageArrived); } + }; + + /** + * An application message, sent or received. + *

+ * Other programming languages, + * Java, + * C. + *

+ * All attributes may be null, which implies the default values. + * + * @name Messaging.Message + * @constructor + * @param {String|ArrayBuffer} payload The message data to be sent. + *

+ * @property {string} payloadString read only The payload as a string if the payload consists of valid UTF-8 characters. + * @property {ArrayBuffer} payloadBytes read only The payload as an ArrayBuffer. + *

+ * @property {string} destinationName mandatory The name of the destination to which the message is to be sent + * (for messages about to be sent) or the name of the destination from which the message has been received. + * (for messages received by the onMessage function). + *

+ * @property {number} qos The Quality of Service used to deliver the message. + *

+ *
0 Best effort (default). + *
1 At least once. + *
2 Exactly once. + *
+ *

+ * @property {Boolean} retained If true, the message is to be retained by the server and delivered + * to both current and future subscriptions. + * If false the server only delivers the message to current subscribers, this is the default for new Messages. + * A received message has the retained boolean set to true if the message was published + * with the retained boolean set to true + * and the subscrption was made after the message has been published. + *

+ * @property {Boolean} duplicate read only If true, this message might be a duplicate of one which has already been received. + * This is only set on messages received from the server. + * + */ + var Message = function (newPayload) { + var payload; + if ( typeof newPayload === "string" + || newPayload instanceof ArrayBuffer + || newPayload instanceof Int8Array + || newPayload instanceof Uint8Array + || newPayload instanceof Int16Array + || newPayload instanceof Uint16Array + || newPayload instanceof Int32Array + || newPayload instanceof Uint32Array + || newPayload instanceof Float32Array + || newPayload instanceof Float64Array + ) { + payload = newPayload; + } else { + throw (format(ERROR.INVALID_ARGUMENT, [newPayload, "newPayload"])); + } + + this._getPayloadString = function () { + if (typeof payload === "string") + return payload; + else + return parseUTF8(payload, 0, payload.length); + }; + + this._getPayloadBytes = function() { + if (typeof payload === "string") { + var buffer = new ArrayBuffer(UTF8Length(payload)); + var byteStream = new Uint8Array(buffer); + stringToUTF8(payload, byteStream, 0); + + return byteStream; + } else { + return payload; + }; + }; + + var destinationName = undefined; + this._getDestinationName = function() { return destinationName; }; + this._setDestinationName = function(newDestinationName) { + if (typeof newDestinationName === "string") + destinationName = newDestinationName; + else + throw new Error(format(ERROR.INVALID_ARGUMENT, [newDestinationName, "newDestinationName"])); + }; + + var qos = 0; + this._getQos = function() { return qos; }; + this._setQos = function(newQos) { + if (newQos === 0 || newQos === 1 || newQos === 2 ) + qos = newQos; + else + throw new Error("Invalid argument:"+newQos); + }; + + var retained = false; + this._getRetained = function() { return retained; }; + this._setRetained = function(newRetained) { + if (typeof newRetained === "boolean") + retained = newRetained; + else + throw new Error(format(ERROR.INVALID_ARGUMENT, [newRetained, "newRetained"])); + }; + + var duplicate = false; + this._getDuplicate = function() { return duplicate; }; + this._setDuplicate = function(newDuplicate) { duplicate = newDuplicate; }; + }; + + Message.prototype = { + get payloadString() { return this._getPayloadString(); }, + get payloadBytes() { return this._getPayloadBytes(); }, + + get destinationName() { return this._getDestinationName(); }, + set destinationName(newDestinationName) { this._setDestinationName(newDestinationName); }, + + get qos() { return this._getQos(); }, + set qos(newQos) { this._setQos(newQos); }, + + get retained() { return this._getRetained(); }, + set retained(newRetained) { this._setRetained(newRetained); }, + + get duplicate() { return this._getDuplicate(); }, + set duplicate(newDuplicate) { this._setDuplicate(newDuplicate); } + }; + + // Module contents. + return { + Client: Client, + Message: Message + }; })(window); diff --git a/assembly/src/release/bin/win64/wrapper.conf b/assembly/src/release/bin/win64/wrapper.conf index 8c5bf474b1..448ea05f04 100644 --- a/assembly/src/release/bin/win64/wrapper.conf +++ b/assembly/src/release/bin/win64/wrapper.conf @@ -1,144 +1,144 @@ -# ------------------------------------------------------------------------ -# 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. -# ------------------------------------------------------------------------ - -#******************************************************************** -# Wrapper Properties -#******************************************************************** - -# wrapper.debug=TRUE -set.default.ACTIVEMQ_HOME=../.. -set.default.ACTIVEMQ_BASE=../.. -set.default.ACTIVEMQ_CONF=%ACTIVEMQ_BASE%/conf -set.default.ACTIVEMQ_DATA=%ACTIVEMQ_BASE%/data -wrapper.working.dir=. - -# Java Application -wrapper.java.command=java - -# Java Main class. This class must implement the WrapperListener interface -# or guarantee that the WrapperManager class is initialized. Helper -# classes are provided to do this for you. See the Integration section -# of the documentation for details. -wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp - -# Java Classpath (include wrapper.jar) Add class path elements as -# needed starting from 1 -wrapper.java.classpath.1=%ACTIVEMQ_HOME%/bin/wrapper.jar -wrapper.java.classpath.2=%ACTIVEMQ_HOME%/bin/activemq.jar - -# Java Library Path (location of Wrapper.DLL or libwrapper.so) -wrapper.java.library.path.1=%ACTIVEMQ_HOME%/bin/win64 - -# Java Additional Parameters -# note that n is the parameter number starting from 1. -wrapper.java.additional.1=-Dactivemq.home="%ACTIVEMQ_HOME%" -wrapper.java.additional.2=-Dactivemq.base="%ACTIVEMQ_BASE%" -wrapper.java.additional.3=-Djavax.net.ssl.keyStorePassword=password -wrapper.java.additional.4=-Djavax.net.ssl.trustStorePassword=password -wrapper.java.additional.5=-Djavax.net.ssl.keyStore="%ACTIVEMQ_CONF%/broker.ks" -wrapper.java.additional.6=-Djavax.net.ssl.trustStore="%ACTIVEMQ_CONF%/broker.ts" -wrapper.java.additional.7=-Dcom.sun.management.jmxremote -wrapper.java.additional.8=-Dorg.apache.activemq.UseDedicatedTaskRunner=true -wrapper.java.additional.9=-Djava.util.logging.config.file=logging.properties -wrapper.java.additional.10=-Dactivemq.conf="%ACTIVEMQ_CONF%" -wrapper.java.additional.11=-Dactivemq.data="%ACTIVEMQ_DATA%" -wrapper.java.additional.12=-Djava.security.auth.login.config="%ACTIVEMQ_CONF%/login.config" - -# Uncomment to enable remote jmx -#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.port=1616 -#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.authenticate=false -#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.ssl=false - -# Uncomment to enable YourKit profiling -#wrapper.java.additional.n=-Xrunyjpagent - -# Uncomment to enable remote debugging -#wrapper.java.additional.n=-Xdebug -Xnoagent -Djava.compiler=NONE -#wrapper.java.additional.n=-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 - -# Initial Java Heap Size (in MB) -#wrapper.java.initmemory=3 - -# Maximum Java Heap Size (in MB) -wrapper.java.maxmemory=1024 - -# Application parameters. Add parameters as needed starting from 1 -wrapper.app.parameter.1=org.apache.activemq.console.Main -wrapper.app.parameter.2=start - -#******************************************************************** -# Wrapper Logging Properties -#******************************************************************** -# Format of output for the console. (See docs for formats) -wrapper.console.format=PM - -# Log Level for console output. (See docs for log levels) -wrapper.console.loglevel=INFO - -# Log file to use for wrapper output logging. -wrapper.logfile=%ACTIVEMQ_DATA%/wrapper.log - -# Format of output for the log file. (See docs for formats) -wrapper.logfile.format=LPTM - -# Log Level for log file output. (See docs for log levels) -wrapper.logfile.loglevel=INFO - -# Maximum size that the log file will be allowed to grow to before -# the log is rolled. Size is specified in bytes. The default value -# of 0, disables log rolling. May abbreviate with the 'k' (kb) or -# 'm' (mb) suffix. For example: 10m = 10 megabytes. -wrapper.logfile.maxsize=0 - -# Maximum number of rolled log files which will be allowed before old -# files are deleted. The default value of 0 implies no limit. -wrapper.logfile.maxfiles=0 - -# Log Level for sys/event log output. (See docs for log levels) -wrapper.syslog.loglevel=NONE - -#******************************************************************** -# Wrapper Windows Properties -#******************************************************************** -# Title to use when running as a console -wrapper.console.title=ActiveMQ - -#******************************************************************** -# Wrapper Windows NT/2000/XP Service Properties -#******************************************************************** -# WARNING - Do not modify any of these properties when an application -# using this configuration file has been installed as a service. -# Please uninstall the service before modifying this section. The -# service can then be reinstalled. - -# Name of the service -wrapper.ntservice.name=ActiveMQ - -# Display name of the service -wrapper.ntservice.displayname=ActiveMQ - -# Description of the service -wrapper.ntservice.description=ActiveMQ Broker - -# Service dependencies. Add dependencies as needed starting from 1 -wrapper.ntservice.dependency.1= - -# Mode in which the service is installed. AUTO_START or DEMAND_START -wrapper.ntservice.starttype=AUTO_START - -# Allow the service to interact with the desktop. -wrapper.ntservice.interactive=false +# ------------------------------------------------------------------------ +# 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. +# ------------------------------------------------------------------------ + +#******************************************************************** +# Wrapper Properties +#******************************************************************** + +# wrapper.debug=TRUE +set.default.ACTIVEMQ_HOME=../.. +set.default.ACTIVEMQ_BASE=../.. +set.default.ACTIVEMQ_CONF=%ACTIVEMQ_BASE%/conf +set.default.ACTIVEMQ_DATA=%ACTIVEMQ_BASE%/data +wrapper.working.dir=. + +# Java Application +wrapper.java.command=java + +# Java Main class. This class must implement the WrapperListener interface +# or guarantee that the WrapperManager class is initialized. Helper +# classes are provided to do this for you. See the Integration section +# of the documentation for details. +wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp + +# Java Classpath (include wrapper.jar) Add class path elements as +# needed starting from 1 +wrapper.java.classpath.1=%ACTIVEMQ_HOME%/bin/wrapper.jar +wrapper.java.classpath.2=%ACTIVEMQ_HOME%/bin/activemq.jar + +# Java Library Path (location of Wrapper.DLL or libwrapper.so) +wrapper.java.library.path.1=%ACTIVEMQ_HOME%/bin/win64 + +# Java Additional Parameters +# note that n is the parameter number starting from 1. +wrapper.java.additional.1=-Dactivemq.home="%ACTIVEMQ_HOME%" +wrapper.java.additional.2=-Dactivemq.base="%ACTIVEMQ_BASE%" +wrapper.java.additional.3=-Djavax.net.ssl.keyStorePassword=password +wrapper.java.additional.4=-Djavax.net.ssl.trustStorePassword=password +wrapper.java.additional.5=-Djavax.net.ssl.keyStore="%ACTIVEMQ_CONF%/broker.ks" +wrapper.java.additional.6=-Djavax.net.ssl.trustStore="%ACTIVEMQ_CONF%/broker.ts" +wrapper.java.additional.7=-Dcom.sun.management.jmxremote +wrapper.java.additional.8=-Dorg.apache.activemq.UseDedicatedTaskRunner=true +wrapper.java.additional.9=-Djava.util.logging.config.file=logging.properties +wrapper.java.additional.10=-Dactivemq.conf="%ACTIVEMQ_CONF%" +wrapper.java.additional.11=-Dactivemq.data="%ACTIVEMQ_DATA%" +wrapper.java.additional.12=-Djava.security.auth.login.config="%ACTIVEMQ_CONF%/login.config" + +# Uncomment to enable remote jmx +#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.port=1616 +#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.authenticate=false +#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.ssl=false + +# Uncomment to enable YourKit profiling +#wrapper.java.additional.n=-Xrunyjpagent + +# Uncomment to enable remote debugging +#wrapper.java.additional.n=-Xdebug -Xnoagent -Djava.compiler=NONE +#wrapper.java.additional.n=-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 + +# Initial Java Heap Size (in MB) +#wrapper.java.initmemory=3 + +# Maximum Java Heap Size (in MB) +wrapper.java.maxmemory=1024 + +# Application parameters. Add parameters as needed starting from 1 +wrapper.app.parameter.1=org.apache.activemq.console.Main +wrapper.app.parameter.2=start + +#******************************************************************** +# Wrapper Logging Properties +#******************************************************************** +# Format of output for the console. (See docs for formats) +wrapper.console.format=PM + +# Log Level for console output. (See docs for log levels) +wrapper.console.loglevel=INFO + +# Log file to use for wrapper output logging. +wrapper.logfile=%ACTIVEMQ_DATA%/wrapper.log + +# Format of output for the log file. (See docs for formats) +wrapper.logfile.format=LPTM + +# Log Level for log file output. (See docs for log levels) +wrapper.logfile.loglevel=INFO + +# Maximum size that the log file will be allowed to grow to before +# the log is rolled. Size is specified in bytes. The default value +# of 0, disables log rolling. May abbreviate with the 'k' (kb) or +# 'm' (mb) suffix. For example: 10m = 10 megabytes. +wrapper.logfile.maxsize=0 + +# Maximum number of rolled log files which will be allowed before old +# files are deleted. The default value of 0 implies no limit. +wrapper.logfile.maxfiles=0 + +# Log Level for sys/event log output. (See docs for log levels) +wrapper.syslog.loglevel=NONE + +#******************************************************************** +# Wrapper Windows Properties +#******************************************************************** +# Title to use when running as a console +wrapper.console.title=ActiveMQ + +#******************************************************************** +# Wrapper Windows NT/2000/XP Service Properties +#******************************************************************** +# WARNING - Do not modify any of these properties when an application +# using this configuration file has been installed as a service. +# Please uninstall the service before modifying this section. The +# service can then be reinstalled. + +# Name of the service +wrapper.ntservice.name=ActiveMQ + +# Display name of the service +wrapper.ntservice.displayname=ActiveMQ + +# Description of the service +wrapper.ntservice.description=ActiveMQ Broker + +# Service dependencies. Add dependencies as needed starting from 1 +wrapper.ntservice.dependency.1= + +# Mode in which the service is installed. AUTO_START or DEMAND_START +wrapper.ntservice.starttype=AUTO_START + +# Allow the service to interact with the desktop. +wrapper.ntservice.interactive=false diff --git a/assembly/src/release/examples/mqtt/websocket/js/mqttws31.js b/assembly/src/release/examples/mqtt/websocket/js/mqttws31.js index 5c2f3e3dc2..26cfaace3b 100644 --- a/assembly/src/release/examples/mqtt/websocket/js/mqttws31.js +++ b/assembly/src/release/examples/mqtt/websocket/js/mqttws31.js @@ -1,529 +1,529 @@ -/******************************************************************************* - * Copyright (c) 2013 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Andrew Banks - initial API and implementation and initial documentation - *******************************************************************************/ - - -// Only expose a single object name in the global namespace. -// Everything must go through this module. Global Messaging module -// only has a single public function, client, which returns -// a Messaging client object given connection details. - -/** - * @namespace Messaging - * Send and receive messages using web browsers. - *

- * This programming interface lets a JavaScript client application use the MQTT V3.1 protocol to - * connect to an MQTT-supporting messaging server. - * - * The function supported includes: - *

    - *
  1. Connecting to and disconnecting from a server. The server is identified by its host name and port number. - *
  2. Specifying options that relate to the communications link with the server, - * for example the frequency of keep-alive heartbeats, and whether SSL/TLS is required. - *
  3. Subscribing to and receiving messages from MQTT Topics. - *
  4. Publishing messages to MQTT Topics. - *
- *

- *

The API consists of two main objects:

- * The Messaging.Client object. This contains methods that provide the functionality of the API, - * including provision of callbacks that notify the application when a message arrives from or is delivered to the messaging server, - * or when the status of its connection to the messaging server changes. - *

- * The Messaging.Message object. This encapsulates the payload of the message along with various attributes - * associated with its delivery, in particular the destination to which it has been (or is about to be) sent. - *

- * The programming interface validates parameters passed to it, and will throw an Error containing an error message - * intended for developer use, if it detects an error with any parameter. - *

- * Example: - * - *

-client = new Messaging.Client(location.hostname, Number(location.port), "clientId");
-client.onConnectionLost = onConnectionLost;
-client.onMessageArrived = onMessageArrived;
-client.connect({onSuccess:onConnect});
-
-function onConnect() {
-  // Once a connection has been made, make a subscription and send a message.
-  console.log("onConnect");
-  client.subscribe("/World");
-  message = new Messaging.Message("Hello");
-  message.destinationName = "/World";
-  client.send(message); 
-};
-function onConnectionLost(responseObject) {
-  if (responseObject.errorCode !== 0)
-    console.log("onConnectionLost:"+responseObject.errorMessage);
-};
-function onMessageArrived(message) {
-  console.log("onMessageArrived:"+message.payloadString);
-  client.disconnect(); 
-};	
- * 
- *

- * Other programming languages, - * Java, - * C. - */ -Messaging = (function (global) { - - // Private variables below, these are only visible inside the function closure - // which is used to define the module. - - var version = "0.0.0.0"; - var buildLevel = "@BUILDLEVEL@"; - - /** - * Unique message type identifiers, with associated - * associated integer values. - * @private - */ - var MESSAGE_TYPE = { - CONNECT: 1, - CONNACK: 2, - PUBLISH: 3, - PUBACK: 4, - PUBREC: 5, - PUBREL: 6, - PUBCOMP: 7, - SUBSCRIBE: 8, - SUBACK: 9, - UNSUBSCRIBE: 10, - UNSUBACK: 11, - PINGREQ: 12, - PINGRESP: 13, - DISCONNECT: 14 - }; - - // Collection of utility methods used to simplify module code - // and promote the DRY pattern. - - /** - * Validate an object's parameter names to ensure they - * match a list of expected variables name for this option - * type. Used to ensure option object passed into the API don't - * contain erroneous parameters. - * @param {Object} obj User options object - * @param {key:type, key2:type, ...} valid keys and types that may exist in obj. - * @throws {Error} Invalid option parameter found. - * @private - */ - var validate = function(obj, keys) { - for(key in obj) { - if (obj.hasOwnProperty(key)) { - if (keys.hasOwnProperty(key)) { - if (typeof obj[key] !== keys[key]) - throw new Error(format(ERROR.INVALID_TYPE, [typeof obj[key], key])); - } else { - var errorStr = "Unknown property, " + key + ". Valid properties are:"; - for (key in keys) - if (keys.hasOwnProperty(key)) - errorStr = errorStr+" "+key; - throw new Error(errorStr); - } - } - } - }; - - /** - * Return a new function which runs the user function bound - * to a fixed scope. - * @param {function} User function - * @param {object} Function scope - * @return {function} User function bound to another scope - * @private - */ - var scope = function (f, scope) { - return function () { - return f.apply(scope, arguments); - }; - }; - - /** - * Unique message type identifiers, with associated - * associated integer values. - * @private - */ - var ERROR = { - OK: {code:0, text:"AMQJSC0000I OK."}, - CONNECT_TIMEOUT: {code:1, text:"AMQJSC0001E Connect timed out."}, - SUBSCRIBE_TIMEOUT: {code:2, text:"AMQJS0002E Subscribe timed out."}, - UNSUBSCRIBE_TIMEOUT: {code:3, text:"AMQJS0003E Unsubscribe timed out."}, - PING_TIMEOUT: {code:4, text:"AMQJS0004E Ping timed out."}, - INTERNAL_ERROR: {code:5, text:"AMQJS0005E Internal error."}, - CONNACK_RETURNCODE: {code:6, text:"AMQJS0006E Bad Connack return code:{0} {1}."}, - SOCKET_ERROR: {code:7, text:"AMQJS0007E Socket error:{0}."}, - SOCKET_CLOSE: {code:8, text:"AMQJS0008I Socket closed."}, - MALFORMED_UTF: {code:9, text:"AMQJS0009E Malformed UTF data:{0} {1} {2}."}, - UNSUPPORTED: {code:10, text:"AMQJS0010E {0} is not supported by this browser."}, - INVALID_STATE: {code:11, text:"AMQJS0011E Invalid state {0}."}, - INVALID_TYPE: {code:12, text:"AMQJS0012E Invalid type {0} for {1}."}, - INVALID_ARGUMENT: {code:13, text:"AMQJS0013E Invalid argument {0} for {1}."}, - UNSUPPORTED_OPERATION: {code:14, text:"AMQJS0014E Unsupported operation."}, - INVALID_STORED_DATA: {code:15, text:"AMQJS0015E Invalid data in local storage key={0} value={1}."}, - INVALID_MQTT_MESSAGE_TYPE: {code:16, text:"AMQJS0016E Invalid MQTT message type {0}."}, - MALFORMED_UNICODE: {code:17, text:"AMQJS0017E Malformed Unicode string:{0} {1}."}, - }; - - /** CONNACK RC Meaning. */ - var CONNACK_RC = { - 0:"Connection Accepted", - 1:"Connection Refused: unacceptable protocol version", - 2:"Connection Refused: identifier rejected", - 3:"Connection Refused: server unavailable", - 4:"Connection Refused: bad user name or password", - 5:"Connection Refused: not authorized" - }; - - /** - * Format an error message text. - * @private - * @param {error} ERROR.KEY value above. - * @param {substitutions} [array] substituted into the text. - * @return the text with the substitutions made. - */ - var format = function(error, substitutions) { - var text = error.text; - if (substitutions) { - for (var i=0; i 0) { - var part1 = text.substring(0,start); - var part2 = text.substring(start+field.length); - text = part1+substitutions[i]+part2; - } - } - } - return text; - }; - - //MQTT protocol and version 6 M Q I s d p 3 - var MqttProtoIdentifier = [0x00,0x06,0x4d,0x51,0x49,0x73,0x64,0x70,0x03]; - - /** - * @ignore - * Construct an MQTT wire protocol message. - * @param type MQTT packet type. - * @param options optional wire message attributes. - * - * Optional properties - * - * messageIdentifier: message ID in the range [0..65535] - * payloadMessage: Application Message - PUBLISH only - * connectStrings: array of 0 or more Strings to be put into the CONNECT payload - * topics: array of strings (SUBSCRIBE, UNSUBSCRIBE) - * requestQoS: array of QoS values [0..2] - * - * "Flag" properties - * cleanSession: true if present / false if absent (CONNECT) - * willMessage: true if present / false if absent (CONNECT) - * isRetained: true if present / false if absent (CONNECT) - * userName: true if present / false if absent (CONNECT) - * password: true if present / false if absent (CONNECT) - * keepAliveInterval: integer [0..65535] (CONNECT) - * - * @private - */ - var WireMessage = function (type, options) { - this.type = type; - for(name in options) { - if (options.hasOwnProperty(name)) { - this[name] = options[name]; - } - } - }; - - WireMessage.prototype.encode = function() { - // Compute the first byte of the fixed header - var first = ((this.type & 0x0f) << 4); - - /* - * Now calculate the length of the variable header + payload by adding up the lengths - * of all the component parts - */ - - remLength = 0; - topicStrLength = new Array(); - - // if the message contains a messageIdentifier then we need two bytes for that - if (this.messageIdentifier != undefined) - remLength += 2; - - switch(this.type) { - // If this a Connect then we need to include 12 bytes for its header - case MESSAGE_TYPE.CONNECT: - remLength += MqttProtoIdentifier.length + 3; - remLength += UTF8Length(this.clientId) + 2; - if (this.willMessage != undefined) { - remLength += UTF8Length(this.willMessage.destinationName) + 2; - // Will message is always a string, sent as UTF-8 characters with a preceding length. - var willMessagePayloadBytes = this.willMessage.payloadBytes; - if (!(willMessagePayloadBytes instanceof Uint8Array)) - willMessagePayloadBytes = new Uint8Array(payloadBytes); - remLength += willMessagePayloadBytes.byteLength +2; - } - if (this.userName != undefined) - remLength += UTF8Length(this.userName) + 2; - if (this.password != undefined) - remLength += UTF8Length(this.password) + 2; - break; - - // Subscribe, Unsubscribe can both contain topic strings - case MESSAGE_TYPE.SUBSCRIBE: - first |= 0x02; // Qos = 1; - for ( var i = 0; i < this.topics.length; i++) { - topicStrLength[i] = UTF8Length(this.topics[i]); - remLength += topicStrLength[i] + 2; - } - remLength += this.requestedQos.length; // 1 byte for each topic's Qos - // QoS on Subscribe only - break; - - case MESSAGE_TYPE.UNSUBSCRIBE: - first |= 0x02; // Qos = 1; - for ( var i = 0; i < this.topics.length; i++) { - topicStrLength[i] = UTF8Length(this.topics[i]); - remLength += topicStrLength[i] + 2; - } - break; - - case MESSAGE_TYPE.PUBLISH: - if (this.payloadMessage.duplicate) first |= 0x08; - first = first |= (this.payloadMessage.qos << 1); - if (this.payloadMessage.retained) first |= 0x01; - destinationNameLength = UTF8Length(this.payloadMessage.destinationName); - remLength += destinationNameLength + 2; - var payloadBytes = this.payloadMessage.payloadBytes; - remLength += payloadBytes.byteLength; - if (payloadBytes instanceof ArrayBuffer) - payloadBytes = new Uint8Array(payloadBytes); - else if (!(payloadBytes instanceof Uint8Array)) - payloadBytes = new Uint8Array(payloadBytes.buffer); - break; - - case MESSAGE_TYPE.DISCONNECT: - break; - - default: - ; - } - - // Now we can allocate a buffer for the message - - var mbi = encodeMBI(remLength); // Convert the length to MQTT MBI format - var pos = mbi.length + 1; // Offset of start of variable header - var buffer = new ArrayBuffer(remLength + pos); - var byteStream = new Uint8Array(buffer); // view it as a sequence of bytes - - //Write the fixed header into the buffer - byteStream[0] = first; - byteStream.set(mbi,1); - - // If this is a PUBLISH then the variable header starts with a topic - if (this.type == MESSAGE_TYPE.PUBLISH) - pos = writeString(this.payloadMessage.destinationName, destinationNameLength, byteStream, pos); - // If this is a CONNECT then the variable header contains the protocol name/version, flags and keepalive time - - else if (this.type == MESSAGE_TYPE.CONNECT) { - byteStream.set(MqttProtoIdentifier, pos); - pos += MqttProtoIdentifier.length; - var connectFlags = 0; - if (this.cleanSession) - connectFlags = 0x02; - if (this.willMessage != undefined ) { - connectFlags |= 0x04; - connectFlags |= (this.willMessage.qos<<3); - if (this.willMessage.retained) { - connectFlags |= 0x20; - } - } - if (this.userName != undefined) - connectFlags |= 0x80; - if (this.password != undefined) - connectFlags |= 0x40; - byteStream[pos++] = connectFlags; - pos = writeUint16 (this.keepAliveInterval, byteStream, pos); - } - - // Output the messageIdentifier - if there is one - if (this.messageIdentifier != undefined) - pos = writeUint16 (this.messageIdentifier, byteStream, pos); - - switch(this.type) { - case MESSAGE_TYPE.CONNECT: - pos = writeString(this.clientId, UTF8Length(this.clientId), byteStream, pos); - if (this.willMessage != undefined) { - pos = writeString(this.willMessage.destinationName, UTF8Length(this.willMessage.destinationName), byteStream, pos); - pos = writeUint16(willMessagePayloadBytes.byteLength, byteStream, pos); - byteStream.set(willMessagePayloadBytes, pos); - pos += willMessagePayloadBytes.byteLength; - - } - if (this.userName != undefined) - pos = writeString(this.userName, UTF8Length(this.userName), byteStream, pos); - if (this.password != undefined) - pos = writeString(this.password, UTF8Length(this.password), byteStream, pos); - break; - - case MESSAGE_TYPE.PUBLISH: - // PUBLISH has a text or binary payload, if text do not add a 2 byte length field, just the UTF characters. - byteStream.set(payloadBytes, pos); - - break; - -// case MESSAGE_TYPE.PUBREC: -// case MESSAGE_TYPE.PUBREL: -// case MESSAGE_TYPE.PUBCOMP: -// break; - - case MESSAGE_TYPE.SUBSCRIBE: - // SUBSCRIBE has a list of topic strings and request QoS - for (var i=0; i> 4; - var messageInfo = first &= 0x0f; - var pos = 1; - - - // Decode the remaining length (MBI format) - - var digit; - var remLength = 0; - var multiplier = 1; - do { - digit = input[pos++]; - remLength += ((digit & 0x7F) * multiplier); - multiplier *= 128; - } while ((digit & 0x80) != 0); - - var wireMessage = new WireMessage(type); - switch(type) { - case MESSAGE_TYPE.CONNACK: - wireMessage.topicNameCompressionResponse = input[pos++]; - wireMessage.returnCode = input[pos++]; - break; - - case MESSAGE_TYPE.PUBLISH: - var qos = (messageInfo >> 1) & 0x03; - - var len = readUint16(input, pos); - pos += 2; - var topicName = parseUTF8(input, pos, len); - pos += len; - // If QoS 1 or 2 there will be a messageIdentifier - if (qos > 0) { - wireMessage.messageIdentifier = readUint16(input, pos); - pos += 2; - } - - var message = new Messaging.Message(input.subarray(pos)); - if ((messageInfo & 0x01) == 0x01) - message.retained = true; - if ((messageInfo & 0x08) == 0x08) - message.duplicate = true; - message.qos = qos; - message.destinationName = topicName; - wireMessage.payloadMessage = message; - break; - - case MESSAGE_TYPE.PUBACK: - case MESSAGE_TYPE.PUBREC: - case MESSAGE_TYPE.PUBREL: - case MESSAGE_TYPE.PUBCOMP: - case MESSAGE_TYPE.UNSUBACK: - wireMessage.messageIdentifier = readUint16(input, pos); - break; - - case MESSAGE_TYPE.SUBACK: - wireMessage.messageIdentifier = readUint16(input, pos); - pos += 2; - wireMessage.grantedQos = input.subarray(pos); - break; - - default: - ; - } - - return wireMessage; - } - - function writeUint16(input, buffer, offset) { - buffer[offset++] = input >> 8; //MSB - buffer[offset++] = input % 256; //LSB - return offset; - } - - function writeString(input, utf8Length, buffer, offset) { - offset = writeUint16(utf8Length, buffer, offset); - stringToUTF8(input, buffer, offset); - return offset + utf8Length; - } - - function readUint16(buffer, offset) { - return 256*buffer[offset] + buffer[offset+1]; - } - - /** - * Encodes an MQTT Multi-Byte Integer - * @private - */ - function encodeMBI(number) { - var output = new Array(1); - var numBytes = 0; - - do { - var digit = number % 128; - number = number >> 7; - if (number > 0) { - digit |= 0x80; - } - output[numBytes++] = digit; - } while ( (number > 0) && (numBytes<4) ); - - return output; - } - - /** - * Takes a String and calculates its length in bytes when encoded in UTF8. - * @private - */ - function UTF8Length(input) { - var output = 0; - for (var i = 0; i + * This programming interface lets a JavaScript client application use the MQTT V3.1 protocol to + * connect to an MQTT-supporting messaging server. + * + * The function supported includes: + *

    + *
  1. Connecting to and disconnecting from a server. The server is identified by its host name and port number. + *
  2. Specifying options that relate to the communications link with the server, + * for example the frequency of keep-alive heartbeats, and whether SSL/TLS is required. + *
  3. Subscribing to and receiving messages from MQTT Topics. + *
  4. Publishing messages to MQTT Topics. + *
+ *

+ *

The API consists of two main objects:

+ * The Messaging.Client object. This contains methods that provide the functionality of the API, + * including provision of callbacks that notify the application when a message arrives from or is delivered to the messaging server, + * or when the status of its connection to the messaging server changes. + *

+ * The Messaging.Message object. This encapsulates the payload of the message along with various attributes + * associated with its delivery, in particular the destination to which it has been (or is about to be) sent. + *

+ * The programming interface validates parameters passed to it, and will throw an Error containing an error message + * intended for developer use, if it detects an error with any parameter. + *

+ * Example: + * + *

+client = new Messaging.Client(location.hostname, Number(location.port), "clientId");
+client.onConnectionLost = onConnectionLost;
+client.onMessageArrived = onMessageArrived;
+client.connect({onSuccess:onConnect});
+
+function onConnect() {
+  // Once a connection has been made, make a subscription and send a message.
+  console.log("onConnect");
+  client.subscribe("/World");
+  message = new Messaging.Message("Hello");
+  message.destinationName = "/World";
+  client.send(message); 
+};
+function onConnectionLost(responseObject) {
+  if (responseObject.errorCode !== 0)
+    console.log("onConnectionLost:"+responseObject.errorMessage);
+};
+function onMessageArrived(message) {
+  console.log("onMessageArrived:"+message.payloadString);
+  client.disconnect(); 
+};	
+ * 
+ *

+ * Other programming languages, + * Java, + * C. + */ +Messaging = (function (global) { + + // Private variables below, these are only visible inside the function closure + // which is used to define the module. + + var version = "0.0.0.0"; + var buildLevel = "@BUILDLEVEL@"; + + /** + * Unique message type identifiers, with associated + * associated integer values. + * @private + */ + var MESSAGE_TYPE = { + CONNECT: 1, + CONNACK: 2, + PUBLISH: 3, + PUBACK: 4, + PUBREC: 5, + PUBREL: 6, + PUBCOMP: 7, + SUBSCRIBE: 8, + SUBACK: 9, + UNSUBSCRIBE: 10, + UNSUBACK: 11, + PINGREQ: 12, + PINGRESP: 13, + DISCONNECT: 14 + }; + + // Collection of utility methods used to simplify module code + // and promote the DRY pattern. + + /** + * Validate an object's parameter names to ensure they + * match a list of expected variables name for this option + * type. Used to ensure option object passed into the API don't + * contain erroneous parameters. + * @param {Object} obj User options object + * @param {key:type, key2:type, ...} valid keys and types that may exist in obj. + * @throws {Error} Invalid option parameter found. + * @private + */ + var validate = function(obj, keys) { + for(key in obj) { + if (obj.hasOwnProperty(key)) { + if (keys.hasOwnProperty(key)) { + if (typeof obj[key] !== keys[key]) + throw new Error(format(ERROR.INVALID_TYPE, [typeof obj[key], key])); + } else { + var errorStr = "Unknown property, " + key + ". Valid properties are:"; + for (key in keys) + if (keys.hasOwnProperty(key)) + errorStr = errorStr+" "+key; + throw new Error(errorStr); + } + } + } + }; + + /** + * Return a new function which runs the user function bound + * to a fixed scope. + * @param {function} User function + * @param {object} Function scope + * @return {function} User function bound to another scope + * @private + */ + var scope = function (f, scope) { + return function () { + return f.apply(scope, arguments); + }; + }; + + /** + * Unique message type identifiers, with associated + * associated integer values. + * @private + */ + var ERROR = { + OK: {code:0, text:"AMQJSC0000I OK."}, + CONNECT_TIMEOUT: {code:1, text:"AMQJSC0001E Connect timed out."}, + SUBSCRIBE_TIMEOUT: {code:2, text:"AMQJS0002E Subscribe timed out."}, + UNSUBSCRIBE_TIMEOUT: {code:3, text:"AMQJS0003E Unsubscribe timed out."}, + PING_TIMEOUT: {code:4, text:"AMQJS0004E Ping timed out."}, + INTERNAL_ERROR: {code:5, text:"AMQJS0005E Internal error."}, + CONNACK_RETURNCODE: {code:6, text:"AMQJS0006E Bad Connack return code:{0} {1}."}, + SOCKET_ERROR: {code:7, text:"AMQJS0007E Socket error:{0}."}, + SOCKET_CLOSE: {code:8, text:"AMQJS0008I Socket closed."}, + MALFORMED_UTF: {code:9, text:"AMQJS0009E Malformed UTF data:{0} {1} {2}."}, + UNSUPPORTED: {code:10, text:"AMQJS0010E {0} is not supported by this browser."}, + INVALID_STATE: {code:11, text:"AMQJS0011E Invalid state {0}."}, + INVALID_TYPE: {code:12, text:"AMQJS0012E Invalid type {0} for {1}."}, + INVALID_ARGUMENT: {code:13, text:"AMQJS0013E Invalid argument {0} for {1}."}, + UNSUPPORTED_OPERATION: {code:14, text:"AMQJS0014E Unsupported operation."}, + INVALID_STORED_DATA: {code:15, text:"AMQJS0015E Invalid data in local storage key={0} value={1}."}, + INVALID_MQTT_MESSAGE_TYPE: {code:16, text:"AMQJS0016E Invalid MQTT message type {0}."}, + MALFORMED_UNICODE: {code:17, text:"AMQJS0017E Malformed Unicode string:{0} {1}."}, + }; + + /** CONNACK RC Meaning. */ + var CONNACK_RC = { + 0:"Connection Accepted", + 1:"Connection Refused: unacceptable protocol version", + 2:"Connection Refused: identifier rejected", + 3:"Connection Refused: server unavailable", + 4:"Connection Refused: bad user name or password", + 5:"Connection Refused: not authorized" + }; + + /** + * Format an error message text. + * @private + * @param {error} ERROR.KEY value above. + * @param {substitutions} [array] substituted into the text. + * @return the text with the substitutions made. + */ + var format = function(error, substitutions) { + var text = error.text; + if (substitutions) { + for (var i=0; i 0) { + var part1 = text.substring(0,start); + var part2 = text.substring(start+field.length); + text = part1+substitutions[i]+part2; + } + } + } + return text; + }; + + //MQTT protocol and version 6 M Q I s d p 3 + var MqttProtoIdentifier = [0x00,0x06,0x4d,0x51,0x49,0x73,0x64,0x70,0x03]; + + /** + * @ignore + * Construct an MQTT wire protocol message. + * @param type MQTT packet type. + * @param options optional wire message attributes. + * + * Optional properties + * + * messageIdentifier: message ID in the range [0..65535] + * payloadMessage: Application Message - PUBLISH only + * connectStrings: array of 0 or more Strings to be put into the CONNECT payload + * topics: array of strings (SUBSCRIBE, UNSUBSCRIBE) + * requestQoS: array of QoS values [0..2] + * + * "Flag" properties + * cleanSession: true if present / false if absent (CONNECT) + * willMessage: true if present / false if absent (CONNECT) + * isRetained: true if present / false if absent (CONNECT) + * userName: true if present / false if absent (CONNECT) + * password: true if present / false if absent (CONNECT) + * keepAliveInterval: integer [0..65535] (CONNECT) + * + * @private + */ + var WireMessage = function (type, options) { + this.type = type; + for(name in options) { + if (options.hasOwnProperty(name)) { + this[name] = options[name]; + } + } + }; + + WireMessage.prototype.encode = function() { + // Compute the first byte of the fixed header + var first = ((this.type & 0x0f) << 4); + + /* + * Now calculate the length of the variable header + payload by adding up the lengths + * of all the component parts + */ + + remLength = 0; + topicStrLength = new Array(); + + // if the message contains a messageIdentifier then we need two bytes for that + if (this.messageIdentifier != undefined) + remLength += 2; + + switch(this.type) { + // If this a Connect then we need to include 12 bytes for its header + case MESSAGE_TYPE.CONNECT: + remLength += MqttProtoIdentifier.length + 3; + remLength += UTF8Length(this.clientId) + 2; + if (this.willMessage != undefined) { + remLength += UTF8Length(this.willMessage.destinationName) + 2; + // Will message is always a string, sent as UTF-8 characters with a preceding length. + var willMessagePayloadBytes = this.willMessage.payloadBytes; + if (!(willMessagePayloadBytes instanceof Uint8Array)) + willMessagePayloadBytes = new Uint8Array(payloadBytes); + remLength += willMessagePayloadBytes.byteLength +2; + } + if (this.userName != undefined) + remLength += UTF8Length(this.userName) + 2; + if (this.password != undefined) + remLength += UTF8Length(this.password) + 2; + break; + + // Subscribe, Unsubscribe can both contain topic strings + case MESSAGE_TYPE.SUBSCRIBE: + first |= 0x02; // Qos = 1; + for ( var i = 0; i < this.topics.length; i++) { + topicStrLength[i] = UTF8Length(this.topics[i]); + remLength += topicStrLength[i] + 2; + } + remLength += this.requestedQos.length; // 1 byte for each topic's Qos + // QoS on Subscribe only + break; + + case MESSAGE_TYPE.UNSUBSCRIBE: + first |= 0x02; // Qos = 1; + for ( var i = 0; i < this.topics.length; i++) { + topicStrLength[i] = UTF8Length(this.topics[i]); + remLength += topicStrLength[i] + 2; + } + break; + + case MESSAGE_TYPE.PUBLISH: + if (this.payloadMessage.duplicate) first |= 0x08; + first = first |= (this.payloadMessage.qos << 1); + if (this.payloadMessage.retained) first |= 0x01; + destinationNameLength = UTF8Length(this.payloadMessage.destinationName); + remLength += destinationNameLength + 2; + var payloadBytes = this.payloadMessage.payloadBytes; + remLength += payloadBytes.byteLength; + if (payloadBytes instanceof ArrayBuffer) + payloadBytes = new Uint8Array(payloadBytes); + else if (!(payloadBytes instanceof Uint8Array)) + payloadBytes = new Uint8Array(payloadBytes.buffer); + break; + + case MESSAGE_TYPE.DISCONNECT: + break; + + default: + ; + } + + // Now we can allocate a buffer for the message + + var mbi = encodeMBI(remLength); // Convert the length to MQTT MBI format + var pos = mbi.length + 1; // Offset of start of variable header + var buffer = new ArrayBuffer(remLength + pos); + var byteStream = new Uint8Array(buffer); // view it as a sequence of bytes + + //Write the fixed header into the buffer + byteStream[0] = first; + byteStream.set(mbi,1); + + // If this is a PUBLISH then the variable header starts with a topic + if (this.type == MESSAGE_TYPE.PUBLISH) + pos = writeString(this.payloadMessage.destinationName, destinationNameLength, byteStream, pos); + // If this is a CONNECT then the variable header contains the protocol name/version, flags and keepalive time + + else if (this.type == MESSAGE_TYPE.CONNECT) { + byteStream.set(MqttProtoIdentifier, pos); + pos += MqttProtoIdentifier.length; + var connectFlags = 0; + if (this.cleanSession) + connectFlags = 0x02; + if (this.willMessage != undefined ) { + connectFlags |= 0x04; + connectFlags |= (this.willMessage.qos<<3); + if (this.willMessage.retained) { + connectFlags |= 0x20; + } + } + if (this.userName != undefined) + connectFlags |= 0x80; + if (this.password != undefined) + connectFlags |= 0x40; + byteStream[pos++] = connectFlags; + pos = writeUint16 (this.keepAliveInterval, byteStream, pos); + } + + // Output the messageIdentifier - if there is one + if (this.messageIdentifier != undefined) + pos = writeUint16 (this.messageIdentifier, byteStream, pos); + + switch(this.type) { + case MESSAGE_TYPE.CONNECT: + pos = writeString(this.clientId, UTF8Length(this.clientId), byteStream, pos); + if (this.willMessage != undefined) { + pos = writeString(this.willMessage.destinationName, UTF8Length(this.willMessage.destinationName), byteStream, pos); + pos = writeUint16(willMessagePayloadBytes.byteLength, byteStream, pos); + byteStream.set(willMessagePayloadBytes, pos); + pos += willMessagePayloadBytes.byteLength; + + } + if (this.userName != undefined) + pos = writeString(this.userName, UTF8Length(this.userName), byteStream, pos); + if (this.password != undefined) + pos = writeString(this.password, UTF8Length(this.password), byteStream, pos); + break; + + case MESSAGE_TYPE.PUBLISH: + // PUBLISH has a text or binary payload, if text do not add a 2 byte length field, just the UTF characters. + byteStream.set(payloadBytes, pos); + + break; + +// case MESSAGE_TYPE.PUBREC: +// case MESSAGE_TYPE.PUBREL: +// case MESSAGE_TYPE.PUBCOMP: +// break; + + case MESSAGE_TYPE.SUBSCRIBE: + // SUBSCRIBE has a list of topic strings and request QoS + for (var i=0; i> 4; + var messageInfo = first &= 0x0f; + var pos = 1; + + + // Decode the remaining length (MBI format) + + var digit; + var remLength = 0; + var multiplier = 1; + do { + digit = input[pos++]; + remLength += ((digit & 0x7F) * multiplier); + multiplier *= 128; + } while ((digit & 0x80) != 0); + + var wireMessage = new WireMessage(type); + switch(type) { + case MESSAGE_TYPE.CONNACK: + wireMessage.topicNameCompressionResponse = input[pos++]; + wireMessage.returnCode = input[pos++]; + break; + + case MESSAGE_TYPE.PUBLISH: + var qos = (messageInfo >> 1) & 0x03; + + var len = readUint16(input, pos); + pos += 2; + var topicName = parseUTF8(input, pos, len); + pos += len; + // If QoS 1 or 2 there will be a messageIdentifier + if (qos > 0) { + wireMessage.messageIdentifier = readUint16(input, pos); + pos += 2; + } + + var message = new Messaging.Message(input.subarray(pos)); + if ((messageInfo & 0x01) == 0x01) + message.retained = true; + if ((messageInfo & 0x08) == 0x08) + message.duplicate = true; + message.qos = qos; + message.destinationName = topicName; + wireMessage.payloadMessage = message; + break; + + case MESSAGE_TYPE.PUBACK: + case MESSAGE_TYPE.PUBREC: + case MESSAGE_TYPE.PUBREL: + case MESSAGE_TYPE.PUBCOMP: + case MESSAGE_TYPE.UNSUBACK: + wireMessage.messageIdentifier = readUint16(input, pos); + break; + + case MESSAGE_TYPE.SUBACK: + wireMessage.messageIdentifier = readUint16(input, pos); + pos += 2; + wireMessage.grantedQos = input.subarray(pos); + break; + + default: + ; + } + + return wireMessage; + } + + function writeUint16(input, buffer, offset) { + buffer[offset++] = input >> 8; //MSB + buffer[offset++] = input % 256; //LSB + return offset; + } + + function writeString(input, utf8Length, buffer, offset) { + offset = writeUint16(utf8Length, buffer, offset); + stringToUTF8(input, buffer, offset); + return offset + utf8Length; + } + + function readUint16(buffer, offset) { + return 256*buffer[offset] + buffer[offset+1]; + } + + /** + * Encodes an MQTT Multi-Byte Integer + * @private + */ + function encodeMBI(number) { + var output = new Array(1); + var numBytes = 0; + + do { + var digit = number % 128; + number = number >> 7; + if (number > 0) { + digit |= 0x80; + } + output[numBytes++] = digit; + } while ( (number > 0) && (numBytes<4) ); + + return output; + } + + /** + * Takes a String and calculates its length in bytes when encoded in UTF8. + * @private + */ + function UTF8Length(input) { + var output = 0; + for (var i = 0; i 0x7FF) { @@ -532,90 +532,90 @@ Messaging = (function (global) { { i++; output++; - } + } output +=3; - } - else if (charCode > 0x7F) - output +=2; - else - output++; - } - return output; - } - - /** - * Takes a String and writes it into an array as UTF8 encoded bytes. - * @private - */ - function stringToUTF8(input, output, start) { - var pos = start; - for (var i = 0; i>6 & 0x1F | 0xC0; - output[pos++] = charCode & 0x3F | 0x80; - } else if (charCode <= 0xFFFF) { - output[pos++] = charCode>>12 & 0x0F | 0xE0; - output[pos++] = charCode>>6 & 0x3F | 0x80; - output[pos++] = charCode & 0x3F | 0x80; - } else { - output[pos++] = charCode>>18 & 0x07 | 0xF0; - output[pos++] = charCode>>12 & 0x3F | 0x80; - output[pos++] = charCode>>6 & 0x3F | 0x80; - output[pos++] = charCode & 0x3F | 0x80; - }; - } - return output; - } - - function parseUTF8(input, offset, length) { - var output = ""; - var utf16; - var pos = offset; - - while (pos < offset+length) - { - var byte1 = input[pos++]; - if (byte1 < 128) - utf16 = byte1; - else - { - var byte2 = input[pos++]-128; - if (byte2 < 0) - throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16),""])); - if (byte1 < 0xE0) // 2 byte character - utf16 = 64*(byte1-0xC0) + byte2; - else - { - var byte3 = input[pos++]-128; - if (byte3 < 0) - throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16)])); - if (byte1 < 0xF0) // 3 byte character + } + else if (charCode > 0x7F) + output +=2; + else + output++; + } + return output; + } + + /** + * Takes a String and writes it into an array as UTF8 encoded bytes. + * @private + */ + function stringToUTF8(input, output, start) { + var pos = start; + for (var i = 0; i>6 & 0x1F | 0xC0; + output[pos++] = charCode & 0x3F | 0x80; + } else if (charCode <= 0xFFFF) { + output[pos++] = charCode>>12 & 0x0F | 0xE0; + output[pos++] = charCode>>6 & 0x3F | 0x80; + output[pos++] = charCode & 0x3F | 0x80; + } else { + output[pos++] = charCode>>18 & 0x07 | 0xF0; + output[pos++] = charCode>>12 & 0x3F | 0x80; + output[pos++] = charCode>>6 & 0x3F | 0x80; + output[pos++] = charCode & 0x3F | 0x80; + }; + } + return output; + } + + function parseUTF8(input, offset, length) { + var output = ""; + var utf16; + var pos = offset; + + while (pos < offset+length) + { + var byte1 = input[pos++]; + if (byte1 < 128) + utf16 = byte1; + else + { + var byte2 = input[pos++]-128; + if (byte2 < 0) + throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16),""])); + if (byte1 < 0xE0) // 2 byte character + utf16 = 64*(byte1-0xC0) + byte2; + else + { + var byte3 = input[pos++]-128; + if (byte3 < 0) + throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16)])); + if (byte1 < 0xF0) // 3 byte character utf16 = 4096*(byte1-0xE0) + 64*byte2 + byte3; else { var byte4 = input[pos++]-128; - if (byte4 < 0) + if (byte4 < 0) throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)])); if (byte1 < 0xF8) // 4 byte character - utf16 = 262144*(byte1-0xF0) + 4096*byte2 + 64*byte3 + byte4; - else // longer encodings are not supported + utf16 = 262144*(byte1-0xF0) + 4096*byte2 + 64*byte3 + byte4; + else // longer encodings are not supported throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)])); - } - } + } + } } if (utf16 > 0xFFFF) // 4 byte character - express as a surrogate pair @@ -623,1254 +623,1254 @@ Messaging = (function (global) { utf16 -= 0x10000; output += String.fromCharCode(0xD800 + (utf16 >> 10)); // lead character utf16 = 0xDC00 + (utf16 & 0x3FF); // trail character - } - output += String.fromCharCode(utf16); - } - return output; - } - - /** @ignore Repeat keepalive requests, monitor responses.*/ - var Pinger = function(client, window, keepAliveInterval) { - this._client = client; - this._window = window; - this._keepAliveInterval = keepAliveInterval*1000; - this.isReset = false; - - var pingReq = new WireMessage(MESSAGE_TYPE.PINGREQ).encode(); - - var doTimeout = function (pinger) { - return function () { - return doPing.apply(pinger); - }; - }; - - /** @ignore */ - var doPing = function() { - if (!this.isReset) { - this._client._trace("Pinger.doPing", "Timed out"); - this._client._disconnected( ERROR.PING_TIMEOUT.code , format(ERROR.PING_TIMEOUT)); - } else { - this.isReset = false; - this._client._trace("Pinger.doPing", "send PINGREQ"); - this._client.socket.send(pingReq); - this.timeout = this._window.setTimeout(doTimeout(this), this._keepAliveInterval); - } - } - - this.reset = function() { - this.isReset = true; - this._window.clearTimeout(this.timeout); - if (this._keepAliveInterval > 0) - this.timeout = setTimeout(doTimeout(this), this._keepAliveInterval); - } - - this.cancel = function() { - this._window.clearTimeout(this.timeout); - } - }; - - /** @ignore Monitor request completion. */ - var Timeout = function(client, window, timeoutSeconds, action, args) { - this._window = window; - if (!timeoutSeconds) - timeoutSeconds = 30; - - var doTimeout = function (action, client, args) { - return function () { - return action.apply(client, args); - }; - }; - this.timeout = setTimeout(doTimeout(action, client, args), timeoutSeconds * 1000); - - this.cancel = function() { - this._window.clearTimeout(this.timeout); - } - }; - - /* - * Internal implementation of the Websockets MQTT V3.1 client. - * - * @name Messaging.ClientImpl @constructor - * @param {String} host the DNS nameof the webSocket host. - * @param {Number} port the port number for that host. - * @param {String} clientId the MQ client identifier. - */ - var ClientImpl = function (host, port, clientId) { - // Check dependencies are satisfied in this browser. - if (!("WebSocket" in global && global["WebSocket"] !== null)) { - throw new Error(format(ERROR.UNSUPPORTED, ["WebSocket"])); - } - if (!("localStorage" in global && global["localStorage"] !== null)) { - throw new Error(format(ERROR.UNSUPPORTED, ["localStorage"])); - } - if (!("ArrayBuffer" in global && global["ArrayBuffer"] !== null)) { - throw new Error(format(ERROR.UNSUPPORTED, ["ArrayBuffer"])); - } - - this._trace("Messaging.Client", host, port, clientId); - - this.host = host; - this.port = port; - this.clientId = clientId; - - // Local storagekeys are qualified with the following string. - this._localKey=host+":"+port+":"+clientId+":"; - - // Create private instance-only message queue - // Internal queue of messages to be sent, in sending order. - this._msg_queue = []; - - // Messages we have sent and are expecting a response for, indexed by their respective message ids. - this._sentMessages = {}; - - // Messages we have received and acknowleged and are expecting a confirm message for - // indexed by their respective message ids. - this._receivedMessages = {}; - - // Internal list of callbacks to be executed when messages - // have been successfully sent over web socket, e.g. disconnect - // when it doesn't have to wait for ACK, just message is dispatched. - this._notify_msg_sent = {}; - - // Unique identifier for SEND messages, incrementing - // counter as messages are sent. - this._message_identifier = 1; - - // Used to determine the transmission sequence of stored sent messages. - this._sequence = 0; - - - // Load the local state, if any, from the saved version, only restore state relevant to this client. - for(key in localStorage) - if ( key.indexOf("Sent:"+this._localKey) == 0 - || key.indexOf("Received:"+this._localKey) == 0) - this.restore(key); - }; - - // Messaging Client public instance members. - ClientImpl.prototype.host; - ClientImpl.prototype.port; - ClientImpl.prototype.clientId; - - // Messaging Client private instance members. - ClientImpl.prototype.socket; - /* true once we have received an acknowledgement to a CONNECT packet. */ - ClientImpl.prototype.connected = false; - /* The largest message identifier allowed, may not be larger than 2**16 but - * if set smaller reduces the maximum number of outbound messages allowed. - */ - ClientImpl.prototype.maxMessageIdentifier = 65536; - ClientImpl.prototype.connectOptions; - ClientImpl.prototype.hostIndex; - ClientImpl.prototype.onConnectionLost; - ClientImpl.prototype.onMessageDelivered; - ClientImpl.prototype.onMessageArrived; - ClientImpl.prototype._msg_queue = null; - ClientImpl.prototype._connectTimeout; - /* The sendPinger monitors how long we allow before we send data to prove to the server that we are alive. */ - ClientImpl.prototype.sendPinger = null; - /* The receivePinger monitors how long we allow before we require evidence that the server is alive. */ - ClientImpl.prototype.receivePinger = null; - - ClientImpl.prototype._traceBuffer = null; - ClientImpl.prototype._MAX_TRACE_ENTRIES = 100; - - ClientImpl.prototype.connect = function (connectOptions) { - var connectOptionsMasked = this._traceMask(connectOptions, "password"); - this._trace("Client.connect", connectOptionsMasked, this.socket, this.connected); - - if (this.connected) - throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); - if (this.socket) - throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); - - this.connectOptions = connectOptions; - - if (connectOptions.hosts) { - this.hostIndex = 0; - this._doConnect(connectOptions.hosts[0], connectOptions.ports[0]); - } else { - this._doConnect(this.host, this.port); - } - - }; - - ClientImpl.prototype.subscribe = function (filter, subscribeOptions) { - this._trace("Client.subscribe", filter, subscribeOptions); - - if (!this.connected) - throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); - - var wireMessage = new WireMessage(MESSAGE_TYPE.SUBSCRIBE); - wireMessage.topics=[filter]; - if (subscribeOptions.qos != undefined) - wireMessage.requestedQos = [subscribeOptions.qos]; - else - wireMessage.requestedQos = [0]; - - if (subscribeOptions.onSuccess) { - wireMessage.callback = function() {subscribeOptions.onSuccess({invocationContext:subscribeOptions.invocationContext});}; - } - if (subscribeOptions.timeout) { - wireMessage.timeOut = new Timeout(this, window, subscribeOptions.timeout, subscribeOptions.onFailure - , [{invocationContext:subscribeOptions.invocationContext, - errorCode:ERROR.SUBSCRIBE_TIMEOUT.code, - errorMessage:format(ERROR.SUBSCRIBE_TIMEOUT)}]); - } - - // All subscriptions return a SUBACK. - this._requires_ack(wireMessage); - this._schedule_message(wireMessage); - }; - - /** @ignore */ - ClientImpl.prototype.unsubscribe = function(filter, unsubscribeOptions) { - this._trace("Client.unsubscribe", filter, unsubscribeOptions); - - if (!this.connected) - throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); - - var wireMessage = new WireMessage(MESSAGE_TYPE.UNSUBSCRIBE); - wireMessage.topics = [filter]; - - if (unsubscribeOptions.onSuccess) { - wireMessage.callback = function() {unsubscribeOptions.onSuccess({invocationContext:unsubscribeOptions.invocationContext});}; - } - if (unsubscribeOptions.timeout) { - wireMessage.timeOut = new Timeout(this, window, unsubscribeOptions.timeout, unsubscribeOptions.onFailure - , [{invocationContext:unsubscribeOptions.invocationContext, - errorCode:ERROR.UNSUBSCRIBE_TIMEOUT.code, - errorMessage:format(ERROR.UNSUBSCRIBE_TIMEOUT)}]); - } - - // All unsubscribes return a SUBACK. - this._requires_ack(wireMessage); - this._schedule_message(wireMessage); - }; - - ClientImpl.prototype.send = function (message) { - this._trace("Client.send", message); - - if (!this.connected) - throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); - - wireMessage = new WireMessage(MESSAGE_TYPE.PUBLISH); - wireMessage.payloadMessage = message; - - if (message.qos > 0) - this._requires_ack(wireMessage); - else if (this.onMessageDelivered) - this._notify_msg_sent[wireMessage] = this.onMessageDelivered(wireMessage.payloadMessage); - this._schedule_message(wireMessage); - }; - - ClientImpl.prototype.disconnect = function () { - this._trace("Client.disconnect"); - - if (!this.socket) - throw new Error(format(ERROR.INVALID_STATE, ["not connecting or connected"])); - - wireMessage = new WireMessage(MESSAGE_TYPE.DISCONNECT); - - // Run the disconnected call back as soon as the message has been sent, - // in case of a failure later on in the disconnect processing. - // as a consequence, the _disconected call back may be run several times. - this._notify_msg_sent[wireMessage] = scope(this._disconnected, this); - - this._schedule_message(wireMessage); - }; - - ClientImpl.prototype.getTraceLog = function () { - if ( this._traceBuffer !== null ) { - this._trace("Client.getTraceLog", new Date()); - this._trace("Client.getTraceLog in flight messages", this._sentMessages.length); - for (key in this._sentMessages) - this._trace("_sentMessages ",key, this._sentMessages[key]); - for (key in this._receivedMessages) - this._trace("_receivedMessages ",key, this._receivedMessages[key]); - - return this._traceBuffer; - } - }; - - ClientImpl.prototype.startTrace = function () { - if ( this._traceBuffer === null ) { - this._traceBuffer = []; - } - this._trace("Client.startTrace", new Date(), version); - }; - - ClientImpl.prototype.stopTrace = function () { - delete this._traceBuffer; - }; - - ClientImpl.prototype._doConnect = function (host, port) { - // When the socket is open, this client will send the CONNECT WireMessage using the saved parameters. - if (this.connectOptions.useSSL) - wsurl = ["wss://", host, ":", port, "/mqtt"].join(""); - else - wsurl = ["ws://", host, ":", port, "/mqtt"].join(""); - this.connected = false; - this.socket = new WebSocket(wsurl, 'mqttv3.1'); - this.socket.binaryType = 'arraybuffer'; - this.socket.onopen = scope(this._on_socket_open, this); - this.socket.onmessage = scope(this._on_socket_message, this); - this.socket.onerror = scope(this._on_socket_error, this); - this.socket.onclose = scope(this._on_socket_close, this); - - this.sendPinger = new Pinger(this, window, this.connectOptions.keepAliveInterval); - this.receivePinger = new Pinger(this, window, this.connectOptions.keepAliveInterval); - - this._connectTimeout = new Timeout(this, window, this.connectOptions.timeout, this._disconnected, [ERROR.CONNECT_TIMEOUT.code, format(ERROR.CONNECT_TIMEOUT)]); - }; - - - // Schedule a new message to be sent over the WebSockets - // connection. CONNECT messages cause WebSocket connection - // to be started. All other messages are queued internally - // until this has happened. When WS connection starts, process - // all outstanding messages. - ClientImpl.prototype._schedule_message = function (message) { - this._msg_queue.push(message); - // Process outstanding messages in the queue if we have an open socket, and have received CONNACK. - if (this.connected) { - this._process_queue(); - } - }; - - ClientImpl.prototype.store = function(prefix, wireMessage) { - storedMessage = {type:wireMessage.type, messageIdentifier:wireMessage.messageIdentifier, version:1}; - - switch(wireMessage.type) { - case MESSAGE_TYPE.PUBLISH: - if(wireMessage.pubRecReceived) - storedMessage.pubRecReceived = true; - - // Convert the payload to a hex string. - storedMessage.payloadMessage = {}; - var hex = ""; - var messageBytes = wireMessage.payloadMessage.payloadBytes; - for (var i=0; i= 2) { - var x = parseInt(hex.substring(0, 2), 16); - hex = hex.substring(2, hex.length); - byteStream[i++] = x; - } - var payloadMessage = new Messaging.Message(byteStream); - - payloadMessage.qos = storedMessage.payloadMessage.qos; - payloadMessage.destinationName = storedMessage.payloadMessage.destinationName; - if (storedMessage.payloadMessage.duplicate) - payloadMessage.duplicate = true; - if (storedMessage.payloadMessage.retained) - payloadMessage.retained = true; - wireMessage.payloadMessage = payloadMessage; - - break; - - default: - throw Error(format(ERROR.INVALID_STORED_DATA, [key, value])); - } - - if (key.indexOf("Sent:"+this._localKey) == 0) { - this._sentMessages[wireMessage.messageIdentifier] = wireMessage; - } else if (key.indexOf("Received:"+this._localKey) == 0) { - this._receivedMessages[wireMessage.messageIdentifier] = wireMessage; - } - }; - - ClientImpl.prototype._process_queue = function () { - var message = null; - // Process messages in order they were added - var fifo = this._msg_queue.reverse(); - - // Send all queued messages down socket connection - while ((message = fifo.pop())) { - this._socket_send(message); - // Notify listeners that message was successfully sent - if (this._notify_msg_sent[message]) { - this._notify_msg_sent[message](); - delete this._notify_msg_sent[message]; - } - } - }; - - /** - * @ignore - * Expect an ACK response for this message. Add message to the set of in progress - * messages and set an unused identifier in this message. - */ - ClientImpl.prototype._requires_ack = function (wireMessage) { - var messageCount = Object.keys(this._sentMessages).length; - if (messageCount > this.maxMessageIdentifier) - throw Error ("Too many messages:"+messageCount); - - while(this._sentMessages[this._message_identifier] !== undefined) { - this._message_identifier++; - } - wireMessage.messageIdentifier = this._message_identifier; - this._sentMessages[wireMessage.messageIdentifier] = wireMessage; - if (wireMessage.type === MESSAGE_TYPE.PUBLISH) { - this.store("Sent:", wireMessage); - } - if (this._message_identifier === this.maxMessagIdentifier) { - this._message_identifier = 1; - } - }; - - /** - * @ignore - * Called when the underlying websocket has been opened. - */ - ClientImpl.prototype._on_socket_open = function () { - // Create the CONNECT message object. - var wireMessage = new WireMessage(MESSAGE_TYPE.CONNECT, this.connectOptions); - wireMessage.clientId = this.clientId; - this._socket_send(wireMessage); - }; - - /** - * @ignore - * Called when the underlying websocket has received a complete packet. - */ - ClientImpl.prototype._on_socket_message = function (event) { - this._trace("Client._on_socket_message", event.data); - - // Reset the receive ping timer, we now have evidence the server is alive. - this.receivePinger.reset(); - var byteArray = new Uint8Array(event.data); - try { - var wireMessage = decodeMessage(byteArray); - } catch (error) { - this._disconnected(ERROR.INTERNAL_ERROR.code , format(ERROR.INTERNAL_ERROR, [error.message])); - return; - } - this._trace("Client._on_socket_message", wireMessage); - - switch(wireMessage.type) { - case MESSAGE_TYPE.CONNACK: - this._connectTimeout.cancel(); - - // If we have started using clean session then clear up the local state. - if (this.connectOptions.cleanSession) { - for (key in this._sentMessages) { - var sentMessage = this._sentMessages[key]; - localStorage.removeItem("Sent:"+this._localKey+sentMessage.messageIdentifier); - } - this._sentMessages = {}; - - for (key in this._receivedMessages) { - var receivedMessage = this._receivedMessages[key]; - localStorage.removeItem("Received:"+this._localKey+receivedMessage.messageIdentifier); - } - this._receivedMessages = {}; - } - // Client connected and ready for business. - if (wireMessage.returnCode === 0) { - this.connected = true; - // Jump to the end of the list of hosts and stop looking for a good host. - if (this.connectOptions.hosts) - this.hostIndex = this.connectOptions.hosts.length; - } else { - this._disconnected(ERROR.CONNACK_RETURNCODE.code , format(ERROR.CONNACK_RETURNCODE, [wireMessage.returnCode, CONNACK_RC[wireMessage.returnCode]])); - break; - } - - // Resend messages. - var sequencedMessages = new Array(); - for (var msgId in this._sentMessages) { - if (this._sentMessages.hasOwnProperty(msgId)) - sequencedMessages.push(this._sentMessages[msgId]); - } - - // Sort sentMessages into the original sent order. - var sequencedMessages = sequencedMessages.sort(function(a,b) {return a.sequence - b.sequence;} ); - for (var i=0, len=sequencedMessages.length; i - * Other programming languages, - * Java, - * C. - *

- * Most applications will create just one Client object and then call its connect() method, - * however applications can create more than one Client object if they wish. - * In this case the combination of host, port and clientId attributes must be different for each Client object. - *

- * The send, subscribe and unsubscribe methods are implemented as asynchronous JavaScript methods - * (even though the underlying protocol exchange might be synchronous in nature). - * This means they signal their completion by calling back to the application, - * via Success or Failure callback functions provided by the application on the method in question. - * Such callbacks are called at most once per method invocation and do not persist beyond the lifetime - * of the script that made the invocation. - *

- * In contrast there are some callback functions most notably onMessageArrived - * that are defined on the Messaging.Client object. - * These may get called multiple times, and aren't directly related to specific method invocations made by the client. - * - * @name Messaging.Client - * - * @constructor - * Creates a Messaging.Client object that can be used to communicate with a Messaging server. - * - * @param {string} host the address of the messaging server, as a DNS name or dotted decimal IP address. - * @param {number} port the port number in the host to connect to. - * @param {string} clientId the Messaging client identifier, between 1 and 23 characters in length. - * - * @property {string} host read only the server's DNS hostname or dotted decimal IP address. - * @property {number} port read only the server's port. - * @property {string} clientId read only used when connecting to the server. - * @property {function} onConnectionLost called when a connection has been lost, - * after a connect() method has succeeded. - * Establish the call back used when a connection has been lost. The connection may be - * lost because the client initiates a disconnect or because the server or network - * cause the client to be disconnected. The disconnect call back may be called without - * the connectionComplete call back being invoked if, for example the client fails to - * connect. - * A single response object parameter is passed to the onConnectionLost callback containing the following fields: - *

    - *
  1. errorCode - *
  2. errorMessage - *
- * @property {function} onMessageDelivered called when a message has been delivered. - * All processing that this Client will ever do has been completed. So, for example, - * in the case of a Qos=2 message sent by this client, the PubComp flow has been received from the server - * and the message has been removed from persistent storage before this callback is invoked. - * Parameters passed to the onMessageDelivered callback are: - *
    - *
  1. Messaging.Message that was delivered. - *
- * @property {function} onMessageArrived called when a message has arrived in this Messaging.client. - * Parameters passed to the onMessageArrived callback are: - *
    - *
  1. Messaging.Message that has arrived. - *
- */ - var Client = function (host, port, clientId) { - if (typeof host !== "string") - throw new Error(format(ERROR.INVALID_TYPE, [typeof host, "host"])); - if (typeof port !== "number" || port < 0) - throw new Error(format(ERROR.INVALID_TYPE, [typeof port, "port"])); - - var clientIdLength = 0; - for (var i = 0; i 23) - throw new Error(format(ERROR.INVALID_ARGUMENT, [clientId, "clientId"])); - - var client = new ClientImpl(host, port, clientId); - this._getHost = function() { return client.host; }; - this._setHost = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; - - this._getPort = function() { return client.port; }; - this._setPort = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; - - this._getClientId = function() { return client.clientId; }; - this._setClientId = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; - - this._getOnConnectionLost = function() { return client.onConnectionLost; }; - this._setOnConnectionLost = function(newOnConnectionLost) { - if (typeof newOnConnectionLost === "function") - client.onConnectionLost = newOnConnectionLost; - else - throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnConnectionLost, "onConnectionLost"])); - }; - - this._getOnMessageDelivered = function() { return client.onMessageDelivered; }; - this._setOnMessageDelivered = function(newOnMessageDelivered) { - if (typeof newOnMessageDelivered === "function") - client.onMessageDelivered = newOnMessageDelivered; - else - throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageDelivered, "onMessageDelivered"])); - }; - - this._getOnMessageArrived = function() { return client.onMessageArrived; }; - this._setOnMessageArrived = function(newOnMessageArrived) { - if (typeof newOnMessageArrived === "function") - client.onMessageArrived = newOnMessageArrived; - else - throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageArrived, "onMessageArrived"])); - }; - - /** - * Connect this Messaging client to its server. - * - * @name Messaging.Client#connect - * @function - * @param {Object} [connectOptions] attributes used with the connection. - *

- * Properties of the connect options are: - * @config {number} [timeout] If the connect has not succeeded within this number of seconds, it is deemed to have failed. - * The default is 30 seconds. - * @config {string} [userName] Authentication username for this connection. - * @config {string} [password] Authentication password for this connection. - * @config {Messaging.Message} [willMessage] sent by the server when the client disconnects abnormally. - * @config {Number} [keepAliveInterval] the server disconnects this client if there is no activity for this - * number of seconds. The default value of 60 seconds is assumed if not set. - * @config {boolean} [cleanSession] if true(default) the client and server persistent state is deleted on successful connect. - * @config {boolean} [useSSL] if present and true, use an SSL Websocket connection. - * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. - * @config {function} [onSuccess] called when the connect acknowledgement has been received from the server. - * A single response object parameter is passed to the onSuccess callback containing the following fields: - *

    - *
  1. invocationContext as passed in to the onSuccess method in the connectOptions. - *
- * @config {function} [onFailure] called when the connect request has failed or timed out. - * A single response object parameter is passed to the onFailure callback containing the following fields: - *
    - *
  1. invocationContext as passed in to the onFailure method in the connectOptions. - *
  2. errorCode a number indicating the nature of the error. - *
  3. errorMessage text describing the error. - *
- * @config {Array} [hosts] If present this set of hostnames is tried in order in place - * of the host and port paramater on the construtor. The hosts and the matching ports are tried one at at time in order until - * one of then succeeds. - * @config {Array} [ports] If present this set of ports matching the hosts. - * @throws {InvalidState} if the client is not in disconnected state. The client must have received connectionLost - * or disconnected before calling connect for a second or subsequent time. - */ - this.connect = function (connectOptions) { - connectOptions = connectOptions || {} ; - validate(connectOptions, {timeout:"number", - userName:"string", - password:"string", - willMessage:"object", - keepAliveInterval:"number", - cleanSession:"boolean", - useSSL:"boolean", - invocationContext:"object", - onSuccess:"function", - onFailure:"function", - hosts:"object", - ports:"object"}); - - // If no keep alive interval is set, assume 60 seconds. - if (connectOptions.keepAliveInterval === undefined) - connectOptions.keepAliveInterval = 60; - - if (connectOptions.willMessage) { - if (!(connectOptions.willMessage instanceof Message)) - throw new Error(format(ERROR.INVALID_TYPE, [connectOptions.willMessage, "connectOptions.willMessage"])); - // The will message must have a payload that can be represented as a string. - // Cause the willMessage to throw an exception if this is not the case. - connectOptions.willMessage.stringPayload; - - if (typeof connectOptions.willMessage.destinationName === "undefined") - throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.willMessage.destinationName, "connectOptions.willMessage.destinationName"])); - } - if (typeof connectOptions.cleanSession === "undefined") - connectOptions.cleanSession = true; - if (connectOptions.hosts) { - if (!connectOptions.ports) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); - if (!(connectOptions.hosts instanceof Array) ) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); - if (!(connectOptions.ports instanceof Array) ) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); - if (connectOptions.hosts.length <1 ) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); - if (connectOptions.hosts.length != connectOptions.ports.length) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); - for (var i = 0; i - * @param {object} [subscribeOptions] used to control the subscription, as follows: - *

- * @config {number} [qos] the maiximum qos of any publications sent as a result of making this subscription. - * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. - * @config {function} [onSuccess] called when the subscribe acknowledgement has been received from the server. - * A single response object parameter is passed to the onSuccess callback containing the following fields: - *

    - *
  1. invocationContext if set in the subscribeOptions. - *
- * @config {function} [onFailure] called when the subscribe request has failed or timed out. - * A single response object parameter is passed to the onFailure callback containing the following fields: - *
    - *
  1. invocationContext if set in the subscribeOptions. - *
  2. errorCode a number indicating the nature of the error. - *
  3. errorMessage text describing the error. - *
- * @config {number} [timeout] which if present determines the number of seconds after which the onFailure calback is called - * the presence of a timeout does not prevent the onSuccess callback from being called when the MQTT Suback is eventually received. - * @throws {InvalidState} if the client is not in connected state. - */ - this.subscribe = function (filter, subscribeOptions) { - if (typeof filter !== "string") - throw new Error("Invalid argument:"+filter); - subscribeOptions = subscribeOptions || {} ; - validate(subscribeOptions, {qos:"number", - invocationContext:"object", - onSuccess:"function", - onFailure:"function", - timeout:"number" - }); - if (subscribeOptions.timeout && !subscribeOptions.onFailure) - throw new Error("subscribeOptions.timeout specified with no onFailure callback."); - if (typeof subscribeOptions.qos !== "undefined" - && !(subscribeOptions.qos === 0 || subscribeOptions.qos === 1 || subscribeOptions.qos === 2 )) - throw new Error(format(ERROR.INVALID_ARGUMENT, [subscribeOptions.qos, "subscribeOptions.qos"])); - client.subscribe(filter, subscribeOptions); - }; - - /** - * Unsubscribe for messages, stop receiving messages sent to destinations described by the filter. - * - * @name Messaging.Client#unsubscribe - * @function - * @param {string} filter describing the destinations to receive messages from. - * @param {object} [unsubscribeOptions] used to control the subscription, as follows: - *

- * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. - * @config {function} [onSuccess] called when the unsubscribe acknowledgement has been receive dfrom the server. - * A single response object parameter is passed to the onSuccess callback containing the following fields: - *

    - *
  1. invocationContext if set in the unsubscribeOptions. - *
- * @config {function} [onFailure] called when the unsubscribe request has failed or timed out. - * A single response object parameter is passed to the onFailure callback containing the following fields: - *
    - *
  1. invocationContext if set in the unsubscribeOptions. - *
  2. errorCode a number indicating the nature of the error. - *
  3. errorMessage text describing the error. - *
- * @config {number} [timeout] which if present determines the number of seconds after which the onFailure callback is called, the - * presence of a timeout does not prevent the onSuccess callback from being called when the MQTT UnSuback is eventually received. - * @throws {InvalidState} if the client is not in connected state. - */ - this.unsubscribe = function (filter, unsubscribeOptions) { - if (typeof filter !== "string") - throw new Error("Invalid argument:"+filter); - unsubscribeOptions = unsubscribeOptions || {} ; - validate(unsubscribeOptions, {invocationContext:"object", - onSuccess:"function", - onFailure:"function", - timeout:"number" - }); - if (unsubscribeOptions.timeout && !unsubscribeOptions.onFailure) - throw new Error("unsubscribeOptions.timeout specified with no onFailure callback."); - client.unsubscribe(filter, unsubscribeOptions); - }; - - /** - * Send a message to the consumers of the destination in the Message. - * - * @name Messaging.Client#send - * @function - * @param {Messaging.Message} message to send. - - * @throws {InvalidState} if the client is not in connected state. - */ - this.send = function (message) { - if (!(message instanceof Message)) - throw new Error("Invalid argument:"+typeof message); - if (typeof message.destinationName === "undefined") - throw new Error("Invalid parameter Message.destinationName:"+message.destinationName); - - client.send(message); - }; - - /** - * Normal disconnect of this Messaging client from its server. - * - * @name Messaging.Client#disconnect - * @function - * @throws {InvalidState} if the client is not in connected or connecting state. - */ - this.disconnect = function () { - client.disconnect(); - }; - - /** - * Get the contents of the trace log. - * - * @name Messaging.Client#getTraceLog - * @function - * @return {Object[]} tracebuffer containing the time ordered trace records. - */ - this.getTraceLog = function () { - return client.getTraceLog(); - } - - /** - * Start tracing. - * - * @name Messaging.Client#startTrace - * @function - */ - this.startTrace = function () { - client.startTrace(); - }; - - /** - * Stop tracing. - * - * @name Messaging.Client#stopTrace - * @function - */ - this.stopTrace = function () { - client.stopTrace(); - }; - }; - - Client.prototype = { - get host() { return this._getHost(); }, - set host(newHost) { this._setHost(newHost); }, - - get port() { return this._getPort(); }, - set port(newPort) { this._setPort(newPort); }, - - get clientId() { return this._getClientId(); }, - set clientId(newClientId) { this._setClientId(newClientId); }, - - get onConnectionLost() { return this._getOnConnectionLost(); }, - set onConnectionLost(newOnConnectionLost) { this._setOnConnectionLost(newOnConnectionLost); }, - - get onMessageDelivered() { return this._getOnMessageDelivered(); }, - set onMessageDelivered(newOnMessageDelivered) { this._setOnMessageDelivered(newOnMessageDelivered); }, - - get onMessageArrived() { return this._getOnMessageArrived(); }, - set onMessageArrived(newOnMessageArrived) { this._setOnMessageArrived(newOnMessageArrived); } - }; - - /** - * An application message, sent or received. - *

- * Other programming languages, - * Java, - * C. - *

- * All attributes may be null, which implies the default values. - * - * @name Messaging.Message - * @constructor - * @param {String|ArrayBuffer} payload The message data to be sent. - *

- * @property {string} payloadString read only The payload as a string if the payload consists of valid UTF-8 characters. - * @property {ArrayBuffer} payloadBytes read only The payload as an ArrayBuffer. - *

- * @property {string} destinationName mandatory The name of the destination to which the message is to be sent - * (for messages about to be sent) or the name of the destination from which the message has been received. - * (for messages received by the onMessage function). - *

- * @property {number} qos The Quality of Service used to deliver the message. - *

- *
0 Best effort (default). - *
1 At least once. - *
2 Exactly once. - *
- *

- * @property {Boolean} retained If true, the message is to be retained by the server and delivered - * to both current and future subscriptions. - * If false the server only delivers the message to current subscribers, this is the default for new Messages. - * A received message has the retained boolean set to true if the message was published - * with the retained boolean set to true - * and the subscrption was made after the message has been published. - *

- * @property {Boolean} duplicate read only If true, this message might be a duplicate of one which has already been received. - * This is only set on messages received from the server. - * - */ - var Message = function (newPayload) { - var payload; - if ( typeof newPayload === "string" - || newPayload instanceof ArrayBuffer - || newPayload instanceof Int8Array - || newPayload instanceof Uint8Array - || newPayload instanceof Int16Array - || newPayload instanceof Uint16Array - || newPayload instanceof Int32Array - || newPayload instanceof Uint32Array - || newPayload instanceof Float32Array - || newPayload instanceof Float64Array - ) { - payload = newPayload; - } else { - throw (format(ERROR.INVALID_ARGUMENT, [newPayload, "newPayload"])); - } - - this._getPayloadString = function () { - if (typeof payload === "string") - return payload; - else - return parseUTF8(payload, 0, payload.length); - }; - - this._getPayloadBytes = function() { - if (typeof payload === "string") { - var buffer = new ArrayBuffer(UTF8Length(payload)); - var byteStream = new Uint8Array(buffer); - stringToUTF8(payload, byteStream, 0); - - return byteStream; - } else { - return payload; - }; - }; - - var destinationName = undefined; - this._getDestinationName = function() { return destinationName; }; - this._setDestinationName = function(newDestinationName) { - if (typeof newDestinationName === "string") - destinationName = newDestinationName; - else - throw new Error(format(ERROR.INVALID_ARGUMENT, [newDestinationName, "newDestinationName"])); - }; - - var qos = 0; - this._getQos = function() { return qos; }; - this._setQos = function(newQos) { - if (newQos === 0 || newQos === 1 || newQos === 2 ) - qos = newQos; - else - throw new Error("Invalid argument:"+newQos); - }; - - var retained = false; - this._getRetained = function() { return retained; }; - this._setRetained = function(newRetained) { - if (typeof newRetained === "boolean") - retained = newRetained; - else - throw new Error(format(ERROR.INVALID_ARGUMENT, [newRetained, "newRetained"])); - }; - - var duplicate = false; - this._getDuplicate = function() { return duplicate; }; - this._setDuplicate = function(newDuplicate) { duplicate = newDuplicate; }; - }; - - Message.prototype = { - get payloadString() { return this._getPayloadString(); }, - get payloadBytes() { return this._getPayloadBytes(); }, - - get destinationName() { return this._getDestinationName(); }, - set destinationName(newDestinationName) { this._setDestinationName(newDestinationName); }, - - get qos() { return this._getQos(); }, - set qos(newQos) { this._setQos(newQos); }, - - get retained() { return this._getRetained(); }, - set retained(newRetained) { this._setRetained(newRetained); }, - - get duplicate() { return this._getDuplicate(); }, - set duplicate(newDuplicate) { this._setDuplicate(newDuplicate); } - }; - - // Module contents. - return { - Client: Client, - Message: Message - }; + } + output += String.fromCharCode(utf16); + } + return output; + } + + /** @ignore Repeat keepalive requests, monitor responses.*/ + var Pinger = function(client, window, keepAliveInterval) { + this._client = client; + this._window = window; + this._keepAliveInterval = keepAliveInterval*1000; + this.isReset = false; + + var pingReq = new WireMessage(MESSAGE_TYPE.PINGREQ).encode(); + + var doTimeout = function (pinger) { + return function () { + return doPing.apply(pinger); + }; + }; + + /** @ignore */ + var doPing = function() { + if (!this.isReset) { + this._client._trace("Pinger.doPing", "Timed out"); + this._client._disconnected( ERROR.PING_TIMEOUT.code , format(ERROR.PING_TIMEOUT)); + } else { + this.isReset = false; + this._client._trace("Pinger.doPing", "send PINGREQ"); + this._client.socket.send(pingReq); + this.timeout = this._window.setTimeout(doTimeout(this), this._keepAliveInterval); + } + } + + this.reset = function() { + this.isReset = true; + this._window.clearTimeout(this.timeout); + if (this._keepAliveInterval > 0) + this.timeout = setTimeout(doTimeout(this), this._keepAliveInterval); + } + + this.cancel = function() { + this._window.clearTimeout(this.timeout); + } + }; + + /** @ignore Monitor request completion. */ + var Timeout = function(client, window, timeoutSeconds, action, args) { + this._window = window; + if (!timeoutSeconds) + timeoutSeconds = 30; + + var doTimeout = function (action, client, args) { + return function () { + return action.apply(client, args); + }; + }; + this.timeout = setTimeout(doTimeout(action, client, args), timeoutSeconds * 1000); + + this.cancel = function() { + this._window.clearTimeout(this.timeout); + } + }; + + /* + * Internal implementation of the Websockets MQTT V3.1 client. + * + * @name Messaging.ClientImpl @constructor + * @param {String} host the DNS nameof the webSocket host. + * @param {Number} port the port number for that host. + * @param {String} clientId the MQ client identifier. + */ + var ClientImpl = function (host, port, clientId) { + // Check dependencies are satisfied in this browser. + if (!("WebSocket" in global && global["WebSocket"] !== null)) { + throw new Error(format(ERROR.UNSUPPORTED, ["WebSocket"])); + } + if (!("localStorage" in global && global["localStorage"] !== null)) { + throw new Error(format(ERROR.UNSUPPORTED, ["localStorage"])); + } + if (!("ArrayBuffer" in global && global["ArrayBuffer"] !== null)) { + throw new Error(format(ERROR.UNSUPPORTED, ["ArrayBuffer"])); + } + + this._trace("Messaging.Client", host, port, clientId); + + this.host = host; + this.port = port; + this.clientId = clientId; + + // Local storagekeys are qualified with the following string. + this._localKey=host+":"+port+":"+clientId+":"; + + // Create private instance-only message queue + // Internal queue of messages to be sent, in sending order. + this._msg_queue = []; + + // Messages we have sent and are expecting a response for, indexed by their respective message ids. + this._sentMessages = {}; + + // Messages we have received and acknowleged and are expecting a confirm message for + // indexed by their respective message ids. + this._receivedMessages = {}; + + // Internal list of callbacks to be executed when messages + // have been successfully sent over web socket, e.g. disconnect + // when it doesn't have to wait for ACK, just message is dispatched. + this._notify_msg_sent = {}; + + // Unique identifier for SEND messages, incrementing + // counter as messages are sent. + this._message_identifier = 1; + + // Used to determine the transmission sequence of stored sent messages. + this._sequence = 0; + + + // Load the local state, if any, from the saved version, only restore state relevant to this client. + for(key in localStorage) + if ( key.indexOf("Sent:"+this._localKey) == 0 + || key.indexOf("Received:"+this._localKey) == 0) + this.restore(key); + }; + + // Messaging Client public instance members. + ClientImpl.prototype.host; + ClientImpl.prototype.port; + ClientImpl.prototype.clientId; + + // Messaging Client private instance members. + ClientImpl.prototype.socket; + /* true once we have received an acknowledgement to a CONNECT packet. */ + ClientImpl.prototype.connected = false; + /* The largest message identifier allowed, may not be larger than 2**16 but + * if set smaller reduces the maximum number of outbound messages allowed. + */ + ClientImpl.prototype.maxMessageIdentifier = 65536; + ClientImpl.prototype.connectOptions; + ClientImpl.prototype.hostIndex; + ClientImpl.prototype.onConnectionLost; + ClientImpl.prototype.onMessageDelivered; + ClientImpl.prototype.onMessageArrived; + ClientImpl.prototype._msg_queue = null; + ClientImpl.prototype._connectTimeout; + /* The sendPinger monitors how long we allow before we send data to prove to the server that we are alive. */ + ClientImpl.prototype.sendPinger = null; + /* The receivePinger monitors how long we allow before we require evidence that the server is alive. */ + ClientImpl.prototype.receivePinger = null; + + ClientImpl.prototype._traceBuffer = null; + ClientImpl.prototype._MAX_TRACE_ENTRIES = 100; + + ClientImpl.prototype.connect = function (connectOptions) { + var connectOptionsMasked = this._traceMask(connectOptions, "password"); + this._trace("Client.connect", connectOptionsMasked, this.socket, this.connected); + + if (this.connected) + throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); + if (this.socket) + throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); + + this.connectOptions = connectOptions; + + if (connectOptions.hosts) { + this.hostIndex = 0; + this._doConnect(connectOptions.hosts[0], connectOptions.ports[0]); + } else { + this._doConnect(this.host, this.port); + } + + }; + + ClientImpl.prototype.subscribe = function (filter, subscribeOptions) { + this._trace("Client.subscribe", filter, subscribeOptions); + + if (!this.connected) + throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); + + var wireMessage = new WireMessage(MESSAGE_TYPE.SUBSCRIBE); + wireMessage.topics=[filter]; + if (subscribeOptions.qos != undefined) + wireMessage.requestedQos = [subscribeOptions.qos]; + else + wireMessage.requestedQos = [0]; + + if (subscribeOptions.onSuccess) { + wireMessage.callback = function() {subscribeOptions.onSuccess({invocationContext:subscribeOptions.invocationContext});}; + } + if (subscribeOptions.timeout) { + wireMessage.timeOut = new Timeout(this, window, subscribeOptions.timeout, subscribeOptions.onFailure + , [{invocationContext:subscribeOptions.invocationContext, + errorCode:ERROR.SUBSCRIBE_TIMEOUT.code, + errorMessage:format(ERROR.SUBSCRIBE_TIMEOUT)}]); + } + + // All subscriptions return a SUBACK. + this._requires_ack(wireMessage); + this._schedule_message(wireMessage); + }; + + /** @ignore */ + ClientImpl.prototype.unsubscribe = function(filter, unsubscribeOptions) { + this._trace("Client.unsubscribe", filter, unsubscribeOptions); + + if (!this.connected) + throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); + + var wireMessage = new WireMessage(MESSAGE_TYPE.UNSUBSCRIBE); + wireMessage.topics = [filter]; + + if (unsubscribeOptions.onSuccess) { + wireMessage.callback = function() {unsubscribeOptions.onSuccess({invocationContext:unsubscribeOptions.invocationContext});}; + } + if (unsubscribeOptions.timeout) { + wireMessage.timeOut = new Timeout(this, window, unsubscribeOptions.timeout, unsubscribeOptions.onFailure + , [{invocationContext:unsubscribeOptions.invocationContext, + errorCode:ERROR.UNSUBSCRIBE_TIMEOUT.code, + errorMessage:format(ERROR.UNSUBSCRIBE_TIMEOUT)}]); + } + + // All unsubscribes return a SUBACK. + this._requires_ack(wireMessage); + this._schedule_message(wireMessage); + }; + + ClientImpl.prototype.send = function (message) { + this._trace("Client.send", message); + + if (!this.connected) + throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); + + wireMessage = new WireMessage(MESSAGE_TYPE.PUBLISH); + wireMessage.payloadMessage = message; + + if (message.qos > 0) + this._requires_ack(wireMessage); + else if (this.onMessageDelivered) + this._notify_msg_sent[wireMessage] = this.onMessageDelivered(wireMessage.payloadMessage); + this._schedule_message(wireMessage); + }; + + ClientImpl.prototype.disconnect = function () { + this._trace("Client.disconnect"); + + if (!this.socket) + throw new Error(format(ERROR.INVALID_STATE, ["not connecting or connected"])); + + wireMessage = new WireMessage(MESSAGE_TYPE.DISCONNECT); + + // Run the disconnected call back as soon as the message has been sent, + // in case of a failure later on in the disconnect processing. + // as a consequence, the _disconected call back may be run several times. + this._notify_msg_sent[wireMessage] = scope(this._disconnected, this); + + this._schedule_message(wireMessage); + }; + + ClientImpl.prototype.getTraceLog = function () { + if ( this._traceBuffer !== null ) { + this._trace("Client.getTraceLog", new Date()); + this._trace("Client.getTraceLog in flight messages", this._sentMessages.length); + for (key in this._sentMessages) + this._trace("_sentMessages ",key, this._sentMessages[key]); + for (key in this._receivedMessages) + this._trace("_receivedMessages ",key, this._receivedMessages[key]); + + return this._traceBuffer; + } + }; + + ClientImpl.prototype.startTrace = function () { + if ( this._traceBuffer === null ) { + this._traceBuffer = []; + } + this._trace("Client.startTrace", new Date(), version); + }; + + ClientImpl.prototype.stopTrace = function () { + delete this._traceBuffer; + }; + + ClientImpl.prototype._doConnect = function (host, port) { + // When the socket is open, this client will send the CONNECT WireMessage using the saved parameters. + if (this.connectOptions.useSSL) + wsurl = ["wss://", host, ":", port, "/mqtt"].join(""); + else + wsurl = ["ws://", host, ":", port, "/mqtt"].join(""); + this.connected = false; + this.socket = new WebSocket(wsurl, 'mqttv3.1'); + this.socket.binaryType = 'arraybuffer'; + this.socket.onopen = scope(this._on_socket_open, this); + this.socket.onmessage = scope(this._on_socket_message, this); + this.socket.onerror = scope(this._on_socket_error, this); + this.socket.onclose = scope(this._on_socket_close, this); + + this.sendPinger = new Pinger(this, window, this.connectOptions.keepAliveInterval); + this.receivePinger = new Pinger(this, window, this.connectOptions.keepAliveInterval); + + this._connectTimeout = new Timeout(this, window, this.connectOptions.timeout, this._disconnected, [ERROR.CONNECT_TIMEOUT.code, format(ERROR.CONNECT_TIMEOUT)]); + }; + + + // Schedule a new message to be sent over the WebSockets + // connection. CONNECT messages cause WebSocket connection + // to be started. All other messages are queued internally + // until this has happened. When WS connection starts, process + // all outstanding messages. + ClientImpl.prototype._schedule_message = function (message) { + this._msg_queue.push(message); + // Process outstanding messages in the queue if we have an open socket, and have received CONNACK. + if (this.connected) { + this._process_queue(); + } + }; + + ClientImpl.prototype.store = function(prefix, wireMessage) { + storedMessage = {type:wireMessage.type, messageIdentifier:wireMessage.messageIdentifier, version:1}; + + switch(wireMessage.type) { + case MESSAGE_TYPE.PUBLISH: + if(wireMessage.pubRecReceived) + storedMessage.pubRecReceived = true; + + // Convert the payload to a hex string. + storedMessage.payloadMessage = {}; + var hex = ""; + var messageBytes = wireMessage.payloadMessage.payloadBytes; + for (var i=0; i= 2) { + var x = parseInt(hex.substring(0, 2), 16); + hex = hex.substring(2, hex.length); + byteStream[i++] = x; + } + var payloadMessage = new Messaging.Message(byteStream); + + payloadMessage.qos = storedMessage.payloadMessage.qos; + payloadMessage.destinationName = storedMessage.payloadMessage.destinationName; + if (storedMessage.payloadMessage.duplicate) + payloadMessage.duplicate = true; + if (storedMessage.payloadMessage.retained) + payloadMessage.retained = true; + wireMessage.payloadMessage = payloadMessage; + + break; + + default: + throw Error(format(ERROR.INVALID_STORED_DATA, [key, value])); + } + + if (key.indexOf("Sent:"+this._localKey) == 0) { + this._sentMessages[wireMessage.messageIdentifier] = wireMessage; + } else if (key.indexOf("Received:"+this._localKey) == 0) { + this._receivedMessages[wireMessage.messageIdentifier] = wireMessage; + } + }; + + ClientImpl.prototype._process_queue = function () { + var message = null; + // Process messages in order they were added + var fifo = this._msg_queue.reverse(); + + // Send all queued messages down socket connection + while ((message = fifo.pop())) { + this._socket_send(message); + // Notify listeners that message was successfully sent + if (this._notify_msg_sent[message]) { + this._notify_msg_sent[message](); + delete this._notify_msg_sent[message]; + } + } + }; + + /** + * @ignore + * Expect an ACK response for this message. Add message to the set of in progress + * messages and set an unused identifier in this message. + */ + ClientImpl.prototype._requires_ack = function (wireMessage) { + var messageCount = Object.keys(this._sentMessages).length; + if (messageCount > this.maxMessageIdentifier) + throw Error ("Too many messages:"+messageCount); + + while(this._sentMessages[this._message_identifier] !== undefined) { + this._message_identifier++; + } + wireMessage.messageIdentifier = this._message_identifier; + this._sentMessages[wireMessage.messageIdentifier] = wireMessage; + if (wireMessage.type === MESSAGE_TYPE.PUBLISH) { + this.store("Sent:", wireMessage); + } + if (this._message_identifier === this.maxMessagIdentifier) { + this._message_identifier = 1; + } + }; + + /** + * @ignore + * Called when the underlying websocket has been opened. + */ + ClientImpl.prototype._on_socket_open = function () { + // Create the CONNECT message object. + var wireMessage = new WireMessage(MESSAGE_TYPE.CONNECT, this.connectOptions); + wireMessage.clientId = this.clientId; + this._socket_send(wireMessage); + }; + + /** + * @ignore + * Called when the underlying websocket has received a complete packet. + */ + ClientImpl.prototype._on_socket_message = function (event) { + this._trace("Client._on_socket_message", event.data); + + // Reset the receive ping timer, we now have evidence the server is alive. + this.receivePinger.reset(); + var byteArray = new Uint8Array(event.data); + try { + var wireMessage = decodeMessage(byteArray); + } catch (error) { + this._disconnected(ERROR.INTERNAL_ERROR.code , format(ERROR.INTERNAL_ERROR, [error.message])); + return; + } + this._trace("Client._on_socket_message", wireMessage); + + switch(wireMessage.type) { + case MESSAGE_TYPE.CONNACK: + this._connectTimeout.cancel(); + + // If we have started using clean session then clear up the local state. + if (this.connectOptions.cleanSession) { + for (key in this._sentMessages) { + var sentMessage = this._sentMessages[key]; + localStorage.removeItem("Sent:"+this._localKey+sentMessage.messageIdentifier); + } + this._sentMessages = {}; + + for (key in this._receivedMessages) { + var receivedMessage = this._receivedMessages[key]; + localStorage.removeItem("Received:"+this._localKey+receivedMessage.messageIdentifier); + } + this._receivedMessages = {}; + } + // Client connected and ready for business. + if (wireMessage.returnCode === 0) { + this.connected = true; + // Jump to the end of the list of hosts and stop looking for a good host. + if (this.connectOptions.hosts) + this.hostIndex = this.connectOptions.hosts.length; + } else { + this._disconnected(ERROR.CONNACK_RETURNCODE.code , format(ERROR.CONNACK_RETURNCODE, [wireMessage.returnCode, CONNACK_RC[wireMessage.returnCode]])); + break; + } + + // Resend messages. + var sequencedMessages = new Array(); + for (var msgId in this._sentMessages) { + if (this._sentMessages.hasOwnProperty(msgId)) + sequencedMessages.push(this._sentMessages[msgId]); + } + + // Sort sentMessages into the original sent order. + var sequencedMessages = sequencedMessages.sort(function(a,b) {return a.sequence - b.sequence;} ); + for (var i=0, len=sequencedMessages.length; i + * Other programming languages, + * Java, + * C. + *

+ * Most applications will create just one Client object and then call its connect() method, + * however applications can create more than one Client object if they wish. + * In this case the combination of host, port and clientId attributes must be different for each Client object. + *

+ * The send, subscribe and unsubscribe methods are implemented as asynchronous JavaScript methods + * (even though the underlying protocol exchange might be synchronous in nature). + * This means they signal their completion by calling back to the application, + * via Success or Failure callback functions provided by the application on the method in question. + * Such callbacks are called at most once per method invocation and do not persist beyond the lifetime + * of the script that made the invocation. + *

+ * In contrast there are some callback functions most notably onMessageArrived + * that are defined on the Messaging.Client object. + * These may get called multiple times, and aren't directly related to specific method invocations made by the client. + * + * @name Messaging.Client + * + * @constructor + * Creates a Messaging.Client object that can be used to communicate with a Messaging server. + * + * @param {string} host the address of the messaging server, as a DNS name or dotted decimal IP address. + * @param {number} port the port number in the host to connect to. + * @param {string} clientId the Messaging client identifier, between 1 and 23 characters in length. + * + * @property {string} host read only the server's DNS hostname or dotted decimal IP address. + * @property {number} port read only the server's port. + * @property {string} clientId read only used when connecting to the server. + * @property {function} onConnectionLost called when a connection has been lost, + * after a connect() method has succeeded. + * Establish the call back used when a connection has been lost. The connection may be + * lost because the client initiates a disconnect or because the server or network + * cause the client to be disconnected. The disconnect call back may be called without + * the connectionComplete call back being invoked if, for example the client fails to + * connect. + * A single response object parameter is passed to the onConnectionLost callback containing the following fields: + *

    + *
  1. errorCode + *
  2. errorMessage + *
+ * @property {function} onMessageDelivered called when a message has been delivered. + * All processing that this Client will ever do has been completed. So, for example, + * in the case of a Qos=2 message sent by this client, the PubComp flow has been received from the server + * and the message has been removed from persistent storage before this callback is invoked. + * Parameters passed to the onMessageDelivered callback are: + *
    + *
  1. Messaging.Message that was delivered. + *
+ * @property {function} onMessageArrived called when a message has arrived in this Messaging.client. + * Parameters passed to the onMessageArrived callback are: + *
    + *
  1. Messaging.Message that has arrived. + *
+ */ + var Client = function (host, port, clientId) { + if (typeof host !== "string") + throw new Error(format(ERROR.INVALID_TYPE, [typeof host, "host"])); + if (typeof port !== "number" || port < 0) + throw new Error(format(ERROR.INVALID_TYPE, [typeof port, "port"])); + + var clientIdLength = 0; + for (var i = 0; i 23) + throw new Error(format(ERROR.INVALID_ARGUMENT, [clientId, "clientId"])); + + var client = new ClientImpl(host, port, clientId); + this._getHost = function() { return client.host; }; + this._setHost = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; + + this._getPort = function() { return client.port; }; + this._setPort = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; + + this._getClientId = function() { return client.clientId; }; + this._setClientId = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; + + this._getOnConnectionLost = function() { return client.onConnectionLost; }; + this._setOnConnectionLost = function(newOnConnectionLost) { + if (typeof newOnConnectionLost === "function") + client.onConnectionLost = newOnConnectionLost; + else + throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnConnectionLost, "onConnectionLost"])); + }; + + this._getOnMessageDelivered = function() { return client.onMessageDelivered; }; + this._setOnMessageDelivered = function(newOnMessageDelivered) { + if (typeof newOnMessageDelivered === "function") + client.onMessageDelivered = newOnMessageDelivered; + else + throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageDelivered, "onMessageDelivered"])); + }; + + this._getOnMessageArrived = function() { return client.onMessageArrived; }; + this._setOnMessageArrived = function(newOnMessageArrived) { + if (typeof newOnMessageArrived === "function") + client.onMessageArrived = newOnMessageArrived; + else + throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageArrived, "onMessageArrived"])); + }; + + /** + * Connect this Messaging client to its server. + * + * @name Messaging.Client#connect + * @function + * @param {Object} [connectOptions] attributes used with the connection. + *

+ * Properties of the connect options are: + * @config {number} [timeout] If the connect has not succeeded within this number of seconds, it is deemed to have failed. + * The default is 30 seconds. + * @config {string} [userName] Authentication username for this connection. + * @config {string} [password] Authentication password for this connection. + * @config {Messaging.Message} [willMessage] sent by the server when the client disconnects abnormally. + * @config {Number} [keepAliveInterval] the server disconnects this client if there is no activity for this + * number of seconds. The default value of 60 seconds is assumed if not set. + * @config {boolean} [cleanSession] if true(default) the client and server persistent state is deleted on successful connect. + * @config {boolean} [useSSL] if present and true, use an SSL Websocket connection. + * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. + * @config {function} [onSuccess] called when the connect acknowledgement has been received from the server. + * A single response object parameter is passed to the onSuccess callback containing the following fields: + *

    + *
  1. invocationContext as passed in to the onSuccess method in the connectOptions. + *
+ * @config {function} [onFailure] called when the connect request has failed or timed out. + * A single response object parameter is passed to the onFailure callback containing the following fields: + *
    + *
  1. invocationContext as passed in to the onFailure method in the connectOptions. + *
  2. errorCode a number indicating the nature of the error. + *
  3. errorMessage text describing the error. + *
+ * @config {Array} [hosts] If present this set of hostnames is tried in order in place + * of the host and port paramater on the construtor. The hosts and the matching ports are tried one at at time in order until + * one of then succeeds. + * @config {Array} [ports] If present this set of ports matching the hosts. + * @throws {InvalidState} if the client is not in disconnected state. The client must have received connectionLost + * or disconnected before calling connect for a second or subsequent time. + */ + this.connect = function (connectOptions) { + connectOptions = connectOptions || {} ; + validate(connectOptions, {timeout:"number", + userName:"string", + password:"string", + willMessage:"object", + keepAliveInterval:"number", + cleanSession:"boolean", + useSSL:"boolean", + invocationContext:"object", + onSuccess:"function", + onFailure:"function", + hosts:"object", + ports:"object"}); + + // If no keep alive interval is set, assume 60 seconds. + if (connectOptions.keepAliveInterval === undefined) + connectOptions.keepAliveInterval = 60; + + if (connectOptions.willMessage) { + if (!(connectOptions.willMessage instanceof Message)) + throw new Error(format(ERROR.INVALID_TYPE, [connectOptions.willMessage, "connectOptions.willMessage"])); + // The will message must have a payload that can be represented as a string. + // Cause the willMessage to throw an exception if this is not the case. + connectOptions.willMessage.stringPayload; + + if (typeof connectOptions.willMessage.destinationName === "undefined") + throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.willMessage.destinationName, "connectOptions.willMessage.destinationName"])); + } + if (typeof connectOptions.cleanSession === "undefined") + connectOptions.cleanSession = true; + if (connectOptions.hosts) { + if (!connectOptions.ports) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); + if (!(connectOptions.hosts instanceof Array) ) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); + if (!(connectOptions.ports instanceof Array) ) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); + if (connectOptions.hosts.length <1 ) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); + if (connectOptions.hosts.length != connectOptions.ports.length) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); + for (var i = 0; i + * @param {object} [subscribeOptions] used to control the subscription, as follows: + *

+ * @config {number} [qos] the maiximum qos of any publications sent as a result of making this subscription. + * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. + * @config {function} [onSuccess] called when the subscribe acknowledgement has been received from the server. + * A single response object parameter is passed to the onSuccess callback containing the following fields: + *

    + *
  1. invocationContext if set in the subscribeOptions. + *
+ * @config {function} [onFailure] called when the subscribe request has failed or timed out. + * A single response object parameter is passed to the onFailure callback containing the following fields: + *
    + *
  1. invocationContext if set in the subscribeOptions. + *
  2. errorCode a number indicating the nature of the error. + *
  3. errorMessage text describing the error. + *
+ * @config {number} [timeout] which if present determines the number of seconds after which the onFailure calback is called + * the presence of a timeout does not prevent the onSuccess callback from being called when the MQTT Suback is eventually received. + * @throws {InvalidState} if the client is not in connected state. + */ + this.subscribe = function (filter, subscribeOptions) { + if (typeof filter !== "string") + throw new Error("Invalid argument:"+filter); + subscribeOptions = subscribeOptions || {} ; + validate(subscribeOptions, {qos:"number", + invocationContext:"object", + onSuccess:"function", + onFailure:"function", + timeout:"number" + }); + if (subscribeOptions.timeout && !subscribeOptions.onFailure) + throw new Error("subscribeOptions.timeout specified with no onFailure callback."); + if (typeof subscribeOptions.qos !== "undefined" + && !(subscribeOptions.qos === 0 || subscribeOptions.qos === 1 || subscribeOptions.qos === 2 )) + throw new Error(format(ERROR.INVALID_ARGUMENT, [subscribeOptions.qos, "subscribeOptions.qos"])); + client.subscribe(filter, subscribeOptions); + }; + + /** + * Unsubscribe for messages, stop receiving messages sent to destinations described by the filter. + * + * @name Messaging.Client#unsubscribe + * @function + * @param {string} filter describing the destinations to receive messages from. + * @param {object} [unsubscribeOptions] used to control the subscription, as follows: + *

+ * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. + * @config {function} [onSuccess] called when the unsubscribe acknowledgement has been receive dfrom the server. + * A single response object parameter is passed to the onSuccess callback containing the following fields: + *

    + *
  1. invocationContext if set in the unsubscribeOptions. + *
+ * @config {function} [onFailure] called when the unsubscribe request has failed or timed out. + * A single response object parameter is passed to the onFailure callback containing the following fields: + *
    + *
  1. invocationContext if set in the unsubscribeOptions. + *
  2. errorCode a number indicating the nature of the error. + *
  3. errorMessage text describing the error. + *
+ * @config {number} [timeout] which if present determines the number of seconds after which the onFailure callback is called, the + * presence of a timeout does not prevent the onSuccess callback from being called when the MQTT UnSuback is eventually received. + * @throws {InvalidState} if the client is not in connected state. + */ + this.unsubscribe = function (filter, unsubscribeOptions) { + if (typeof filter !== "string") + throw new Error("Invalid argument:"+filter); + unsubscribeOptions = unsubscribeOptions || {} ; + validate(unsubscribeOptions, {invocationContext:"object", + onSuccess:"function", + onFailure:"function", + timeout:"number" + }); + if (unsubscribeOptions.timeout && !unsubscribeOptions.onFailure) + throw new Error("unsubscribeOptions.timeout specified with no onFailure callback."); + client.unsubscribe(filter, unsubscribeOptions); + }; + + /** + * Send a message to the consumers of the destination in the Message. + * + * @name Messaging.Client#send + * @function + * @param {Messaging.Message} message to send. + + * @throws {InvalidState} if the client is not in connected state. + */ + this.send = function (message) { + if (!(message instanceof Message)) + throw new Error("Invalid argument:"+typeof message); + if (typeof message.destinationName === "undefined") + throw new Error("Invalid parameter Message.destinationName:"+message.destinationName); + + client.send(message); + }; + + /** + * Normal disconnect of this Messaging client from its server. + * + * @name Messaging.Client#disconnect + * @function + * @throws {InvalidState} if the client is not in connected or connecting state. + */ + this.disconnect = function () { + client.disconnect(); + }; + + /** + * Get the contents of the trace log. + * + * @name Messaging.Client#getTraceLog + * @function + * @return {Object[]} tracebuffer containing the time ordered trace records. + */ + this.getTraceLog = function () { + return client.getTraceLog(); + } + + /** + * Start tracing. + * + * @name Messaging.Client#startTrace + * @function + */ + this.startTrace = function () { + client.startTrace(); + }; + + /** + * Stop tracing. + * + * @name Messaging.Client#stopTrace + * @function + */ + this.stopTrace = function () { + client.stopTrace(); + }; + }; + + Client.prototype = { + get host() { return this._getHost(); }, + set host(newHost) { this._setHost(newHost); }, + + get port() { return this._getPort(); }, + set port(newPort) { this._setPort(newPort); }, + + get clientId() { return this._getClientId(); }, + set clientId(newClientId) { this._setClientId(newClientId); }, + + get onConnectionLost() { return this._getOnConnectionLost(); }, + set onConnectionLost(newOnConnectionLost) { this._setOnConnectionLost(newOnConnectionLost); }, + + get onMessageDelivered() { return this._getOnMessageDelivered(); }, + set onMessageDelivered(newOnMessageDelivered) { this._setOnMessageDelivered(newOnMessageDelivered); }, + + get onMessageArrived() { return this._getOnMessageArrived(); }, + set onMessageArrived(newOnMessageArrived) { this._setOnMessageArrived(newOnMessageArrived); } + }; + + /** + * An application message, sent or received. + *

+ * Other programming languages, + * Java, + * C. + *

+ * All attributes may be null, which implies the default values. + * + * @name Messaging.Message + * @constructor + * @param {String|ArrayBuffer} payload The message data to be sent. + *

+ * @property {string} payloadString read only The payload as a string if the payload consists of valid UTF-8 characters. + * @property {ArrayBuffer} payloadBytes read only The payload as an ArrayBuffer. + *

+ * @property {string} destinationName mandatory The name of the destination to which the message is to be sent + * (for messages about to be sent) or the name of the destination from which the message has been received. + * (for messages received by the onMessage function). + *

+ * @property {number} qos The Quality of Service used to deliver the message. + *

+ *
0 Best effort (default). + *
1 At least once. + *
2 Exactly once. + *
+ *

+ * @property {Boolean} retained If true, the message is to be retained by the server and delivered + * to both current and future subscriptions. + * If false the server only delivers the message to current subscribers, this is the default for new Messages. + * A received message has the retained boolean set to true if the message was published + * with the retained boolean set to true + * and the subscrption was made after the message has been published. + *

+ * @property {Boolean} duplicate read only If true, this message might be a duplicate of one which has already been received. + * This is only set on messages received from the server. + * + */ + var Message = function (newPayload) { + var payload; + if ( typeof newPayload === "string" + || newPayload instanceof ArrayBuffer + || newPayload instanceof Int8Array + || newPayload instanceof Uint8Array + || newPayload instanceof Int16Array + || newPayload instanceof Uint16Array + || newPayload instanceof Int32Array + || newPayload instanceof Uint32Array + || newPayload instanceof Float32Array + || newPayload instanceof Float64Array + ) { + payload = newPayload; + } else { + throw (format(ERROR.INVALID_ARGUMENT, [newPayload, "newPayload"])); + } + + this._getPayloadString = function () { + if (typeof payload === "string") + return payload; + else + return parseUTF8(payload, 0, payload.length); + }; + + this._getPayloadBytes = function() { + if (typeof payload === "string") { + var buffer = new ArrayBuffer(UTF8Length(payload)); + var byteStream = new Uint8Array(buffer); + stringToUTF8(payload, byteStream, 0); + + return byteStream; + } else { + return payload; + }; + }; + + var destinationName = undefined; + this._getDestinationName = function() { return destinationName; }; + this._setDestinationName = function(newDestinationName) { + if (typeof newDestinationName === "string") + destinationName = newDestinationName; + else + throw new Error(format(ERROR.INVALID_ARGUMENT, [newDestinationName, "newDestinationName"])); + }; + + var qos = 0; + this._getQos = function() { return qos; }; + this._setQos = function(newQos) { + if (newQos === 0 || newQos === 1 || newQos === 2 ) + qos = newQos; + else + throw new Error("Invalid argument:"+newQos); + }; + + var retained = false; + this._getRetained = function() { return retained; }; + this._setRetained = function(newRetained) { + if (typeof newRetained === "boolean") + retained = newRetained; + else + throw new Error(format(ERROR.INVALID_ARGUMENT, [newRetained, "newRetained"])); + }; + + var duplicate = false; + this._getDuplicate = function() { return duplicate; }; + this._setDuplicate = function(newDuplicate) { duplicate = newDuplicate; }; + }; + + Message.prototype = { + get payloadString() { return this._getPayloadString(); }, + get payloadBytes() { return this._getPayloadBytes(); }, + + get destinationName() { return this._getDestinationName(); }, + set destinationName(newDestinationName) { this._setDestinationName(newDestinationName); }, + + get qos() { return this._getQos(); }, + set qos(newQos) { this._setQos(newQos); }, + + get retained() { return this._getRetained(); }, + set retained(newRetained) { this._setRetained(newRetained); }, + + get duplicate() { return this._getDuplicate(); }, + set duplicate(newDuplicate) { this._setDuplicate(newDuplicate); } + }; + + // Module contents. + return { + Client: Client, + Message: Message + }; })(window); diff --git a/assembly/src/release/examples/openwire/csharp/ActiveMQExamples/ActiveMQExamples.sln b/assembly/src/release/examples/openwire/csharp/ActiveMQExamples/ActiveMQExamples.sln index ade0d240e3..f9a94a4921 100644 --- a/assembly/src/release/examples/openwire/csharp/ActiveMQExamples/ActiveMQExamples.sln +++ b/assembly/src/release/examples/openwire/csharp/ActiveMQExamples/ActiveMQExamples.sln @@ -1,26 +1,26 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Publisher", "Publisher\Publisher.csproj", "{0FEEC122-F3FD-4148-A461-0A724AE0C691}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Listener", "Listener\Listener.csproj", "{08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Debug|x86.ActiveCfg = Debug|x86 - {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Debug|x86.Build.0 = Debug|x86 - {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Release|x86.ActiveCfg = Release|x86 - {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Release|x86.Build.0 = Release|x86 - {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Debug|x86.ActiveCfg = Debug|x86 - {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Debug|x86.Build.0 = Debug|x86 - {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Release|x86.ActiveCfg = Release|x86 - {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = Listener\Listener.csproj - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Publisher", "Publisher\Publisher.csproj", "{0FEEC122-F3FD-4148-A461-0A724AE0C691}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Listener", "Listener\Listener.csproj", "{08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Debug|x86.ActiveCfg = Debug|x86 + {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Debug|x86.Build.0 = Debug|x86 + {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Release|x86.ActiveCfg = Release|x86 + {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Release|x86.Build.0 = Release|x86 + {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Debug|x86.ActiveCfg = Debug|x86 + {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Debug|x86.Build.0 = Debug|x86 + {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Release|x86.ActiveCfg = Release|x86 + {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = Listener\Listener.csproj + EndGlobalSection +EndGlobal diff --git a/assembly/src/release/examples/openwire/csharp/ActiveMQExamples/Listener/Listener.csproj b/assembly/src/release/examples/openwire/csharp/ActiveMQExamples/Listener/Listener.csproj index 2fe971c0d2..30245994d5 100644 --- a/assembly/src/release/examples/openwire/csharp/ActiveMQExamples/Listener/Listener.csproj +++ b/assembly/src/release/examples/openwire/csharp/ActiveMQExamples/Listener/Listener.csproj @@ -1,52 +1,52 @@ - - - - Debug - x86 - 9.0.21022 - 2.0 - {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3} - Exe - Listener - Listener - v3.5 - - - true - full - false - bin\Debug - DEBUG - prompt - 4 - x86 - true - - - none - false - bin\Release - prompt - 4 - x86 - true - - - - - ..\..\..\..\..\..\..\..\..\..\activemq\NMS.ActiveMQ\lib\Apache.NMS\mono-2.0\Apache.NMS.dll - - - ..\..\..\..\..\..\..\..\..\..\activemq\NMS.ActiveMQ\lib\DotNetZip\mono-2.0\Ionic.Zlib.dll - - - ..\..\..\..\..\..\..\..\..\..\activemq\NMS.ActiveMQ\build\mono-2.0\debug\Apache.NMS.ActiveMQ.dll - - - - - - - - + + + + Debug + x86 + 9.0.21022 + 2.0 + {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3} + Exe + Listener + Listener + v3.5 + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + x86 + true + + + none + false + bin\Release + prompt + 4 + x86 + true + + + + + ..\..\..\..\..\..\..\..\..\..\activemq\NMS.ActiveMQ\lib\Apache.NMS\mono-2.0\Apache.NMS.dll + + + ..\..\..\..\..\..\..\..\..\..\activemq\NMS.ActiveMQ\lib\DotNetZip\mono-2.0\Ionic.Zlib.dll + + + ..\..\..\..\..\..\..\..\..\..\activemq\NMS.ActiveMQ\build\mono-2.0\debug\Apache.NMS.ActiveMQ.dll + + + + + + + + \ No newline at end of file diff --git a/assembly/src/release/examples/openwire/csharp/ActiveMQExamples/Publisher/Publisher.csproj b/assembly/src/release/examples/openwire/csharp/ActiveMQExamples/Publisher/Publisher.csproj index 1d4dfd7196..c23ceca291 100644 --- a/assembly/src/release/examples/openwire/csharp/ActiveMQExamples/Publisher/Publisher.csproj +++ b/assembly/src/release/examples/openwire/csharp/ActiveMQExamples/Publisher/Publisher.csproj @@ -1,52 +1,52 @@ - - - - Debug - x86 - 9.0.21022 - 2.0 - {0FEEC122-F3FD-4148-A461-0A724AE0C691} - Exe - Publisher - Publisher - v3.5 - - - true - full - false - bin\Debug - DEBUG - prompt - 4 - x86 - true - - - none - false - bin\Release - prompt - 4 - x86 - true - - - - - ..\..\..\..\..\..\..\..\..\..\activemq\NMS.ActiveMQ\build\mono-2.0\debug\Apache.NMS.ActiveMQ.dll - - - ..\..\..\..\..\..\..\..\..\..\activemq\NMS.ActiveMQ\lib\Apache.NMS\mono-2.0\Apache.NMS.dll - - - ..\..\..\..\..\..\..\..\..\..\activemq\NMS.ActiveMQ\lib\DotNetZip\mono-2.0\Ionic.Zlib.dll - - - - - - - - + + + + Debug + x86 + 9.0.21022 + 2.0 + {0FEEC122-F3FD-4148-A461-0A724AE0C691} + Exe + Publisher + Publisher + v3.5 + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + x86 + true + + + none + false + bin\Release + prompt + 4 + x86 + true + + + + + ..\..\..\..\..\..\..\..\..\..\activemq\NMS.ActiveMQ\build\mono-2.0\debug\Apache.NMS.ActiveMQ.dll + + + ..\..\..\..\..\..\..\..\..\..\activemq\NMS.ActiveMQ\lib\Apache.NMS\mono-2.0\Apache.NMS.dll + + + ..\..\..\..\..\..\..\..\..\..\activemq\NMS.ActiveMQ\lib\DotNetZip\mono-2.0\Ionic.Zlib.dll + + + + + + + + \ No newline at end of file diff --git a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/QueueMonitor/QueueMonitor.java b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/QueueMonitor/QueueMonitor.java index b7807714a5..19a81b1633 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/QueueMonitor/QueueMonitor.java +++ b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/QueueMonitor/QueueMonitor.java @@ -1,352 +1,352 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - -Sample Application - -Writing a Basic JMS Application using: - - QueueBrowser - - JMS with a Graphical Interface - - behavior based on message type - -When you run this program, it will read all the parameters out -of the QueueMonitor.properties file. In this file you can specify -which queues you want to monitor. Then a Java window will open and -every time you click the Browse button, The current contents of the queues -will be displayed in the text window. - -Usage: - java QueueMonitor - -Suggested demonstration: - - Start one instance of this application: - java QueueMonitor - - Run on or more Talk applications (without the receiving queue). - - Enter messages on various Talk windows. - - Watch the QueueMonitor display the messages. - -*/ -import org.apache.activemq.*; - -import javax.swing.JTextArea; -import javax.swing.JFrame; -import javax.swing.JScrollPane; -import javax.swing.JButton; - -import javax.swing.text.Highlighter; -import javax.swing.text.DefaultHighlighter; -import javax.swing.text.BadLocationException; - -import javax.swing.border.CompoundBorder; -import javax.swing.border.EmptyBorder; -import javax.swing.border.BevelBorder; -import javax.swing.border.SoftBevelBorder; - -import java.awt.Toolkit; -import java.awt.Dimension; -import java.awt.BorderLayout; -import java.awt.Rectangle; - -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.awt.event.ActionListener; -import java.awt.event.ActionEvent; - -import java.io.FileInputStream; - -import java.util.Vector; -import java.util.Enumeration; -import java.util.Properties; -import java.util.StringTokenizer; - -public class QueueMonitor -extends JFrame -{ - private static final String DEFAULT_PROPERTIES_FILE = "QueueMonitor.properties"; - - String propertiesFile = DEFAULT_PROPERTIES_FILE; - String broker = "tcp://localhost:61616"; - String connectID = "QueueMonitor"; - String username = "QueueMonitor"; - String password = "QueueMonitor"; - String browseQueues = "Q1,Q2,Q3"; - String textFontName = "Dialog"; - String textFontStyle = "PLAIN"; - String textFontSize = "12"; - String title = "QueueMonitor"; - - JTextArea textArea = new JTextArea(); - JScrollPane scrollPane = new JScrollPane(textArea); - JButton browseButton = new JButton("Browse Queues"); - - Vector theQueues = new Vector(); - - private javax.jms.Connection connect = null; - private javax.jms.Session session = null; - -/** Constructor for MessageMonitor window. */ - public QueueMonitor() - { - loadProperties(); - - setTitle(title); - - // Connect to Message Broker - try - { - javax.jms.ConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - - connect = factory.createConnection (username, password); - session = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("Cannot connect to Broker"); - jmse.printStackTrace(); - System.exit(1); - } - - // Set up Queues: - StringTokenizer queues = new StringTokenizer(browseQueues, ","); - while (queues.hasMoreTokens()) - { - try{ - String queueName = queues.nextToken(); - System.out.println ("Monitoring " + queueName); - theQueues.addElement(session.createQueue(queueName)); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - - - // After init it is time to start the connection - try - { - connect.start(); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("Cannot start connection"); - jmse.printStackTrace(); - System.exit(1); - } - - //Elements visible on the screen - textArea.setEditable(false); - scrollPane.setBorder(new CompoundBorder(new EmptyBorder(6,6,6,6), - new SoftBevelBorder(BevelBorder.LOWERED))); - getContentPane().add(scrollPane,BorderLayout.CENTER); - getContentPane().add(browseButton,BorderLayout.SOUTH); - - browseButton.addActionListener(new OnBrowse()); - - } - - - - /** Main program entry point. */ - public static void main(String[] args) - { - // There should be no arguments to this program. - if (args.length > 0) { - printUsage(); - System.exit(1); - } - - QueueMonitor queueMonitor = new QueueMonitor(); - - queueMonitor.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) - { - System.exit(0); - } - }); - - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - screenSize.height = screenSize.height / 2 ; - screenSize.width = screenSize.width / 2 ; - queueMonitor.setSize(screenSize); - queueMonitor.setVisible(true); - - } - - /** Prints the usage. */ - private static void printUsage() - { - StringBuffer use = new StringBuffer(); - use.append("\nusage: QueueMonitor\n\n"); - use.append("Properties for this sample can be set in a properties file.\n"); - String dfltFile = System.getProperty("propertiesFile", DEFAULT_PROPERTIES_FILE); - use.append("[Default file: " + dfltFile +"]\n\n"); - System.out.print(use); - } - - /** Load the window and JMS properties from a file. */ - private void loadProperties() - { - try - { - Properties properties = new Properties(); - - propertiesFile = System.getProperty("propertiesFile", propertiesFile); - - properties.load(new FileInputStream(propertiesFile)); - - // Connection Properties - broker = properties.getProperty("broker",broker).trim(); - connectID = properties.getProperty("connectID",connectID).trim(); - username = properties.getProperty("username",username).trim(); - password = properties.getProperty("password",password).trim(); - - // Queue Properties - browseQueues = properties.getProperty("browseQueues", browseQueues).trim(); - - // Text Properties - textFontName = properties.getProperty("textFontName", textFontName).trim(); - textFontStyle = properties.getProperty("textFontStyle", textFontStyle).trim(); - textFontSize = properties.getProperty("textFontSize", textFontSize).trim(); - - // Window Properties - title = properties.getProperty("title", title).trim(); - - } - catch (java.io.FileNotFoundException fnfe) - { - System.out.println (propertiesFile + " not found: using defaults"); // Use Defaults - } - catch (java.io.IOException ioe) - { - ioe.printStackTrace(); - } - } - - /** Class to handle the "Browse" button action. */ - public class OnBrowse implements ActionListener - { - - public void actionPerformed(ActionEvent evt) - { - // Clear the textArea. - textArea.setText(""); - textArea.paintImmediately(textArea.getBounds()); - - if(theQueues.size() == 0){ - textArea.setText("No Queues to be monitored"); - } - else{ - for(int i = 0; i getting message " + String.valueOf(++cnt) + "..."); - javax.jms.Message message = (javax.jms.Message) e.nextElement(); - System.out.println("[" + message + "]"); - if (message != null) - { - String msgText = getContents (message); - textArea.append(msgText + "\n"); - try - { - // Scroll the text area to show the message - Rectangle area = textArea.modelToView(textArea.getText().length()); - textArea.scrollRectToVisible(area); - textArea.paintImmediately(textArea.getBounds()); - } - catch(Exception jle) { jle.printStackTrace();} - } - } - } - // Free any resources in the browser. - browser.close(); - textArea.append ("\n"); - } - catch (javax.jms.JMSException jmse){ - jmse.printStackTrace(); - } - } - try - { - // Scroll the text area to show the message - Rectangle area = textArea.modelToView(textArea.getText().length()); - textArea.scrollRectToVisible(area); - textArea.paintImmediately(textArea.getBounds()); - } - catch(Exception jle) { jle.printStackTrace();} - } - } - } - - public String getContents (javax.jms.Message message){ - - - String msgBody = null; - String msgClass = message.getClass().getName(); - - if (message instanceof javax.jms.TextMessage) - { - msgClass = "javax.jms.TextMessage"; - try - { - msgBody = ((javax.jms.TextMessage)message).getText(); - } - catch (javax.jms.JMSException jmse) - { - msgBody = ""; - } - } - - else if (message instanceof org.apache.activemq.command.ActiveMQMapMessage) - { - System.out.println ("(Name value pairs in the MapMessage are not displayed.)"); - } - else if (message instanceof javax.jms.BytesMessage) - { - System.out.println ("Warning: A bytes message was discarded because it could not be processed as a javax.jms.TextMessage."); - } - else if (message instanceof javax.jms.ObjectMessage) - { - System.out.println ("Warning: An object message was discarded because it could not be processed as a javax.jms.TextMessage."); - } - - else if (message instanceof javax.jms.StreamMessage) - { - System.out.println ("Warning: A stream message was discarded because it could not be processed as a javax.jms.TextMessage."); - } - return "- " + msgClass + " from " + msgBody ; - - } +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + +Sample Application + +Writing a Basic JMS Application using: + - QueueBrowser + - JMS with a Graphical Interface + - behavior based on message type + +When you run this program, it will read all the parameters out +of the QueueMonitor.properties file. In this file you can specify +which queues you want to monitor. Then a Java window will open and +every time you click the Browse button, The current contents of the queues +will be displayed in the text window. + +Usage: + java QueueMonitor + +Suggested demonstration: + - Start one instance of this application: + java QueueMonitor + - Run on or more Talk applications (without the receiving queue). + - Enter messages on various Talk windows. + - Watch the QueueMonitor display the messages. + +*/ +import org.apache.activemq.*; + +import javax.swing.JTextArea; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JButton; + +import javax.swing.text.Highlighter; +import javax.swing.text.DefaultHighlighter; +import javax.swing.text.BadLocationException; + +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.BevelBorder; +import javax.swing.border.SoftBevelBorder; + +import java.awt.Toolkit; +import java.awt.Dimension; +import java.awt.BorderLayout; +import java.awt.Rectangle; + +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import java.io.FileInputStream; + +import java.util.Vector; +import java.util.Enumeration; +import java.util.Properties; +import java.util.StringTokenizer; + +public class QueueMonitor +extends JFrame +{ + private static final String DEFAULT_PROPERTIES_FILE = "QueueMonitor.properties"; + + String propertiesFile = DEFAULT_PROPERTIES_FILE; + String broker = "tcp://localhost:61616"; + String connectID = "QueueMonitor"; + String username = "QueueMonitor"; + String password = "QueueMonitor"; + String browseQueues = "Q1,Q2,Q3"; + String textFontName = "Dialog"; + String textFontStyle = "PLAIN"; + String textFontSize = "12"; + String title = "QueueMonitor"; + + JTextArea textArea = new JTextArea(); + JScrollPane scrollPane = new JScrollPane(textArea); + JButton browseButton = new JButton("Browse Queues"); + + Vector theQueues = new Vector(); + + private javax.jms.Connection connect = null; + private javax.jms.Session session = null; + +/** Constructor for MessageMonitor window. */ + public QueueMonitor() + { + loadProperties(); + + setTitle(title); + + // Connect to Message Broker + try + { + javax.jms.ConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + + connect = factory.createConnection (username, password); + session = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("Cannot connect to Broker"); + jmse.printStackTrace(); + System.exit(1); + } + + // Set up Queues: + StringTokenizer queues = new StringTokenizer(browseQueues, ","); + while (queues.hasMoreTokens()) + { + try{ + String queueName = queues.nextToken(); + System.out.println ("Monitoring " + queueName); + theQueues.addElement(session.createQueue(queueName)); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + + + // After init it is time to start the connection + try + { + connect.start(); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("Cannot start connection"); + jmse.printStackTrace(); + System.exit(1); + } + + //Elements visible on the screen + textArea.setEditable(false); + scrollPane.setBorder(new CompoundBorder(new EmptyBorder(6,6,6,6), + new SoftBevelBorder(BevelBorder.LOWERED))); + getContentPane().add(scrollPane,BorderLayout.CENTER); + getContentPane().add(browseButton,BorderLayout.SOUTH); + + browseButton.addActionListener(new OnBrowse()); + + } + + + + /** Main program entry point. */ + public static void main(String[] args) + { + // There should be no arguments to this program. + if (args.length > 0) { + printUsage(); + System.exit(1); + } + + QueueMonitor queueMonitor = new QueueMonitor(); + + queueMonitor.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) + { + System.exit(0); + } + }); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + screenSize.height = screenSize.height / 2 ; + screenSize.width = screenSize.width / 2 ; + queueMonitor.setSize(screenSize); + queueMonitor.setVisible(true); + + } + + /** Prints the usage. */ + private static void printUsage() + { + StringBuffer use = new StringBuffer(); + use.append("\nusage: QueueMonitor\n\n"); + use.append("Properties for this sample can be set in a properties file.\n"); + String dfltFile = System.getProperty("propertiesFile", DEFAULT_PROPERTIES_FILE); + use.append("[Default file: " + dfltFile +"]\n\n"); + System.out.print(use); + } + + /** Load the window and JMS properties from a file. */ + private void loadProperties() + { + try + { + Properties properties = new Properties(); + + propertiesFile = System.getProperty("propertiesFile", propertiesFile); + + properties.load(new FileInputStream(propertiesFile)); + + // Connection Properties + broker = properties.getProperty("broker",broker).trim(); + connectID = properties.getProperty("connectID",connectID).trim(); + username = properties.getProperty("username",username).trim(); + password = properties.getProperty("password",password).trim(); + + // Queue Properties + browseQueues = properties.getProperty("browseQueues", browseQueues).trim(); + + // Text Properties + textFontName = properties.getProperty("textFontName", textFontName).trim(); + textFontStyle = properties.getProperty("textFontStyle", textFontStyle).trim(); + textFontSize = properties.getProperty("textFontSize", textFontSize).trim(); + + // Window Properties + title = properties.getProperty("title", title).trim(); + + } + catch (java.io.FileNotFoundException fnfe) + { + System.out.println (propertiesFile + " not found: using defaults"); // Use Defaults + } + catch (java.io.IOException ioe) + { + ioe.printStackTrace(); + } + } + + /** Class to handle the "Browse" button action. */ + public class OnBrowse implements ActionListener + { + + public void actionPerformed(ActionEvent evt) + { + // Clear the textArea. + textArea.setText(""); + textArea.paintImmediately(textArea.getBounds()); + + if(theQueues.size() == 0){ + textArea.setText("No Queues to be monitored"); + } + else{ + for(int i = 0; i getting message " + String.valueOf(++cnt) + "..."); + javax.jms.Message message = (javax.jms.Message) e.nextElement(); + System.out.println("[" + message + "]"); + if (message != null) + { + String msgText = getContents (message); + textArea.append(msgText + "\n"); + try + { + // Scroll the text area to show the message + Rectangle area = textArea.modelToView(textArea.getText().length()); + textArea.scrollRectToVisible(area); + textArea.paintImmediately(textArea.getBounds()); + } + catch(Exception jle) { jle.printStackTrace();} + } + } + } + // Free any resources in the browser. + browser.close(); + textArea.append ("\n"); + } + catch (javax.jms.JMSException jmse){ + jmse.printStackTrace(); + } + } + try + { + // Scroll the text area to show the message + Rectangle area = textArea.modelToView(textArea.getText().length()); + textArea.scrollRectToVisible(area); + textArea.paintImmediately(textArea.getBounds()); + } + catch(Exception jle) { jle.printStackTrace();} + } + } + } + + public String getContents (javax.jms.Message message){ + + + String msgBody = null; + String msgClass = message.getClass().getName(); + + if (message instanceof javax.jms.TextMessage) + { + msgClass = "javax.jms.TextMessage"; + try + { + msgBody = ((javax.jms.TextMessage)message).getText(); + } + catch (javax.jms.JMSException jmse) + { + msgBody = ""; + } + } + + else if (message instanceof org.apache.activemq.command.ActiveMQMapMessage) + { + System.out.println ("(Name value pairs in the MapMessage are not displayed.)"); + } + else if (message instanceof javax.jms.BytesMessage) + { + System.out.println ("Warning: A bytes message was discarded because it could not be processed as a javax.jms.TextMessage."); + } + else if (message instanceof javax.jms.ObjectMessage) + { + System.out.println ("Warning: An object message was discarded because it could not be processed as a javax.jms.TextMessage."); + } + + else if (message instanceof javax.jms.StreamMessage) + { + System.out.println ("Warning: A stream message was discarded because it could not be processed as a javax.jms.TextMessage."); + } + return "- " + msgClass + " from " + msgBody ; + + } } \ No newline at end of file diff --git a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/QueueMonitor/QueueMonitor.properties b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/QueueMonitor/QueueMonitor.properties index 7f296f5371..4c714ef71c 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/QueueMonitor/QueueMonitor.properties +++ b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/QueueMonitor/QueueMonitor.properties @@ -1,72 +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. -## --------------------------------------------------------------------------- - -# This is the properties file for the QueueMonitor Class. -# -# A QueueMonitor object will load this file when constructed. -# By default, a QueueMonitor object searches for and load a -# file named "QueueMonitor.properties" in the JVM's "current" directory. -# -# This behavior may be overriden by assigning an alternate property file -# name and location to the System property "propertiesFile". -# -# Property: broker -# Default Value: localhost -# Description: Specifies the host and port of the Messaging Broker to connect to. -broker tcp://localhost:61616 -# -# Property: connectID -# Default Value: QueueMonitor -# Description: Specifies the program identifier used to connect to the Messaging Broker. -connectID QueueMonitor -# -# Property: userID -# Default Value: Administrator -# Description: Specifies the user identifier used to connect to the Messaging Broker. -username Administrator -# -# Property: password -# Default Value: Administrator -# Description: Specifies the password used to connect to the Messaging Broker. -password Administrator -# -# Property: browseQueues -# Default Value: -# Description: A comma-separated list of queue names that QueueMonitor browses. -# NOTE: An empty list will subscribe to no queues. -browseQueues Q1,Q2,Q3,Q4 -## -# Property: textFontName -# Default Value: Dialog -# Description: The name of the font used to display recieved messages. -textFontName Dialog -# -# Property: textFontStyle -# Default Value: PLAIN -# Description: The style of the font used to display received messages. -textFontStyle PLAIN -# -# Property: textFontSize -# Default Value: 12 -# Description: The size of the font used to display recieved messages. -textFontSize 12 -# -# Property: title -# Default Value: QueueMonitor -# Description: The title of the QueueMonitor Window. -title QueueMonitor -.. +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +# This is the properties file for the QueueMonitor Class. +# +# A QueueMonitor object will load this file when constructed. +# By default, a QueueMonitor object searches for and load a +# file named "QueueMonitor.properties" in the JVM's "current" directory. +# +# This behavior may be overriden by assigning an alternate property file +# name and location to the System property "propertiesFile". +# +# Property: broker +# Default Value: localhost +# Description: Specifies the host and port of the Messaging Broker to connect to. +broker tcp://localhost:61616 +# +# Property: connectID +# Default Value: QueueMonitor +# Description: Specifies the program identifier used to connect to the Messaging Broker. +connectID QueueMonitor +# +# Property: userID +# Default Value: Administrator +# Description: Specifies the user identifier used to connect to the Messaging Broker. +username Administrator +# +# Property: password +# Default Value: Administrator +# Description: Specifies the password used to connect to the Messaging Broker. +password Administrator +# +# Property: browseQueues +# Default Value: +# Description: A comma-separated list of queue names that QueueMonitor browses. +# NOTE: An empty list will subscribe to no queues. +browseQueues Q1,Q2,Q3,Q4 +## +# Property: textFontName +# Default Value: Dialog +# Description: The name of the font used to display recieved messages. +textFontName Dialog +# +# Property: textFontStyle +# Default Value: PLAIN +# Description: The style of the font used to display received messages. +textFontStyle PLAIN +# +# Property: textFontSize +# Default Value: 12 +# Description: The size of the font used to display recieved messages. +textFontSize 12 +# +# Property: title +# Default Value: QueueMonitor +# Description: The title of the QueueMonitor Window. +title QueueMonitor +.. diff --git a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/QueueRoundTrip/QueueRoundTrip.java b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/QueueRoundTrip/QueueRoundTrip.java index 9732501618..7b9d4fcb3e 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/QueueRoundTrip/QueueRoundTrip.java +++ b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/QueueRoundTrip/QueueRoundTrip.java @@ -1,242 +1,242 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - -Sample Application - -Queue/PTP -Send and receive using multiple sessions and a temporary queue - -This sample shows the round trip time for a message being sent to the broker -and received. A temporary queue is used to guarantee that each instance of this -sample receives its own messages only. - -Usage: - - java QueueRoundTrip -b -u -p -n -h - Optional Parameters: - -b broker:port Broker name and port of your message server - Default: tcp://localhost:61616 - -u username Default: user (username required) - -p password Default: password (not checked) - -n numTests The number of messages to be sent/received - Default: 100 - -h Prints help screen. - */ -import org.apache.activemq.*; - - -public class QueueRoundTrip -{ - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - private static final String DEFAULT_PASSWORD = "password"; - private static final String DEFAULT_USER_NAME = "user"; - private static final int DEFAULT_NUM_TESTS = 100; - - private static final int msgSize = 1400; - private static byte[] msgBody = new byte[msgSize]; - - private javax.jms.ConnectionFactory factory = null; - private javax.jms.Connection connection = null; - private javax.jms.Session sendSession = null; - private javax.jms.Session receiveSession = null; - private javax.jms.MessageProducer sender = null; - private javax.jms.MessageConsumer receiver = null; - - private void QueueRoundTripper(String broker, String username, String password, int numTests){ - - try - { - //Set up two sessions, one for sending and the other for receiving - factory = new ActiveMQConnectionFactory(username, password, broker); - connection = factory.createConnection(username, password); - sendSession = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); - receiveSession = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println ("error: Cannot connect to broker- " + broker + ".\n"); - jmse.printStackTrace (); - System.exit(1); - } - - try - { - //Set up a temporary Queue: - javax.jms.TemporaryQueue tempQueue = sendSession.createTemporaryQueue(); - receiver = receiveSession.createConsumer(tempQueue); - sender = sendSession.createProducer(tempQueue); - connection.start(); - } - catch (javax.jms.JMSException jmse){ - System.err.println("error: Connection couldn't be started.\n"); - jmse.printStackTrace(); - System.exit(1); - } - - //Send messages using Temporary Queue: - try { - System.out.println("QueueRoundTrip application:"); - System.out.println("==========================="); - System.out.println("Sending Messages to Temporary Queue..."); - - //create a message to send - javax.jms.BytesMessage msg = sendSession.createBytesMessage(); - msg.writeBytes(msgBody); - - //send and receive the message the specified number of times: - long time = System.currentTimeMillis(); - for (int i = 0; i < numTests; i++){ - sender.send(msg); - msg = (javax.jms.BytesMessage)receiver.receive(); - } - time = System.currentTimeMillis()-time; - - System.out.println("\nTime for " + numTests + " sends and receives:\t\t" + - time + "ms\n" + - "Average Time per message:\t\t\t" + (float)time/(float)numTests + "ms\n"); - System.out.println("\n\nPress Enter to close this window."); - java.io.BufferedReader stdin = - new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); - stdin.readLine(); - System.exit(0); - } - - catch (javax.jms.JMSException jmse) { - System.err.println("error: message not sent/received.\n"); - jmse.printStackTrace(); - System.exit(1); - } - - catch (java.io.IOException ioe) { - ioe.printStackTrace(); - } - - - } - - /** Cleanup resources and then exit. */ - private void exit() - { - try - { - connection.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - public static void main (String argv[]) - { - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = DEFAULT_USER_NAME; - String password = DEFAULT_PASSWORD; - int numTests = DEFAULT_NUM_TESTS; - - // Check parameters - if(argv.length > 0){ - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - // Options - if (!arg.startsWith("-")) { - System.err.println ("error: unexpected argument - "+arg); - printUsage(); - System.exit(1); - } - else { - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - if (arg.equals("-n")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing number of test to run."); - System.exit(1); - } - numTests = (new Integer(argv[++i])).intValue(); - continue; - } - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - } - } - } - - // create the payload - byte charToWrite = (0x30); - for (int i = 0; i < msgSize; i++) - { - msgBody[i] = charToWrite; - charToWrite = (byte) ((int) charToWrite + (int) 0x01); - if (charToWrite == (0x39)) - { - charToWrite = (0x30); - } - } - - // Start the JMS client for the test. - QueueRoundTrip queueRoundTripper = new QueueRoundTrip(); - queueRoundTripper.QueueRoundTripper(broker, username, password, numTests); - - } - - private static void printUsage() - { - StringBuffer use = new StringBuffer(); - - use.append("Usage:\n"); - use.append("java QueueRoundTrip (options)...\n\n"); - use.append("options:\n"); - use.append("-b broker:port Broker name and port of your message server\n"); - use.append(" Default: tcp://localhost:61616\n"); - use.append("-u username Default: user (username required)\n"); - use.append("-p password Default: password (not checked)\n"); - use.append("-n numTests The number of messages to be sent/received\n"); - use.append(" Default: 100\n"); - use.append("-h This help screen"); - System.err.println (use); - } - -} +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + +Sample Application + +Queue/PTP +Send and receive using multiple sessions and a temporary queue + +This sample shows the round trip time for a message being sent to the broker +and received. A temporary queue is used to guarantee that each instance of this +sample receives its own messages only. + +Usage: + + java QueueRoundTrip -b -u -p -n -h + Optional Parameters: + -b broker:port Broker name and port of your message server + Default: tcp://localhost:61616 + -u username Default: user (username required) + -p password Default: password (not checked) + -n numTests The number of messages to be sent/received + Default: 100 + -h Prints help screen. + */ +import org.apache.activemq.*; + + +public class QueueRoundTrip +{ + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + private static final String DEFAULT_PASSWORD = "password"; + private static final String DEFAULT_USER_NAME = "user"; + private static final int DEFAULT_NUM_TESTS = 100; + + private static final int msgSize = 1400; + private static byte[] msgBody = new byte[msgSize]; + + private javax.jms.ConnectionFactory factory = null; + private javax.jms.Connection connection = null; + private javax.jms.Session sendSession = null; + private javax.jms.Session receiveSession = null; + private javax.jms.MessageProducer sender = null; + private javax.jms.MessageConsumer receiver = null; + + private void QueueRoundTripper(String broker, String username, String password, int numTests){ + + try + { + //Set up two sessions, one for sending and the other for receiving + factory = new ActiveMQConnectionFactory(username, password, broker); + connection = factory.createConnection(username, password); + sendSession = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + receiveSession = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println ("error: Cannot connect to broker- " + broker + ".\n"); + jmse.printStackTrace (); + System.exit(1); + } + + try + { + //Set up a temporary Queue: + javax.jms.TemporaryQueue tempQueue = sendSession.createTemporaryQueue(); + receiver = receiveSession.createConsumer(tempQueue); + sender = sendSession.createProducer(tempQueue); + connection.start(); + } + catch (javax.jms.JMSException jmse){ + System.err.println("error: Connection couldn't be started.\n"); + jmse.printStackTrace(); + System.exit(1); + } + + //Send messages using Temporary Queue: + try { + System.out.println("QueueRoundTrip application:"); + System.out.println("==========================="); + System.out.println("Sending Messages to Temporary Queue..."); + + //create a message to send + javax.jms.BytesMessage msg = sendSession.createBytesMessage(); + msg.writeBytes(msgBody); + + //send and receive the message the specified number of times: + long time = System.currentTimeMillis(); + for (int i = 0; i < numTests; i++){ + sender.send(msg); + msg = (javax.jms.BytesMessage)receiver.receive(); + } + time = System.currentTimeMillis()-time; + + System.out.println("\nTime for " + numTests + " sends and receives:\t\t" + + time + "ms\n" + + "Average Time per message:\t\t\t" + (float)time/(float)numTests + "ms\n"); + System.out.println("\n\nPress Enter to close this window."); + java.io.BufferedReader stdin = + new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); + stdin.readLine(); + System.exit(0); + } + + catch (javax.jms.JMSException jmse) { + System.err.println("error: message not sent/received.\n"); + jmse.printStackTrace(); + System.exit(1); + } + + catch (java.io.IOException ioe) { + ioe.printStackTrace(); + } + + + } + + /** Cleanup resources and then exit. */ + private void exit() + { + try + { + connection.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + public static void main (String argv[]) + { + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = DEFAULT_USER_NAME; + String password = DEFAULT_PASSWORD; + int numTests = DEFAULT_NUM_TESTS; + + // Check parameters + if(argv.length > 0){ + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + // Options + if (!arg.startsWith("-")) { + System.err.println ("error: unexpected argument - "+arg); + printUsage(); + System.exit(1); + } + else { + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + if (arg.equals("-n")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing number of test to run."); + System.exit(1); + } + numTests = (new Integer(argv[++i])).intValue(); + continue; + } + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + } + } + } + + // create the payload + byte charToWrite = (0x30); + for (int i = 0; i < msgSize; i++) + { + msgBody[i] = charToWrite; + charToWrite = (byte) ((int) charToWrite + (int) 0x01); + if (charToWrite == (0x39)) + { + charToWrite = (0x30); + } + } + + // Start the JMS client for the test. + QueueRoundTrip queueRoundTripper = new QueueRoundTrip(); + queueRoundTripper.QueueRoundTripper(broker, username, password, numTests); + + } + + private static void printUsage() + { + StringBuffer use = new StringBuffer(); + + use.append("Usage:\n"); + use.append("java QueueRoundTrip (options)...\n\n"); + use.append("options:\n"); + use.append("-b broker:port Broker name and port of your message server\n"); + use.append(" Default: tcp://localhost:61616\n"); + use.append("-u username Default: user (username required)\n"); + use.append("-p password Default: password (not checked)\n"); + use.append("-n numTests The number of messages to be sent/received\n"); + use.append(" Default: 100\n"); + use.append("-h This help screen"); + System.err.println (use); + } + +} diff --git a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/RequestReply/Replier.java b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/RequestReply/Replier.java index a7f521a14c..e43df0448b 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/RequestReply/Replier.java +++ b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/RequestReply/Replier.java @@ -1,338 +1,338 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - -Sample Application - -Writing a Basic JMS Application with Point-to-Point Queues, -using: - - Synchronous Request/Reply - - javax.jms.QueueRequestor class - - JMSReplyTo Header - -When this program runs, it waits for messages on the queue, -"SampleQ1" (by default). -When that message arrives, a response based on the request -is sent back to the "Requestor" specified in the JMSReplyTo header. - -This sample replies with a simple text manipulation of the request; -the text is either folded to all UPPERCASE or all lowercase. - -Usage: - java Replier -b -u -p -qr -m - -b broker:port points to your message broker - Default: tcp://localhost:61616 - -u username must be unique (but is not checked) - Default: SampleReplier - -p password password for user (not checked) - Default: password - -qr queue name of queue for receiving requests - Default: Q1 - -m mode replier mode (uppercase, or lowercase) - Default: uppercase - -Suggested demonstration: - - In a console window with the environment set, - start a copy of the Replier. For example: - java Replier -u SampleQReplier - - In another console window, start a Requestor. - For example: - java Requestor -u SampleQRequestor - - Enter text in the Requestor window then press Enter. - The Replier responds with the message in all uppercase characters. - - Start other Requestors with different user names to see that - replies are not broadcast to all users. For example: - java Requestor -u SampleRequestorFoo - - - Start other Repliers. - - See that only one replier is receiving messages,(as it should). - - See the Requestor only receives one response. - java Replier -u toLower -m lowercase - - */ -import org.apache.activemq.*; - - -public class Replier - implements javax.jms.MessageListener -{ - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - private static final String DEFAULT_USER_NAME = "SampleReplier"; - private static final String DEFAULT_PASSWORD = "password"; - private static final String DEFAULT_QUEUE = "Q1"; - private static final String DEFAULT_MODE = "uppercase"; - private static final int UPPERCASE = 0; - private static final int LOWERCASE = 1; - - private javax.jms.Connection connect = null; - private javax.jms.Session session = null; - private javax.jms.MessageProducer replier = null; - - private int imode = UPPERCASE; - - /** Create JMS client for sending and receiving messages. */ - private void start ( String broker, String username, String password, String rQueue, String mode) - { - // Set the operation mode - imode = (mode.equals("uppercase")) ? UPPERCASE : LOWERCASE; - - // Create a connection. - try - { - javax.jms.ConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - connect = factory.createConnection (username, password); - session = connect.createSession(true, javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("error: Cannot connect to Broker - " + broker); - jmse.printStackTrace(); - System.exit(1); - } - - // Create Receivers to application queues as well as a Sender - // to use for JMS replies. - try - { - javax.jms.Queue queue = session.createQueue (rQueue); - javax.jms.MessageConsumer receiver = session.createConsumer(queue); - receiver.setMessageListener(this); - replier = session.createProducer(null); // Queue will be set for each reply - // Now that all setup is complete, start the Connection - connect.start(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - exit(); - } - - try - { - // Read standard input waiting for "EXIT" command. - java.io.BufferedReader stdin = - new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); - while ( true ) - { - System.out.println ("\nReplier application:\n" - + "============================\n" - + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" - + "The application gets requests with JMSReplyTo set on the " + DEFAULT_QUEUE + " queue." - + "The message is transformed to all uppercase or all lowercase, and then returned to the requestor." - + "The Requestor application displays the result.\n\n" - + "Enter EXIT or press Ctrl+C to close the Replier.\n"); - String s = stdin.readLine(); - if ( s == null || s.equalsIgnoreCase("EXIT")) - { - System.out.println ("\nStopping Replier. Please wait..\n>"); - exit(); - } - } - } - catch ( java.io.IOException ioe ) - { - ioe.printStackTrace(); - } - } - - /** - * Handle the message. - * (as specified in the javax.jms.MessageListener interface). - * - * IMPORTANT NOTES: - * (1)We must follow the design paradigm for JMS - * synchronous requests. That is, we must: - * - get the message - * - look for the header specifying JMSReplyTo - * - send a reply to the queue specified there. - * Failing to follow these steps might leave the originator - * of the request waiting forever. - * (2)Unlike the 'Talk' sample and others using an asynchronous - * message listener, it is possible here to use ONLY - * ONE SESSION because the messages being sent are sent from - * the same thread of control handling message delivery. For - * more information see the JMS spec v1.0.2 section 4.4.6. - * - * OPTIONAL BEHAVIOR: The following actions taken by the - * message handler represent good programming style, but are - * not required by the design paradigm for JMS requests. - * - set the JMSCorrelationID (tying the response back to - * the original request. - * - use transacted session "commit" so receipt of request - * won't happen without the reply being sent. - * - */ - public void onMessage( javax.jms.Message aMessage) - { - try - { - // Cast the message as a text message. - javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; - - // This handler reads a single String from the - // message and prints it to the standard output. - try - { - String string = textMessage.getText(); - System.out.println( "[Request] " + string ); - - // Check for a ReplyTo Queue - javax.jms.Queue replyQueue = (javax.jms.Queue) aMessage.getJMSReplyTo(); - if (replyQueue != null) - { - // Send the modified message back. - javax.jms.TextMessage reply = session.createTextMessage(); - if (imode == UPPERCASE) - reply.setText("Uppercasing-" + string.toUpperCase()); - else - reply.setText("Lowercasing-" + string.toLowerCase()); - reply.setJMSCorrelationID(aMessage.getJMSMessageID()); - replier.send (replyQueue, reply); - session.commit(); - } - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - catch (java.lang.RuntimeException rte) - { - rte.printStackTrace(); - } - } - - /** Cleanup resources cleanly and exit. */ - private void exit() - { - try - { - connect.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String argv[]) { - - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = DEFAULT_USER_NAME; - String password = DEFAULT_PASSWORD; - String queue = DEFAULT_QUEUE; - String mode = DEFAULT_MODE; - - // Check parameters - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - - if (arg.equals("-qr")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing queue"); - System.exit(1); - } - queue = argv[++i]; - continue; - } - - if (arg.equals("-m")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing mode"); - System.exit(1); - } - mode = argv[++i]; - if (!(mode.equals("uppercase") || mode.equals("lowercase"))) { - System.err.println("error: mode must be 'uppercase' or 'lowercase'"); - System.exit(1); - } - continue; - } - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - - // Invalid argument - System.err.println ("error: unexpected argument: "+arg); - printUsage(); - System.exit(1); - } - - // Start the JMS client. - Replier replier = new Replier (); - replier.start (broker, username, password, queue, mode); - } - - /** Prints the usage. */ - private static void printUsage() { - - StringBuffer use = new StringBuffer(); - use.append("usage: java Replier (options) ...\n\n"); - use.append("options:\n"); - use.append(" -b name:port Specify name:port of broker.\n"); - use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); - use.append(" -u name Specify unique user name.\n"); - use.append(" Default broker: "+DEFAULT_USER_NAME+"\n"); - use.append(" -p password Specify password for user.\n"); - use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); - use.append(" -m mode Replier operating mode - uppercase or lowercase.\n"); - use.append(" Default mode: "+DEFAULT_MODE+"\n"); - use.append(" -qr queue Specify name of queue for receiving.\n"); - use.append(" Default queue: "+DEFAULT_QUEUE+"\n"); - use.append(" -h This help screen.\n"); - System.err.println (use); - } - -} - +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + +Sample Application + +Writing a Basic JMS Application with Point-to-Point Queues, +using: + - Synchronous Request/Reply + - javax.jms.QueueRequestor class + - JMSReplyTo Header + +When this program runs, it waits for messages on the queue, +"SampleQ1" (by default). +When that message arrives, a response based on the request +is sent back to the "Requestor" specified in the JMSReplyTo header. + +This sample replies with a simple text manipulation of the request; +the text is either folded to all UPPERCASE or all lowercase. + +Usage: + java Replier -b -u -p -qr -m + -b broker:port points to your message broker + Default: tcp://localhost:61616 + -u username must be unique (but is not checked) + Default: SampleReplier + -p password password for user (not checked) + Default: password + -qr queue name of queue for receiving requests + Default: Q1 + -m mode replier mode (uppercase, or lowercase) + Default: uppercase + +Suggested demonstration: + - In a console window with the environment set, + start a copy of the Replier. For example: + java Replier -u SampleQReplier + - In another console window, start a Requestor. + For example: + java Requestor -u SampleQRequestor + - Enter text in the Requestor window then press Enter. + The Replier responds with the message in all uppercase characters. + - Start other Requestors with different user names to see that + replies are not broadcast to all users. For example: + java Requestor -u SampleRequestorFoo + + - Start other Repliers. + - See that only one replier is receiving messages,(as it should). + - See the Requestor only receives one response. + java Replier -u toLower -m lowercase + + */ +import org.apache.activemq.*; + + +public class Replier + implements javax.jms.MessageListener +{ + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + private static final String DEFAULT_USER_NAME = "SampleReplier"; + private static final String DEFAULT_PASSWORD = "password"; + private static final String DEFAULT_QUEUE = "Q1"; + private static final String DEFAULT_MODE = "uppercase"; + private static final int UPPERCASE = 0; + private static final int LOWERCASE = 1; + + private javax.jms.Connection connect = null; + private javax.jms.Session session = null; + private javax.jms.MessageProducer replier = null; + + private int imode = UPPERCASE; + + /** Create JMS client for sending and receiving messages. */ + private void start ( String broker, String username, String password, String rQueue, String mode) + { + // Set the operation mode + imode = (mode.equals("uppercase")) ? UPPERCASE : LOWERCASE; + + // Create a connection. + try + { + javax.jms.ConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + connect = factory.createConnection (username, password); + session = connect.createSession(true, javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("error: Cannot connect to Broker - " + broker); + jmse.printStackTrace(); + System.exit(1); + } + + // Create Receivers to application queues as well as a Sender + // to use for JMS replies. + try + { + javax.jms.Queue queue = session.createQueue (rQueue); + javax.jms.MessageConsumer receiver = session.createConsumer(queue); + receiver.setMessageListener(this); + replier = session.createProducer(null); // Queue will be set for each reply + // Now that all setup is complete, start the Connection + connect.start(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + exit(); + } + + try + { + // Read standard input waiting for "EXIT" command. + java.io.BufferedReader stdin = + new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); + while ( true ) + { + System.out.println ("\nReplier application:\n" + + "============================\n" + + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" + + "The application gets requests with JMSReplyTo set on the " + DEFAULT_QUEUE + " queue." + + "The message is transformed to all uppercase or all lowercase, and then returned to the requestor." + + "The Requestor application displays the result.\n\n" + + "Enter EXIT or press Ctrl+C to close the Replier.\n"); + String s = stdin.readLine(); + if ( s == null || s.equalsIgnoreCase("EXIT")) + { + System.out.println ("\nStopping Replier. Please wait..\n>"); + exit(); + } + } + } + catch ( java.io.IOException ioe ) + { + ioe.printStackTrace(); + } + } + + /** + * Handle the message. + * (as specified in the javax.jms.MessageListener interface). + * + * IMPORTANT NOTES: + * (1)We must follow the design paradigm for JMS + * synchronous requests. That is, we must: + * - get the message + * - look for the header specifying JMSReplyTo + * - send a reply to the queue specified there. + * Failing to follow these steps might leave the originator + * of the request waiting forever. + * (2)Unlike the 'Talk' sample and others using an asynchronous + * message listener, it is possible here to use ONLY + * ONE SESSION because the messages being sent are sent from + * the same thread of control handling message delivery. For + * more information see the JMS spec v1.0.2 section 4.4.6. + * + * OPTIONAL BEHAVIOR: The following actions taken by the + * message handler represent good programming style, but are + * not required by the design paradigm for JMS requests. + * - set the JMSCorrelationID (tying the response back to + * the original request. + * - use transacted session "commit" so receipt of request + * won't happen without the reply being sent. + * + */ + public void onMessage( javax.jms.Message aMessage) + { + try + { + // Cast the message as a text message. + javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; + + // This handler reads a single String from the + // message and prints it to the standard output. + try + { + String string = textMessage.getText(); + System.out.println( "[Request] " + string ); + + // Check for a ReplyTo Queue + javax.jms.Queue replyQueue = (javax.jms.Queue) aMessage.getJMSReplyTo(); + if (replyQueue != null) + { + // Send the modified message back. + javax.jms.TextMessage reply = session.createTextMessage(); + if (imode == UPPERCASE) + reply.setText("Uppercasing-" + string.toUpperCase()); + else + reply.setText("Lowercasing-" + string.toLowerCase()); + reply.setJMSCorrelationID(aMessage.getJMSMessageID()); + replier.send (replyQueue, reply); + session.commit(); + } + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + catch (java.lang.RuntimeException rte) + { + rte.printStackTrace(); + } + } + + /** Cleanup resources cleanly and exit. */ + private void exit() + { + try + { + connect.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String argv[]) { + + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = DEFAULT_USER_NAME; + String password = DEFAULT_PASSWORD; + String queue = DEFAULT_QUEUE; + String mode = DEFAULT_MODE; + + // Check parameters + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + + if (arg.equals("-qr")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing queue"); + System.exit(1); + } + queue = argv[++i]; + continue; + } + + if (arg.equals("-m")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing mode"); + System.exit(1); + } + mode = argv[++i]; + if (!(mode.equals("uppercase") || mode.equals("lowercase"))) { + System.err.println("error: mode must be 'uppercase' or 'lowercase'"); + System.exit(1); + } + continue; + } + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + + // Invalid argument + System.err.println ("error: unexpected argument: "+arg); + printUsage(); + System.exit(1); + } + + // Start the JMS client. + Replier replier = new Replier (); + replier.start (broker, username, password, queue, mode); + } + + /** Prints the usage. */ + private static void printUsage() { + + StringBuffer use = new StringBuffer(); + use.append("usage: java Replier (options) ...\n\n"); + use.append("options:\n"); + use.append(" -b name:port Specify name:port of broker.\n"); + use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); + use.append(" -u name Specify unique user name.\n"); + use.append(" Default broker: "+DEFAULT_USER_NAME+"\n"); + use.append(" -p password Specify password for user.\n"); + use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); + use.append(" -m mode Replier operating mode - uppercase or lowercase.\n"); + use.append(" Default mode: "+DEFAULT_MODE+"\n"); + use.append(" -qr queue Specify name of queue for receiving.\n"); + use.append(" Default queue: "+DEFAULT_QUEUE+"\n"); + use.append(" -h This help screen.\n"); + System.err.println (use); + } + +} + diff --git a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/RequestReply/Requestor.java b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/RequestReply/Requestor.java index 76a8d963e6..9d0adb3b11 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/RequestReply/Requestor.java +++ b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/RequestReply/Requestor.java @@ -1,264 +1,264 @@ -/** - * 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. - */ - -/* -Copyright 2001-2008, Progress Software Corporation - All Rights Reserved - -Sample Application - -Writing a Basic JMS Application with Point-to-Point Queues, -using: - - Synchronous Request/Reply - - javax.jms.QueueRequestor class - - JMSReplyTo Header - -When this program runs, it reads input from System.in -and then sends the text as a message to the queue, "Q1" -(by default). - -A "Replier" class should be waiting for the request. -It will reply with a message. - -NOTE: Unlike the Publish-Subscribe example, you need -not run the Replier first. However, this Requestor -will block until the Replier is started to service the queue. - -Usage: - java Requestor -b -u -p -qs - -b broker:port points to your message broker - Default: tcp://localhost:61616 - -u username must be unique (but is not checked) - Default: SampleRequestor - -p password password for user (not checked) - Default: password - -qs queue name of queue for sending requests - Default: SampleQ1 - -Suggested demonstration: - - In a console window with the environment set, - start a copy of the Replier. For example: - java Replier -u SampleQReplier - - In another console window, start a Requestor. - For example: - java Requestor -u SampleQRequestor - - Enter text in the Requestor window then press Enter. - The Replier responds with the message in all uppercase characters. - - Start other Requestors with different user names to see that - replies are not broadcast to all users. For example: - java Requestor -u SampleRequestorFoo - - Start other Repliers. - - See that only one replier is receiving messages,(as it should). - - See the Requestor only receives one response. - java Replier -u toLower -m lowercase - -*/ -import org.apache.activemq.*; - - -public class Requestor -{ - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - private static final String DEFAULT_USER_NAME = "SampleRequestor"; - private static final String DEFAULT_PASSWORD = "password"; - private static final String DEFAULT_QUEUE = "Q1"; - - private javax.jms.QueueConnection connect = null; - private javax.jms.QueueSession session = null; - private javax.jms.QueueRequestor requestor = null; - - /** Create JMS client for sending messages. */ - private void start ( String broker, String username, String password, String sQueue) - { - // Create a connection. - try - { - javax.jms.QueueConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - connect = factory.createQueueConnection (username, password); - session = connect.createQueueSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("error: Cannot connect to Broker - " + broker); - jmse.printStackTrace(); - System.exit(1); - } - - // Create the Queue and QueueRequestor for sending requests. - javax.jms.Queue queue = null; - try - { - queue = session.createQueue (sQueue); - requestor = new javax.jms.QueueRequestor(session, queue); - - // Now that all setup is complete, start the Connection. - connect.start(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - exit(); - } - - try - { - // Read all standard input and send it as a message. - java.io.BufferedReader stdin = - new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); - System.out.println ("\nRequestor application:\n" - + "============================\n" - + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" - + "The application uses a QueueRequestor to on the " + DEFAULT_QUEUE + " queue." - + "The Replier application gets the message, and transforms it." - + "The Requestor application displays the result.\n\n" - + "Type some mixed case text, and then press Enter to make a request.\n"); - while ( true ) - { - String s = stdin.readLine(); - - if ( s == null ) - exit(); - else if ( s.length() > 0 ) - { - javax.jms.TextMessage msg = session.createTextMessage(); - msg.setText( username + ": " + s ); - // Instead of sending, we will use the QueueRequestor. - javax.jms.Message response = requestor.request(msg); - // The message should be a TextMessage. Just report it. - javax.jms.TextMessage textMessage = (javax.jms.TextMessage) response; - System.out.println( "[Reply] " + textMessage.getText() ); - } - } - } - catch ( java.io.IOException ioe ) - { - ioe.printStackTrace(); - } - catch ( javax.jms.JMSException jmse ) - { - jmse.printStackTrace(); - } - } - - /** Cleanup resources cleanly and exit. */ - private void exit() - { - try - { - requestor.close(); - connect.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String argv[]) { - - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = DEFAULT_USER_NAME; - String password = DEFAULT_PASSWORD; - String queue = DEFAULT_QUEUE; - - // Check parameters - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - - if (arg.equals("-qs")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing queue"); - System.exit(1); - } - queue = argv[++i]; - continue; - } - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - - // Invalid argument - System.err.println ("error: unexpected argument: "+arg); - printUsage(); - System.exit(1); - } - - // Start the JMS client for sending requests. - Requestor requestor = new Requestor(); - requestor.start (broker, username, password, queue); - - } - - /** Prints the usage. */ - private static void printUsage() { - - StringBuffer use = new StringBuffer(); - use.append("usage: java Requestor (options) ...\n\n"); - use.append("options:\n"); - use.append(" -b name:port Specify name:port of broker.\n"); - use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); - use.append(" -u name Specify unique user name.\n"); - use.append(" Default broker: "+DEFAULT_USER_NAME+"\n"); - use.append(" -p password Specify password for user.\n"); - use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); - use.append(" -qs queue Specify name of queue for sending.\n"); - use.append(" Default queue: "+DEFAULT_QUEUE+"\n"); - use.append(" -h This help screen.\n"); - System.err.println (use); - } - -} - +/** + * 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. + */ + +/* +Copyright 2001-2008, Progress Software Corporation - All Rights Reserved + +Sample Application + +Writing a Basic JMS Application with Point-to-Point Queues, +using: + - Synchronous Request/Reply + - javax.jms.QueueRequestor class + - JMSReplyTo Header + +When this program runs, it reads input from System.in +and then sends the text as a message to the queue, "Q1" +(by default). + +A "Replier" class should be waiting for the request. +It will reply with a message. + +NOTE: Unlike the Publish-Subscribe example, you need +not run the Replier first. However, this Requestor +will block until the Replier is started to service the queue. + +Usage: + java Requestor -b -u -p -qs + -b broker:port points to your message broker + Default: tcp://localhost:61616 + -u username must be unique (but is not checked) + Default: SampleRequestor + -p password password for user (not checked) + Default: password + -qs queue name of queue for sending requests + Default: SampleQ1 + +Suggested demonstration: + - In a console window with the environment set, + start a copy of the Replier. For example: + java Replier -u SampleQReplier + - In another console window, start a Requestor. + For example: + java Requestor -u SampleQRequestor + - Enter text in the Requestor window then press Enter. + The Replier responds with the message in all uppercase characters. + - Start other Requestors with different user names to see that + replies are not broadcast to all users. For example: + java Requestor -u SampleRequestorFoo + - Start other Repliers. + - See that only one replier is receiving messages,(as it should). + - See the Requestor only receives one response. + java Replier -u toLower -m lowercase + +*/ +import org.apache.activemq.*; + + +public class Requestor +{ + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + private static final String DEFAULT_USER_NAME = "SampleRequestor"; + private static final String DEFAULT_PASSWORD = "password"; + private static final String DEFAULT_QUEUE = "Q1"; + + private javax.jms.QueueConnection connect = null; + private javax.jms.QueueSession session = null; + private javax.jms.QueueRequestor requestor = null; + + /** Create JMS client for sending messages. */ + private void start ( String broker, String username, String password, String sQueue) + { + // Create a connection. + try + { + javax.jms.QueueConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + connect = factory.createQueueConnection (username, password); + session = connect.createQueueSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("error: Cannot connect to Broker - " + broker); + jmse.printStackTrace(); + System.exit(1); + } + + // Create the Queue and QueueRequestor for sending requests. + javax.jms.Queue queue = null; + try + { + queue = session.createQueue (sQueue); + requestor = new javax.jms.QueueRequestor(session, queue); + + // Now that all setup is complete, start the Connection. + connect.start(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + exit(); + } + + try + { + // Read all standard input and send it as a message. + java.io.BufferedReader stdin = + new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); + System.out.println ("\nRequestor application:\n" + + "============================\n" + + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" + + "The application uses a QueueRequestor to on the " + DEFAULT_QUEUE + " queue." + + "The Replier application gets the message, and transforms it." + + "The Requestor application displays the result.\n\n" + + "Type some mixed case text, and then press Enter to make a request.\n"); + while ( true ) + { + String s = stdin.readLine(); + + if ( s == null ) + exit(); + else if ( s.length() > 0 ) + { + javax.jms.TextMessage msg = session.createTextMessage(); + msg.setText( username + ": " + s ); + // Instead of sending, we will use the QueueRequestor. + javax.jms.Message response = requestor.request(msg); + // The message should be a TextMessage. Just report it. + javax.jms.TextMessage textMessage = (javax.jms.TextMessage) response; + System.out.println( "[Reply] " + textMessage.getText() ); + } + } + } + catch ( java.io.IOException ioe ) + { + ioe.printStackTrace(); + } + catch ( javax.jms.JMSException jmse ) + { + jmse.printStackTrace(); + } + } + + /** Cleanup resources cleanly and exit. */ + private void exit() + { + try + { + requestor.close(); + connect.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String argv[]) { + + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = DEFAULT_USER_NAME; + String password = DEFAULT_PASSWORD; + String queue = DEFAULT_QUEUE; + + // Check parameters + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + + if (arg.equals("-qs")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing queue"); + System.exit(1); + } + queue = argv[++i]; + continue; + } + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + + // Invalid argument + System.err.println ("error: unexpected argument: "+arg); + printUsage(); + System.exit(1); + } + + // Start the JMS client for sending requests. + Requestor requestor = new Requestor(); + requestor.start (broker, username, password, queue); + + } + + /** Prints the usage. */ + private static void printUsage() { + + StringBuffer use = new StringBuffer(); + use.append("usage: java Requestor (options) ...\n\n"); + use.append("options:\n"); + use.append(" -b name:port Specify name:port of broker.\n"); + use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); + use.append(" -u name Specify unique user name.\n"); + use.append(" Default broker: "+DEFAULT_USER_NAME+"\n"); + use.append(" -p password Specify password for user.\n"); + use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); + use.append(" -qs queue Specify name of queue for sending.\n"); + use.append(" Default queue: "+DEFAULT_QUEUE+"\n"); + use.append(" -h This help screen.\n"); + System.err.println (use); + } + +} + diff --git a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/SelectorTalk/SelectorTalk.java b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/SelectorTalk/SelectorTalk.java index f2609e7750..05803b619f 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/SelectorTalk/SelectorTalk.java +++ b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/SelectorTalk/SelectorTalk.java @@ -1,351 +1,351 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - -Sample Application - -Writing a Basic JMS Application using Point-to-Point (Queues) with -a Message Selector - -This sample starts up with a username, and the queues you are -sending on, and receiving on. - -When messages are sent to a queue a property is set in the message header to -a property value supplied on the command line. A separate command line -value is used as a message selector for messages in the receive queue. In this -sample we choose to create a property named "Department" - -Writing a line and pressing enter will send the message only to the -indicated 'Talk' partner (or QueueReceiver). - -Usage: - java SelectorTalk -b -u -p -qs -qr - -s - -b broker:port points to your message broker - Default: tcp://localhost:61616 - -u username must be unique (but is not checked) - -p password password for user (not checked) - -qr queue name of queue to receive - -qs queue name of queue to send - -s selector the message selection value - -You must specify either a queue for sending or receiving (or both) as well as -a value with which to select received messages. - - -Suggested demonstration: - - In separate console windows with the environment set, - start instances of the application under unique user names. - For example: - java SelectorTalk -u BOB -s SALES -qr Q1 -qs Q2 - java SelectorTalk -u SALLY -s SALES -qr Q2 -qs Q1 - - Enter text and then press Enter to send the message. - - See messages appear under the user's indicated name. - - Try changing the message selector on one console and note that it will - no longer receive messages. - - Stop a session by pressing CTRL+C in its console window. - -*/ -import org.apache.activemq.*; - -public class SelectorTalk - implements javax.jms.MessageListener -{ - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - private static final String PROPERTY_NAME = "Department"; - private static final String DEFAULT_PASSWORD = "password"; - private static final int MESSAGE_LIFESPAN = 1800000; // milliseconds (30 minutes) - - private javax.jms.Connection connect = null; - private javax.jms.Session sendSession = null; - private javax.jms.Session receiveSession = null; - private javax.jms.MessageProducer sender = null; - - /** Create JMS client for sending and receiving messages. */ - private void talker( String broker, String username, String password, String rQueue, String sQueue, String selection) - { - // Create a connection. - try - { - javax.jms.ConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - connect = factory.createConnection (username, password); - sendSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - receiveSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("error: Cannot connect to Broker - " + broker); - jmse.printStackTrace(); - System.exit(1); - } - - // Create Sender and Receiver 'Talk' queues - try - { - if (sQueue != null) - { - javax.jms.Queue sendQueue = sendSession.createQueue (sQueue); - sender = sendSession.createProducer(sendQueue); - } - if (rQueue != null) - { - //NOTE: the Queue Receiver is set up with the Message Selector: - javax.jms.Queue receiveQueue = receiveSession.createQueue (rQueue); - javax.jms.MessageConsumer qReceiver = receiveSession.createConsumer(receiveQueue, PROPERTY_NAME + " = \'" + selection + "\'"); - qReceiver.setMessageListener(this); - connect.start(); - } - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - exit(); - } - - try - { - if (rQueue != null) - System.out.println (""); - else - System.out.println ("\nNo receiving queue specified.\n"); - - // Read all standard input and send it as a message. - java.io.BufferedReader stdin = - new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); - if (sQueue != null) - System.out.println ("SelectorTalk application:\n" - + "=========================\n" - + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" - + "The application will send messages with " + PROPERTY_NAME + " set to " + selection + " to the " + sQueue + " queue.\n" - + "The application creates a receiver on the " + rQueue + " queue selecting only messages where " + PROPERTY_NAME + " is " + selection + ".\n\n" - - + "Type some text, and then press Enter to publish it as a TextMesssage from " + username + ".\n"); - else - System.out.println ("\nPress CTRL-C to exit.\n"); - - while ( true ) - { - String s = stdin.readLine(); - - if ( s == null ) - exit(); - else if ( s.length() > 0 && sQueue != null) - { - javax.jms.TextMessage msg = sendSession.createTextMessage(); - msg.setText( username + ": " + s ); - // NOTE: here we set the property for each sent message. - msg.setStringProperty(PROPERTY_NAME, selection); - sender.send( msg, - javax.jms.DeliveryMode.PERSISTENT, - javax.jms.Message.DEFAULT_PRIORITY, - MESSAGE_LIFESPAN); - } - } - } - catch ( java.io.IOException ioe ) - { - ioe.printStackTrace(); - } - catch ( javax.jms.JMSException jmse ) - { - jmse.printStackTrace(); - } - // Close the connection. - exit(); - } - - /** - * Handle the message - * (as specified in the javax.jms.MessageListener interface). - */ - public void onMessage( javax.jms.Message aMessage) - { - try - { - // Cast the message as a text message. - javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; - - // This handler reads a single String from the - // message and prints it to the standard output. - try - { - String string = textMessage.getText(); - System.out.println( string ); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - catch (java.lang.RuntimeException rte) - { - rte.printStackTrace(); - } - } - - /** Cleanup resources and then exit. */ - private void exit() - { - try - { - connect.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String argv[]) { - - // Is there anything to do? - if (argv.length == 0) { - printUsage(); - System.exit(1); - } - - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = null; - String password = DEFAULT_PASSWORD; - String qSender = null; - String qReceiver = null; - String selection = null; - - // Check parameters - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - // Options - if (!arg.startsWith("-")) { - System.err.println ("error: unexpected argument - "+arg); - printUsage(); - System.exit(1); - } - else { - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - - if (arg.equals("-qr")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing receive queue parameter"); - System.exit(1); - } - qReceiver = argv[++i]; - continue; - } - - if (arg.equals("-qs")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing send queue parameter"); - System.exit(1); - } - qSender = argv[++i]; - continue; - } - - if (arg.equals("-s")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing selectiion"); - System.exit(1); - } - selection = argv[++i]; - continue; - } - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - } - } - - // Check values read in. - if (username == null) { - System.err.println ("error: user name must be supplied"); - printUsage(); - System.exit(1); - } - - if (qReceiver == null && qSender == null) { - System.err.println ("error: receive queue, or send queue, must be supplied"); - printUsage(); - System.exit(1); - } - - if (selection == null) { - System.err.println ("error: selection must be supplied (e.g. -s SALES)\n"); - printUsage(); - System.exit(1); - } - - - // Start the JMS client for the "Talk". - SelectorTalk talk = new SelectorTalk(); - talk.talker (broker, username, password, qReceiver, qSender, selection); - - } - - /** Prints the usage. */ - private static void printUsage() { - - StringBuffer use = new StringBuffer(); - use.append("usage: java SelectorTalk (options) ...\n\n"); - use.append("options:\n"); - use.append(" -b name:port Specify name:port of broker.\n"); - use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); - use.append(" -u name Specify unique user name. (Required)\n"); - use.append(" -p password Specify password for user.\n"); - use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); - use.append(" -qr queue Specify queue for receiving messages.\n"); - use.append(" -qs queue Specify queue for sending messages.\n"); - use.append(" -s selection required, selection used to receive messages.\n"); - use.append(" -h This help screen.\n"); - System.err.println (use); - } - -} +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + +Sample Application + +Writing a Basic JMS Application using Point-to-Point (Queues) with +a Message Selector + +This sample starts up with a username, and the queues you are +sending on, and receiving on. + +When messages are sent to a queue a property is set in the message header to +a property value supplied on the command line. A separate command line +value is used as a message selector for messages in the receive queue. In this +sample we choose to create a property named "Department" + +Writing a line and pressing enter will send the message only to the +indicated 'Talk' partner (or QueueReceiver). + +Usage: + java SelectorTalk -b -u -p -qs -qr + -s + -b broker:port points to your message broker + Default: tcp://localhost:61616 + -u username must be unique (but is not checked) + -p password password for user (not checked) + -qr queue name of queue to receive + -qs queue name of queue to send + -s selector the message selection value + +You must specify either a queue for sending or receiving (or both) as well as +a value with which to select received messages. + + +Suggested demonstration: + - In separate console windows with the environment set, + start instances of the application under unique user names. + For example: + java SelectorTalk -u BOB -s SALES -qr Q1 -qs Q2 + java SelectorTalk -u SALLY -s SALES -qr Q2 -qs Q1 + - Enter text and then press Enter to send the message. + - See messages appear under the user's indicated name. + - Try changing the message selector on one console and note that it will + no longer receive messages. + - Stop a session by pressing CTRL+C in its console window. + +*/ +import org.apache.activemq.*; + +public class SelectorTalk + implements javax.jms.MessageListener +{ + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + private static final String PROPERTY_NAME = "Department"; + private static final String DEFAULT_PASSWORD = "password"; + private static final int MESSAGE_LIFESPAN = 1800000; // milliseconds (30 minutes) + + private javax.jms.Connection connect = null; + private javax.jms.Session sendSession = null; + private javax.jms.Session receiveSession = null; + private javax.jms.MessageProducer sender = null; + + /** Create JMS client for sending and receiving messages. */ + private void talker( String broker, String username, String password, String rQueue, String sQueue, String selection) + { + // Create a connection. + try + { + javax.jms.ConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + connect = factory.createConnection (username, password); + sendSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + receiveSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("error: Cannot connect to Broker - " + broker); + jmse.printStackTrace(); + System.exit(1); + } + + // Create Sender and Receiver 'Talk' queues + try + { + if (sQueue != null) + { + javax.jms.Queue sendQueue = sendSession.createQueue (sQueue); + sender = sendSession.createProducer(sendQueue); + } + if (rQueue != null) + { + //NOTE: the Queue Receiver is set up with the Message Selector: + javax.jms.Queue receiveQueue = receiveSession.createQueue (rQueue); + javax.jms.MessageConsumer qReceiver = receiveSession.createConsumer(receiveQueue, PROPERTY_NAME + " = \'" + selection + "\'"); + qReceiver.setMessageListener(this); + connect.start(); + } + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + exit(); + } + + try + { + if (rQueue != null) + System.out.println (""); + else + System.out.println ("\nNo receiving queue specified.\n"); + + // Read all standard input and send it as a message. + java.io.BufferedReader stdin = + new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); + if (sQueue != null) + System.out.println ("SelectorTalk application:\n" + + "=========================\n" + + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" + + "The application will send messages with " + PROPERTY_NAME + " set to " + selection + " to the " + sQueue + " queue.\n" + + "The application creates a receiver on the " + rQueue + " queue selecting only messages where " + PROPERTY_NAME + " is " + selection + ".\n\n" + + + "Type some text, and then press Enter to publish it as a TextMesssage from " + username + ".\n"); + else + System.out.println ("\nPress CTRL-C to exit.\n"); + + while ( true ) + { + String s = stdin.readLine(); + + if ( s == null ) + exit(); + else if ( s.length() > 0 && sQueue != null) + { + javax.jms.TextMessage msg = sendSession.createTextMessage(); + msg.setText( username + ": " + s ); + // NOTE: here we set the property for each sent message. + msg.setStringProperty(PROPERTY_NAME, selection); + sender.send( msg, + javax.jms.DeliveryMode.PERSISTENT, + javax.jms.Message.DEFAULT_PRIORITY, + MESSAGE_LIFESPAN); + } + } + } + catch ( java.io.IOException ioe ) + { + ioe.printStackTrace(); + } + catch ( javax.jms.JMSException jmse ) + { + jmse.printStackTrace(); + } + // Close the connection. + exit(); + } + + /** + * Handle the message + * (as specified in the javax.jms.MessageListener interface). + */ + public void onMessage( javax.jms.Message aMessage) + { + try + { + // Cast the message as a text message. + javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; + + // This handler reads a single String from the + // message and prints it to the standard output. + try + { + String string = textMessage.getText(); + System.out.println( string ); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + catch (java.lang.RuntimeException rte) + { + rte.printStackTrace(); + } + } + + /** Cleanup resources and then exit. */ + private void exit() + { + try + { + connect.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String argv[]) { + + // Is there anything to do? + if (argv.length == 0) { + printUsage(); + System.exit(1); + } + + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = null; + String password = DEFAULT_PASSWORD; + String qSender = null; + String qReceiver = null; + String selection = null; + + // Check parameters + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + // Options + if (!arg.startsWith("-")) { + System.err.println ("error: unexpected argument - "+arg); + printUsage(); + System.exit(1); + } + else { + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + + if (arg.equals("-qr")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing receive queue parameter"); + System.exit(1); + } + qReceiver = argv[++i]; + continue; + } + + if (arg.equals("-qs")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing send queue parameter"); + System.exit(1); + } + qSender = argv[++i]; + continue; + } + + if (arg.equals("-s")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing selectiion"); + System.exit(1); + } + selection = argv[++i]; + continue; + } + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + } + } + + // Check values read in. + if (username == null) { + System.err.println ("error: user name must be supplied"); + printUsage(); + System.exit(1); + } + + if (qReceiver == null && qSender == null) { + System.err.println ("error: receive queue, or send queue, must be supplied"); + printUsage(); + System.exit(1); + } + + if (selection == null) { + System.err.println ("error: selection must be supplied (e.g. -s SALES)\n"); + printUsage(); + System.exit(1); + } + + + // Start the JMS client for the "Talk". + SelectorTalk talk = new SelectorTalk(); + talk.talker (broker, username, password, qReceiver, qSender, selection); + + } + + /** Prints the usage. */ + private static void printUsage() { + + StringBuffer use = new StringBuffer(); + use.append("usage: java SelectorTalk (options) ...\n\n"); + use.append("options:\n"); + use.append(" -b name:port Specify name:port of broker.\n"); + use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); + use.append(" -u name Specify unique user name. (Required)\n"); + use.append(" -p password Specify password for user.\n"); + use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); + use.append(" -qr queue Specify queue for receiving messages.\n"); + use.append(" -qs queue Specify queue for sending messages.\n"); + use.append(" -s selection required, selection used to receive messages.\n"); + use.append(" -h This help screen.\n"); + System.err.println (use); + } + +} diff --git a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/Talk/Talk.java b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/Talk/Talk.java index e849fcca7b..db8edcab49 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/Talk/Talk.java +++ b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/Talk/Talk.java @@ -1,332 +1,332 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - -Sample Application - -Writing a Basic JMS Application using Point-to-Point (Queues). -This sample starts up with a username, and the queues you are -sending on, and receiving on. - -Writing a line and pressing enter will send the message only to the -indicated 'Talk' partner (or QueueReceiver). - -Usage: - java Talk -b -u -p -qs -qr - -b broker:port points to your message broker - Default: tcp://localhost:61616 - -u username must be unique (but is not checked) - -p password password for user (not checked) - -qr queue name of queue to receive - -qs queue name of queue to send - -You must specify either a queue for sending or receiving (or both). - - -Suggested demonstration: - - In separate console windows with the environment set, - start instances of the application under unique user names. - For example: - java Talk -b localhost:2506 -u SALES -qr Q1 -qs Q2 - java Talk -b localhost:2506 -u MARKETTING -qr Q2 -qs Q1 - - Enter text and then press Enter to send the message. - - A message is consumed by only one of the receivers on a queue. - - Stop a session by pressing CTRL+C in its console window. - -*/ -import org.apache.activemq.*; - - -public class Talk - implements javax.jms.MessageListener -{ - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - private static final String DEFAULT_PASSWORD = "password"; - private static final int MESSAGE_LIFESPAN = 1800000; // milliseconds (30 minutes) - - private javax.jms.Connection connect = null; - private javax.jms.Session sendSession = null; - private javax.jms.Session receiveSession = null; - private javax.jms.MessageProducer sender = null; - - /** Create JMS client for sending and receiving messages. */ - private void talker( String broker, String username, String password, String rQueue, String sQueue) - { - // Create a connection. - try - { - javax.jms.ConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - connect = factory.createConnection (username, password); - sendSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - receiveSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("error: Cannot connect to Broker - " + broker); - jmse.printStackTrace(); - System.exit(1); - } - - // Create Sender and Receiver 'Talk' queues - try - { - if (sQueue != null) - { - javax.jms.Queue sendQueue = sendSession.createQueue (sQueue); - sender = sendSession.createProducer(sendQueue); - } - if (rQueue != null) - { - javax.jms.Queue receiveQueue = receiveSession.createQueue (rQueue); - javax.jms.MessageConsumer qReceiver = receiveSession.createConsumer(receiveQueue); - qReceiver.setMessageListener(this); - // Now that 'receive' setup is complete, start the Connection - connect.start(); - } - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - exit(); - } - - try - { - if (rQueue != null) - System.out.println (""); - else - System.out.println ("\nNo receiving queue specified.\n"); - - // Read all standard input and send it as a message. - java.io.BufferedReader stdin = - new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); - if (sQueue != null) - System.out.println ("\nTalk application:\n" - + "=================\n" - + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" - + "The application will send messages to the " + sQueue + " queue.\n" - + "The application creates a receiver on the " + rQueue + " queue to consume any messages allocated to it.\n\n" - + "Type some text, and then press Enter to send it as a TextMesssage from " + username + ".\n"); - - else - System.out.println ("\nPress CTRL-C to exit.\n"); - - while ( true ) - { - String s = stdin.readLine(); - - if ( s == null ) - exit(); - else if ( s.length() > 0 && sQueue != null) - { - javax.jms.TextMessage msg = sendSession.createTextMessage(); - msg.setText( username + ": " + s ); - // Queues usually are used for PERSISTENT messages. - // Hold messages for 30 minutes (1,800,000 millisecs). - sender.send( msg, - javax.jms.DeliveryMode.PERSISTENT, - javax.jms.Message.DEFAULT_PRIORITY, - MESSAGE_LIFESPAN); - } - } - } - catch ( java.io.IOException ioe ) - { - ioe.printStackTrace(); - } - catch ( javax.jms.JMSException jmse ) - { - jmse.printStackTrace(); - } - // Close the connection. - exit(); - } - - /** - * Handle the message - * (as specified in the javax.jms.MessageListener interface). - */ - public void onMessage( javax.jms.Message aMessage) - { - try - { - // Cast the message as a text message. - // Otherwise report that invalid message arrived. - if (aMessage instanceof javax.jms.TextMessage) - { - javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; - - // This handler reads a single String from the - // message and prints it to the standard output. - try - { - String string = textMessage.getText(); - System.out.println( string ); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - else - { - System.out.println ("Warning: A message was discarded because it could not be processed " + - "as a javax.jms.TextMessage."); - } - - } - catch (java.lang.RuntimeException rte) - { - rte.printStackTrace(); - } - } - - /** Cleanup resources and then exit. */ - private void exit() - { - try - { - connect.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String argv[]) { - - // Is there anything to do? - if (argv.length == 0) { - printUsage(); - System.exit(1); - } - - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = null; - String password = DEFAULT_PASSWORD; - String qSender = null; - String qReceiver = null; - - // Check parameters - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - // Options - if (!arg.startsWith("-")) { - System.err.println ("error: unexpected argument - "+arg); - printUsage(); - System.exit(1); - } - else { - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - - if (arg.equals("-qr")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing receive queue parameter"); - System.exit(1); - } - qReceiver = argv[++i]; - continue; - } - - if (arg.equals("-qs")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing send queue parameter"); - System.exit(1); - } - qSender = argv[++i]; - continue; - } - - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - } - } - - // Check values read in. - if (username == null) { - System.err.println ("error: user name must be supplied"); - printUsage(); - System.exit(1); - } - - if (qReceiver == null && qSender == null) { - System.err.println ("error: receive queue, or send queue, must be supplied"); - printUsage(); - System.exit(1); - } - - // Start the JMS client for the "Talk". - Talk talk = new Talk(); - talk.talker (broker, username, password, qReceiver, qSender); - - } - - /** Prints the usage. */ - private static void printUsage() { - - StringBuffer use = new StringBuffer(); - use.append("usage: java Talk (options) ...\n\n"); - use.append("options:\n"); - use.append(" -b name:port Specify name:port of broker.\n"); - use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); - use.append(" -u name Specify unique user name. (Required)\n"); - use.append(" -p password Specify password for user.\n"); - use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); - use.append(" -qr queue Specify queue for receiving messages.\n"); - use.append(" -qs queue Specify queue for sending messages.\n"); - use.append(" -h This help screen.\n"); - System.err.println (use); - } - -} +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + +Sample Application + +Writing a Basic JMS Application using Point-to-Point (Queues). +This sample starts up with a username, and the queues you are +sending on, and receiving on. + +Writing a line and pressing enter will send the message only to the +indicated 'Talk' partner (or QueueReceiver). + +Usage: + java Talk -b -u -p -qs -qr + -b broker:port points to your message broker + Default: tcp://localhost:61616 + -u username must be unique (but is not checked) + -p password password for user (not checked) + -qr queue name of queue to receive + -qs queue name of queue to send + +You must specify either a queue for sending or receiving (or both). + + +Suggested demonstration: + - In separate console windows with the environment set, + start instances of the application under unique user names. + For example: + java Talk -b localhost:2506 -u SALES -qr Q1 -qs Q2 + java Talk -b localhost:2506 -u MARKETTING -qr Q2 -qs Q1 + - Enter text and then press Enter to send the message. + - A message is consumed by only one of the receivers on a queue. + - Stop a session by pressing CTRL+C in its console window. + +*/ +import org.apache.activemq.*; + + +public class Talk + implements javax.jms.MessageListener +{ + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + private static final String DEFAULT_PASSWORD = "password"; + private static final int MESSAGE_LIFESPAN = 1800000; // milliseconds (30 minutes) + + private javax.jms.Connection connect = null; + private javax.jms.Session sendSession = null; + private javax.jms.Session receiveSession = null; + private javax.jms.MessageProducer sender = null; + + /** Create JMS client for sending and receiving messages. */ + private void talker( String broker, String username, String password, String rQueue, String sQueue) + { + // Create a connection. + try + { + javax.jms.ConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + connect = factory.createConnection (username, password); + sendSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + receiveSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("error: Cannot connect to Broker - " + broker); + jmse.printStackTrace(); + System.exit(1); + } + + // Create Sender and Receiver 'Talk' queues + try + { + if (sQueue != null) + { + javax.jms.Queue sendQueue = sendSession.createQueue (sQueue); + sender = sendSession.createProducer(sendQueue); + } + if (rQueue != null) + { + javax.jms.Queue receiveQueue = receiveSession.createQueue (rQueue); + javax.jms.MessageConsumer qReceiver = receiveSession.createConsumer(receiveQueue); + qReceiver.setMessageListener(this); + // Now that 'receive' setup is complete, start the Connection + connect.start(); + } + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + exit(); + } + + try + { + if (rQueue != null) + System.out.println (""); + else + System.out.println ("\nNo receiving queue specified.\n"); + + // Read all standard input and send it as a message. + java.io.BufferedReader stdin = + new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); + if (sQueue != null) + System.out.println ("\nTalk application:\n" + + "=================\n" + + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" + + "The application will send messages to the " + sQueue + " queue.\n" + + "The application creates a receiver on the " + rQueue + " queue to consume any messages allocated to it.\n\n" + + "Type some text, and then press Enter to send it as a TextMesssage from " + username + ".\n"); + + else + System.out.println ("\nPress CTRL-C to exit.\n"); + + while ( true ) + { + String s = stdin.readLine(); + + if ( s == null ) + exit(); + else if ( s.length() > 0 && sQueue != null) + { + javax.jms.TextMessage msg = sendSession.createTextMessage(); + msg.setText( username + ": " + s ); + // Queues usually are used for PERSISTENT messages. + // Hold messages for 30 minutes (1,800,000 millisecs). + sender.send( msg, + javax.jms.DeliveryMode.PERSISTENT, + javax.jms.Message.DEFAULT_PRIORITY, + MESSAGE_LIFESPAN); + } + } + } + catch ( java.io.IOException ioe ) + { + ioe.printStackTrace(); + } + catch ( javax.jms.JMSException jmse ) + { + jmse.printStackTrace(); + } + // Close the connection. + exit(); + } + + /** + * Handle the message + * (as specified in the javax.jms.MessageListener interface). + */ + public void onMessage( javax.jms.Message aMessage) + { + try + { + // Cast the message as a text message. + // Otherwise report that invalid message arrived. + if (aMessage instanceof javax.jms.TextMessage) + { + javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; + + // This handler reads a single String from the + // message and prints it to the standard output. + try + { + String string = textMessage.getText(); + System.out.println( string ); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + else + { + System.out.println ("Warning: A message was discarded because it could not be processed " + + "as a javax.jms.TextMessage."); + } + + } + catch (java.lang.RuntimeException rte) + { + rte.printStackTrace(); + } + } + + /** Cleanup resources and then exit. */ + private void exit() + { + try + { + connect.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String argv[]) { + + // Is there anything to do? + if (argv.length == 0) { + printUsage(); + System.exit(1); + } + + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = null; + String password = DEFAULT_PASSWORD; + String qSender = null; + String qReceiver = null; + + // Check parameters + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + // Options + if (!arg.startsWith("-")) { + System.err.println ("error: unexpected argument - "+arg); + printUsage(); + System.exit(1); + } + else { + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + + if (arg.equals("-qr")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing receive queue parameter"); + System.exit(1); + } + qReceiver = argv[++i]; + continue; + } + + if (arg.equals("-qs")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing send queue parameter"); + System.exit(1); + } + qSender = argv[++i]; + continue; + } + + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + } + } + + // Check values read in. + if (username == null) { + System.err.println ("error: user name must be supplied"); + printUsage(); + System.exit(1); + } + + if (qReceiver == null && qSender == null) { + System.err.println ("error: receive queue, or send queue, must be supplied"); + printUsage(); + System.exit(1); + } + + // Start the JMS client for the "Talk". + Talk talk = new Talk(); + talk.talker (broker, username, password, qReceiver, qSender); + + } + + /** Prints the usage. */ + private static void printUsage() { + + StringBuffer use = new StringBuffer(); + use.append("usage: java Talk (options) ...\n\n"); + use.append("options:\n"); + use.append(" -b name:port Specify name:port of broker.\n"); + use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); + use.append(" -u name Specify unique user name. (Required)\n"); + use.append(" -p password Specify password for user.\n"); + use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); + use.append(" -qr queue Specify queue for receiving messages.\n"); + use.append(" -qs queue Specify queue for sending messages.\n"); + use.append(" -h This help screen.\n"); + System.err.println (use); + } + +} diff --git a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/TransactedTalk/TransactedTalk.java b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/TransactedTalk/TransactedTalk.java index 783a7f42ce..d9aa27ae92 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/TransactedTalk/TransactedTalk.java +++ b/assembly/src/release/examples/openwire/exploring-jms/QueuePTPSamples/TransactedTalk/TransactedTalk.java @@ -1,360 +1,360 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - -Sample Application - -Writing a Basic JMS Application with Point-to-Point Queues, -using: - - Send and Receive - - Transacted Sessions - - Multiple Sessions - -This sample starts up with a username, and the queues you are -sending on, and receiving on. - -Continue writing lines and pressing enter to buffer messages until a -specific key word is used to confirm the messages or to completely -forget them. - -Messages are buffered and sent when a specific string is seen ("COMMIT"). -Messages buffered can be discarded by entering a specified string ("CANCEL"). - -Usage: - java TransactedTalk -b -u -p -qs -qr - -b broker:port points to your message broker - Default: tcp://localhost:61616 - -u username must be unique (but is not checked) - -p password password for user (not checked) - -qr queue name of queue to receive - -qs queue name of queue to send - -You must specify either a queue for sending or receiving (or both). - -Suggested demonstration: - - In separate console windows with the environment set, - start instances of the application under unique user names. - For example: - java TransactedTalk -u OPERATIONS -qr Q1 -qs Q2 - java TransactedTalk -u FACILITIES -qr Q2 -qs Q1 - - Type some text and then press Enter. - - Repeat to create a batch of messages. - - Send the batched messages by entering the text "COMMIT" - - Discard the batched messages by entering the text "CANCEL" - - Stop a session by pressing CTRL+C in its console window. - -*/ - -import org.apache.activemq.*; - -public class TransactedTalk - implements javax.jms.MessageListener -{ - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - private static final String DEFAULT_PASSWORD = "password"; - private static final int MESSAGE_LIFESPAN = 1800000; // milliseconds (30 minutes) - - private javax.jms.Connection connect = null; - private javax.jms.Session sendSession = null; - private javax.jms.Session receiveSession = null; - private javax.jms.MessageProducer sender = null; - - - - - /** Create JMS client for sending and receiving messages. */ - private void talker( String broker, String username, String password, String rQueue, String sQueue) - { - // Create a connection. - try - { - javax.jms.ConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - connect = factory.createConnection (username, password); - // We want to be able up commit/rollback messages sent, - // but not affect messages received. Therefore, we need two sessions. - sendSession = connect.createSession(true,javax.jms.Session.AUTO_ACKNOWLEDGE); - receiveSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("error: Cannot connect to Broker - " + broker); - jmse.printStackTrace(); - System.exit(1); - } - - // Create Sender and Receiver 'Talk' queues - try - { - if (sQueue != null) - { - javax.jms.Queue sendQueue = sendSession.createQueue (sQueue); - sender = sendSession.createProducer(sendQueue); - } - if (rQueue != null) - { - javax.jms.Queue receiveQueue = receiveSession.createQueue (rQueue); - javax.jms.MessageConsumer qReceiver = receiveSession.createConsumer(receiveQueue); - qReceiver.setMessageListener(this); - // Now that 'receive' setup is complete, start the Connection - connect.start(); - } - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - exit(); - } - - try - { - if (rQueue != null) - System.out.println (""); - else - System.out.println ("\nNo receiving queue specified.\n"); - - // Read all standard input and send it as a message. - java.io.BufferedReader stdin = - new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); - - if (sQueue != null){ - System.out.println ("TransactedTalk application:"); - System.out.println ("===========================" ); - System.out.println ("The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + "."); - System.out.println ("The application will stage messages to " + sQueue + " until you either commit them or roll them back."); - System.out.println ("The application receives messages on " + rQueue + " to consume any committed messages sent there.\n"); - System.out.println ("1. Enter text to send and then press Enter to stage the message."); - System.out.println ("2. Add a few messages to the transaction batch."); - System.out.println ("3. Then, either:"); - System.out.println (" o Enter the text 'COMMIT', and press Enter to send all the staged messages."); - System.out.println (" o Enter the text 'CANCEL', and press Enter to drop the staged messages waiting to be sent."); - } - else - System.out.println ("\nPress CTRL-C to exit.\n"); - - while ( true ) - { - String s = stdin.readLine(); - - if ( s == null ) - exit(); - else if (s.trim().equals("CANCEL")) - { - // Rollback the messages. A new transaction is implicitly - // started for following messages. - System.out.print ("Cancelling messages..."); - sendSession.rollback(); - System.out.println ("Staged messages have been cleared."); - } - else if ( s.length() > 0 && sQueue != null) - { - javax.jms.TextMessage msg = sendSession.createTextMessage(); - msg.setText( username + ": " + s ); - // Queues usually are used for PERSISTENT messages. - // Hold messages for 30 minutes (1,800,000 millisecs). - sender.send( msg, - javax.jms.DeliveryMode.PERSISTENT, - javax.jms.Message.DEFAULT_PRIORITY, - MESSAGE_LIFESPAN); - // See if we should send the messages - if (s.trim().equals("COMMIT")) - { - // Commit (send) the messages. A new transaction is - // implicitly started for following messages. - System.out.print ("Committing messages..."); - sendSession.commit(); - System.out.println ("Staged messages have all been sent."); - } - } - } - } - catch ( java.io.IOException ioe ) - { - ioe.printStackTrace(); - } - catch ( javax.jms.JMSException jmse ) - { - jmse.printStackTrace(); - } - // Close the connection. - exit(); - } - - /** - * Handle the message - * (as specified in the javax.jms.MessageListener interface). - */ - public void onMessage( javax.jms.Message aMessage) - { - try - { - // Cast the message as a text message. - javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; - - // This handler reads a single String from the - // message and prints it to the standard output. - try - { - String string = textMessage.getText(); - System.out.println( string ); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - catch (java.lang.RuntimeException rte) - { - rte.printStackTrace(); - } - } - - /** Cleanup resources and then exit. */ - private void exit() - { - try - { - sendSession.rollback(); // Rollback any uncommitted messages. - connect.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String argv[]) { - - // Is there anything to do? - if (argv.length == 0) { - printUsage(); - System.exit(1); - } - - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = null; - String password = DEFAULT_PASSWORD; - String qSender = null; - String qReceiver = null; - - // Check parameters - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - // Options - if (!arg.startsWith("-")) { - System.err.println ("error: unexpected argument - "+arg); - printUsage(); - System.exit(1); - } - else { - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - - if (arg.equals("-qr")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing receive queue parameter"); - System.exit(1); - } - qReceiver = argv[++i]; - continue; - } - - if (arg.equals("-qs")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing send queue parameter"); - System.exit(1); - } - qSender = argv[++i]; - continue; - } - - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - } - } - - // Check values read in. - if (username == null) { - System.err.println ("error: user name must be supplied"); - printUsage(); - System.exit(1); - } - - if (qReceiver == null && qSender == null) { - System.err.println ("error: receive queue, or send queue, must be supplied"); - printUsage(); - System.exit(1); - } - - // Start the JMS client for the "Talk". - TransactedTalk tranTalk = new TransactedTalk(); - tranTalk.talker (broker, username, password, qReceiver, qSender); - - } - - /** Prints the usage. */ - private static void printUsage() { - - StringBuffer use = new StringBuffer(); - use.append("usage: java TransactedTalk (options) ...\n\n"); - use.append("options:\n"); - use.append(" -b name:port Specify name:port of broker.\n"); - use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); - use.append(" -u name Specify unique user name. (Required)\n"); - use.append(" -p password Specify password for user.\n"); - use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); - use.append(" -qr queue Specify queue for receiving messages.\n"); - use.append(" -qs queue Specify queue for sending messages.\n"); - use.append(" -h This help screen.\n"); - System.err.println (use); - } - -} +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + +Sample Application + +Writing a Basic JMS Application with Point-to-Point Queues, +using: + - Send and Receive + - Transacted Sessions + - Multiple Sessions + +This sample starts up with a username, and the queues you are +sending on, and receiving on. + +Continue writing lines and pressing enter to buffer messages until a +specific key word is used to confirm the messages or to completely +forget them. + +Messages are buffered and sent when a specific string is seen ("COMMIT"). +Messages buffered can be discarded by entering a specified string ("CANCEL"). + +Usage: + java TransactedTalk -b -u -p -qs -qr + -b broker:port points to your message broker + Default: tcp://localhost:61616 + -u username must be unique (but is not checked) + -p password password for user (not checked) + -qr queue name of queue to receive + -qs queue name of queue to send + +You must specify either a queue for sending or receiving (or both). + +Suggested demonstration: + - In separate console windows with the environment set, + start instances of the application under unique user names. + For example: + java TransactedTalk -u OPERATIONS -qr Q1 -qs Q2 + java TransactedTalk -u FACILITIES -qr Q2 -qs Q1 + - Type some text and then press Enter. + - Repeat to create a batch of messages. + - Send the batched messages by entering the text "COMMIT" + - Discard the batched messages by entering the text "CANCEL" + - Stop a session by pressing CTRL+C in its console window. + +*/ + +import org.apache.activemq.*; + +public class TransactedTalk + implements javax.jms.MessageListener +{ + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + private static final String DEFAULT_PASSWORD = "password"; + private static final int MESSAGE_LIFESPAN = 1800000; // milliseconds (30 minutes) + + private javax.jms.Connection connect = null; + private javax.jms.Session sendSession = null; + private javax.jms.Session receiveSession = null; + private javax.jms.MessageProducer sender = null; + + + + + /** Create JMS client for sending and receiving messages. */ + private void talker( String broker, String username, String password, String rQueue, String sQueue) + { + // Create a connection. + try + { + javax.jms.ConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + connect = factory.createConnection (username, password); + // We want to be able up commit/rollback messages sent, + // but not affect messages received. Therefore, we need two sessions. + sendSession = connect.createSession(true,javax.jms.Session.AUTO_ACKNOWLEDGE); + receiveSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("error: Cannot connect to Broker - " + broker); + jmse.printStackTrace(); + System.exit(1); + } + + // Create Sender and Receiver 'Talk' queues + try + { + if (sQueue != null) + { + javax.jms.Queue sendQueue = sendSession.createQueue (sQueue); + sender = sendSession.createProducer(sendQueue); + } + if (rQueue != null) + { + javax.jms.Queue receiveQueue = receiveSession.createQueue (rQueue); + javax.jms.MessageConsumer qReceiver = receiveSession.createConsumer(receiveQueue); + qReceiver.setMessageListener(this); + // Now that 'receive' setup is complete, start the Connection + connect.start(); + } + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + exit(); + } + + try + { + if (rQueue != null) + System.out.println (""); + else + System.out.println ("\nNo receiving queue specified.\n"); + + // Read all standard input and send it as a message. + java.io.BufferedReader stdin = + new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); + + if (sQueue != null){ + System.out.println ("TransactedTalk application:"); + System.out.println ("===========================" ); + System.out.println ("The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + "."); + System.out.println ("The application will stage messages to " + sQueue + " until you either commit them or roll them back."); + System.out.println ("The application receives messages on " + rQueue + " to consume any committed messages sent there.\n"); + System.out.println ("1. Enter text to send and then press Enter to stage the message."); + System.out.println ("2. Add a few messages to the transaction batch."); + System.out.println ("3. Then, either:"); + System.out.println (" o Enter the text 'COMMIT', and press Enter to send all the staged messages."); + System.out.println (" o Enter the text 'CANCEL', and press Enter to drop the staged messages waiting to be sent."); + } + else + System.out.println ("\nPress CTRL-C to exit.\n"); + + while ( true ) + { + String s = stdin.readLine(); + + if ( s == null ) + exit(); + else if (s.trim().equals("CANCEL")) + { + // Rollback the messages. A new transaction is implicitly + // started for following messages. + System.out.print ("Cancelling messages..."); + sendSession.rollback(); + System.out.println ("Staged messages have been cleared."); + } + else if ( s.length() > 0 && sQueue != null) + { + javax.jms.TextMessage msg = sendSession.createTextMessage(); + msg.setText( username + ": " + s ); + // Queues usually are used for PERSISTENT messages. + // Hold messages for 30 minutes (1,800,000 millisecs). + sender.send( msg, + javax.jms.DeliveryMode.PERSISTENT, + javax.jms.Message.DEFAULT_PRIORITY, + MESSAGE_LIFESPAN); + // See if we should send the messages + if (s.trim().equals("COMMIT")) + { + // Commit (send) the messages. A new transaction is + // implicitly started for following messages. + System.out.print ("Committing messages..."); + sendSession.commit(); + System.out.println ("Staged messages have all been sent."); + } + } + } + } + catch ( java.io.IOException ioe ) + { + ioe.printStackTrace(); + } + catch ( javax.jms.JMSException jmse ) + { + jmse.printStackTrace(); + } + // Close the connection. + exit(); + } + + /** + * Handle the message + * (as specified in the javax.jms.MessageListener interface). + */ + public void onMessage( javax.jms.Message aMessage) + { + try + { + // Cast the message as a text message. + javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; + + // This handler reads a single String from the + // message and prints it to the standard output. + try + { + String string = textMessage.getText(); + System.out.println( string ); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + catch (java.lang.RuntimeException rte) + { + rte.printStackTrace(); + } + } + + /** Cleanup resources and then exit. */ + private void exit() + { + try + { + sendSession.rollback(); // Rollback any uncommitted messages. + connect.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String argv[]) { + + // Is there anything to do? + if (argv.length == 0) { + printUsage(); + System.exit(1); + } + + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = null; + String password = DEFAULT_PASSWORD; + String qSender = null; + String qReceiver = null; + + // Check parameters + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + // Options + if (!arg.startsWith("-")) { + System.err.println ("error: unexpected argument - "+arg); + printUsage(); + System.exit(1); + } + else { + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + + if (arg.equals("-qr")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing receive queue parameter"); + System.exit(1); + } + qReceiver = argv[++i]; + continue; + } + + if (arg.equals("-qs")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing send queue parameter"); + System.exit(1); + } + qSender = argv[++i]; + continue; + } + + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + } + } + + // Check values read in. + if (username == null) { + System.err.println ("error: user name must be supplied"); + printUsage(); + System.exit(1); + } + + if (qReceiver == null && qSender == null) { + System.err.println ("error: receive queue, or send queue, must be supplied"); + printUsage(); + System.exit(1); + } + + // Start the JMS client for the "Talk". + TransactedTalk tranTalk = new TransactedTalk(); + tranTalk.talker (broker, username, password, qReceiver, qSender); + + } + + /** Prints the usage. */ + private static void printUsage() { + + StringBuffer use = new StringBuffer(); + use.append("usage: java TransactedTalk (options) ...\n\n"); + use.append("options:\n"); + use.append(" -b name:port Specify name:port of broker.\n"); + use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); + use.append(" -u name Specify unique user name. (Required)\n"); + use.append(" -p password Specify password for user.\n"); + use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); + use.append(" -qr queue Specify queue for receiving messages.\n"); + use.append(" -qs queue Specify queue for sending messages.\n"); + use.append(" -h This help screen.\n"); + System.err.println (use); + } + +} diff --git a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/Chat/Chat.java b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/Chat/Chat.java index 1ede2bc838..f30da3440e 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/Chat/Chat.java +++ b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/Chat/Chat.java @@ -1,262 +1,262 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - -Sample Application - -Writing a Basic JMS Application using Publish and Subscribe - -This sample publishes and subscribes to a specified topic. -Text you enter is published and then received by all subscribers. - -Usage: - java Chat -b -u -p - -b broker:port points to your message broker - Default: tcp://localhost:61616 - -u username must be unique (but is not checked) - -p password password for user (not checked) - -Suggested demonstration: - - In separate console windows with the environment set, - start instances of the application - under unique user names. - For example: - java Chat -b tcp://localhost:61616 -u SALES - java Chat -b tcp://localhost:61616 -u MARKETING - - Enter text and then press Enter to publish the message. - - See messages appear under the various user names as you - enter messages in each console window. - - Stop a session by pressing CTRL+C in its console window. - -*/ -import org.apache.activemq.*; - -public class Chat - implements javax.jms.MessageListener -{ - private static final String APP_TOPIC = "jms.samples.chat"; - private static final String DEFAULT_USER = "Chatter"; - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - private static final String DEFAULT_PASSWORD = "password"; - - private javax.jms.Connection connect = null; - private javax.jms.Session pubSession = null; - private javax.jms.Session subSession = null; - private javax.jms.MessageProducer publisher = null; - - /** Create JMS client for publishing and subscribing to messages. */ - private void chatter( String broker, String username, String password) - { - // Create a connection. - try - { - javax.jms.ConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - connect = factory.createConnection (username, password); - pubSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - subSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("error: Cannot connect to Broker - " + broker); - jmse.printStackTrace(); - System.exit(1); - } - - // Create Publisher and Subscriber to 'chat' topics - try - { - javax.jms.Topic topic = pubSession.createTopic (APP_TOPIC); - javax.jms.MessageConsumer subscriber = subSession.createConsumer(topic); - subscriber.setMessageListener(this); - publisher = pubSession.createProducer(topic); - // Now that setup is complete, start the Connection - connect.start(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - try - { - // Read all standard input and send it as a message. - java.io.BufferedReader stdin = - new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); - System.out.println ("\nChat application:\n" - + "=================\n" - + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" - + "The application will publish messages to the " + APP_TOPIC + " topic.\n" - + "The application also subscribes to that topic to consume any messages published there.\n\n" - + "Type some text, and then press Enter to publish it as a TextMesssage from " + username + ".\n"); - while ( true ) - { - String s = stdin.readLine(); - - if ( s == null ) - exit(); - else if ( s.length() > 0 ) - { - javax.jms.TextMessage msg = pubSession.createTextMessage(); - msg.setText( username + ": " + s ); - publisher.send( msg ); - } - } - } - catch ( java.io.IOException ioe ) - { - ioe.printStackTrace(); - } - catch ( javax.jms.JMSException jmse ) - { - jmse.printStackTrace(); - } - } - - /** - * Handle the message - * (as specified in the javax.jms.MessageListener interface). - */ - public void onMessage( javax.jms.Message aMessage) - { - try - { - // Cast the message as a text message. - javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; - - // This handler reads a single String from the - // message and prints it to the standard output. - try - { - String string = textMessage.getText(); - System.out.println( string ); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - catch (java.lang.RuntimeException rte) - { - rte.printStackTrace(); - } - } - - /** Cleanup resources and then exit. */ - private void exit() - { - try - { - connect.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String argv[]) { - - // Is there anything to do? - if (argv.length == 0) { - printUsage(); - System.exit(1); - } - - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = null; - String password = DEFAULT_PASSWORD; - - // Check parameters - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - - // Invalid argument - System.err.println ("error: unexpected argument: "+arg); - printUsage(); - System.exit(1); - } - - // Check values read in. - if (username == null) { - System.err.println ("error: user name must be supplied"); - printUsage(); - System.exit(1); - } - - // Start the JMS client for the "chat". - Chat chat = new Chat(); - chat.chatter (broker, username, password); - - } - - /** Prints the usage. */ - private static void printUsage() { - - StringBuffer use = new StringBuffer(); - use.append("usage: java Chat (options) ...\n\n"); - use.append("options:\n"); - use.append(" -b name:port Specify name:port of broker.\n"); - use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); - use.append(" -u name Specify unique user name. (Required)\n"); - use.append(" -p password Specify password for user.\n"); - use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); - use.append(" -h This help screen.\n"); - System.err.println (use); - } - -} - +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + +Sample Application + +Writing a Basic JMS Application using Publish and Subscribe + +This sample publishes and subscribes to a specified topic. +Text you enter is published and then received by all subscribers. + +Usage: + java Chat -b -u -p + -b broker:port points to your message broker + Default: tcp://localhost:61616 + -u username must be unique (but is not checked) + -p password password for user (not checked) + +Suggested demonstration: + - In separate console windows with the environment set, + start instances of the application + under unique user names. + For example: + java Chat -b tcp://localhost:61616 -u SALES + java Chat -b tcp://localhost:61616 -u MARKETING + - Enter text and then press Enter to publish the message. + - See messages appear under the various user names as you + enter messages in each console window. + - Stop a session by pressing CTRL+C in its console window. + +*/ +import org.apache.activemq.*; + +public class Chat + implements javax.jms.MessageListener +{ + private static final String APP_TOPIC = "jms.samples.chat"; + private static final String DEFAULT_USER = "Chatter"; + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + private static final String DEFAULT_PASSWORD = "password"; + + private javax.jms.Connection connect = null; + private javax.jms.Session pubSession = null; + private javax.jms.Session subSession = null; + private javax.jms.MessageProducer publisher = null; + + /** Create JMS client for publishing and subscribing to messages. */ + private void chatter( String broker, String username, String password) + { + // Create a connection. + try + { + javax.jms.ConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + connect = factory.createConnection (username, password); + pubSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + subSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("error: Cannot connect to Broker - " + broker); + jmse.printStackTrace(); + System.exit(1); + } + + // Create Publisher and Subscriber to 'chat' topics + try + { + javax.jms.Topic topic = pubSession.createTopic (APP_TOPIC); + javax.jms.MessageConsumer subscriber = subSession.createConsumer(topic); + subscriber.setMessageListener(this); + publisher = pubSession.createProducer(topic); + // Now that setup is complete, start the Connection + connect.start(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + try + { + // Read all standard input and send it as a message. + java.io.BufferedReader stdin = + new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); + System.out.println ("\nChat application:\n" + + "=================\n" + + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" + + "The application will publish messages to the " + APP_TOPIC + " topic.\n" + + "The application also subscribes to that topic to consume any messages published there.\n\n" + + "Type some text, and then press Enter to publish it as a TextMesssage from " + username + ".\n"); + while ( true ) + { + String s = stdin.readLine(); + + if ( s == null ) + exit(); + else if ( s.length() > 0 ) + { + javax.jms.TextMessage msg = pubSession.createTextMessage(); + msg.setText( username + ": " + s ); + publisher.send( msg ); + } + } + } + catch ( java.io.IOException ioe ) + { + ioe.printStackTrace(); + } + catch ( javax.jms.JMSException jmse ) + { + jmse.printStackTrace(); + } + } + + /** + * Handle the message + * (as specified in the javax.jms.MessageListener interface). + */ + public void onMessage( javax.jms.Message aMessage) + { + try + { + // Cast the message as a text message. + javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; + + // This handler reads a single String from the + // message and prints it to the standard output. + try + { + String string = textMessage.getText(); + System.out.println( string ); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + catch (java.lang.RuntimeException rte) + { + rte.printStackTrace(); + } + } + + /** Cleanup resources and then exit. */ + private void exit() + { + try + { + connect.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String argv[]) { + + // Is there anything to do? + if (argv.length == 0) { + printUsage(); + System.exit(1); + } + + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = null; + String password = DEFAULT_PASSWORD; + + // Check parameters + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + + // Invalid argument + System.err.println ("error: unexpected argument: "+arg); + printUsage(); + System.exit(1); + } + + // Check values read in. + if (username == null) { + System.err.println ("error: user name must be supplied"); + printUsage(); + System.exit(1); + } + + // Start the JMS client for the "chat". + Chat chat = new Chat(); + chat.chatter (broker, username, password); + + } + + /** Prints the usage. */ + private static void printUsage() { + + StringBuffer use = new StringBuffer(); + use.append("usage: java Chat (options) ...\n\n"); + use.append("options:\n"); + use.append(" -b name:port Specify name:port of broker.\n"); + use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); + use.append(" -u name Specify unique user name. (Required)\n"); + use.append(" -p password Specify password for user.\n"); + use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); + use.append(" -h This help screen.\n"); + System.err.println (use); + } + +} + diff --git a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/DurableChat/DurableChat.java b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/DurableChat/DurableChat.java index 42156b1021..753353ca7a 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/DurableChat/DurableChat.java +++ b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/DurableChat/DurableChat.java @@ -1,283 +1,283 @@ -/** - * 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. - */ - -/* -DurableChat application - -A basic JMS Application that uses: - - Publish and Subscribe - - Durable Subsciptions - - Persistent Messages - -This sample publishes and subscribes to a specified topic. -Text you enter is published to the topic with the user name. -The message will persist for ten minutes if the subscriber is not available. -If the subscriber reconnects within that time, the message is delivered. - -Usage: - java DurableChat -b -u -p - -b broker:port points to your message broker - Default: tcp://localhost:61616 - -u username must be unique (but is not checked) - -p password password for user (not checked) - -Suggested demonstration: - - In separate console windows, start instances of the application - under unique user names.For example: - java DurableChat -b tcp://localhost:61616 -u ACCOUNTING - java DurableChat -b tcp://localhost:61616 -u LEGAL - - Enter text and then press Enter to publish the message. - - See messages appear under the various user names as you - enter messages in each console window. - - Stop a session by pressing CTRL+C in its console window. - - Keep sending messages in other sessions. - - Restart the subscriber username session that was stopped. - - Note that the "missed" messages are still available if the restart is - within thirty minutes. - -*/ -import org.apache.activemq.*; - - -public class DurableChat implements - javax.jms.MessageListener // to handle message subscriptions -{ - private static final String APP_TOPIC = "jms.samples.durablechat"; - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - private static final String DEFAULT_PASSWORD = "password"; - private static final long MESSAGE_LIFESPAN = 1800000; //30 minutes - - private javax.jms.Connection connection = null; - private javax.jms.Session pubSession = null; - private javax.jms.Session subSession = null; - - - public void DurableChatter(String broker, String username, String password) - { - javax.jms.MessageProducer publisher = null; - javax.jms.MessageConsumer subscriber = null; - javax.jms.Topic topic = null; - - //Create a connection: - try{ - javax.jms.ConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - connection = factory.createConnection (username, password); - - //Durable Subscriptions are indexed by username, clientID and subscription name - //It is a good practice to set the clientID: - connection.setClientID(username); - pubSession = connection.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - subSession = connection.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse){ - System.err.println ("Error: Cannot connect to Broker - " + broker); - jmse.printStackTrace(); - System.exit(1); - } - - //Create Publisher and Durable Subscriber: - try{ - - topic = pubSession.createTopic(APP_TOPIC); - subscriber = subSession.createDurableSubscriber(topic, username); - subscriber.setMessageListener(this); - publisher = pubSession.createProducer(topic); - connection.start(); - } - catch (javax.jms.JMSException jmse){ - System.out.println("Error: connection not started."); - jmse.printStackTrace(); - System.exit(1); - } - - //Wait for user input - try - { - System.out.println("\nDurableChat application:\n" - + "========================\n" - + "The user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" - + "The application will publish messages to the " + APP_TOPIC + " topic.\n" - + "The application also creates a durable subscription to that topic to consume any messages published there.\n\n" - + "Type some text, and then press Enter to publish it as a TextMesssage from " + username + ".\n"); - java.io.BufferedReader stdin = - new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); - while (true) - { - String s = stdin.readLine(); - - if(s == null){ - exit(); - } - else if (s.length()>0) - { - try - { - javax.jms.TextMessage msg = pubSession.createTextMessage(); - msg.setText(username + ": " + s); - //Publish the message persistantly: - publisher.send( - msg, //message - javax.jms.DeliveryMode.PERSISTENT, //publish persistently - javax.jms.Message.DEFAULT_PRIORITY,//priority - MESSAGE_LIFESPAN); //Time to Live - } - catch (javax.jms.JMSException jmse){ - System.err.println("Error publishing message:" + jmse.getMessage()); - } - } - } - } - catch (java.io.IOException ioe) - { - ioe.printStackTrace(); - } - } - - /** Message Handler**/ - public void onMessage( javax.jms.Message aMessage) - { - try - { - // Cast the message as a text message. - javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; - - // This handler reads a single String from the - // message and prints it to the standard output. - try - { - String string = textMessage.getText(); - System.out.println( string ); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - catch (java.lang.RuntimeException rte) - { - rte.printStackTrace(); - } - } - - - - /** Cleanup resources cleanly and exit. */ - private void exit() - { - try - { - connection.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String argv[]) { - // Is there anything to do? - if (argv.length == 0) { - printUsage(); - System.exit(1); - } - - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = null; - String password = DEFAULT_PASSWORD; - - // Check parameters - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - - // Invalid argument - System.err.println ("error: unexpected argument: "+arg); - printUsage(); - System.exit(1); - } - - // Check values read in. - if (username == null) { - System.err.println ("error: user name must be supplied"); - printUsage(); - } - - // Start the JMS client for the "chat". - DurableChat durableChat = new DurableChat(); - durableChat.DurableChatter (broker, username, password); - - } - - /** Prints the usage. */ - private static void printUsage() { - - StringBuffer use = new StringBuffer(); - use.append("usage: java DurableChat (options) ...\n\n"); - use.append("options:\n"); - use.append(" -b name:port Specify name:port of broker.\n"); - use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); - use.append(" -u username Specify unique user name. (Required)\n"); - use.append(" -p password Specify password for user.\n"); - use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); - use.append(" -h This help screen.\n"); - System.err.println (use); - } - -} - +/** + * 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. + */ + +/* +DurableChat application + +A basic JMS Application that uses: + - Publish and Subscribe + - Durable Subsciptions + - Persistent Messages + +This sample publishes and subscribes to a specified topic. +Text you enter is published to the topic with the user name. +The message will persist for ten minutes if the subscriber is not available. +If the subscriber reconnects within that time, the message is delivered. + +Usage: + java DurableChat -b -u -p + -b broker:port points to your message broker + Default: tcp://localhost:61616 + -u username must be unique (but is not checked) + -p password password for user (not checked) + +Suggested demonstration: + - In separate console windows, start instances of the application + under unique user names.For example: + java DurableChat -b tcp://localhost:61616 -u ACCOUNTING + java DurableChat -b tcp://localhost:61616 -u LEGAL + - Enter text and then press Enter to publish the message. + - See messages appear under the various user names as you + enter messages in each console window. + - Stop a session by pressing CTRL+C in its console window. + - Keep sending messages in other sessions. + - Restart the subscriber username session that was stopped. + - Note that the "missed" messages are still available if the restart is + within thirty minutes. + +*/ +import org.apache.activemq.*; + + +public class DurableChat implements + javax.jms.MessageListener // to handle message subscriptions +{ + private static final String APP_TOPIC = "jms.samples.durablechat"; + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + private static final String DEFAULT_PASSWORD = "password"; + private static final long MESSAGE_LIFESPAN = 1800000; //30 minutes + + private javax.jms.Connection connection = null; + private javax.jms.Session pubSession = null; + private javax.jms.Session subSession = null; + + + public void DurableChatter(String broker, String username, String password) + { + javax.jms.MessageProducer publisher = null; + javax.jms.MessageConsumer subscriber = null; + javax.jms.Topic topic = null; + + //Create a connection: + try{ + javax.jms.ConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + connection = factory.createConnection (username, password); + + //Durable Subscriptions are indexed by username, clientID and subscription name + //It is a good practice to set the clientID: + connection.setClientID(username); + pubSession = connection.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + subSession = connection.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse){ + System.err.println ("Error: Cannot connect to Broker - " + broker); + jmse.printStackTrace(); + System.exit(1); + } + + //Create Publisher and Durable Subscriber: + try{ + + topic = pubSession.createTopic(APP_TOPIC); + subscriber = subSession.createDurableSubscriber(topic, username); + subscriber.setMessageListener(this); + publisher = pubSession.createProducer(topic); + connection.start(); + } + catch (javax.jms.JMSException jmse){ + System.out.println("Error: connection not started."); + jmse.printStackTrace(); + System.exit(1); + } + + //Wait for user input + try + { + System.out.println("\nDurableChat application:\n" + + "========================\n" + + "The user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" + + "The application will publish messages to the " + APP_TOPIC + " topic.\n" + + "The application also creates a durable subscription to that topic to consume any messages published there.\n\n" + + "Type some text, and then press Enter to publish it as a TextMesssage from " + username + ".\n"); + java.io.BufferedReader stdin = + new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); + while (true) + { + String s = stdin.readLine(); + + if(s == null){ + exit(); + } + else if (s.length()>0) + { + try + { + javax.jms.TextMessage msg = pubSession.createTextMessage(); + msg.setText(username + ": " + s); + //Publish the message persistantly: + publisher.send( + msg, //message + javax.jms.DeliveryMode.PERSISTENT, //publish persistently + javax.jms.Message.DEFAULT_PRIORITY,//priority + MESSAGE_LIFESPAN); //Time to Live + } + catch (javax.jms.JMSException jmse){ + System.err.println("Error publishing message:" + jmse.getMessage()); + } + } + } + } + catch (java.io.IOException ioe) + { + ioe.printStackTrace(); + } + } + + /** Message Handler**/ + public void onMessage( javax.jms.Message aMessage) + { + try + { + // Cast the message as a text message. + javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; + + // This handler reads a single String from the + // message and prints it to the standard output. + try + { + String string = textMessage.getText(); + System.out.println( string ); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + catch (java.lang.RuntimeException rte) + { + rte.printStackTrace(); + } + } + + + + /** Cleanup resources cleanly and exit. */ + private void exit() + { + try + { + connection.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String argv[]) { + // Is there anything to do? + if (argv.length == 0) { + printUsage(); + System.exit(1); + } + + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = null; + String password = DEFAULT_PASSWORD; + + // Check parameters + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + + // Invalid argument + System.err.println ("error: unexpected argument: "+arg); + printUsage(); + System.exit(1); + } + + // Check values read in. + if (username == null) { + System.err.println ("error: user name must be supplied"); + printUsage(); + } + + // Start the JMS client for the "chat". + DurableChat durableChat = new DurableChat(); + durableChat.DurableChatter (broker, username, password); + + } + + /** Prints the usage. */ + private static void printUsage() { + + StringBuffer use = new StringBuffer(); + use.append("usage: java DurableChat (options) ...\n\n"); + use.append("options:\n"); + use.append(" -b name:port Specify name:port of broker.\n"); + use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); + use.append(" -u username Specify unique user name. (Required)\n"); + use.append(" -p password Specify password for user.\n"); + use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); + use.append(" -h This help screen.\n"); + System.err.println (use); + } + +} + diff --git a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/HierarchicalChat/HierarchicalChat.java b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/HierarchicalChat/HierarchicalChat.java index 997e3b599c..ac743b2e7d 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/HierarchicalChat/HierarchicalChat.java +++ b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/HierarchicalChat/HierarchicalChat.java @@ -1,303 +1,303 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - -Sample Application - -Writing a JMS Application using Publish and Subscribe with -Hierarchical Topics - -This sample publishes and subscribes to specified topic nodes. -Text you enter is published and then received by all subscribers to -the that topic on the specified broker. - -Usage: - java HierarchicalChat -b -u -p -t -s - -b broker:port points to a message broker - Default: tcp://localhost:61616 - -u username must be unique (but is not checked) - -p password password for user (not checked) - -t pubTopicname name of topic to which to publish - Default: jms.samples.hierarchicalchat - -s subTopicname name of topic to which to subscribe - Default: jms.samples.* - -Suggested demonstration: - - In separate console windows with the environment set, - start instances of the application - under unique user names. - For example: - java HierarchicalChat -u SALES -t sales -s sales.* - java HierarchicalChat -u USA -t sales.usa -s sales.usa - - Enter text in the USA console window and then press Enter - to publish the message. - - Note that messages published from the SALES console window - to the sales topic are not seen by the USA user listening - to messages on the sales.usa topic - - Message published to the sales.usa are received by the SALES - user listening to sales.* - - Stop a session by pressing CTRL+C in its console window. - -*/ - -import org.apache.activemq.*; - - -public class HierarchicalChat - implements javax.jms.MessageListener -{ - private static final String DEFAULT_PUBLISHER_TOPIC = "jms.samples.hierarchicalchat"; - private static final String DEFAULT_SUBSCRIBER_TOPIC = "jms.samples.*"; - private static final String DEFAULT_SUBSCRIBER_ROOT = "jms.samples"; - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - - private static final String DEFAULT_PASSWORD = "password"; - - private javax.jms.Connection connect = null; - private javax.jms.Session pubSession = null; - private javax.jms.Session subSession = null; - private javax.jms.MessageProducer publisher = null; - - /** Create JMS client for publishing and subscribing to messages. */ - private void chatter( String broker, String username, String password, String pubTopicname, String subTopicname) - { - - // Create a connection. - try - { - javax.jms.ConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - connect = factory.createConnection (username, password); - pubSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - subSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("error: Cannot connect to Broker - " + broker); - jmse.printStackTrace(); - System.exit(1); - } - - // Create Publisher and Subscriber to 'chat' topics - // Note that the publish and subscribe topics are different. - try - { - javax.jms.Topic subscriberTopic = pubSession.createTopic (subTopicname); - javax.jms.MessageConsumer subscriber = subSession.createConsumer(subscriberTopic, null, false); - subscriber.setMessageListener(this); - javax.jms.Topic publisherTopic = pubSession.createTopic (pubTopicname); - publisher = pubSession.createProducer(publisherTopic); - // Now that setup is complete, start the Connection - connect.start(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - try - { - // Read all standard input and send it as a message. - java.io.BufferedReader stdin = - new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); - System.out.println("\nHierarchicalChat application:\n" - + "============================\n" - + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" - + "The application will publish messages to the " + DEFAULT_PUBLISHER_TOPIC + " topic." + ".\n" - + "The application also subscribes to topics using the wildcard syntax " + DEFAULT_SUBSCRIBER_TOPIC - + " so that it can receive all messages to " + DEFAULT_SUBSCRIBER_ROOT + " and its subtopics.\n\n" - + "Type some text, and then press Enter to publish a TextMesssage from " + username + ".\n"); - while ( true ) - { - String s = stdin.readLine(); - - if ( s == null ) - exit(); - else if ( s.length() > 0 ) - { - javax.jms.TextMessage msg = pubSession.createTextMessage(); - msg.setText( username + ": " + s ); - publisher.send( msg ); - } - } - } - catch ( java.io.IOException ioe ) - { - ioe.printStackTrace(); - } - catch ( javax.jms.JMSException jmse ) - { - jmse.printStackTrace(); - } - } - - /** - * Handle the message - * (as specified in the javax.jms.MessageListener interface). - */ - public void onMessage( javax.jms.Message aMessage) - { - try - { - // Cast the message as a text message. - javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; - - // This handler reads a single String from the - // message and prints it to the standard output. - try - { - String string = textMessage.getText(); - System.out.println( string ); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - catch (java.lang.RuntimeException rte) - { - rte.printStackTrace(); - } - } - - /** Cleanup resources and then exit. */ - private void exit() - { - try - { - connect.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String argv[]) { - - // Is there anything to do? - if (argv.length == 0) { - printUsage(); - System.exit(1); - } - - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = null; - String password = DEFAULT_PASSWORD; - String pubTopicname = DEFAULT_PUBLISHER_TOPIC; - String subTopicname = DEFAULT_SUBSCRIBER_TOPIC; - - // Check parameters - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - // Options - if (!arg.startsWith("-")) { - System.err.println ("error: unexpected argument - "+arg); - printUsage(); - System.exit(1); - } - else { - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - if (arg.equals("-t")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing publisher topic name"); - System.exit(1); - } - pubTopicname = argv[++i]; - continue; - } - - if (arg.equals("-s")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing subscriber topic name"); - System.exit(1); - } - subTopicname = argv[++i]; - continue; - } - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - } - } - - // Check values read in. - if (username == null) { - System.err.println ("error: user name must be supplied"); - printUsage(); - } - - // Start the JMS client for the "chat". - HierarchicalChat chat = new HierarchicalChat(); - chat.chatter (broker, username, password, pubTopicname, subTopicname); - - } - - /** Prints the usage. */ - private static void printUsage() { - - StringBuffer use = new StringBuffer(); - use.append("usage: java HierarchicalChat (options) ...\n\n"); - use.append("options:\n"); - use.append(" -b name:port Specify name:port of broker.\n"); - use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); - use.append(" -u name Specify unique user name. (Required)\n"); - use.append(" -p password Specify password for user.\n"); - use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); - use.append(" -t pubTopicname name of topic to which to publish.\n"); - use.append(" Default publisher topic name: "+DEFAULT_PUBLISHER_TOPIC+"\n"); - use.append(" -s subTopicname Specify subscriber topic name.\n"); - use.append(" name of topic to which to subscribe: "+DEFAULT_SUBSCRIBER_TOPIC+"\n"); - use.append(" -h This help screen.\n"); - System.err.println (use); - } - -} - +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + +Sample Application + +Writing a JMS Application using Publish and Subscribe with +Hierarchical Topics + +This sample publishes and subscribes to specified topic nodes. +Text you enter is published and then received by all subscribers to +the that topic on the specified broker. + +Usage: + java HierarchicalChat -b -u -p -t -s + -b broker:port points to a message broker + Default: tcp://localhost:61616 + -u username must be unique (but is not checked) + -p password password for user (not checked) + -t pubTopicname name of topic to which to publish + Default: jms.samples.hierarchicalchat + -s subTopicname name of topic to which to subscribe + Default: jms.samples.* + +Suggested demonstration: + - In separate console windows with the environment set, + start instances of the application + under unique user names. + For example: + java HierarchicalChat -u SALES -t sales -s sales.* + java HierarchicalChat -u USA -t sales.usa -s sales.usa + - Enter text in the USA console window and then press Enter + to publish the message. + - Note that messages published from the SALES console window + to the sales topic are not seen by the USA user listening + to messages on the sales.usa topic + - Message published to the sales.usa are received by the SALES + user listening to sales.* + - Stop a session by pressing CTRL+C in its console window. + +*/ + +import org.apache.activemq.*; + + +public class HierarchicalChat + implements javax.jms.MessageListener +{ + private static final String DEFAULT_PUBLISHER_TOPIC = "jms.samples.hierarchicalchat"; + private static final String DEFAULT_SUBSCRIBER_TOPIC = "jms.samples.*"; + private static final String DEFAULT_SUBSCRIBER_ROOT = "jms.samples"; + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + + private static final String DEFAULT_PASSWORD = "password"; + + private javax.jms.Connection connect = null; + private javax.jms.Session pubSession = null; + private javax.jms.Session subSession = null; + private javax.jms.MessageProducer publisher = null; + + /** Create JMS client for publishing and subscribing to messages. */ + private void chatter( String broker, String username, String password, String pubTopicname, String subTopicname) + { + + // Create a connection. + try + { + javax.jms.ConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + connect = factory.createConnection (username, password); + pubSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + subSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("error: Cannot connect to Broker - " + broker); + jmse.printStackTrace(); + System.exit(1); + } + + // Create Publisher and Subscriber to 'chat' topics + // Note that the publish and subscribe topics are different. + try + { + javax.jms.Topic subscriberTopic = pubSession.createTopic (subTopicname); + javax.jms.MessageConsumer subscriber = subSession.createConsumer(subscriberTopic, null, false); + subscriber.setMessageListener(this); + javax.jms.Topic publisherTopic = pubSession.createTopic (pubTopicname); + publisher = pubSession.createProducer(publisherTopic); + // Now that setup is complete, start the Connection + connect.start(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + try + { + // Read all standard input and send it as a message. + java.io.BufferedReader stdin = + new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); + System.out.println("\nHierarchicalChat application:\n" + + "============================\n" + + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" + + "The application will publish messages to the " + DEFAULT_PUBLISHER_TOPIC + " topic." + ".\n" + + "The application also subscribes to topics using the wildcard syntax " + DEFAULT_SUBSCRIBER_TOPIC + + " so that it can receive all messages to " + DEFAULT_SUBSCRIBER_ROOT + " and its subtopics.\n\n" + + "Type some text, and then press Enter to publish a TextMesssage from " + username + ".\n"); + while ( true ) + { + String s = stdin.readLine(); + + if ( s == null ) + exit(); + else if ( s.length() > 0 ) + { + javax.jms.TextMessage msg = pubSession.createTextMessage(); + msg.setText( username + ": " + s ); + publisher.send( msg ); + } + } + } + catch ( java.io.IOException ioe ) + { + ioe.printStackTrace(); + } + catch ( javax.jms.JMSException jmse ) + { + jmse.printStackTrace(); + } + } + + /** + * Handle the message + * (as specified in the javax.jms.MessageListener interface). + */ + public void onMessage( javax.jms.Message aMessage) + { + try + { + // Cast the message as a text message. + javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; + + // This handler reads a single String from the + // message and prints it to the standard output. + try + { + String string = textMessage.getText(); + System.out.println( string ); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + catch (java.lang.RuntimeException rte) + { + rte.printStackTrace(); + } + } + + /** Cleanup resources and then exit. */ + private void exit() + { + try + { + connect.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String argv[]) { + + // Is there anything to do? + if (argv.length == 0) { + printUsage(); + System.exit(1); + } + + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = null; + String password = DEFAULT_PASSWORD; + String pubTopicname = DEFAULT_PUBLISHER_TOPIC; + String subTopicname = DEFAULT_SUBSCRIBER_TOPIC; + + // Check parameters + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + // Options + if (!arg.startsWith("-")) { + System.err.println ("error: unexpected argument - "+arg); + printUsage(); + System.exit(1); + } + else { + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + if (arg.equals("-t")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing publisher topic name"); + System.exit(1); + } + pubTopicname = argv[++i]; + continue; + } + + if (arg.equals("-s")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing subscriber topic name"); + System.exit(1); + } + subTopicname = argv[++i]; + continue; + } + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + } + } + + // Check values read in. + if (username == null) { + System.err.println ("error: user name must be supplied"); + printUsage(); + } + + // Start the JMS client for the "chat". + HierarchicalChat chat = new HierarchicalChat(); + chat.chatter (broker, username, password, pubTopicname, subTopicname); + + } + + /** Prints the usage. */ + private static void printUsage() { + + StringBuffer use = new StringBuffer(); + use.append("usage: java HierarchicalChat (options) ...\n\n"); + use.append("options:\n"); + use.append(" -b name:port Specify name:port of broker.\n"); + use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); + use.append(" -u name Specify unique user name. (Required)\n"); + use.append(" -p password Specify password for user.\n"); + use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); + use.append(" -t pubTopicname name of topic to which to publish.\n"); + use.append(" Default publisher topic name: "+DEFAULT_PUBLISHER_TOPIC+"\n"); + use.append(" -s subTopicname Specify subscriber topic name.\n"); + use.append(" name of topic to which to subscribe: "+DEFAULT_SUBSCRIBER_TOPIC+"\n"); + use.append(" -h This help screen.\n"); + System.err.println (use); + } + +} + diff --git a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/MessageMonitor/MessageMonitor.java b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/MessageMonitor/MessageMonitor.java index 19ab78fa13..20b03e115b 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/MessageMonitor/MessageMonitor.java +++ b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/MessageMonitor/MessageMonitor.java @@ -1,328 +1,328 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - -Sample Application - -Writing a Basic JMS Application Using - Subscribe - -When you run this program, it will -subscribe to any group of messages specified -in a properties file. [See comments in -MessageMonitor.properties for information on -this file.] - -Every message topic being monitored will be -displayed to a Java window. - -Usage: - java MessageMonitor - -Suggested demonstration: - - Start one instance of this application: - java MessageMonitor - - Run one or more Chat and/or DurableChat window(s). - - Enter messages on the various chat windows. - - Watch the MessageMonitor display the messages. -*/ -import org.apache.activemq.*; - -import javax.swing.JTextArea; -import javax.swing.JFrame; -import javax.swing.JScrollPane; -import javax.swing.JButton; - -import javax.swing.text.Highlighter; -import javax.swing.text.DefaultHighlighter; -import javax.swing.text.BadLocationException; - -import javax.swing.border.CompoundBorder; -import javax.swing.border.EmptyBorder; -import javax.swing.border.BevelBorder; -import javax.swing.border.SoftBevelBorder; - -import java.awt.Toolkit; -import java.awt.Dimension; -import java.awt.BorderLayout; -import java.awt.Rectangle; - -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.awt.event.ActionListener; -import java.awt.event.ActionEvent; - -import javax.jms.Topic; -import javax.jms.Session; -import javax.jms.Connection; -import javax.jms.MessageConsumer; -import javax.jms.MessageListener; - - -import java.io.FileInputStream; - -import java.util.Vector; -import java.util.Properties; -import java.util.StringTokenizer; - -public class MessageMonitor -extends JFrame -{ - private static final String DEFAULT_PROPERTIES_FILE = "MessageMonitor.properties"; - - String propertiesFile = DEFAULT_PROPERTIES_FILE; - String brokerHostPort = "localhost"; - String connectID = "MessageMonitor"; - String userID = "Administrator"; - - String subscriptionTopics = "jms.samples.chat"; - String textFontName = "Dialog"; - String textFontStyle = "PLAIN"; - String textFontSize = "12"; - String title = "MessageMonitor"; - - JTextArea textArea = new JTextArea(); - JScrollPane scrollPane = new JScrollPane(textArea); - JButton clearButton = new JButton("Clear"); - - Connection connection = null; - Session session = null; - - private String user = ActiveMQConnection.DEFAULT_USER; - private String password = ActiveMQConnection.DEFAULT_PASSWORD; - private String url = ActiveMQConnection.DEFAULT_BROKER_URL; - - - /** Constructor for MessageMonitor window. */ - public MessageMonitor() - { - loadProperties(); - - setTitle(title); - - // Connect to Message Broker - try - { - javax.jms.ConnectionFactory factory; - factory = new ActiveMQConnectionFactory(user, password, url); - - connection = factory.createConnection (userID, password); - session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("Cannot connect to Broker"); - jmse.printStackTrace(); - System.exit(1); - } - - // Subscribe to Topics - StringTokenizer topics = new StringTokenizer(subscriptionTopics, ","); - while (topics.hasMoreTokens()) - { - try - { - String topicName = topics.nextToken(); - System.out.println ("Subscribing to: " + topicName); - new Subscription(session.createTopic(topicName)); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - - // Set up the viewing area. - textArea.setEditable(false); - scrollPane.setBorder(new CompoundBorder(new EmptyBorder(6,6,6,6), - new SoftBevelBorder(BevelBorder.LOWERED))); - getContentPane().add(scrollPane,BorderLayout.CENTER); - getContentPane().add(clearButton,BorderLayout.SOUTH); - clearButton.addActionListener(new OnClear()); - // Start the connection so that we can now receive messages. - try - { - connection.start(); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("Cannot start connection"); - jmse.printStackTrace(); - System.exit(1); - } - - } - - /** Class to handle the subsciption to messages. */ - public class Subscription implements javax.jms.MessageListener - { - public Subscription(Topic topic) - { - try - { - topicSubscriber = session.createConsumer(topic); - topicSubscriber.setMessageListener(this); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - - /** - * Handle the text message - * (as specified in the javax.jms.MessageListener interface). - */ - - public void onMessage(javax.jms.Message message) - { - String msgBody = null; - String msgClass = null; - - if (message instanceof javax.jms.TextMessage) - { - msgClass = "javax.jms.TextMessage"; - try - { - msgBody = ((javax.jms.TextMessage)message).getText(); - } - catch (javax.jms.JMSException jmse) - { - msgBody = ""; - } - } - else - { - return; - } - try - { - textArea.append("\n"); - textArea.append("-----------------------------------------------------------------------------------------------------\n"); - // textArea.append("Class: " + msgClass + "\n"); - textArea.append("The following message, received on topic " + ((Topic)message.getJMSDestination()).getTopicName() + ", was sent by\n"); - //textArea.append("\n"); - textArea.append(msgBody); - - // Ensure Appended Text is Visible - Rectangle area = textArea.modelToView(textArea.getText().length()); - if (area != null) textArea.scrollRectToVisible(area); - - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - catch (BadLocationException ble) - { - ble.printStackTrace(); - } - - } - - MessageConsumer topicSubscriber = null; - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String[] args) - { - // There should be no arguments to this program. - if (args.length > 0) { - printUsage(); - System.exit(1); - } - - MessageMonitor messageMonitor = new MessageMonitor(); - - messageMonitor.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) - { - System.exit(0); - } - }); - - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - screenSize.height = screenSize.height / 2 ; - screenSize.width = screenSize.width / 2 ; - messageMonitor.setSize(screenSize); - messageMonitor.setVisible(true); - - } - - /** Prints the usage. */ - private static void printUsage() - { - StringBuffer use = new StringBuffer(); - use.append("\nusage: MessageMonitor\n\n"); - use.append("Properties for this sample can be set in a properties file.\n"); - String dfltFile = System.getProperty("propertiesFile", DEFAULT_PROPERTIES_FILE); - use.append("[Default file: " + dfltFile +"]\n\n"); - - System.out.print(use); - } - - /** Load the window and JMS properties from a file. */ - private void loadProperties() - { - try - { - Properties properties = new Properties(); - - propertiesFile = System.getProperty("propertiesFile", propertiesFile); - - properties.load(new FileInputStream(propertiesFile)); - - // Connection Properties - brokerHostPort = properties.getProperty("brokerHostPort",brokerHostPort).trim(); - connectID = properties.getProperty("connectID",connectID).trim(); - userID = properties.getProperty("userID",userID).trim(); - password = properties.getProperty("password",password).trim(); - - // Subscription Properties - subscriptionTopics = properties.getProperty("subscriptionTopics", subscriptionTopics).trim(); - - // Text Properties - textFontName = properties.getProperty("textFontName", textFontName).trim(); - textFontStyle = properties.getProperty("textFontSize", textFontStyle).trim(); - textFontSize = properties.getProperty("textFontSize", textFontSize).trim(); - - // Window Properties - title = properties.getProperty("title", title).trim(); - - } - catch (java.io.FileNotFoundException fnfe) - { - System.out.println (propertiesFile + " not found: using defaults"); // Use Defaults - } - catch (java.io.IOException ioe) - { - ioe.printStackTrace(); - } - } - - /** Class to handle the "Clear" button action. */ - public class OnClear implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - textArea.setText(""); - } - } - -} +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + +Sample Application + +Writing a Basic JMS Application Using + Subscribe + +When you run this program, it will +subscribe to any group of messages specified +in a properties file. [See comments in +MessageMonitor.properties for information on +this file.] + +Every message topic being monitored will be +displayed to a Java window. + +Usage: + java MessageMonitor + +Suggested demonstration: + - Start one instance of this application: + java MessageMonitor + - Run one or more Chat and/or DurableChat window(s). + - Enter messages on the various chat windows. + - Watch the MessageMonitor display the messages. +*/ +import org.apache.activemq.*; + +import javax.swing.JTextArea; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JButton; + +import javax.swing.text.Highlighter; +import javax.swing.text.DefaultHighlighter; +import javax.swing.text.BadLocationException; + +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.BevelBorder; +import javax.swing.border.SoftBevelBorder; + +import java.awt.Toolkit; +import java.awt.Dimension; +import java.awt.BorderLayout; +import java.awt.Rectangle; + +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import javax.jms.Topic; +import javax.jms.Session; +import javax.jms.Connection; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; + + +import java.io.FileInputStream; + +import java.util.Vector; +import java.util.Properties; +import java.util.StringTokenizer; + +public class MessageMonitor +extends JFrame +{ + private static final String DEFAULT_PROPERTIES_FILE = "MessageMonitor.properties"; + + String propertiesFile = DEFAULT_PROPERTIES_FILE; + String brokerHostPort = "localhost"; + String connectID = "MessageMonitor"; + String userID = "Administrator"; + + String subscriptionTopics = "jms.samples.chat"; + String textFontName = "Dialog"; + String textFontStyle = "PLAIN"; + String textFontSize = "12"; + String title = "MessageMonitor"; + + JTextArea textArea = new JTextArea(); + JScrollPane scrollPane = new JScrollPane(textArea); + JButton clearButton = new JButton("Clear"); + + Connection connection = null; + Session session = null; + + private String user = ActiveMQConnection.DEFAULT_USER; + private String password = ActiveMQConnection.DEFAULT_PASSWORD; + private String url = ActiveMQConnection.DEFAULT_BROKER_URL; + + + /** Constructor for MessageMonitor window. */ + public MessageMonitor() + { + loadProperties(); + + setTitle(title); + + // Connect to Message Broker + try + { + javax.jms.ConnectionFactory factory; + factory = new ActiveMQConnectionFactory(user, password, url); + + connection = factory.createConnection (userID, password); + session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("Cannot connect to Broker"); + jmse.printStackTrace(); + System.exit(1); + } + + // Subscribe to Topics + StringTokenizer topics = new StringTokenizer(subscriptionTopics, ","); + while (topics.hasMoreTokens()) + { + try + { + String topicName = topics.nextToken(); + System.out.println ("Subscribing to: " + topicName); + new Subscription(session.createTopic(topicName)); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + + // Set up the viewing area. + textArea.setEditable(false); + scrollPane.setBorder(new CompoundBorder(new EmptyBorder(6,6,6,6), + new SoftBevelBorder(BevelBorder.LOWERED))); + getContentPane().add(scrollPane,BorderLayout.CENTER); + getContentPane().add(clearButton,BorderLayout.SOUTH); + clearButton.addActionListener(new OnClear()); + // Start the connection so that we can now receive messages. + try + { + connection.start(); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("Cannot start connection"); + jmse.printStackTrace(); + System.exit(1); + } + + } + + /** Class to handle the subsciption to messages. */ + public class Subscription implements javax.jms.MessageListener + { + public Subscription(Topic topic) + { + try + { + topicSubscriber = session.createConsumer(topic); + topicSubscriber.setMessageListener(this); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + + /** + * Handle the text message + * (as specified in the javax.jms.MessageListener interface). + */ + + public void onMessage(javax.jms.Message message) + { + String msgBody = null; + String msgClass = null; + + if (message instanceof javax.jms.TextMessage) + { + msgClass = "javax.jms.TextMessage"; + try + { + msgBody = ((javax.jms.TextMessage)message).getText(); + } + catch (javax.jms.JMSException jmse) + { + msgBody = ""; + } + } + else + { + return; + } + try + { + textArea.append("\n"); + textArea.append("-----------------------------------------------------------------------------------------------------\n"); + // textArea.append("Class: " + msgClass + "\n"); + textArea.append("The following message, received on topic " + ((Topic)message.getJMSDestination()).getTopicName() + ", was sent by\n"); + //textArea.append("\n"); + textArea.append(msgBody); + + // Ensure Appended Text is Visible + Rectangle area = textArea.modelToView(textArea.getText().length()); + if (area != null) textArea.scrollRectToVisible(area); + + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + catch (BadLocationException ble) + { + ble.printStackTrace(); + } + + } + + MessageConsumer topicSubscriber = null; + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String[] args) + { + // There should be no arguments to this program. + if (args.length > 0) { + printUsage(); + System.exit(1); + } + + MessageMonitor messageMonitor = new MessageMonitor(); + + messageMonitor.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) + { + System.exit(0); + } + }); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + screenSize.height = screenSize.height / 2 ; + screenSize.width = screenSize.width / 2 ; + messageMonitor.setSize(screenSize); + messageMonitor.setVisible(true); + + } + + /** Prints the usage. */ + private static void printUsage() + { + StringBuffer use = new StringBuffer(); + use.append("\nusage: MessageMonitor\n\n"); + use.append("Properties for this sample can be set in a properties file.\n"); + String dfltFile = System.getProperty("propertiesFile", DEFAULT_PROPERTIES_FILE); + use.append("[Default file: " + dfltFile +"]\n\n"); + + System.out.print(use); + } + + /** Load the window and JMS properties from a file. */ + private void loadProperties() + { + try + { + Properties properties = new Properties(); + + propertiesFile = System.getProperty("propertiesFile", propertiesFile); + + properties.load(new FileInputStream(propertiesFile)); + + // Connection Properties + brokerHostPort = properties.getProperty("brokerHostPort",brokerHostPort).trim(); + connectID = properties.getProperty("connectID",connectID).trim(); + userID = properties.getProperty("userID",userID).trim(); + password = properties.getProperty("password",password).trim(); + + // Subscription Properties + subscriptionTopics = properties.getProperty("subscriptionTopics", subscriptionTopics).trim(); + + // Text Properties + textFontName = properties.getProperty("textFontName", textFontName).trim(); + textFontStyle = properties.getProperty("textFontSize", textFontStyle).trim(); + textFontSize = properties.getProperty("textFontSize", textFontSize).trim(); + + // Window Properties + title = properties.getProperty("title", title).trim(); + + } + catch (java.io.FileNotFoundException fnfe) + { + System.out.println (propertiesFile + " not found: using defaults"); // Use Defaults + } + catch (java.io.IOException ioe) + { + ioe.printStackTrace(); + } + } + + /** Class to handle the "Clear" button action. */ + public class OnClear implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { + textArea.setText(""); + } + } + +} diff --git a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/MessageMonitor/MessageMonitor.properties b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/MessageMonitor/MessageMonitor.properties index b2ecb47174..d74b92a544 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/MessageMonitor/MessageMonitor.properties +++ b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/MessageMonitor/MessageMonitor.properties @@ -1,72 +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. -## --------------------------------------------------------------------------- - -# -# This is the properties file for the MessageMonitor Class. -# -# A MessageMonitor object will load this file when constructed. -# By default, a MessageMonitor object searches for and load a -# file named "MessageMonitor.properties" in the JVM's "current" directory. -# -# This behavior may be overriden by assigning an alternate property file -# name and location to the System property "propertiesFile". -# -# Property: brokerHostPort -# Default Value: localhost -# Description: Specifies the host and port of the Messaging Broker to connect to. -brokerHostPort localhost -# -# Property: connectID -# Default Value: MessageMonitor -# Description: Specifies the program identifier used to connect to the Messaging Broker. -connectID MessageMonitor -# -# Property: userID -# Default Value: MessageMonitor -# Description: Specifies the user identifier used to connect to the Messaging Broker. -userID Administrator -# -# Property: password -# Default Value: MessageMonitor -# Description: Specifies the password used to connect to the Messaging Broker. -password Administrator -# -# Property: subscriptionTopics -# Default Value: jms.samples.# -# Description: A comma-separated list of topics that MessageMonitor subscribes to. -# An empty list will subscribe to jms.samples.#. -subscriptionTopics jms.samples.* -## -# Property: textFontName -# Default Value: Dialog -# Description: The name of the font used to display recieved messages. -textFontName Dialog -# -# Property: textFontStyle -# Default Value: PLAIN -# Description: The style of the font used to display recieved messages. -textFontStyle PLAIN -# -# Property: textFontSize -# Default Value: 12 -# Description: The size of the font used to display recieved messages. -textFontSize 12 -# -# Property: title -# Default Value: MessageMonitor -# Description: The title of the MessageMonitor Window. -title MessageMonitor +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +# +# This is the properties file for the MessageMonitor Class. +# +# A MessageMonitor object will load this file when constructed. +# By default, a MessageMonitor object searches for and load a +# file named "MessageMonitor.properties" in the JVM's "current" directory. +# +# This behavior may be overriden by assigning an alternate property file +# name and location to the System property "propertiesFile". +# +# Property: brokerHostPort +# Default Value: localhost +# Description: Specifies the host and port of the Messaging Broker to connect to. +brokerHostPort localhost +# +# Property: connectID +# Default Value: MessageMonitor +# Description: Specifies the program identifier used to connect to the Messaging Broker. +connectID MessageMonitor +# +# Property: userID +# Default Value: MessageMonitor +# Description: Specifies the user identifier used to connect to the Messaging Broker. +userID Administrator +# +# Property: password +# Default Value: MessageMonitor +# Description: Specifies the password used to connect to the Messaging Broker. +password Administrator +# +# Property: subscriptionTopics +# Default Value: jms.samples.# +# Description: A comma-separated list of topics that MessageMonitor subscribes to. +# An empty list will subscribe to jms.samples.#. +subscriptionTopics jms.samples.* +## +# Property: textFontName +# Default Value: Dialog +# Description: The name of the font used to display recieved messages. +textFontName Dialog +# +# Property: textFontStyle +# Default Value: PLAIN +# Description: The style of the font used to display recieved messages. +textFontStyle PLAIN +# +# Property: textFontSize +# Default Value: 12 +# Description: The size of the font used to display recieved messages. +textFontSize 12 +# +# Property: title +# Default Value: MessageMonitor +# Description: The title of the MessageMonitor Window. +title MessageMonitor diff --git a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/RequestReply/TopicReplier.java b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/RequestReply/TopicReplier.java index a786afec24..ac474b4ca1 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/RequestReply/TopicReplier.java +++ b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/RequestReply/TopicReplier.java @@ -1,318 +1,318 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - -Sample Application - -Writing a Basic JMS Application using: - - Synchronous Request/Reply - - Publish/Subscribe - - javax.jms.TopicRequestor class - - JMSReplyTo Header - -When this program runs, it waits for -messages to the topic "jms.samples.request". -When that message occurs, a response based on the request -is sent back to the "Requestor" specified in the JMSReplyTo header. - -This sample replies with a simple text manipulation of the request; -the text is either folded to all UPPERCASE or all lowercase. - -Usage: - java TopicReplier -b -u -p -m - -b broker:port points to your message broker - Default: tcp://localhost:61616 - -u username must be unique (but is not checked) - Default: SampleReplier - -p password password for user (not checked) - Default: password - -m mode replier mode (uppercase, or lowercase) - Default: uppercase - -Suggested usage: - - In a console window with the environment set, start a replier: - java TopicReplier -u SampleReplier - - In another console window, start a Requestor: - java TopicRequestor -u SampleRequestor - - Enter text in the Requestor window then press Enter. - - The Replier responds with the message in all uppercase characters. - - Start other TopicRequestors with different user names to see that - replies are not broadcast to all users. For example: - java TopicRequestor -u SampleRequestorToo - - - Start other TopicRepliers. - - See that all repliers are receiving all the messages,(as they should). - - See the Requestor only receives one response. - java TopicReplier -u toLower -m lowercase - - */ - -import org.apache.activemq.*; - - -public class TopicReplier - implements javax.jms.MessageListener -{ - private static final String APP_TOPIC = "jms.samples.request"; - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - private static final String DEFAULT_USER_NAME = "SampleReplier"; - private static final String DEFAULT_PASSWORD = "password"; - private static final String DEFAULT_MODE = "uppercase"; - private static final int UPPERCASE = 0; - private static final int LOWERCASE = 1; - - private javax.jms.Connection connect = null; - private javax.jms.Session session = null; - private javax.jms.MessageProducer replier = null; - - private int imode = UPPERCASE; - - /** Create JMS client for publishing and subscribing to messages. */ - private void start ( String broker, String username, String password, String mode) - { - // Set the operation mode - imode = (mode.equals("uppercase")) ? UPPERCASE : LOWERCASE; - - // Create a connection. - try - { - javax.jms.ConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - connect = factory.createConnection (username, password); - session = connect.createSession(true, javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("error: Cannot connect to Broker - " + broker); - jmse.printStackTrace(); - System.exit(1); - } - - // Create Subscriber to application topics as well as a Publisher - // to use for JMS replies. - try - { - javax.jms.Topic topic = session.createTopic (APP_TOPIC); - javax.jms.MessageConsumer subscriber = session.createConsumer(topic); - subscriber.setMessageListener(this); - replier = session.createProducer(null); // Topic will be set for each reply - // Now that all setup is complete, start the Connection - connect.start(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - try - { - // Read all standard input and send it as a message. - java.io.BufferedReader stdin = - new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); - while ( true ) - { - System.out.println ("\nReplier application:\n" - + "============================\n" - + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" - + "The application gets requests with JMSReplyTo set on the " + APP_TOPIC + " topic." - + "The message is transformed to all uppercase or all lowercase, and then returned to the requestor." - + "The Requestor application displays the result.\n\n" - + "Enter EXIT or press Ctrl+C to close the Replier.\n"); - String s = stdin.readLine(); - if ( s == null || s.equalsIgnoreCase("EXIT")) - { - System.out.println ("\nStopping Replier. Please wait..\n>"); - exit(); - } - } - } - catch ( java.io.IOException ioe ) - { - ioe.printStackTrace(); - } - } - - /** - * Handle the message. - * (as specified in the javax.jms.MessageListener interface). - * - * IMPORTANT NOTE: We must follow the design paradigm for JMS - * synchronous requests. That is, we must: - * - get the message - * - look for the header specifying JMSReplyTo - * - send a reply to the topic specified there. - * Failing to follow these steps might leave the originator - * of the request waiting forever. - * - * OPTIONAL BEHAVIOR: The following actions taken by the - * message handler represent good programming style, but are - * not required by the design paradigm for JMS requests. - * - set the JMSCorrelationID (tying the response back to - * the original request. - * - use transacted session "commit" so receipt of request - * won't happen without the reply being sent. - * - */ - public void onMessage( javax.jms.Message aMessage) - { - try - { - // Cast the message as a text message. - javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; - - // This handler reads a single String from the - // message and prints it to the standard output. - try - { - String string = textMessage.getText(); - System.out.println( "[Request] " + string ); - - // Check for a ReplyTo topic - javax.jms.Topic replyTopic = (javax.jms.Topic) aMessage.getJMSReplyTo(); - if (replyTopic != null) - { - // Send the modified message back. - javax.jms.TextMessage reply = session.createTextMessage(); - if (imode == UPPERCASE) - reply.setText("Transformed " + string + " to all uppercase: " + string.toUpperCase()); - else - reply.setText("Transformed " + string + " to all lowercase " + string.toLowerCase()); - reply.setJMSCorrelationID(aMessage.getJMSMessageID()); - replier.send(replyTopic, reply); - session.commit(); - } - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - catch (java.lang.RuntimeException rte) - { - rte.printStackTrace(); - } - } - - /** Cleanup resources cleanly and exit. */ - private void exit() - { - try - { - connect.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String argv[]) { - - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = DEFAULT_USER_NAME; - String password = DEFAULT_PASSWORD; - String mode = DEFAULT_MODE; - - // Check parameters - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - - if (arg.equals("-m")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing mode"); - System.exit(1); - } - mode = argv[++i]; - if (!(mode.equals("uppercase") || mode.equals("lowercase"))) { - System.err.println("error: mode must be 'uppercase' or 'lowercase'"); - System.exit(1); - } - continue; - } - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - - // Invalid argument - System.err.println ("error: unexpected argument: "+arg); - printUsage(); - System.exit(1); - } - - - // Start the JMS client for the "chat". - TopicReplier replier = new TopicReplier(); - replier.start (broker, username, password, mode); - } - - /** Prints the usage. */ - private static void printUsage() { - - StringBuffer use = new StringBuffer(); - use.append("usage: java Replier (options) ...\n\n"); - use.append("options:\n"); - use.append(" -b name:port Specify name:port of broker.\n"); - use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); - use.append(" -u name Specify unique user name.\n"); - use.append(" Default broker: "+DEFAULT_USER_NAME+"\n"); - use.append(" -p password Specify password for user.\n"); - use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); - use.append(" -m mode Replier operating mode - uppercase or lowercase.\n"); - use.append(" Default mode: "+DEFAULT_MODE+"\n"); - use.append(" -h This help screen.\n"); - System.err.println (use); - } - -} - +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + +Sample Application + +Writing a Basic JMS Application using: + - Synchronous Request/Reply + - Publish/Subscribe + - javax.jms.TopicRequestor class + - JMSReplyTo Header + +When this program runs, it waits for +messages to the topic "jms.samples.request". +When that message occurs, a response based on the request +is sent back to the "Requestor" specified in the JMSReplyTo header. + +This sample replies with a simple text manipulation of the request; +the text is either folded to all UPPERCASE or all lowercase. + +Usage: + java TopicReplier -b -u -p -m + -b broker:port points to your message broker + Default: tcp://localhost:61616 + -u username must be unique (but is not checked) + Default: SampleReplier + -p password password for user (not checked) + Default: password + -m mode replier mode (uppercase, or lowercase) + Default: uppercase + +Suggested usage: + - In a console window with the environment set, start a replier: + java TopicReplier -u SampleReplier + - In another console window, start a Requestor: + java TopicRequestor -u SampleRequestor + - Enter text in the Requestor window then press Enter. + + The Replier responds with the message in all uppercase characters. + - Start other TopicRequestors with different user names to see that + replies are not broadcast to all users. For example: + java TopicRequestor -u SampleRequestorToo + + - Start other TopicRepliers. + - See that all repliers are receiving all the messages,(as they should). + - See the Requestor only receives one response. + java TopicReplier -u toLower -m lowercase + + */ + +import org.apache.activemq.*; + + +public class TopicReplier + implements javax.jms.MessageListener +{ + private static final String APP_TOPIC = "jms.samples.request"; + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + private static final String DEFAULT_USER_NAME = "SampleReplier"; + private static final String DEFAULT_PASSWORD = "password"; + private static final String DEFAULT_MODE = "uppercase"; + private static final int UPPERCASE = 0; + private static final int LOWERCASE = 1; + + private javax.jms.Connection connect = null; + private javax.jms.Session session = null; + private javax.jms.MessageProducer replier = null; + + private int imode = UPPERCASE; + + /** Create JMS client for publishing and subscribing to messages. */ + private void start ( String broker, String username, String password, String mode) + { + // Set the operation mode + imode = (mode.equals("uppercase")) ? UPPERCASE : LOWERCASE; + + // Create a connection. + try + { + javax.jms.ConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + connect = factory.createConnection (username, password); + session = connect.createSession(true, javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("error: Cannot connect to Broker - " + broker); + jmse.printStackTrace(); + System.exit(1); + } + + // Create Subscriber to application topics as well as a Publisher + // to use for JMS replies. + try + { + javax.jms.Topic topic = session.createTopic (APP_TOPIC); + javax.jms.MessageConsumer subscriber = session.createConsumer(topic); + subscriber.setMessageListener(this); + replier = session.createProducer(null); // Topic will be set for each reply + // Now that all setup is complete, start the Connection + connect.start(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + try + { + // Read all standard input and send it as a message. + java.io.BufferedReader stdin = + new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); + while ( true ) + { + System.out.println ("\nReplier application:\n" + + "============================\n" + + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" + + "The application gets requests with JMSReplyTo set on the " + APP_TOPIC + " topic." + + "The message is transformed to all uppercase or all lowercase, and then returned to the requestor." + + "The Requestor application displays the result.\n\n" + + "Enter EXIT or press Ctrl+C to close the Replier.\n"); + String s = stdin.readLine(); + if ( s == null || s.equalsIgnoreCase("EXIT")) + { + System.out.println ("\nStopping Replier. Please wait..\n>"); + exit(); + } + } + } + catch ( java.io.IOException ioe ) + { + ioe.printStackTrace(); + } + } + + /** + * Handle the message. + * (as specified in the javax.jms.MessageListener interface). + * + * IMPORTANT NOTE: We must follow the design paradigm for JMS + * synchronous requests. That is, we must: + * - get the message + * - look for the header specifying JMSReplyTo + * - send a reply to the topic specified there. + * Failing to follow these steps might leave the originator + * of the request waiting forever. + * + * OPTIONAL BEHAVIOR: The following actions taken by the + * message handler represent good programming style, but are + * not required by the design paradigm for JMS requests. + * - set the JMSCorrelationID (tying the response back to + * the original request. + * - use transacted session "commit" so receipt of request + * won't happen without the reply being sent. + * + */ + public void onMessage( javax.jms.Message aMessage) + { + try + { + // Cast the message as a text message. + javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; + + // This handler reads a single String from the + // message and prints it to the standard output. + try + { + String string = textMessage.getText(); + System.out.println( "[Request] " + string ); + + // Check for a ReplyTo topic + javax.jms.Topic replyTopic = (javax.jms.Topic) aMessage.getJMSReplyTo(); + if (replyTopic != null) + { + // Send the modified message back. + javax.jms.TextMessage reply = session.createTextMessage(); + if (imode == UPPERCASE) + reply.setText("Transformed " + string + " to all uppercase: " + string.toUpperCase()); + else + reply.setText("Transformed " + string + " to all lowercase " + string.toLowerCase()); + reply.setJMSCorrelationID(aMessage.getJMSMessageID()); + replier.send(replyTopic, reply); + session.commit(); + } + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + catch (java.lang.RuntimeException rte) + { + rte.printStackTrace(); + } + } + + /** Cleanup resources cleanly and exit. */ + private void exit() + { + try + { + connect.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String argv[]) { + + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = DEFAULT_USER_NAME; + String password = DEFAULT_PASSWORD; + String mode = DEFAULT_MODE; + + // Check parameters + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + + if (arg.equals("-m")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing mode"); + System.exit(1); + } + mode = argv[++i]; + if (!(mode.equals("uppercase") || mode.equals("lowercase"))) { + System.err.println("error: mode must be 'uppercase' or 'lowercase'"); + System.exit(1); + } + continue; + } + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + + // Invalid argument + System.err.println ("error: unexpected argument: "+arg); + printUsage(); + System.exit(1); + } + + + // Start the JMS client for the "chat". + TopicReplier replier = new TopicReplier(); + replier.start (broker, username, password, mode); + } + + /** Prints the usage. */ + private static void printUsage() { + + StringBuffer use = new StringBuffer(); + use.append("usage: java Replier (options) ...\n\n"); + use.append("options:\n"); + use.append(" -b name:port Specify name:port of broker.\n"); + use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); + use.append(" -u name Specify unique user name.\n"); + use.append(" Default broker: "+DEFAULT_USER_NAME+"\n"); + use.append(" -p password Specify password for user.\n"); + use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); + use.append(" -m mode Replier operating mode - uppercase or lowercase.\n"); + use.append(" Default mode: "+DEFAULT_MODE+"\n"); + use.append(" -h This help screen.\n"); + System.err.println (use); + } + +} + diff --git a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/RequestReply/TopicRequestor.java b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/RequestReply/TopicRequestor.java index 4c48fabee7..1c59c65c67 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/RequestReply/TopicRequestor.java +++ b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/RequestReply/TopicRequestor.java @@ -1,241 +1,241 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - - Sample Application - -Writing a Basic JMS Application using: - - Synchronous Request/Reply - - Publish/Subscribe - - javax.jms.TopicRequestor class - - JMSReplyTo Header - -When this program runs, it reads input from System.in -and then sends the text as a message to the topic -"progress.samples.request". - -A "Replier" class should be waiting for the request. -It will reply with a message. - -NOTE: You must run the TopicReplier first. -(Otherwise the syncronous request will block forever.) - -Usage: - java TopicRequestor -b -u -p - -b broker:port points to your message broker - Default: tcp://localhost:61616 - -u username must be unique (but is not checked) - Default: SampleRequestor - -p password password for user (not checked) - Default: password - -Suggested demonstration: - - In a console window with the environment set, - start a copy of the Replier. For example: - java TopicReplier -u SampleReplier - - In another console window, start a Requestor. - For example: - java TopicRequestor -u SampleRequestor - - Enter text in the Requestor window then press Enter. - - The Replier responds with the message in all uppercase characters. - - Start other Requestors with different user names to see that - replies are not broadcast to all users. For example: - java TopicRequestor -u SampleRequestorToo - - - Start other Repliers. - - See that all repliers are receiving all the messages,(as they should). - - See the Requestor only receives one response. - java TopicReplier -u toLower -m lowercase -*/ -import org.apache.activemq.*; - - -public class TopicRequestor -{ - private static final String APP_TOPIC = "jms.samples.request"; - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - private static final String DEFAULT_USER_NAME = "SampleRequestor"; - private static final String DEFAULT_PASSWORD = "password"; - - private javax.jms.TopicConnection connect = null; - private javax.jms.TopicSession session = null; - - /** Create JMS client for publishing and subscribing to messages. */ - private void start ( String broker, String username, String password) - { - // Create a connection. - try - { - javax.jms.TopicConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - connect = factory.createTopicConnection (username, password); - session = connect.createTopicSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("error: Cannot connect to Broker - " + broker); - jmse.printStackTrace(); - System.exit(1); - } - - // Create Topic for all requests. TopicRequestor will be created - // as needed. - javax.jms.Topic topic = null; - try - { - topic = session.createTopic (APP_TOPIC); - // Now that all setup is complete, start the Connection - connect.start(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - try - { - // Read all standard input and send it as a message. - java.io.BufferedReader stdin = - new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); - System.out.println ("\nRequestor application:\n" - + "============================\n" - + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" - + "The application uses a TopicRequestor to on the " + APP_TOPIC + " topic." - + "The Replier application gets the message, and transforms it." - + "The Requestor application displays the result.\n\n" - + "Type some mixed case text, and then press Enter to make a request.\n"); - while ( true ) - { - String s = stdin.readLine(); - - if ( s == null ) - exit(); - else if ( s.length() > 0 ) - { - javax.jms.TextMessage msg = session.createTextMessage(); - msg.setText( username + ": " + s ); - // Instead of publishing, we will use a TopicRequestor. - javax.jms.TopicRequestor requestor = new javax.jms.TopicRequestor(session, topic); - javax.jms.Message response = requestor.request(msg); - // The message should be a TextMessage. Just report it. - javax.jms.TextMessage textMessage = (javax.jms.TextMessage) response; - System.out.println( "[Reply] " + textMessage.getText() ); - } - } - } - catch ( java.io.IOException ioe ) - { - ioe.printStackTrace(); - } - catch ( javax.jms.JMSException jmse ) - { - jmse.printStackTrace(); - } - } - - /** Cleanup resources cleanly and exit. */ - private void exit() - { - try - { - connect.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String argv[]) { - - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = DEFAULT_USER_NAME; - String password = DEFAULT_PASSWORD; - - // Check parameters - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - - // Invalid argument - System.err.println ("error: unexpected argument: "+arg); - printUsage(); - System.exit(1); - } - - // Start the JMS client for the "chat". - TopicRequestor requestor = new TopicRequestor(); - requestor.start (broker, username, password); - - } - - /** Prints the usage. */ - private static void printUsage() { - - StringBuffer use = new StringBuffer(); - use.append("usage: java Requestor (options) ...\n\n"); - use.append("options:\n"); - use.append(" -b name:port Specify name:port of broker.\n"); - use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); - use.append(" -u name Specify unique user name.\n"); - use.append(" Default broker: "+DEFAULT_USER_NAME+"\n"); - use.append(" -p password Specify password for user.\n"); - use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); - use.append(" -h This help screen.\n"); - System.err.println (use); - } - -} - +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + + Sample Application + +Writing a Basic JMS Application using: + - Synchronous Request/Reply + - Publish/Subscribe + - javax.jms.TopicRequestor class + - JMSReplyTo Header + +When this program runs, it reads input from System.in +and then sends the text as a message to the topic +"progress.samples.request". + +A "Replier" class should be waiting for the request. +It will reply with a message. + +NOTE: You must run the TopicReplier first. +(Otherwise the syncronous request will block forever.) + +Usage: + java TopicRequestor -b -u -p + -b broker:port points to your message broker + Default: tcp://localhost:61616 + -u username must be unique (but is not checked) + Default: SampleRequestor + -p password password for user (not checked) + Default: password + +Suggested demonstration: + - In a console window with the environment set, + start a copy of the Replier. For example: + java TopicReplier -u SampleReplier + - In another console window, start a Requestor. + For example: + java TopicRequestor -u SampleRequestor + - Enter text in the Requestor window then press Enter. + + The Replier responds with the message in all uppercase characters. + - Start other Requestors with different user names to see that + replies are not broadcast to all users. For example: + java TopicRequestor -u SampleRequestorToo + + - Start other Repliers. + - See that all repliers are receiving all the messages,(as they should). + - See the Requestor only receives one response. + java TopicReplier -u toLower -m lowercase +*/ +import org.apache.activemq.*; + + +public class TopicRequestor +{ + private static final String APP_TOPIC = "jms.samples.request"; + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + private static final String DEFAULT_USER_NAME = "SampleRequestor"; + private static final String DEFAULT_PASSWORD = "password"; + + private javax.jms.TopicConnection connect = null; + private javax.jms.TopicSession session = null; + + /** Create JMS client for publishing and subscribing to messages. */ + private void start ( String broker, String username, String password) + { + // Create a connection. + try + { + javax.jms.TopicConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + connect = factory.createTopicConnection (username, password); + session = connect.createTopicSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("error: Cannot connect to Broker - " + broker); + jmse.printStackTrace(); + System.exit(1); + } + + // Create Topic for all requests. TopicRequestor will be created + // as needed. + javax.jms.Topic topic = null; + try + { + topic = session.createTopic (APP_TOPIC); + // Now that all setup is complete, start the Connection + connect.start(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + try + { + // Read all standard input and send it as a message. + java.io.BufferedReader stdin = + new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); + System.out.println ("\nRequestor application:\n" + + "============================\n" + + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" + + "The application uses a TopicRequestor to on the " + APP_TOPIC + " topic." + + "The Replier application gets the message, and transforms it." + + "The Requestor application displays the result.\n\n" + + "Type some mixed case text, and then press Enter to make a request.\n"); + while ( true ) + { + String s = stdin.readLine(); + + if ( s == null ) + exit(); + else if ( s.length() > 0 ) + { + javax.jms.TextMessage msg = session.createTextMessage(); + msg.setText( username + ": " + s ); + // Instead of publishing, we will use a TopicRequestor. + javax.jms.TopicRequestor requestor = new javax.jms.TopicRequestor(session, topic); + javax.jms.Message response = requestor.request(msg); + // The message should be a TextMessage. Just report it. + javax.jms.TextMessage textMessage = (javax.jms.TextMessage) response; + System.out.println( "[Reply] " + textMessage.getText() ); + } + } + } + catch ( java.io.IOException ioe ) + { + ioe.printStackTrace(); + } + catch ( javax.jms.JMSException jmse ) + { + jmse.printStackTrace(); + } + } + + /** Cleanup resources cleanly and exit. */ + private void exit() + { + try + { + connect.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String argv[]) { + + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = DEFAULT_USER_NAME; + String password = DEFAULT_PASSWORD; + + // Check parameters + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + + // Invalid argument + System.err.println ("error: unexpected argument: "+arg); + printUsage(); + System.exit(1); + } + + // Start the JMS client for the "chat". + TopicRequestor requestor = new TopicRequestor(); + requestor.start (broker, username, password); + + } + + /** Prints the usage. */ + private static void printUsage() { + + StringBuffer use = new StringBuffer(); + use.append("usage: java Requestor (options) ...\n\n"); + use.append("options:\n"); + use.append(" -b name:port Specify name:port of broker.\n"); + use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); + use.append(" -u name Specify unique user name.\n"); + use.append(" Default broker: "+DEFAULT_USER_NAME+"\n"); + use.append(" -p password Specify password for user.\n"); + use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); + use.append(" -h This help screen.\n"); + System.err.println (use); + } + +} + diff --git a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/SelectorChat/SelectorChat.java b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/SelectorChat/SelectorChat.java index 64c9a776ac..ca5cfbd37d 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/SelectorChat/SelectorChat.java +++ b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/SelectorChat/SelectorChat.java @@ -1,296 +1,296 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - -Sample Application - -Writing a Basic JMS Application using Publish and Subscribe using -Message Selectors - -This sample publishes messages and sets a property in the message header when -publishing messages. The subscriber set a message selector to select these -messages. In this sample we name the property "Department" - -Usage: - java SelectorChat -b -u -p -v -s - -b broker:port points to your message broker - Default: tcp://localhost:61616 - -u username must be unique (but is not checked) - -p password (optional) password for user (not checked) - Default: password - -s selection required, selection value to set to message property - - -Suggested demonstration: - - In separate console windows with the environment set, - start instances of the application - under unique user names. - For example: - java SelectorChat -u BOB -s MARKETING - java SelectorChat -u JANE -s MARKETING - - Enter text and then press Enter to publish the message. - - See messages appear under the various user names as you - enter messages in each console window. - - Try starting a new instance with a different message selector - - Stop a session by pressing CTRL+C in its console window. - -*/ - -import org.apache.activemq.*; - - -public class SelectorChat - implements javax.jms.MessageListener -{ - private static final String APP_TOPIC = "jms.samples.chat"; - private static final String PROPERTY_NAME = "Department"; - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - private static final String DEFAULT_PASSWORD = "password"; - - private javax.jms.Connection connect = null; - private javax.jms.Session pubSession = null; - private javax.jms.Session subSession = null; - private javax.jms.MessageProducer publisher = null; - - /** Create JMS client for publishing and subscribing to messages. */ - private void chatter( String broker, String username, String password, String selection) - { - - // Create a connection. - try - { - javax.jms.ConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - connect = factory.createConnection (username, password); - pubSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - subSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("error: Cannot connect to Broker - " + broker); - jmse.printStackTrace(); - System.exit(1); - } - - // Create Publisher and Subscriber to 'chat' topics - - try - { - javax.jms.Topic topic = pubSession.createTopic (APP_TOPIC); - // NOTE: The subscriber's message selector will now be set: - javax.jms.MessageConsumer subscriber = subSession.createConsumer(topic, PROPERTY_NAME + " = \'" + selection +"\'", false); - subscriber.setMessageListener(this); - publisher = pubSession.createProducer(topic); - // Now that setup is complete, start the Connection - connect.start(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - System.exit(1); - } - - try - { - // Read all standard input and send it as a message. - - java.io.BufferedReader stdin = - new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); - System.out.println ("\nSelectorChat application:\n" - + "===========================\n" - + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" - + "The application will publish messages with " + PROPERTY_NAME + " set to " + selection + " to the " + APP_TOPIC + " topic .\n" - + "The application also subscribes to that topic, selecting only messages where " + PROPERTY_NAME + " is " + selection + ".\n" - + "Type some text, and then press Enter to publish it as a TextMesssage from " + username + ".\n"); - - while ( true ) - { - String s = stdin.readLine(); - - if ( s == null ) - exit(); - else if ( s.length() > 0 ) - { - javax.jms.TextMessage msg = pubSession.createTextMessage(); - msg.setText( username + ": " + s ); - // NOTE: here we set a property on messages to be published: - msg.setStringProperty(PROPERTY_NAME, selection); - publisher.send( msg ); - } - } - } - catch ( java.io.IOException ioe ) - { - ioe.printStackTrace(); - } - catch ( javax.jms.JMSException jmse ) - { - jmse.printStackTrace(); - } - } - - /** - * Handle the message - * (as specified in the javax.jms.MessageListener interface). - */ - public void onMessage( javax.jms.Message aMessage) - { - try - { - // Cast the message as a text message. - javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; - - // This handler reads a single String from the - // message and prints it to the standard output. - try - { - String string = textMessage.getText(); - System.out.println( string ); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - catch (java.lang.RuntimeException rte) - { - rte.printStackTrace(); - } - } - - /** Cleanup resources and then exit. */ - private void exit() - { - try - { - connect.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String argv[]) { - - // Is there anything to do? - if (argv.length == 0) { - printUsage(); - System.exit(1); - } - - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = null; - String password = DEFAULT_PASSWORD; - String selection = null; - - // Check parameters - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - // Options - if (!arg.startsWith("-")) { - System.err.println ("error: unexpected argument - "+arg); - printUsage(); - System.exit(1); - } - else { - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - - if (arg.equals("-s")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing selection"); - System.exit(1); - } - selection = argv[++i]; - continue; - } - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - } - } - - // Check values read in. - if (username == null) { - System.err.println ("error: user name must be supplied"); - printUsage(); - } - - if (selection == null) { - System.err.println ("error: selection must be supplied"); - printUsage(); - System.exit(1); - } - - // Start the JMS client for the "chat". - SelectorChat chat = new SelectorChat(); - chat.chatter (broker, username, password, selection); - - } - - /** Prints the usage. */ - private static void printUsage() { - - StringBuffer use = new StringBuffer(); - use.append("usage: java SelectorChat (options) ...\n\n"); - use.append("options:\n"); - use.append(" -b name:port Specify name:port of broker.\n"); - use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); - use.append(" -u name Specify unique user name. (Required)\n"); - use.append(" -p password Specify password for user.\n"); - use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); - use.append(" -s selection Message selector value. (Required)\n"); - use.append(" -h This help screen\n"); - System.err.println (use); - } - -} - +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + +Sample Application + +Writing a Basic JMS Application using Publish and Subscribe using +Message Selectors + +This sample publishes messages and sets a property in the message header when +publishing messages. The subscriber set a message selector to select these +messages. In this sample we name the property "Department" + +Usage: + java SelectorChat -b -u -p -v -s + -b broker:port points to your message broker + Default: tcp://localhost:61616 + -u username must be unique (but is not checked) + -p password (optional) password for user (not checked) + Default: password + -s selection required, selection value to set to message property + + +Suggested demonstration: + - In separate console windows with the environment set, + start instances of the application + under unique user names. + For example: + java SelectorChat -u BOB -s MARKETING + java SelectorChat -u JANE -s MARKETING + - Enter text and then press Enter to publish the message. + - See messages appear under the various user names as you + enter messages in each console window. + - Try starting a new instance with a different message selector + - Stop a session by pressing CTRL+C in its console window. + +*/ + +import org.apache.activemq.*; + + +public class SelectorChat + implements javax.jms.MessageListener +{ + private static final String APP_TOPIC = "jms.samples.chat"; + private static final String PROPERTY_NAME = "Department"; + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + private static final String DEFAULT_PASSWORD = "password"; + + private javax.jms.Connection connect = null; + private javax.jms.Session pubSession = null; + private javax.jms.Session subSession = null; + private javax.jms.MessageProducer publisher = null; + + /** Create JMS client for publishing and subscribing to messages. */ + private void chatter( String broker, String username, String password, String selection) + { + + // Create a connection. + try + { + javax.jms.ConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + connect = factory.createConnection (username, password); + pubSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + subSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("error: Cannot connect to Broker - " + broker); + jmse.printStackTrace(); + System.exit(1); + } + + // Create Publisher and Subscriber to 'chat' topics + + try + { + javax.jms.Topic topic = pubSession.createTopic (APP_TOPIC); + // NOTE: The subscriber's message selector will now be set: + javax.jms.MessageConsumer subscriber = subSession.createConsumer(topic, PROPERTY_NAME + " = \'" + selection +"\'", false); + subscriber.setMessageListener(this); + publisher = pubSession.createProducer(topic); + // Now that setup is complete, start the Connection + connect.start(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + System.exit(1); + } + + try + { + // Read all standard input and send it as a message. + + java.io.BufferedReader stdin = + new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); + System.out.println ("\nSelectorChat application:\n" + + "===========================\n" + + "The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + ".\n" + + "The application will publish messages with " + PROPERTY_NAME + " set to " + selection + " to the " + APP_TOPIC + " topic .\n" + + "The application also subscribes to that topic, selecting only messages where " + PROPERTY_NAME + " is " + selection + ".\n" + + "Type some text, and then press Enter to publish it as a TextMesssage from " + username + ".\n"); + + while ( true ) + { + String s = stdin.readLine(); + + if ( s == null ) + exit(); + else if ( s.length() > 0 ) + { + javax.jms.TextMessage msg = pubSession.createTextMessage(); + msg.setText( username + ": " + s ); + // NOTE: here we set a property on messages to be published: + msg.setStringProperty(PROPERTY_NAME, selection); + publisher.send( msg ); + } + } + } + catch ( java.io.IOException ioe ) + { + ioe.printStackTrace(); + } + catch ( javax.jms.JMSException jmse ) + { + jmse.printStackTrace(); + } + } + + /** + * Handle the message + * (as specified in the javax.jms.MessageListener interface). + */ + public void onMessage( javax.jms.Message aMessage) + { + try + { + // Cast the message as a text message. + javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; + + // This handler reads a single String from the + // message and prints it to the standard output. + try + { + String string = textMessage.getText(); + System.out.println( string ); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + catch (java.lang.RuntimeException rte) + { + rte.printStackTrace(); + } + } + + /** Cleanup resources and then exit. */ + private void exit() + { + try + { + connect.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String argv[]) { + + // Is there anything to do? + if (argv.length == 0) { + printUsage(); + System.exit(1); + } + + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = null; + String password = DEFAULT_PASSWORD; + String selection = null; + + // Check parameters + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + // Options + if (!arg.startsWith("-")) { + System.err.println ("error: unexpected argument - "+arg); + printUsage(); + System.exit(1); + } + else { + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + + if (arg.equals("-s")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing selection"); + System.exit(1); + } + selection = argv[++i]; + continue; + } + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + } + } + + // Check values read in. + if (username == null) { + System.err.println ("error: user name must be supplied"); + printUsage(); + } + + if (selection == null) { + System.err.println ("error: selection must be supplied"); + printUsage(); + System.exit(1); + } + + // Start the JMS client for the "chat". + SelectorChat chat = new SelectorChat(); + chat.chatter (broker, username, password, selection); + + } + + /** Prints the usage. */ + private static void printUsage() { + + StringBuffer use = new StringBuffer(); + use.append("usage: java SelectorChat (options) ...\n\n"); + use.append("options:\n"); + use.append(" -b name:port Specify name:port of broker.\n"); + use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); + use.append(" -u name Specify unique user name. (Required)\n"); + use.append(" -p password Specify password for user.\n"); + use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); + use.append(" -s selection Message selector value. (Required)\n"); + use.append(" -h This help screen\n"); + System.err.println (use); + } + +} + diff --git a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/TransactedChat/TransactedChat.java b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/TransactedChat/TransactedChat.java index 312278398d..1a95f2e5d2 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/TransactedChat/TransactedChat.java +++ b/assembly/src/release/examples/openwire/exploring-jms/TopicPubSubSamples/TransactedChat/TransactedChat.java @@ -1,296 +1,296 @@ -/* - * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. - * - * Licensed 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. - -Sample Application - -Writing a Basic JMS Application using - - Publish and Subscribe - - Transacted Sessions - - Multiple Sessions - -Run this program to publish and subscribe to the specified topic. -Messages are buffered and sent when a specific string is seen ("COMMIT"). -Messages buffered can be discarded by entering a specified string ("CANCEL"). - -Usage: - java TransactedChat -b -u -p - -b broker:port points to your message broker - Default: tcp://localhost:61616 - -u username must be unique (but is not checked) - -p password password for user (not checked) - -Suggested demonstration: - - In a console window with the environment set, start this - application. In other console windows start other sessions - under different user names. For example: - java TransactedChat -u ADMINISTRATION - java TransactedChat -u FACILITIES - - Type some text and then press Enter. - - Repeat to create a batch of messages. - - Send the batched messages by entering the text "COMMIT" - - Discard the batched messages by entering the text "CANCEL" - - -*/ -import org.apache.activemq.*; - -public class TransactedChat - implements javax.jms.MessageListener -{ - private static final String APP_TOPIC = "jms.samples.chat"; - private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; - private static final String DEFAULT_PASSWORD = "password"; - - private javax.jms.Connection connect = null; - private javax.jms.Session publishSession = null; - private javax.jms.Session subscribeSession = null; - private javax.jms.MessageProducer publisher = null; - - /** Create JMS client for publishing and subscribing to messages. */ - private void chatter( String broker, String username, String password) - { - // Create a connection. - try - { - javax.jms.ConnectionFactory factory; - factory = new ActiveMQConnectionFactory(username, password, broker); - connect = factory.createConnection (username, password); - // We want to be able up commit/rollback messages published, - // but not affect messages consumed. Therefore, we need two sessions. - publishSession = connect.createSession(true, javax.jms.Session.AUTO_ACKNOWLEDGE); - subscribeSession = connect.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); - } - catch (javax.jms.JMSException jmse) - { - System.err.println("error: Cannot connect to Broker - " + broker); - jmse.printStackTrace(); - System.exit(1); - } - - // Create Publisher and Subscriber to 'chat' topics - try - { - javax.jms.Topic topic = subscribeSession.createTopic (APP_TOPIC); - javax.jms.MessageConsumer subscriber = subscribeSession.createConsumer(topic); - subscriber.setMessageListener(this); - publisher = publishSession.createProducer(topic); - // Now start the Connection - connect.start(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - try - { - // Read all standard input and send it as a message. - java.io.BufferedReader stdin = - new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); - boolean showMessage = true; - while ( true ) - { - if (showMessage) - { - System.out.println ("TransactedChat application:"); - System.out.println ("===========================" ); - System.out.println ("The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + "."); - System.out.println ("The application will stage messages to the " + APP_TOPIC + " topic until you either commit them or roll them back."); - System.out.println ("The application also subscribes to that topic to consume any committed messages published there.\n"); - System.out.println ("1. Enter text to publish and then press Enter to stage the message."); - System.out.println ("2. Add a few messages to the transaction batch."); - System.out.println ("3. Then, either:"); - System.out.println (" o Enter the text 'COMMIT', and press Enter to publish all the staged messages."); - System.out.println (" o Enter the text 'CANCEL', and press Enter to drop the staged messages waiting to be sent."); - showMessage = false; - } - String s = stdin.readLine(); - - if ( s == null ) - exit(); - else if (s.trim().equals("CANCEL")) - { - // Rollback the messages. A new transaction is implicitly - // started for following messages. - System.out.println ("Cancelling messages..."); - publishSession.rollback(); - System.out.println ("Staged messages have been cleared."); - showMessage = false; // don't show the help message again. - } - else if ( s.length() > 0 ) - // See if we should send the messages - if (s.trim().equals("COMMIT")) - { - // Commit (send) the messages. A new transaction is - // implicitly started for following messages. - System.out.println ("Committing messages... "); - publishSession.commit(); - System.out.println ("Staged messages have all been sent."); - showMessage = false; // dont't show the help message again. - } - else - { - javax.jms.TextMessage msg = publishSession.createTextMessage(); - msg.setText( username + ": " + s ); - // Publish the message persistently - publisher.send( msg ); - } - } - } - catch ( java.io.IOException ioe ) - { - ioe.printStackTrace(); - } - catch ( javax.jms.JMSException jmse ) - { - jmse.printStackTrace(); - } - } - - /** - * Handle the message - * (as specified in the javax.jms.MessageListener interface). - */ - public void onMessage( javax.jms.Message aMessage) - { - try - { - // Cast the message as a text message. - javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; - - // This handler reads a single String from the - // message and prints it to the standard output. - try - { - String string = textMessage.getText(); - System.out.println( string ); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - } - catch (java.lang.RuntimeException rte) - { - rte.printStackTrace(); - } - } - - /** Cleanup resources cleanly and exit. */ - private void exit() - { - try - { - publishSession.rollback(); // Rollback any uncommitted messages. - connect.close(); - } - catch (javax.jms.JMSException jmse) - { - jmse.printStackTrace(); - } - - System.exit(0); - } - - // - // NOTE: the remainder of this sample deals with reading arguments - // and does not utilize any JMS classes or code. - // - - /** Main program entry point. */ - public static void main(String argv[]) { - - // Is there anything to do? - if (argv.length == 0) { - printUsage(); - System.exit(1); - } - - // Values to be read from parameters - String broker = DEFAULT_BROKER_NAME; - String username = null; - String password = DEFAULT_PASSWORD; - - // Check parameters - for (int i = 0; i < argv.length; i++) { - String arg = argv[i]; - - - if (arg.equals("-b")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing broker name:port"); - System.exit(1); - } - broker = argv[++i]; - continue; - } - - if (arg.equals("-u")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing user name"); - System.exit(1); - } - username = argv[++i]; - continue; - } - - if (arg.equals("-p")) { - if (i == argv.length - 1 || argv[i+1].startsWith("-")) { - System.err.println("error: missing password"); - System.exit(1); - } - password = argv[++i]; - continue; - } - - if (arg.equals("-h")) { - printUsage(); - System.exit(1); - } - - // Invalid argument - System.err.println ("error: unexpected argument: "+arg); - printUsage(); - System.exit(1); - } - - // Check values read in. - if (username == null) { - System.err.println ("error: user name must be supplied"); - printUsage(); - } - - // Start the JMS client for the "chat". - TransactedChat chat = new TransactedChat(); - chat.chatter (broker, username, password); - - } - - /** Prints the usage. */ - private static void printUsage() { - - StringBuffer use = new StringBuffer(); - use.append("usage: java TransactedChat (options) ...\n\n"); - use.append("options:\n"); - use.append(" -b name:port Specify name:port of broker.\n"); - use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); - use.append(" -u name Specify unique user name. (Required)\n"); - use.append(" -p password Specify password for user.\n"); - use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); - use.append(" -h This help screen.\n"); - System.err.println (use); - } -} - +/* + * © 2001-2009, Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. + * + * Licensed 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. + +Sample Application + +Writing a Basic JMS Application using + - Publish and Subscribe + - Transacted Sessions + - Multiple Sessions + +Run this program to publish and subscribe to the specified topic. +Messages are buffered and sent when a specific string is seen ("COMMIT"). +Messages buffered can be discarded by entering a specified string ("CANCEL"). + +Usage: + java TransactedChat -b -u -p + -b broker:port points to your message broker + Default: tcp://localhost:61616 + -u username must be unique (but is not checked) + -p password password for user (not checked) + +Suggested demonstration: + - In a console window with the environment set, start this + application. In other console windows start other sessions + under different user names. For example: + java TransactedChat -u ADMINISTRATION + java TransactedChat -u FACILITIES + - Type some text and then press Enter. + - Repeat to create a batch of messages. + - Send the batched messages by entering the text "COMMIT" + - Discard the batched messages by entering the text "CANCEL" + + +*/ +import org.apache.activemq.*; + +public class TransactedChat + implements javax.jms.MessageListener +{ + private static final String APP_TOPIC = "jms.samples.chat"; + private static final String DEFAULT_BROKER_NAME = "tcp://localhost:61616"; + private static final String DEFAULT_PASSWORD = "password"; + + private javax.jms.Connection connect = null; + private javax.jms.Session publishSession = null; + private javax.jms.Session subscribeSession = null; + private javax.jms.MessageProducer publisher = null; + + /** Create JMS client for publishing and subscribing to messages. */ + private void chatter( String broker, String username, String password) + { + // Create a connection. + try + { + javax.jms.ConnectionFactory factory; + factory = new ActiveMQConnectionFactory(username, password, broker); + connect = factory.createConnection (username, password); + // We want to be able up commit/rollback messages published, + // but not affect messages consumed. Therefore, we need two sessions. + publishSession = connect.createSession(true, javax.jms.Session.AUTO_ACKNOWLEDGE); + subscribeSession = connect.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + } + catch (javax.jms.JMSException jmse) + { + System.err.println("error: Cannot connect to Broker - " + broker); + jmse.printStackTrace(); + System.exit(1); + } + + // Create Publisher and Subscriber to 'chat' topics + try + { + javax.jms.Topic topic = subscribeSession.createTopic (APP_TOPIC); + javax.jms.MessageConsumer subscriber = subscribeSession.createConsumer(topic); + subscriber.setMessageListener(this); + publisher = publishSession.createProducer(topic); + // Now start the Connection + connect.start(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + try + { + // Read all standard input and send it as a message. + java.io.BufferedReader stdin = + new java.io.BufferedReader( new java.io.InputStreamReader( System.in ) ); + boolean showMessage = true; + while ( true ) + { + if (showMessage) + { + System.out.println ("TransactedChat application:"); + System.out.println ("===========================" ); + System.out.println ("The application user " + username + " connects to the broker at " + DEFAULT_BROKER_NAME + "."); + System.out.println ("The application will stage messages to the " + APP_TOPIC + " topic until you either commit them or roll them back."); + System.out.println ("The application also subscribes to that topic to consume any committed messages published there.\n"); + System.out.println ("1. Enter text to publish and then press Enter to stage the message."); + System.out.println ("2. Add a few messages to the transaction batch."); + System.out.println ("3. Then, either:"); + System.out.println (" o Enter the text 'COMMIT', and press Enter to publish all the staged messages."); + System.out.println (" o Enter the text 'CANCEL', and press Enter to drop the staged messages waiting to be sent."); + showMessage = false; + } + String s = stdin.readLine(); + + if ( s == null ) + exit(); + else if (s.trim().equals("CANCEL")) + { + // Rollback the messages. A new transaction is implicitly + // started for following messages. + System.out.println ("Cancelling messages..."); + publishSession.rollback(); + System.out.println ("Staged messages have been cleared."); + showMessage = false; // don't show the help message again. + } + else if ( s.length() > 0 ) + // See if we should send the messages + if (s.trim().equals("COMMIT")) + { + // Commit (send) the messages. A new transaction is + // implicitly started for following messages. + System.out.println ("Committing messages... "); + publishSession.commit(); + System.out.println ("Staged messages have all been sent."); + showMessage = false; // dont't show the help message again. + } + else + { + javax.jms.TextMessage msg = publishSession.createTextMessage(); + msg.setText( username + ": " + s ); + // Publish the message persistently + publisher.send( msg ); + } + } + } + catch ( java.io.IOException ioe ) + { + ioe.printStackTrace(); + } + catch ( javax.jms.JMSException jmse ) + { + jmse.printStackTrace(); + } + } + + /** + * Handle the message + * (as specified in the javax.jms.MessageListener interface). + */ + public void onMessage( javax.jms.Message aMessage) + { + try + { + // Cast the message as a text message. + javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage; + + // This handler reads a single String from the + // message and prints it to the standard output. + try + { + String string = textMessage.getText(); + System.out.println( string ); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + } + catch (java.lang.RuntimeException rte) + { + rte.printStackTrace(); + } + } + + /** Cleanup resources cleanly and exit. */ + private void exit() + { + try + { + publishSession.rollback(); // Rollback any uncommitted messages. + connect.close(); + } + catch (javax.jms.JMSException jmse) + { + jmse.printStackTrace(); + } + + System.exit(0); + } + + // + // NOTE: the remainder of this sample deals with reading arguments + // and does not utilize any JMS classes or code. + // + + /** Main program entry point. */ + public static void main(String argv[]) { + + // Is there anything to do? + if (argv.length == 0) { + printUsage(); + System.exit(1); + } + + // Values to be read from parameters + String broker = DEFAULT_BROKER_NAME; + String username = null; + String password = DEFAULT_PASSWORD; + + // Check parameters + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + + + if (arg.equals("-b")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing broker name:port"); + System.exit(1); + } + broker = argv[++i]; + continue; + } + + if (arg.equals("-u")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing user name"); + System.exit(1); + } + username = argv[++i]; + continue; + } + + if (arg.equals("-p")) { + if (i == argv.length - 1 || argv[i+1].startsWith("-")) { + System.err.println("error: missing password"); + System.exit(1); + } + password = argv[++i]; + continue; + } + + if (arg.equals("-h")) { + printUsage(); + System.exit(1); + } + + // Invalid argument + System.err.println ("error: unexpected argument: "+arg); + printUsage(); + System.exit(1); + } + + // Check values read in. + if (username == null) { + System.err.println ("error: user name must be supplied"); + printUsage(); + } + + // Start the JMS client for the "chat". + TransactedChat chat = new TransactedChat(); + chat.chatter (broker, username, password); + + } + + /** Prints the usage. */ + private static void printUsage() { + + StringBuffer use = new StringBuffer(); + use.append("usage: java TransactedChat (options) ...\n\n"); + use.append("options:\n"); + use.append(" -b name:port Specify name:port of broker.\n"); + use.append(" Default broker: "+DEFAULT_BROKER_NAME+"\n"); + use.append(" -u name Specify unique user name. (Required)\n"); + use.append(" -p password Specify password for user.\n"); + use.append(" Default password: "+DEFAULT_PASSWORD+"\n"); + use.append(" -h This help screen.\n"); + System.err.println (use); + } +} + diff --git a/assembly/src/release/examples/openwire/exploring-jms/build.xml b/assembly/src/release/examples/openwire/exploring-jms/build.xml index 9318351d07..c546a9cf15 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/build.xml +++ b/assembly/src/release/examples/openwire/exploring-jms/build.xml @@ -1,334 +1,334 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assembly/src/release/examples/openwire/exploring-jms/conf/log4j.properties b/assembly/src/release/examples/openwire/exploring-jms/conf/log4j.properties index e5326c5254..ac07489134 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/conf/log4j.properties +++ b/assembly/src/release/examples/openwire/exploring-jms/conf/log4j.properties @@ -1,39 +1,39 @@ -## --------------------------------------------------------------------------- -## 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. -## --------------------------------------------------------------------------- - -# -# The logging properties used by the standalone ActiveMQ broker -# -log4j.rootLogger=INFO, stdout, logfile - -# CONSOLE appender -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss} %-5p %m%n - -# Log File appender -log4j.appender.logfile=org.apache.log4j.FileAppender -log4j.appender.logfile.layout=org.apache.log4j.PatternLayout -log4j.appender.logfile.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.logfile.file=activemq.log -log4j.appender.logfile.append=true - -# -# You can change logger levels here. -# -log4j.logger.org.apache.activemq=INFO -log4j.logger.org.apache.activemq.spring=WARN +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +# +# The logging properties used by the standalone ActiveMQ broker +# +log4j.rootLogger=INFO, stdout, logfile + +# CONSOLE appender +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss} %-5p %m%n + +# Log File appender +log4j.appender.logfile=org.apache.log4j.FileAppender +log4j.appender.logfile.layout=org.apache.log4j.PatternLayout +log4j.appender.logfile.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +log4j.appender.logfile.file=activemq.log +log4j.appender.logfile.append=true + +# +# You can change logger levels here. +# +log4j.logger.org.apache.activemq=INFO +log4j.logger.org.apache.activemq.spring=WARN diff --git a/assembly/src/release/examples/openwire/exploring-jms/readme.txt b/assembly/src/release/examples/openwire/exploring-jms/readme.txt index 04f3a60dd8..c34951f9de 100644 --- a/assembly/src/release/examples/openwire/exploring-jms/readme.txt +++ b/assembly/src/release/examples/openwire/exploring-jms/readme.txt @@ -1,7 +1,7 @@ -## Overview - -This is an example that demonstrates how to use the JMS client APIs to -accomplish basic messaging tasks. - -The build.xml file provides targets for rebuilding the examples and for -launching them. +## Overview + +This is an example that demonstrates how to use the JMS client APIs to +accomplish basic messaging tasks. + +The build.xml file provides targets for rebuilding the examples and for +launching them. diff --git a/assembly/src/release/examples/openwire/swissarmy/src/CommandLineSupport.java b/assembly/src/release/examples/openwire/swissarmy/src/CommandLineSupport.java index cbde8a4973..62b1de2178 100644 --- a/assembly/src/release/examples/openwire/swissarmy/src/CommandLineSupport.java +++ b/assembly/src/release/examples/openwire/swissarmy/src/CommandLineSupport.java @@ -1,120 +1,120 @@ -/** - * 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. - */ - -import java.util.ArrayList; - -import org.apache.activemq.util.IntrospectionSupport; - -/** - * Helper utility that can be used to set the properties on any object using - * command line arguments. - * - * @author Hiram Chirino - */ -public final class CommandLineSupport { - - private CommandLineSupport() { - } - - /** - * Sets the properties of an object given the command line args. - * - * if args contains: --ack-mode=AUTO --url=tcp://localhost:61616 --persistent - * - * then it will try to call the following setters on the target object. - * - * target.setAckMode("AUTO"); - * target.setURL(new URI("tcp://localhost:61616") ); - * target.setPersistent(true); - * - * Notice the the proper conversion for the argument is determined by examining the - * setter arguement type. - * - * @param target the object that will have it's properties set - * @param args the commline options - * @return any arguments that are not valid options for the target - */ - public static String[] setOptions(Object target, String[] args) { - ArrayList rc = new ArrayList(); - - for (int i = 0; i < args.length; i++) { - if (args[i] == null) { - continue; - } - - if (args[i].startsWith("--")) { - - // --options without a specified value are considered boolean - // flags that are enabled. - String value = "true"; - String name = args[i].substring(2); - - // if --option=value case - int p = name.indexOf("="); - if (p > 0) { - value = name.substring(p + 1); - name = name.substring(0, p); - } - - // name not set, then it's an unrecognized option - if (name.length() == 0) { - rc.add(args[i]); - continue; - } - - String propName = convertOptionToPropertyName(name); - if (!IntrospectionSupport.setProperty(target, propName, value)) { - rc.add(args[i]); - continue; - } - } - - } - - String r[] = new String[rc.size()]; - rc.toArray(r); - return r; - } - - /** - * converts strings like: test-enabled to testEnabled - * - * @param name - * @return - */ - private static String convertOptionToPropertyName(String name) { - String rc = ""; - - // Look for '-' and strip and then convert the subsequent char to - // uppercase - int p = name.indexOf("-"); - while (p > 0) { - // strip - rc += name.substring(0, p); - name = name.substring(p + 1); - - // can I convert the next char to upper? - if (name.length() > 0) { - rc += name.substring(0, 1).toUpperCase(); - name = name.substring(1); - } - - p = name.indexOf("-"); - } - return rc + name; - } -} +/** + * 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. + */ + +import java.util.ArrayList; + +import org.apache.activemq.util.IntrospectionSupport; + +/** + * Helper utility that can be used to set the properties on any object using + * command line arguments. + * + * @author Hiram Chirino + */ +public final class CommandLineSupport { + + private CommandLineSupport() { + } + + /** + * Sets the properties of an object given the command line args. + * + * if args contains: --ack-mode=AUTO --url=tcp://localhost:61616 --persistent + * + * then it will try to call the following setters on the target object. + * + * target.setAckMode("AUTO"); + * target.setURL(new URI("tcp://localhost:61616") ); + * target.setPersistent(true); + * + * Notice the the proper conversion for the argument is determined by examining the + * setter arguement type. + * + * @param target the object that will have it's properties set + * @param args the commline options + * @return any arguments that are not valid options for the target + */ + public static String[] setOptions(Object target, String[] args) { + ArrayList rc = new ArrayList(); + + for (int i = 0; i < args.length; i++) { + if (args[i] == null) { + continue; + } + + if (args[i].startsWith("--")) { + + // --options without a specified value are considered boolean + // flags that are enabled. + String value = "true"; + String name = args[i].substring(2); + + // if --option=value case + int p = name.indexOf("="); + if (p > 0) { + value = name.substring(p + 1); + name = name.substring(0, p); + } + + // name not set, then it's an unrecognized option + if (name.length() == 0) { + rc.add(args[i]); + continue; + } + + String propName = convertOptionToPropertyName(name); + if (!IntrospectionSupport.setProperty(target, propName, value)) { + rc.add(args[i]); + continue; + } + } + + } + + String r[] = new String[rc.size()]; + rc.toArray(r); + return r; + } + + /** + * converts strings like: test-enabled to testEnabled + * + * @param name + * @return + */ + private static String convertOptionToPropertyName(String name) { + String rc = ""; + + // Look for '-' and strip and then convert the subsequent char to + // uppercase + int p = name.indexOf("-"); + while (p > 0) { + // strip + rc += name.substring(0, p); + name = name.substring(p + 1); + + // can I convert the next char to upper? + if (name.length() > 0) { + rc += name.substring(0, 1).toUpperCase(); + name = name.substring(1); + } + + p = name.indexOf("-"); + } + return rc + name; + } +} diff --git a/assembly/src/release/examples/stomp/csharp/ActiveMQExamples/ActiveMQExamples.sln b/assembly/src/release/examples/stomp/csharp/ActiveMQExamples/ActiveMQExamples.sln index ade0d240e3..f9a94a4921 100644 --- a/assembly/src/release/examples/stomp/csharp/ActiveMQExamples/ActiveMQExamples.sln +++ b/assembly/src/release/examples/stomp/csharp/ActiveMQExamples/ActiveMQExamples.sln @@ -1,26 +1,26 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Publisher", "Publisher\Publisher.csproj", "{0FEEC122-F3FD-4148-A461-0A724AE0C691}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Listener", "Listener\Listener.csproj", "{08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Debug|x86.ActiveCfg = Debug|x86 - {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Debug|x86.Build.0 = Debug|x86 - {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Release|x86.ActiveCfg = Release|x86 - {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Release|x86.Build.0 = Release|x86 - {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Debug|x86.ActiveCfg = Debug|x86 - {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Debug|x86.Build.0 = Debug|x86 - {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Release|x86.ActiveCfg = Release|x86 - {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = Listener\Listener.csproj - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Publisher", "Publisher\Publisher.csproj", "{0FEEC122-F3FD-4148-A461-0A724AE0C691}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Listener", "Listener\Listener.csproj", "{08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Debug|x86.ActiveCfg = Debug|x86 + {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Debug|x86.Build.0 = Debug|x86 + {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Release|x86.ActiveCfg = Release|x86 + {08413D64-4C72-4F92-9B4A-9BAECCDB6DC3}.Release|x86.Build.0 = Release|x86 + {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Debug|x86.ActiveCfg = Debug|x86 + {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Debug|x86.Build.0 = Debug|x86 + {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Release|x86.ActiveCfg = Release|x86 + {0FEEC122-F3FD-4148-A461-0A724AE0C691}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = Listener\Listener.csproj + EndGlobalSection +EndGlobal