
com.sun.messaging.jmq.admin.jmsspi.JMSAdminImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2000, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2022 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.messaging.jmq.admin.jmsspi;
import static java.lang.System.Logger.Level.DEBUG;
import static java.lang.System.Logger.Level.ERROR;
import static java.lang.System.Logger.Level.INFO;
import static java.lang.System.Logger.Level.WARNING;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import jakarta.jms.Connection;
import jakarta.jms.DeliveryMode;
import jakarta.jms.ExceptionListener;
import jakarta.jms.InvalidSelectorException;
import jakarta.jms.JMSException;
import jakarta.jms.Message;
import jakarta.jms.ObjectMessage;
import jakarta.jms.Queue;
import jakarta.jms.QueueConnection;
import jakarta.jms.QueueReceiver;
import jakarta.jms.QueueSender;
import jakarta.jms.QueueSession;
import jakarta.jms.Session;
import jakarta.jms.TemporaryQueue;
import com.sun.messaging.AdministeredObject;
import com.sun.messaging.ConnectionConfiguration;
import com.sun.messaging.DestinationConfiguration;
import com.sun.messaging.QueueConnectionFactory;
import com.sun.messaging.jmq.ClientConstants;
import com.sun.messaging.jmq.Version;
import com.sun.messaging.jmq.admin.apps.broker.BrokerCmdOptions;
import com.sun.messaging.jmq.admin.bkrutil.BrokerConstants;
import com.sun.messaging.jmq.admin.resources.AdminResources;
import com.sun.messaging.jmq.admin.util.Globals;
import com.sun.messaging.jmq.admin.util.JMSObjFactory;
import com.sun.messaging.jmq.jmsclient.ConnectionImpl;
import com.sun.messaging.jmq.jmsclient.JMSXAWrappedConnectionFactoryImpl;
import com.sun.messaging.jmq.jmsspi.JMSAdmin;
import com.sun.messaging.jmq.jmsspi.PropertiesHolder;
import com.sun.messaging.jmq.util.BrokerExitCode;
import com.sun.messaging.jmq.util.DestType;
import com.sun.messaging.jmq.util.admin.DestinationInfo;
import com.sun.messaging.jmq.util.admin.MessageType;
public class JMSAdminImpl implements JMSAdmin, ExceptionListener {
private static AdminResources ar = Globals.getAdminResources();
public static final long DEFAULT_TIMEOUT = 5000;
private String adminuserName = null;
private String adminPassword = null;
private long timeout = -1;
private QueueConnectionFactory qcf;
private QueueConnection connection;
private QueueSession session;
private Queue requestQueue;
private TemporaryQueue replyQueue;
private QueueSender sender;
protected QueueReceiver receiver;
private AdministeredObject tmpDestination = null;
private AdministeredObject tmpConnFactory = null;
private boolean secureTransportUsed = false;
// When this class is used to start a broker, these properties are passed to the broker
private PropertiesHolder brokerPropertiesHolder;
protected static final System.Logger logger = System.getLogger(JMSAdminImpl.class.getName());
/*
* This class should only be instantiated via JMSAdminFactoryImpl. REVISIT: hard-coded error messages
*/
protected JMSAdminImpl(Properties connectionProps, PropertiesHolder brokerPropertiesHolder, String username, String adminPassword) throws JMSException {
this.timeout = DEFAULT_TIMEOUT;
this.adminuserName = username;
this.adminPassword = adminPassword;
this.brokerPropertiesHolder = brokerPropertiesHolder;
createFactory(connectionProps); // init the qcf !!
String connType = connectionProps.getProperty(ConnectionConfiguration.imqConnectionType, "");
if (connType.equals("TLS") || connType.equals("SSL")) {
secureTransportUsed = true;
}
}
/**
* Return the SPI version
*/
@Override
public String getVersion() {
Version version = new Version();
return (version.getJMSAdminSpiVersion());
}
/**
* Create a ConnectionFactory administered object
*
* @param type Either QUEUE or TOPIC.
* @param properties Connection specific properties.
* @return New created ConnectionFactory administered object.
* @exception JMSException thrown if connectionFactory could not be created.
*/
@Override
public Object createConnectionFactoryObject(int type, java.util.Map properties) throws JMSException {
Object cf = null;
Properties tmpProps = getProperties(properties);
fillDefaultConnectionFactoryProperties(tmpProps);
if (type == TOPIC) {
cf = JMSObjFactory.createTopicConnectionFactory(tmpProps);
} else if (type == QUEUE) {
cf = JMSObjFactory.createQueueConnectionFactory(tmpProps);
} else {
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_INVALID_DOMAIN_TYPE));
}
return cf;
}
/**
* Create a XAConnectionFactory administered object
*
* @param type Either QUEUE or TOPIC.
* @param properties Connection specific properties.
* @return New created JMSXAConnectionFactory administered object. ^^^^^
* @exception JMSException thrown if XAConnectionFactory could not be created.
*/
@Override
public Object createXAConnectionFactoryObject(int type, java.util.Map properties) throws JMSException {
Object xcf = null;
Properties tmpProps = getProperties(properties);
fillDefaultConnectionFactoryProperties(tmpProps);
if (type == TOPIC) {
xcf = new com.sun.messaging.jmq.jmsclient.JMSXATopicConnectionFactoryImpl();
setProperties((AdministeredObject) xcf, tmpProps);
} else if (type == QUEUE) {
xcf = new com.sun.messaging.jmq.jmsclient.JMSXAQueueConnectionFactoryImpl();
setProperties((AdministeredObject) xcf, tmpProps);
} else {
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_INVALID_DOMAIN_TYPE));
}
return xcf;
}
/**
* Create a Destination administered object
*
* @param destinationName The destination name.
* @param type Either QUEUE or TOPIC.
* @param properties destination specific properties.
* @return New created Destination administered object.
* @exception JMSException thrown if destination object could not be created.
*/
@Override
public Object createDestinationObject(String destinationName, int type, java.util.Map properties) throws JMSException {
Map props = properties;
if (props == null) {
props = new Properties();
}
props.put(DestinationConfiguration.imqDestinationName, destinationName);
return createDestinationObject(type, props);
}
/**
* Create a Destination administered object. The destination name is assumed in properties like imqDestinationName=xxxxx
*
* @param type Either QUEUE or TOPIC.
* @param properties destination specific properties.
* @return New created Destination administered object.
* @exception JMSException thrown if destination object could not be created.
*/
@Override
public Object createDestinationObject(int type, java.util.Map properties) throws JMSException {
if (properties == null || properties.get(DestinationConfiguration.imqDestinationName) == null) {
throw new JMSException(ar.getKString(ar.X_JMSSPI_NO_DESTINATION_NAME));
}
Object dest = null;
Properties tmpProps = getProperties(properties);
if (type == TOPIC) {
dest = JMSObjFactory.createTopic(tmpProps);
} else if (type == QUEUE) {
dest = JMSObjFactory.createQueue(tmpProps);
} else {
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_INVALID_DOMAIN_TYPE));
}
return dest;
}
/**
* Wrap a standard JMS ConnectionFactory administered object
*
* @param obj a XAQueue/TopicConnectionFactory or Queue/TopicConnectionFactory object
* @return a JMSXAConnectionFactory object ^^^^^
* @exception JMSException if fail to wrap
*/
@Override
public Object wrapJMSConnectionFactoryObject(Object obj) throws JMSException {
if (obj instanceof jakarta.jms.XAQueueConnectionFactory) {
return new JMSXAWrappedConnectionFactoryImpl((jakarta.jms.XAQueueConnectionFactory) obj);
}
if (obj instanceof jakarta.jms.XATopicConnectionFactory) {
return new JMSXAWrappedConnectionFactoryImpl((jakarta.jms.XATopicConnectionFactory) obj);
}
if (obj instanceof jakarta.jms.QueueConnectionFactory) {
return new JMSXAWrappedConnectionFactoryImpl((jakarta.jms.QueueConnectionFactory) obj);
}
if (obj instanceof jakarta.jms.TopicConnectionFactory) {
return new JMSXAWrappedConnectionFactoryImpl((jakarta.jms.TopicConnectionFactory) obj);
}
throw new JMSException(ar.getKString(ar.X_JMSSPI_INVALID_OBJECT_TYPE));
}
@Override
public void validateJMSSelector(String selector) throws JMSException {
com.sun.messaging.jmq.jmsselector.JMSSelector jmsselector = new com.sun.messaging.jmq.jmsselector.JMSSelector();
try {
jmsselector.validateSelectorPattern(selector);
} catch (com.sun.messaging.jmq.jmsselector.InvalidJMSSelectorException e) {
throw new InvalidSelectorException(e.getMessage());
} catch (Exception e) {
throw new JMSException(e.getMessage());
}
}
/**
* @return the client-id property name
*/
@Override
public String clientIDPropertyName() {
return ConnectionConfiguration.imqConfiguredClientID;
}
/**
* Returns a map of all the properties that a destination object has.
*
* For each property name returned, a corresponding label that can be used for output display purposes is also returned.
* The Map returned contains the property names and property labels as key-value pairs. This information can be used to
* display more readable output containing property labels and not property names.
*
* @param type Either QUEUE or TOPIC.
* @return Map containing attribute value pairs of property names and their display labels.
*/
@Override
public Map getAllDestinationObjectProperties(int type) throws JMSException {
Properties ret;
if (tmpDestination == null) {
tmpDestination = (AdministeredObject) JMSObjFactory.createTopic(new Properties());
}
ret = new Properties();
for (Enumeration e = tmpDestination.enumeratePropertyNames(); e.hasMoreElements();) {
String propName = (String) e.nextElement();
String propLabel = tmpDestination.getPropertyLabel(propName);
ret.setProperty(propName, propLabel);
}
return (ret);
}
/**
* Returns a subset of all the properties that a destination object has.
*
* This collection of properties can be used to construct a user interface where not all of the object's properties will
* be displayed, and only a selected few (more important) ones are.
*
* For each property name returned, a corresponding label that can be used for output display purposes is also returned.
* The Map returned contains the property names and property labels as key-value pairs. This information can be used to
* display more readable output containing property labels and not property names.
*
* @param type Either QUEUE or TOPIC.
* @return Map containing attribute value pairs of property names and their display labels.
*/
@Override
public Map getDisplayedDestinationObjectProperties(int type) throws JMSException {
Properties ret;
String propName, propLabel;
if (tmpDestination == null) {
tmpDestination = (AdministeredObject) JMSObjFactory.createTopic(new Properties());
}
ret = new Properties();
propName = DestinationConfiguration.imqDestinationName;
propLabel = tmpDestination.getPropertyLabel(propName);
ret.setProperty(propName, propLabel);
return (ret);
}
/**
* Returns a map of all the properties that a connection factory object has.
*
* For each property name returned, a corresponding label that can be used for output display purposes is also returned.
* The Map returned contains the property names and property labels as key-value pairs. This information can be used to
* display more readable output containing property labels and not property names.
*
* @param type Either QUEUE or TOPIC.
* @return Map containing attribute value pairs of property names and their display labels.
*/
@Override
public Map getAllConnectionFactoryObjectProperies(int type) throws JMSException {
Properties ret;
if (tmpConnFactory == null) {
tmpConnFactory = (AdministeredObject) JMSObjFactory.createTopicConnectionFactory(new Properties());
}
ret = new Properties();
for (Enumeration e = tmpConnFactory.enumeratePropertyNames(); e.hasMoreElements();) {
String propName = (String) e.nextElement();
String propLabel = tmpConnFactory.getPropertyLabel(propName);
ret.setProperty(propName, propLabel);
}
return (ret);
}
/**
* Returns a subset of all the properties that a connection factory object has.
*
* This collection of properties can be used to construct a user interface where not all of the object's properties will
* be displayed, and only a selected few (more important) ones are.
*
* For each property name returned, a corresponding label that can be used for output display purposes is also returned.
* The Map returned contains the property names and property labels as key-value pairs. This information can be used to
* display more readable output containing property labels and not property names.
*
* @param type Either QUEUE or TOPIC.
* @return Map containing attribute value pairs of property names and their display labels.
*/
@Override
public Map getDisplayedConnectionFactoryObjectProperies(int type) throws JMSException {
Properties ret;
String propName, propLabel;
if (tmpConnFactory == null) {
tmpConnFactory = (AdministeredObject) JMSObjFactory.createTopicConnectionFactory(new Properties());
}
ret = new Properties();
propName = ConnectionConfiguration.imqBrokerHostName;
propLabel = tmpConnFactory.getPropertyLabel(propName);
ret.setProperty(propName, propLabel);
propName = ConnectionConfiguration.imqBrokerHostPort;
propLabel = tmpConnFactory.getPropertyLabel(propName);
ret.setProperty(propName, propLabel);
propName = ConnectionConfiguration.imqConfiguredClientID;
propLabel = tmpConnFactory.getPropertyLabel(propName);
ret.setProperty(propName, propLabel);
return (ret);
}
/**
* Connect to the provider.
*
* @exception JMSException thrown if connection to the provider cannot be established or if an error occurs
*/
@Override
public void connectToProvider() throws JMSException {
connection = qcf.createQueueConnection(adminuserName, adminPassword);
connection.setExceptionListener(this);
connection.start();
session = connection.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
requestQueue = session.createQueue(MessageType.JMQ_ADMIN_DEST);
replyQueue = session.createTemporaryQueue();
sender = session.createSender(requestQueue);
sender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
receiver = session.createReceiver(replyQueue);
hello();
}
/**
* Disconnect from the provider.
*
* @exception JMSException thrown if connection to the provider cannot be closed or ir an error occurs
*/
@Override
public void disconnectFromProvider() throws JMSException {
sender.close();
receiver.close();
session.close();
try {
connection.close();
} catch (JMSException jmse) {
if (!secureTransportUsed) {
throw (jmse);
}
}
}
/**
* Create a physical Destination within the JMS Provider using the provided properties to define provider specific
* attributes. Destination is not automatically bound into JNDI namespace.
*
* @param destinationType QUEUE or TOPIC
* @param properties creation properties.
* @exception JMSException thrown if Queue could not be created.
*/
@Override
public void createProviderDestination(String destinationName, int destinationType, java.util.Map properties) throws JMSException {
ObjectMessage requestMesg = null;
Message replyMesg = null;
// Create DestinationInfo.
DestinationInfo di = new DestinationInfo();
di.setName(destinationName);
// REVISIT:
// We only check for queueDeliveryPolicy in the properties
// hard-coded string; left here because we are not sure
// if we are going to expose this attribute yet
String qPolicy = null;
/*
* Early initialization to be used in error messages
*/
int typeMask = this.getDestTypeMask(destinationType, null);
if (properties != null) {
if (properties.containsKey("queueDeliveryPolicy")) {
qPolicy = (String) properties.get("queueDeliveryPolicy");
typeMask = this.getDestTypeMask(destinationType, qPolicy);
}
if (properties.containsKey(BrokerCmdOptions.PROP_NAME_MAX_ACTIVE_CONSUMER_COUNT)) {
Object tmp = null;
String val = null;
tmp = properties.get(BrokerCmdOptions.PROP_NAME_MAX_ACTIVE_CONSUMER_COUNT);
if (tmp != null) {
if (!(tmp instanceof String)) {
JMSException jmse;
jmse = new JMSException(ar.getString(ar.E_SPI_DEST_CREATION_FAILED, DestType.toString(typeMask), destinationName) + "\n"
+ ar.getString(ar.E_SPI_ATTR_TYPE_NOT_STRING, BrokerCmdOptions.PROP_NAME_MAX_ACTIVE_CONSUMER_COUNT));
throw jmse;
}
val = (String) tmp;
try {
di.setMaxActiveConsumers(Integer.parseInt(val));
} catch (Exception e) {
JMSException jmse;
jmse = new JMSException(ar.getString(ar.E_SPI_DEST_CREATION_FAILED, DestType.toString(typeMask), destinationName) + "\n"
+ ar.getString(ar.E_INVALID_INTEGER_VALUE, val, BrokerCmdOptions.PROP_NAME_MAX_ACTIVE_CONSUMER_COUNT));
throw (jmse);
}
}
}
}
di.setType(typeMask);
requestMesg = session.createObjectMessage();
requestMesg.setJMSReplyTo(replyQueue);
requestMesg.setIntProperty(MessageType.JMQ_MESSAGE_TYPE, MessageType.CREATE_DESTINATION);
requestMesg.setObject(di);
sender.send(requestMesg);
replyMesg = receiver.receive(timeout);
replyMesg.acknowledge();
checkReplyTypeStatus(replyMesg, MessageType.CREATE_DESTINATION_REPLY);
}
/**
* Delete a physical destination within the JMS Provider.
*
* @param type Either QUEUE or TOPIC.
* @exception JMSException thrown if Queue could not be deleted.
*/
@Override
public void deleteProviderDestination(String destinationName, int type) throws JMSException {
ObjectMessage requestMesg = null;
Message replyMesg = null;
requestMesg = session.createObjectMessage();
requestMesg.setJMSReplyTo(replyQueue);
requestMesg.setIntProperty(MessageType.JMQ_MESSAGE_TYPE, MessageType.DESTROY_DESTINATION);
requestMesg.setStringProperty(MessageType.JMQ_DESTINATION, destinationName);
requestMesg.setIntProperty(MessageType.JMQ_DEST_TYPE, this.getDestTypeMask(type, null));
sender.send(requestMesg);
replyMesg = receiver.receive(timeout);
replyMesg.acknowledge();
checkReplyTypeStatus(replyMesg, MessageType.DESTROY_DESTINATION_REPLY);
}
/**
* Get all provider destinations.
*
* @return A multi dimensional array containing information about the JMS destinations. array[0] is a String[] listing
* the destination names. array[1] is a String[] listing the destination types.
* @exception JMSException thrown if array could not be obtained.
*/
@Override
public String[][] getProviderDestinations() throws JMSException {
ObjectMessage mesg = null;
mesg = session.createObjectMessage();
mesg.setJMSReplyTo(replyQueue);
mesg.setIntProperty(MessageType.JMQ_MESSAGE_TYPE, MessageType.GET_DESTINATIONS);
sender.send(mesg);
mesg = (ObjectMessage) receiver.receive(timeout);
mesg.acknowledge();
checkReplyTypeStatus(mesg, MessageType.GET_DESTINATIONS_REPLY);
Object obj;
if ((obj = mesg.getObject()) != null) {
if (obj instanceof Vector) {
Vector dests = (Vector) obj;
/*
* Remove temporary, internal and admin destinations by creating a new Vector and adding all the valid destinations into
* this new Vector.
*/
Vector validDests = new Vector();
for (int i = 0; i < dests.size(); i++) {
DestinationInfo di = (DestinationInfo) dests.elementAt(i);
if ((!MessageType.JMQ_ADMIN_DEST.equals(di.name)) && (!MessageType.JMQ_BRIDGE_ADMIN_DEST.equals(di.name))
&& (!DestType.isInternal(di.fulltype)) && (!DestType.isTemporary(di.type))) {
validDests.add(di);
}
}
int numElements = validDests.size();
String[][] destinations = new String[2][numElements];
for (int i = 0; i < numElements; i++) {
DestinationInfo di = (DestinationInfo) validDests.get(i);
destinations[0][i] = di.name;
destinations[1][i] = this.getDestinationType(di.type);
}
return destinations;
}
}
return null;
}
/**
* Start the provider.
*
* @param iMQHome Location of the broker executable.
* @param optArgs Array of optional broker command line arguments.
* @param serverName Instance name of the server.
* @exception IOException thrown if the server startup fails.
*/
@Override
public void startProvider(String iMQHome, String optArgs[], String serverName) throws IOException, JMSException {
String[] cmdLine = makeStartProviderCmdLine(iMQHome, optArgs, serverName, false);
// Log the command line we are about to execute
if (logger.isLoggable(DEBUG)) {
String commandLineToLog = "";
for (int i = 0; i < cmdLine.length; i++) {
commandLineToLog = commandLineToLog + cmdLine[i] + " ";
}
logger.log(DEBUG, "MQJMSRA: Starting LOCAL broker with command line: " + commandLineToLog);
}
launchAndWatch(cmdLine);
}
/**
* @param iMQHome Location of the broker executable, ignored if argsOnly true
*/
@Override
public String[] makeStartProviderCmdLine(String iMQHome, String optArgs[], String serverName, boolean argsOnly) throws IOException, JMSException {
Vector v = new Vector();
String append = null;
if (!argsOnly) {
String iMQBrokerPath;
if (java.io.File.separator.equals("\\")) {
// Windows path
// \\imqbrokersvc.exe -console
iMQBrokerPath = iMQHome + java.io.File.separator + "imqbrokersvc.exe";
append = "-console";
} else {
// Unix path.
// /bin/imqbrokerd
iMQBrokerPath = iMQHome + java.io.File.separator + "imqbrokerd";
}
v.add(iMQBrokerPath);
}
String portStr = qcf.getProperty(ConnectionConfiguration.imqBrokerHostPort);
// Important : Do not change the command line parameter order
// unless you know what you are doing. Here are the
// constraints -
//
// 1. The "-javahome" argument, if present, must be first.
// Otherwise it does not work on Windows. The caller (of
// this method) is responsible for making sure that the
// "-javahome" argument is first in the 'optArgs' string.
//
// 2. The -name, -port and -silent arguments must be at
// placed at the end, so that they override anything
// specified in the optArgs...
//
// 3. The -bgnd argument is required for proper signal
// handling on solaris.
//
if (optArgs != null) {
for (int i = 0; i < optArgs.length; i++) {
v.add(optArgs[i]);
}
}
if (serverName != null) {
v.add("-name");
v.add(serverName);
}
if (append != null) {
v.add(append);
}
v.add("-port");
v.add(portStr);
// CR 6944162 Disable use of -bgnd (used on Solaris)
// because it prevents passwords being passed to the broker via standard input
// v.add("-bgnd");
// disable use of -errorwarn if DEBUG logging is enabled
if (!logger.isLoggable(DEBUG)) {
// normally we only want to see errors in stdout
v.add("-ttyerrors");
}
if (!argsOnly) {
v.add("-managed");
v.add("-read-stdin");
}
return (String[]) v.toArray(new String[0]);
}
private void launchAndWatch(String[] cmdLine) throws IOException {
Process p = Runtime.getRuntime().exec(cmdLine);
BrokerWatcher brokerWatcher = new BrokerWatcher(p, cmdLine);
brokerWatcher.setDaemon(true);
brokerWatcher.start();
// start a thread to log any error messages from the new broker as a WARN message
boolean isError = true;
StreamGobbler errorGobbler = new StreamGobbler(p.getErrorStream(), isError, "Local broker: ");
errorGobbler.setDaemon(true);
errorGobbler.start();
// start a thread to log any output messages from the new broker as an INFO message
isError = false;
StreamGobbler outputGobbler = new StreamGobbler(p.getInputStream(), isError, "Local broker: ");
outputGobbler.setDaemon(true);
outputGobbler.start();
// generate the broker properties afresh
Properties brokerProps = brokerPropertiesHolder.getProperties();
// write properties to the input stream of the new process
try {
OutputStreamWriter osw = new OutputStreamWriter(p.getOutputStream());
BufferedWriter bw = new BufferedWriter(osw);
for (Enumeration e = brokerProps.propertyNames(); e.hasMoreElements();) {
String thisPropertyName = (String) e.nextElement();
String thisPropertyValue = brokerProps.getProperty(thisPropertyName);
bw.write(thisPropertyName + "=" + thisPropertyValue);
bw.newLine();
}
if (this.adminPassword != null) {
// if set, override any admin password set via brokerProps
bw.write("imq.imqcmd.password=" + this.adminPassword);
bw.newLine();
}
bw.close();
} catch (IOException ioe) {
logger.log(ERROR, ioe.getMessage(), ioe);
}
}
/**
* Ping the provider.
*
* @exception JMSException thrown if ping fails
*/
@Override
public void pingProvider() throws JMSException {
// temporarily turn off logging because we do not want to log a message if the provider is not running
// as this might not be an error.
Logger rootLogger = Logger.getLogger(ConnectionImpl.ROOT_LOGGER_NAME);
Level savedLevel = rootLogger.getLevel();
rootLogger.setLevel(Level.SEVERE);
Connection pingConn = null;
try {
pingConn = qcf.createQueueConnection(adminuserName, adminPassword);
} finally {
// re-enable logging logging
rootLogger.setLevel(savedLevel);
}
try {
pingConn.setExceptionListener(this);
pingConn.start();
} finally {
try {
pingConn.close();
} catch (JMSException jmse) {
if (!secureTransportUsed) {
throw (jmse);
}
}
}
}
/**
* Get the property value from a provider
*
* @exception JMSException thrown if the get fails.
*/
private String getProviderPropertyValue(String propName) throws JMSException {
ObjectMessage requestMesg = null;
ObjectMessage replyMesg = null;
String propVal = null;
requestMesg = session.createObjectMessage();
requestMesg.setJMSReplyTo(replyQueue);
requestMesg.setIntProperty(MessageType.JMQ_MESSAGE_TYPE, MessageType.GET_BROKER_PROPS);
sender.send(requestMesg);
replyMesg = (ObjectMessage) receiver.receive(timeout);
replyMesg.acknowledge();
checkReplyTypeStatus(replyMesg, MessageType.GET_BROKER_PROPS_REPLY);
Object obj;
if ((obj = replyMesg.getObject()) != null) {
if (obj instanceof Properties) {
Properties props = (Properties) obj;
propVal = props.getProperty(propName);
}
}
return (propVal);
}
/**
* Get the instance name of the provider
*
* @exception JMSException thrown if the get fails.
*/
@Override
public String getProviderInstanceName() throws JMSException {
return (getProviderPropertyValue(BrokerConstants.PROP_NAME_BKR_INSTANCE_NAME));
}
/**
* Get the VARHOME directory of the provider
*
* @exception JMSException thrown if the get fails.
*/
@Override
public String getProviderVarHome() throws JMSException {
return (getProviderPropertyValue(BrokerConstants.PROP_NAME_BKR_VARHOME));
}
/**
* Shutdown the provider. @exception JMSException thrown if the shutdown fails @throws
*/
@Override
public void shutdownProvider() throws JMSException {
try {
tryToShutdownProvider();
} catch (JMSException jmse) {
if ((com.sun.messaging.jmq.jmsclient.resources.ClientResources.X_CONSUMER_CLOSED.equals(jmse.getErrorCode()))
|| (com.sun.messaging.jmq.jmsclient.resources.ClientResources.X_SESSION_CLOSED.equals(jmse.getErrorCode()))) {
// we seem to have lost the connection - try once to reconnect before giving up
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
connectToProvider();
tryToShutdownProvider();
} else {
// re-throw the exception if not X_CONSUMER_CLOSED or X_SESSION_CLOSED
throw jmse;
}
}
}
private void tryToShutdownProvider() throws JMSException {
ObjectMessage requestMesg;
Message replyMesg;
requestMesg = session.createObjectMessage();
requestMesg.setJMSReplyTo(replyQueue);
requestMesg.setIntProperty(MessageType.JMQ_MESSAGE_TYPE, MessageType.SHUTDOWN);
sender.send(requestMesg);
// disable reconnect since we don't want the client to try to reconnect
// ConnectionImpl ci = (ConnectionImpl)connection;
// ci.setImqReconnect(false);
replyMesg = receiver.receive(timeout);
checkReplyTypeStatus(replyMesg, MessageType.SHUTDOWN_REPLY);
}
/**
* Restart the provider.
*
* @exception JMSException thrown if the restart fails
*/
@Override
public void restartProvider() throws JMSException {
ObjectMessage requestMesg = null;
Message replyMesg = null;
requestMesg = session.createObjectMessage();
requestMesg.setJMSReplyTo(replyQueue);
requestMesg.setIntProperty(MessageType.JMQ_MESSAGE_TYPE, MessageType.SHUTDOWN);
requestMesg.setBooleanProperty(MessageType.JMQ_RESTART, true);
sender.send(requestMesg);
replyMesg = receiver.receive(timeout);
checkReplyTypeStatus(replyMesg, MessageType.SHUTDOWN_REPLY);
}
/**
* Return the provider host name
*/
@Override
public String getProviderHostName() throws JMSException {
return qcf.getProperty(ConnectionConfiguration.imqBrokerHostName);
}
/**
* Return the provider host port number
*/
@Override
public String getProviderHostPort() throws JMSException {
return qcf.getProperty(ConnectionConfiguration.imqBrokerHostPort);
}
/**
* Delete provider instance.
*
* @param mqBinDir Location of MQ's bin directory, which contains the broker executable.
* @param optArgs Optional broker command line arguments.
* @param serverName instance name of MQ broker to delete
* @exception JMSException thrown if the delete fails.
*/
@Override
public void deleteProviderInstance(String mqBinDir, String optArgs, String serverName) throws IOException, JMSException {
String iMQBrokerPath;
int exitCode = 0;
boolean interrupted = false;
if (java.io.File.separator.equals("\\")) {
// Windows path
// \\bin\\imqbrokerd.exe
iMQBrokerPath = mqBinDir + java.io.File.separator + "imqbrokerd.exe";
} else {
// Unix path.
// /bin/imqbrokerd
iMQBrokerPath = mqBinDir + java.io.File.separator + "imqbrokerd";
}
// String portStr =
// qcf.getProperty(ConnectionConfiguration.imqBrokerHostPort);
// Important : Do not change the command line parameter order
// unless you know what you are doing. Here are the
// constraints -
//
// 1. The "-javahome" argument, if present, must be first.
// Otherwise it does not work on Windoze. The caller (of
// this method) is responsible for making sure that the
// "-javahome" argument is first in the 'optArgs' string.
//
// 2. The -name, -port and -silent arguments must be at
// placed at the end, so that they override anything
// specified in the optArgs...
//
// 3. The -bgnd argument is required for proper signal
// handling on solaris.
//
String cmdLine = iMQBrokerPath;
if (optArgs != null) {
cmdLine = cmdLine + " " + optArgs;
}
if (serverName != null) {
cmdLine = cmdLine + " -name " + serverName;
}
cmdLine = cmdLine + " -remove instance -silent -force";
Process p = Runtime.getRuntime().exec(cmdLine);
// Close the receiver end of stdout and stderr streams.
// Otherwise the child process blocks while trying to
// write...
p.getInputStream().close();
p.getErrorStream().close();
try {
p.waitFor();
exitCode = p.exitValue();
} catch (InterruptedException iex) {
interrupted = true;
}
if (interrupted) {
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_INT_PREM, serverName));
}
/*
* Proper handling of exit codes will be done once the following bug is fixed: 4713518 - imqbrokerd -remove:
* Enhancements needed for app server integration
*/
switch (exitCode) {
case BrokerExitCode.NORMAL:
break;
case BrokerExitCode.INSTANCE_NOT_EXISTS:
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_NOT_EXIST, serverName));
case BrokerExitCode.INSTANCE_BEING_USED:
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_BEING_USED, serverName));
case BrokerExitCode.NO_PERMISSION_ON_INSTANCE:
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_NO_PERM, serverName));
case BrokerExitCode.PROBLEM_REMOVING_PERSISTENT_STORE:
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_PROB_RM_STORE, serverName));
case BrokerExitCode.IOEXCEPTION:
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_IOEXCEPTION, serverName));
default:
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_UNKNOWN, serverName));
}
}
/**
* Delete provider instance.
*
* @param mqBinDir Location of MQ's bin directory, which contains the broker executable.
* @param optArgs Array of optional broker command line arguments.
* @param serverName instance name of MQ broker to delete
* @exception JMSException thrown if the delete fails.
*/
@Override
public void deleteProviderInstance(String mqBinDir, String optArgs[], String serverName) throws IOException, JMSException {
String iMQBrokerPath;
int exitCode = 0;
boolean interrupted = false;
if (java.io.File.separator.equals("\\")) {
// Windows path
// \\bin\\imqbrokerd.exe
iMQBrokerPath = mqBinDir + java.io.File.separator + "imqbrokerd.exe";
} else {
// Unix path.
// /bin/imqbrokerd
iMQBrokerPath = mqBinDir + java.io.File.separator + "imqbrokerd";
}
// String portStr =
// qcf.getProperty(ConnectionConfiguration.imqBrokerHostPort);
// Important : Do not change the command line parameter order
// unless you know what you are doing. Here are the
// constraints -
//
// 1. The "-javahome" argument, if present, must be first.
// Otherwise it does not work on Windoze. The caller (of
// this method) is responsible for making sure that the
// "-javahome" argument is first in the 'optArgs' string.
//
// 2. The -name, -port and -silent arguments must be at
// placed at the end, so that they override anything
// specified in the optArgs...
//
// 3. The -bgnd argument is required for proper signal
// handling on solaris.
//
Vector v = new Vector();
v.add(iMQBrokerPath);
if (optArgs != null) {
for (int i = 0; i < optArgs.length; i++) {
v.add(optArgs[i]);
}
}
if (serverName != null) {
v.add("-name");
v.add(serverName);
}
v.add("-remove");
v.add("instance");
v.add("-silent");
v.add("-force");
String[] cmdLine = (String[]) v.toArray(new String[0]);
Process p = Runtime.getRuntime().exec(cmdLine);
// Close the receiver end of stdout and stderr streams.
// Otherwise the child process blocks while trying to
// write...
p.getInputStream().close();
p.getErrorStream().close();
try {
p.waitFor();
exitCode = p.exitValue();
} catch (InterruptedException iex) {
interrupted = true;
}
if (interrupted) {
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_INT_PREM, serverName));
}
/*
* Proper handling of exit codes will be done once the following bug is fixed: 4713518 - imqbrokerd -remove:
* Enhancements needed for app server integration
*/
switch (exitCode) {
case BrokerExitCode.NORMAL:
break;
case BrokerExitCode.INSTANCE_NOT_EXISTS:
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_NOT_EXIST, serverName));
case BrokerExitCode.INSTANCE_BEING_USED:
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_BEING_USED, serverName));
case BrokerExitCode.NO_PERMISSION_ON_INSTANCE:
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_NO_PERM, serverName));
case BrokerExitCode.PROBLEM_REMOVING_PERSISTENT_STORE:
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_PROB_RM_STORE, serverName));
case BrokerExitCode.IOEXCEPTION:
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_IOEXCEPTION, serverName));
default:
throw new jakarta.jms.JMSException(ar.getKString(ar.X_JMSSPI_DELETE_INST_UNKNOWN, serverName));
}
}
private void hello() throws JMSException {
ObjectMessage requestMesg = null;
Message replyMesg = null;
requestMesg = session.createObjectMessage();
requestMesg.setJMSReplyTo(replyQueue);
requestMesg.setIntProperty(MessageType.JMQ_MESSAGE_TYPE, MessageType.HELLO);
sender.send(requestMesg);
replyMesg = receiver.receive(timeout);
replyMesg.acknowledge();
checkReplyTypeStatus(replyMesg, MessageType.HELLO_REPLY);
}
/*
* Map is an interface and its implementation can be anything that implements the Map interface. Whatever it is, convert
* it to a Properties object.
*/
private Properties getProperties(java.util.Map properties) {
Properties tmpProps = null;
if (properties == null) {
tmpProps = new Properties();
}
else if (properties instanceof Properties) {
tmpProps = (Properties) properties;
} else {
tmpProps = new Properties();
Iterator iterator = properties.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
tmpProps.put(entry.getKey(), entry.getValue());
}
}
return tmpProps;
}
/*
* Set the properties on this object.
*/
private static void setProperties(AdministeredObject obj, Properties objProps) throws JMSException {
/*
* Set the specified properties on the new object.
*/
for (Enumeration e = objProps.propertyNames(); e.hasMoreElements();) {
String propName = (String) e.nextElement();
String value = objProps.getProperty(propName);
if (value != null) {
obj.setProperty(propName, value);
}
}
}
private void createFactory(Properties connectionProps) throws JMSException {
qcf = new QueueConnectionFactory();
qcf.setConnectionType(ClientConstants.CONNECTIONTYPE_ADMIN);
for (Enumeration e = connectionProps.propertyNames(); e.hasMoreElements();) {
String propName = (String) e.nextElement();
String value = connectionProps.getProperty(propName);
if (value != null) {
qcf.setProperty(propName, value);
}
}
}
private void fillDefaultConnectionFactoryProperties(Properties props) throws JMSException {
String val;
if (props.getProperty(ConnectionConfiguration.imqBrokerHostName) == null
&& (val = qcf.getProperty(ConnectionConfiguration.imqBrokerHostName)) != null) {
props.setProperty(ConnectionConfiguration.imqBrokerHostName, val);
}
if (props.getProperty(ConnectionConfiguration.imqBrokerHostPort) == null
&& (val = qcf.getProperty(ConnectionConfiguration.imqBrokerHostPort)) != null) {
props.setProperty(ConnectionConfiguration.imqBrokerHostPort, val);
}
}
// REVISIT:
// hard-coded string
@SuppressWarnings("deprecation")
private static int getDestTypeMask(int type, String policy) {
int mask = -1;
if (type == TOPIC) {
mask = DestType.DEST_TYPE_TOPIC;
}
else if (type == QUEUE) {
mask = DestType.DEST_TYPE_QUEUE;
}
if (policy == null) {
return mask;
}
if (type == QUEUE) {
if (policy.equals("s")) {
mask |= DestType.DEST_FLAVOR_SINGLE;
} else if (policy.equals("f")) {
mask |= DestType.DEST_FLAVOR_FAILOVER;
} else if (policy.equals("r")) {
mask |= DestType.DEST_FLAVOR_RROBIN;
}
}
return mask;
}
private static String getDestinationType(int mask) {
if (DestType.isTopic(mask)) {
return String.valueOf(TOPIC);
} else if (DestType.isQueue(mask)) {
return String.valueOf(QUEUE);
} else {
return String.valueOf(UNKNOWN);
}
}
private void checkReplyTypeStatus(Message mesg, int msgType) throws JMSException {
int actualMsgType = -1, actualReplyStatus = -1;
/*
* There is a timing problem in the protocol. The GOODBYE message could be processed before the SHUTDOWN_REPLY message
* and therefore could be sending null as a value for 'mesg' when receive() returns. We will assume that the SHUTDOWN
* operation was successful when we receive status == 200 or mesg == null.
*/
if (mesg == null) {
if (msgType == MessageType.SHUTDOWN_REPLY) {
return;
}
}
actualMsgType = mesg.getIntProperty(MessageType.JMQ_MESSAGE_TYPE);
actualReplyStatus = mesg.getIntProperty(MessageType.JMQ_STATUS);
if ((msgType == actualMsgType) && (actualReplyStatus == MessageType.OK)) {
return;
}
/*
* Otherwise, report an error
*/
String error = null;
try {
error = mesg.getStringProperty(MessageType.JMQ_ERROR_STRING);
} catch (Exception e) {
}
throw new JMSException(error);
}
/*
* BEGIN INTERFACE ExceptionListener
*/
@Override
public void onException(JMSException jmse) {
}
/*
* END INTERFACE ExceptionListener
*/
class BrokerWatcher extends Thread {
Process p;
String[] cmdLine;
BrokerWatcher(Process p, String[] cmdLine) {
this.p = p;
this.cmdLine = cmdLine;
}
@Override
public void run() {
logger.log(DEBUG, "BrokerWatcher started");
try {
int status = p.waitFor();
logger.log(DEBUG, "BrokerWatcher: Broker returned with status=" + status);
if (status == 255) {
// restart broker
launchAndWatch(cmdLine);
logger.log(DEBUG, "BrokerWatcher terminating after restarting broker");
}
logger.log(DEBUG, "BrokerWatcher terminating after normal exit");
} catch (InterruptedException e) {
logger.log(WARNING, "InterruptedException watching broker: " + e.getMessage());
} catch (IOException e) {
logger.log(WARNING, "Error restarting broker: " + e.getMessage());
}
}
}
static class StreamGobbler extends Thread {
InputStream is;
boolean isError;
String prefix;
/**
*
* @param isError If true, log messages as a WARN message. Otherwise log messages as an INFO message
*/
StreamGobbler(InputStream is, boolean isError, String prefix) {
this.is = is;
this.isError = isError;
this.prefix = prefix;
}
@Override
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
String message = prefix;
int noOfLines = 0;
while ((line = br.readLine()) != null) {
noOfLines++;
if (noOfLines > 1) {
message = message + "\n";
}
message = message + line;
}
if (noOfLines > 0) {
if (isError) {
logger.log(WARNING, message);
} else {
logger.log(INFO, message);
}
}
} catch (IOException ioe) {
logger.log(ERROR, ioe.getMessage(), ioe);
}
}
}
}