package org.marketcetera.module;
import org.marketcetera.util.misc.ClassVersion;
import org.marketcetera.util.except.I18NException;
import java.util.LinkedList;
import java.util.List;
import java.util.ArrayList;
/* $License$ */
/**
* The implementation for the ModuleManagerMXBean interface.
* This implementation delegates to the module manager. Its primary
* function is to convert all the simple java parameter types that it
* accepts for its operations to the java types that are used by
* the Module Manager.
*
* @author anshul@marketcetera.com
* @version $Id: ModuleManagerMXBeanImpl.java 16154 2012-07-14 16:34:05Z colin $
* @since 1.0.0
*/
@ClassVersion("$Id: ModuleManagerMXBeanImpl.java 16154 2012-07-14 16:34:05Z colin $") //$NON-NLS-1$
class ModuleManagerMXBeanImpl implements ModuleManagerMXBean {
@Override
public List<String> getProviders() {
return toString(mManager.getProviders());
}
@Override
public List<String> getInstances() {
return getModuleInstances(null);
}
@Override
public ProviderInfo getProviderInfo(String providerURN){
try {
return mManager.getProviderInfo(toModuleURN(providerURN));
} catch (I18NException e) {
throw transformFailure(e);
}
}
@Override
public List<String> getModuleInstances(String providerURN){
try {
return toString(mManager.getModuleInstances(
toModuleURN(providerURN)));
} catch (I18NException e) {
throw transformFailure(e);
}
}
@Override
public String createModule(String providerURN, String parameters) {
try {
ModuleURN urn = mManager.createModuleJMX(
new ModuleURN(providerURN), parameters);
return urn.getValue();
} catch (ModuleException e) {
throw transformFailure(e);
}
}
@Override
public void deleteModule(String inModuleURN){
try {
mManager.deleteModule(toModuleURN(inModuleURN));
} catch (I18NException e) {
throw transformFailure(e);
}
}
@Override
public ModuleInfo getModuleInfo(String inModuleURN) {
try {
return mManager.getModuleInfo(toModuleURN(inModuleURN));
} catch (I18NException e) {
throw transformFailure(e);
}
}
@Override
public void start(String inModuleURN) {
try {
mManager.start(toModuleURN(inModuleURN));
} catch (I18NException e) {
throw transformFailure(e);
}
}
@Override
public void stop(String inModuleURN) {
try {
mManager.stop(toModuleURN(inModuleURN));
} catch (I18NException e) {
throw transformFailure(e);
}
}
@Override
public DataFlowID createDataFlow(String inRequests) {
try {
return mManager.createDataFlow(parseDataRequests(inRequests));
} catch (ModuleException e) {
throw transformFailure(e);
}
}
@Override
public DataFlowID createDataFlow(String inRequests,
boolean inAppendSink) {
try {
return mManager.createDataFlow(parseDataRequests(inRequests),
inAppendSink);
} catch (ModuleException e) {
throw transformFailure(e);
}
}
@Override
public void cancel(String inFlowID) {
try {
mManager.cancel(toFlowID(inFlowID));
} catch (I18NException e) {
throw transformFailure(e);
}
}
@Override
public List<DataFlowID> getDataFlows(boolean inIncludeModuleCreated) {
return mManager.getDataFlows(inIncludeModuleCreated);
}
@Override
public DataFlowInfo getDataFlowInfo(String inFlowID) {
try {
return mManager.getDataFlowInfo(toFlowID(inFlowID));
} catch (DataFlowNotFoundException e) {
throw transformFailure(e);
}
}
@Override
public void refresh() {
try {
mManager.refresh();
} catch (I18NException e) {
throw transformFailure(e);
}
}
@Override
public List<DataFlowInfo> getDataFlowHistory() {
return mManager.getDataFlowHistory();
}
@Override
public int getMaxFlowHistory() {
return mManager.getMaxFlowHistory();
}
@Override
public void setMaxFlowHistory(int inMaxFlowHistory) {
mManager.setMaxFlowHistory(inMaxFlowHistory);
}
/**
* Creates an instance.
*
* @param inManager the module manager instance.
*/
ModuleManagerMXBeanImpl(ModuleManager inManager) {
assert inManager != null;
mManager = inManager;
}
/**
* Parses the requests as follows. Each data request is
* delimited by '^' character. If a data request needs to
* include the '^' character they can avoid having it
* interpreted as an escape character by including it twice,
* like so '^^'.
*
* Within each data request string, individual entries are
* delimited by ';' character. The first entry is always
* interpreted as the ModuleURN. The second entry is interpreted
* as the coupling type, if it matches any of the known coupling types
* otherwise its interpreted as a string request parameter and
* the coupling type is defaulted to {@link DataCoupling#SYNC}.
*
* @param inRequests the data requests in string syntax.
*
* @return the parsed data request objects.
*/
static DataRequest[] parseDataRequests(String inRequests) {
if(inRequests == null || inRequests.trim().isEmpty()) {
throw new RuntimeException(
Messages.EMPTY_STRING_DATA_REQUEST.getText());
}
String[] requests = inRequests.split(REQUESTS_SEPARATOR_REGEX);
LinkedList<String> list = new LinkedList<String>();
boolean append = false;
//Coalesce consecutive '^' chars, which is implied by an empty string.
for (String request : requests) {
if (append) {
append = false;
list.add(list.removeLast() + REQUESTS_SEPARATOR + request);
} else if (request.isEmpty()) {
// the next element should be appended to the last one in
// the list with the '^' character
append = true;
} else {
list.add(request);
}
}
//Parse a data request out of each element of the list.
DataRequest[] dataRequests = new DataRequest[list.size()];
int i = 0;
for(String request: list) {
dataRequests[i++] = parseDataRequest(request);
}
return dataRequests;
}
/**
* Parses the supplied string as a data request. Individual
* elements are delimited by ';' character. If the supplied
* string has a ';' character, the text before its first
* occurrence is interpreted as the module URN. Otherwise,
* the entire string is interpreted as a module URN.
* <p>
* If the string has another ';' character, if the text between
* the two ';' characters matches a supported
* {@link DataCoupling} value, a data request is created using
* that data coupling value and the string after the second ';'
* character as the request parameter, otherwise, a data request is
* created with the entire
* string after the first ';' character as the request parameter
* and data coupling defaulted to {@link DataCoupling#SYNC}
* <p>
* If the string does not have another ';' character, if the text
* after ';' character matches a supported {@link DataCoupling} value,
* a data request is created with that data coupling value and a null
* request parameter, otherwise, a data request is created with the
* text after ';' as the data request parameter and data coupling
* type defaulted to {@link DataCoupling#SYNC}.
*
* @param inRequest the string representation of the request
*
* @return the data request object.
*/
private static DataRequest parseDataRequest(String inRequest) {
int idx = inRequest.indexOf(DATA_SEPARATOR);
ModuleURN urn;
DataCoupling coupling = DataCoupling.SYNC;
if(idx > 0) {
//Has at least one separator, parse module urn out.
urn = new ModuleURN(inRequest.substring(0, idx));
if (++idx < inRequest.length()) {
inRequest = inRequest.substring(idx);
idx = inRequest.indexOf(DATA_SEPARATOR);
if (idx >= 0) {
//has another separator, parse this element out
String s = inRequest.substring(0, idx);
DataCoupling c = toCoupling(s);
if(c == null) {
// not a valid coupling value, use the entire string
// as request parameter
coupling = DataCoupling.SYNC;
} else {
//record the coupling value and use the rest of the
//string as request parameter, if any is left.
coupling = c;
if(++idx < inRequest.length()) {
inRequest = inRequest.substring(idx);
} else {
inRequest = null;
}
}
} else {
//no more separator, test this string for coupling
//value, if no matches, use the value as request parameter
DataCoupling c = toCoupling(inRequest);
if(c == null) {
coupling = DataCoupling.SYNC;
} else {
coupling = c;
inRequest = null;
}
}
} else {
//nothing found after the separator, use defaults
coupling = DataCoupling.SYNC;
inRequest = null;
}
} else {
//No separators, interpret the entire string as
//module URN
urn = new ModuleURN(inRequest);
inRequest = null;
}
return new DataRequest(urn, coupling, inRequest);
}
/**
* Converts the string to a data coupling.
*
* @param inString the string that needs to be converted to coupling.
*
* @return the data coupling, if the string matches an available
* coupling, null otherwise.
*/
private static DataCoupling toCoupling(String inString) {
try {
return DataCoupling.valueOf(inString);
} catch (IllegalArgumentException e) {
return null;
}
}
/**
* Converts a string value to Data Flow ID. If the supplied string
* is null or empty, the returned value is null.
*
* @param inFlowID the flow ID represented as a string.
*
* @return the data flow ID.
*/
private DataFlowID toFlowID(String inFlowID) {
return inFlowID == null ||
inFlowID.trim().isEmpty()
? null
: new DataFlowID(inFlowID);
}
/**
* Converts a string value to Module URN value. If the supplied
* string value is null or empty, the returned value is null.
*
* @param inURN the module URN represented as a string.
*
* @return the module URN value.
*/
private ModuleURN toModuleURN(String inURN) {
return inURN == null || inURN.trim().isEmpty()
? null
: new ModuleURN(inURN);
}
/**
* Converts an array of module URN values to an array of strings.
* If the supplied value is null, the returned value is null.
*
* @param inURNs the array of module URNs.
*
* @return the array of string values of the module URN.
*/
private List<String> toString(List<ModuleURN> inURNs) {
if(inURNs == null) {
return null;
}
ArrayList<String> list = new ArrayList<String>(inURNs.size());
for(ModuleURN urn: inURNs) {
list.add(urn.getValue());
}
return list;
}
/**
* Transforms the failure into a runtime exception and copies over the stack
* trace. The original exception should not be added to the RuntimeException
* as the JMX client may not have the classes available to deserialize
* them.
*
* @param inException the exception that needs to be transformed.
*
* @return the wrapped runtime exception.
*/
private static RuntimeException transformFailure(I18NException inException) {
RuntimeException runtimeException = new RuntimeException(
inException.getLocalizedDetail());
runtimeException.setStackTrace(inException.getStackTrace());
return runtimeException;
}
private final ModuleManager mManager;
private static final String REQUESTS_SEPARATOR = "^"; //$NON-NLS-1$
private static final String REQUESTS_SEPARATOR_REGEX = "\\^"; //$NON-NLS-1$
private static final String DATA_SEPARATOR = ";"; //$NON-NLS-1$
}