package org.marketcetera.module;
import org.marketcetera.util.misc.ClassVersion;
import org.marketcetera.util.log.I18NBoundMessage1P;
import javax.management.ObjectName;
import javax.management.MalformedObjectNameException;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.Serializable;
import java.util.Arrays;
import java.beans.ConstructorProperties;
/* $License$ */
/**
* Represents a URN that can be used within the module
* framework system. Instances of this class maintain a normalized
* version of a URN. This ensures that equivalent representations
* of the URN evaluate as equal.
* <p>
* The URNs have the following format
* <br/>
* <code>metc:provType:provider:instance</code>
*<br/>
* <b>where</b>:
* <ul>
* <li><b>metc</b>: The URN prefix <code>metc</code> indicating a marketcetera URN</li>
* <li><b>provType</b>: Identifies a data provider type, for example, <code>mdata</code>
* for market data providers, <code>cep</code> for complex event processors,
* <code>news</code> for news providers, <code>system</code> for system data
* providers for data like trade suggestions, execution reports, etc.
* This field can be omitted if the provider type is unknown or if the provider
* type can be safely omitted for the particular usage of the URN</li>
* <li><b>provider</b>: the name of the provider of data. For example, for
* market data, this can be <code>opentick</code> or <code>activ</code>.
* In case, the name of the provider is omitted, the request may be routed
* to any provider for the specified <i>provType</i>, if one is specified,
* in the URN and if a provider is available for that provider type.
* If more than one provider is available, system may arbitrarily choose
* any of the available providers. </li>
* <li><b>instance</b>: identifies the particular instance of the module. This
* field can be omitted for singleton module instances. For modules that
* have multiple instances, like strategy, this may refer to the module name
* which, for example, may be the name of the strategy.</li>
* </ul>
*
*
* @author anshul@marketcetera.com
* @version $Id: ModuleURN.java 16154 2012-07-14 16:34:05Z colin $
* @since 1.0.0
*/
@ClassVersion("$Id: ModuleURN.java 16154 2012-07-14 16:34:05Z colin $") //$NON-NLS-1$
@XmlJavaTypeAdapter(ModuleURNXmlAdapter.class)
public final class ModuleURN implements Serializable {
/**
* Creates a new instance given the string representation.
* Each component of the URN is trimmed. Any trailing empty
* elements of the URN are pruned.
*
* @param inString the string representation of the URN,
* cannot be null or an empty string.
*
* @throws IllegalArgumentException if the URN string is empty
* or the URN has no non-empty fields.
*/
@ConstructorProperties({"value"}) //$NON-NLS-1$
public ModuleURN(String inString) {
String[] urnPieces = inString.split(URN_SEPARATOR_CHAR, MAX_ELEMENTS);
if(urnPieces.length == MAX_ELEMENTS) {
//The last element can have separators if the supplied URN
//has extra fields, prune them.
int idx = urnPieces[MAX_ELEMENTS - 1].indexOf(URN_SEPARATOR_CHAR);
if(idx >= 0) {
urnPieces[MAX_ELEMENTS - 1] =
urnPieces[MAX_ELEMENTS - 1].substring(0,idx);
}
}
int lastEmpty = urnPieces.length;
boolean lookForEmpty = true;
//Remove spaces and prune empty trailing fields
for(int i = urnPieces.length - 1; i >= 0; i--) {
urnPieces[i] = urnPieces[i].trim();
if(urnPieces[i].isEmpty()) {
urnPieces[i] = null;
if(lookForEmpty) {
lastEmpty = i;
}
} else {
lookForEmpty = false;
}
}
if(lastEmpty == 0) {
throw new IllegalArgumentException(
Messages.EMPTY_URN.getText(inString));
}
if(lastEmpty < urnPieces.length) {
urnPieces = Arrays.copyOfRange(urnPieces,0,lastEmpty);
}
mURNPieces = urnPieces;
}
/**
* Creates a child URN of the supplied parent URN, appending
* the <code>inInstance</code> value to it.
*
* If the supplied URN is a provider type URN, the returned
* URN will be a provider URN.
*
* If the supplied URN is a provider URN, the returned URN
* will be an instance URN.
*
* If the supplied URN is an instance URN, the returned
* URN will be a copy of the supplied instance URN. The
* <code>inInstance</code> value will be ignored.
*
* if <code>inInstance</code> value is null, only has
* whitespaces, is empty or has ':' as its first non-whitespace
* character, it will be ignored and the returned URN will
* be a copy of the supplied <code>inParent</code> URN.
*
* if <code>inInstance</code> has any ':' characters, the first
* instance of ':' character and any characters following it
* are pruned before appending <code>inInstance</code> to
* the supplied <code>inParent</code>.
*
* @param inParent the parent URN
* @param inInstance the text element to add to the parent URN
*/
public ModuleURN(ModuleURN inParent, String inInstance) {
if(inParent.mURNPieces.length == MAX_ELEMENTS ||
inInstance == null ||
inInstance.trim().isEmpty()) {
//make it a copy of the supplied parent URN.
mURNPieces = Arrays.copyOf(inParent.mURNPieces,
inParent.mURNPieces.length);
return;
}
//if inInstance has any separators remove them.
int idx = inInstance.indexOf(URN_SEPARATOR_CHAR);
if(idx >= 0) {
inInstance = inInstance.substring(0, idx);
}
inInstance = inInstance.trim();
if(inInstance.isEmpty()) {
//make it a copy of the supplied parent URN.
mURNPieces = Arrays.copyOf(inParent.mURNPieces,
inParent.mURNPieces.length);
return;
}
mURNPieces = Arrays.copyOf(inParent.mURNPieces,
inParent.mURNPieces.length + 1);
mURNPieces[mURNPieces.length - 1] = inInstance;
}
/**
* Returns the URN scheme.
*
* @return the URN scheme.
*/
public String scheme() {
return mURNPieces.length > 0? mURNPieces[0]: null;
}
/**
* Returns the provider type.
*
* @return the provider type.
*/
public String providerType() {
return mURNPieces.length > 1? mURNPieces[1]: null;
}
/**
* Returns the provider name.
*
* @return the provider name.
*/
public String providerName() {
return mURNPieces.length > 2? mURNPieces[2]: null;
}
/**
* Returns the instance name.
*
* @return the instance name.
*/
public String instanceName() {
return mURNPieces.length > 3? mURNPieces[3]: null;
}
/**
* Returns true if this is a instance URN.
*
* @return if this is a instance URN.
*/
public boolean instanceURN() {
return instanceName() != null;
}
/**
* Returns true if this URN is a parent of the supplied URN.
*
* @param inChild if the URN that needs to be compared with
* this URN.
*
* @return if this URN is a parent of the supplied URN.
*/
public boolean parentOf(ModuleURN inChild) {
if(mURNPieces.length == (inChild.mURNPieces.length - 1)) {
for(int i = 0; i < mURNPieces.length; i++) {
if(mURNPieces[i] == null) {
if(inChild.mURNPieces[i] != null) {
return false;
}
} else {
if(!mURNPieces[i].equals(inChild.mURNPieces[i])) {
return false;
}
}
}
return true;
}
return false;
}
/**
* Returns the parent URN of this URN, if this is a provider
* or an instance URN, null otherwise.
*
* @return the parent URN if this is a provider or an instance URN.
*/
public ModuleURN parent() {
if(mURNPieces.length > 2) {
return new ModuleURN(Arrays.copyOfRange(mURNPieces,
0, mURNPieces.length - 1));
}
return null;
}
/**
* The value of this URN as string.
*
* @return the string value of this URN.
*/
public String getValue() {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < mURNPieces.length; i++) {
if(mURNPieces[i] != null) {
sb.append(mURNPieces[i]);
}
if(i < (mURNPieces.length - 1)) {
sb.append(URN_SEPARATOR_CHAR);
}
}
return sb.toString();
}
/**
* Converts this URN to the object name that can be used to lookup
* instances of the MXBean for the object indentified by this URN
* in the MBean server.
*
* @return the object name for this URN.
*
* @throws MXBeanOperationException if there were errors creating
* the object name.
*/
public ObjectName toObjectName() throws MXBeanOperationException {
StringBuilder sb = new StringBuilder(
ModuleManager.MBEAN_DOMAIN_NAME).append(":"); //$NON-NLS-1$
// Each URN field is the value of the corresponding key
// from MBEAN_NAME_KEYS
boolean addComma = false;
for(int i = 1; i < mURNPieces.length;i++) {
if(mURNPieces[i] != null) {
if(addComma) {
sb.append(","); //$NON-NLS-1$
}
sb.append(MBEAN_NAME_KEYS[i]).append('=').append(mURNPieces[i]); //$NON-NLS-1$
addComma = true;
}
}
try {
return new ObjectName(sb.toString());
} catch (MalformedObjectNameException e) {
throw new MXBeanOperationException(e,
new I18NBoundMessage1P(
Messages.BEAN_OBJECT_NAME_ERROR,toString()));
}
}
@Override
public String toString() {
return getValue();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ModuleURN moduleURN = (ModuleURN) o;
return Arrays.equals(mURNPieces, moduleURN.mURNPieces);
}
@Override
public int hashCode() {
return (mURNPieces != null ? Arrays.hashCode(mURNPieces) : 0);
}
/**
* The scheme for all system URNs
*/
public static final String SCHEME = "metc"; //$NON-NLS-1$
/**
* Creates an instance.
*
* @param providerType the provider type
* @param providerName the provider name
* @param instanceName the instance name
*/
ModuleURN (String providerType, String providerName, String instanceName) {
String[] urnPieces = null;
if(instanceName != null) {
urnPieces = new String[MAX_ELEMENTS];
urnPieces[MAX_ELEMENTS - 1] = instanceName;
}
if(providerName != null) {
if(urnPieces == null) {
urnPieces = new String[MAX_ELEMENTS - 1];
}
urnPieces[MAX_ELEMENTS - 2] = providerName;
}
if(providerType != null) {
if(urnPieces == null) {
urnPieces = new String[MAX_ELEMENTS - 2];
}
urnPieces[MAX_ELEMENTS - 3] = providerType;
}
if(urnPieces == null) {
urnPieces = new String[MAX_ELEMENTS - 3];
}
urnPieces[0] = SCHEME;
mURNPieces = urnPieces;
}
/**
* Creates an instance.
*
* @param inPieces The individual pieces of the URN.
*/
private ModuleURN(String[] inPieces) {
mURNPieces = inPieces;
}
private final String[] mURNPieces;
static final String URN_SEPARATOR_CHAR = ":"; //$NON-NLS-1$
/**
* MBean object name keys corresponding to fields in the
* provider / instance URN.
*/
private static final String [] MBEAN_NAME_KEYS = {
"", //This value is not used, its a placeholder for scheme //$NON-NLS-1$
"type", //$NON-NLS-1$
"provider", //$NON-NLS-1$
"name"}; //$NON-NLS-1$
private static final long serialVersionUID = -4563867950366932693L;
private static final int MAX_ELEMENTS = 4;
}