/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.component.factory.engine;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.fudgemsg.FudgeContext;
import org.joda.beans.BeanBuilder;
import org.joda.beans.BeanDefinition;
import org.joda.beans.JodaBeanUtils;
import org.joda.beans.MetaProperty;
import org.joda.beans.Property;
import org.joda.beans.PropertyDefinition;
import org.joda.beans.impl.direct.DirectBeanBuilder;
import org.joda.beans.impl.direct.DirectMetaProperty;
import org.joda.beans.impl.direct.DirectMetaPropertyMap;
import com.google.common.base.Supplier;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.component.ComponentInfo;
import com.opengamma.component.ComponentRepository;
import com.opengamma.component.factory.AbstractComponentFactory;
import com.opengamma.engine.calcnode.CalcNodeSocketConfiguration;
import com.opengamma.transport.jaxrs.UriEndPointDescriptionProvider;
import com.opengamma.util.GUIDGenerator;
import com.opengamma.util.fudgemsg.OpenGammaFudgeContext;
import com.opengamma.util.rest.DataConfigurationResource;
/**
* Component factory providing a managed sub set of the server capabilities.
*/
@BeanDefinition
public class EngineConfigurationComponentFactory extends AbstractComponentFactory {
/**
* The name of the configuration document published.
* <p>
* This is used to support servers which publish multiple configurations, for example if they host multiple view processors, or that act as aggregators for a number of other servers at the
* installation site.
* <p>
* This default name may be hard-coded in native code and installation scripts. Changes may cause client tools such as Excel to stop working correctly.
*/
private static final String DEFAULT_CONFIGURATION_DOCUMENT_ID = "0";
/**
* The field name under which the logical server unique identifier is published.
* <p>
* This property may be set explicitly by calling {@link #setLogicalServerId}, or if omitted will be generated randomly.
* <p>
* This default name is hard-coded in native code. Changes may cause client tools such as Excel to stop working correctly.
*/
private static final String LOGICAL_SERVER_UNIQUE_IDENTIFIER = "lsid";
/**
* The classifier that the factory should publish under.
*/
@PropertyDefinition(validate = "notNull")
private String _classifier;
/**
* The Fudge context.
*/
@PropertyDefinition(validate = "notNull")
private FudgeContext _fudgeContext = OpenGammaFudgeContext.getInstance();
/**
* The logical server unique identifier. This is defined by the data environment. Clustered servers (that is, they appear suitably identical to any connecting clients) should have the same logical
* identifier to reflect this. Any server backed by a unique data environment must have a correspondingly unique identifier. If a server has a transient or temporary data environment it must
* generate a new logical identifier whenever that environment is flushed.
* <p>
* The default behavior, if this is not specified in the configuration file, is to generate a unique identifier at start up. This is suitable for most standard installations which include temporary
* (for example, in-memory) masters or other data stores.
*/
@PropertyDefinition
private String _logicalServerId;
/**
* Creates a random logical server unique identifier. This is used if an explicit identifier is not set in the configuration file.
* <p>
* This is a 24 character string using base-64 characters, created using the algorithm from {@link GUIDGenerator} for uniqueness.
*
* @return the logical server unique identifier, not null
*/
protected String createLogicalServerId() {
final UUID uuid = GUIDGenerator.generate();
final byte[] bytes = new byte[16];
long x = uuid.getMostSignificantBits();
bytes[0] = (byte) x;
bytes[1] = (byte) (x >> 8);
bytes[2] = (byte) (x >> 16);
bytes[3] = (byte) (x >> 24);
bytes[4] = (byte) (x >> 32);
bytes[5] = (byte) (x >> 40);
bytes[6] = (byte) (x >> 48);
bytes[7] = (byte) (x >> 56);
x = uuid.getLeastSignificantBits();
bytes[8] = (byte) x;
bytes[9] = (byte) (x >> 8);
bytes[10] = (byte) (x >> 16);
bytes[11] = (byte) (x >> 24);
bytes[12] = (byte) (x >> 32);
bytes[13] = (byte) (x >> 40);
bytes[14] = (byte) (x >> 48);
bytes[15] = (byte) (x >> 56);
return Base64.encodeBase64String(bytes);
}
protected void afterPropertiesSet() {
if (getLogicalServerId() == null) {
setLogicalServerId(createLogicalServerId());
}
}
protected void buildConfiguration(final ComponentRepository repo, final Map<String, String> configuration, final Map<String, Object> map) {
map.put(LOGICAL_SERVER_UNIQUE_IDENTIFIER, getLogicalServerId());
for (final String key : configuration.keySet()) {
final String valueStr = configuration.get(key);
Object targetValue = valueStr;
if (valueStr.contains("::")) {
final String type = StringUtils.substringBefore(valueStr, "::");
final String classifier = StringUtils.substringAfter(valueStr, "::");
final ComponentInfo info = repo.findInfo(type, classifier);
if (info == null) {
throw new IllegalArgumentException("Component not found: " + valueStr);
}
final Object instance = repo.getInstance(info);
if ((instance instanceof CalcNodeSocketConfiguration) || (instance instanceof Supplier)) {
targetValue = instance;
} else {
if (info.getUri() == null) {
throw new OpenGammaRuntimeException("Unable to add component to configuration as it has not been published by REST: " + valueStr);
}
targetValue = new UriEndPointDescriptionProvider(info.getUri().toString());
}
}
buildMap(map, key, targetValue);
}
}
@Override
public void init(final ComponentRepository repo, final LinkedHashMap<String, String> configuration) {
afterPropertiesSet();
final Map<String, Object> map = new LinkedHashMap<String, Object>();
buildConfiguration(repo, configuration, map);
final Map<String, Object> outer = new LinkedHashMap<String, Object>();
outer.put(DEFAULT_CONFIGURATION_DOCUMENT_ID, map);
final DataConfigurationResource resource = new DataConfigurationResource(getFudgeContext(), outer);
repo.getRestComponents().publishResource(resource);
// indicate that all component configuration was used
configuration.clear();
}
/**
* Builds the map, handling dot separate keys.
*
* @param map the map, not null
* @param key the key, not null
* @param targetValue the target value,not null
*/
protected void buildMap(final Map<String, Object> map, final String key, final Object targetValue) {
if (key.contains(".")) {
final String key1 = StringUtils.substringBefore(key, ".");
final String key2 = StringUtils.substringAfter(key, ".");
@SuppressWarnings("unchecked")
Map<String, Object> subMap = (Map<String, Object>) map.get(key1);
if (subMap == null) {
subMap = new LinkedHashMap<String, Object>();
map.put(key1, subMap);
}
buildMap(subMap, key2, targetValue);
} else {
map.put(key, targetValue);
}
}
//------------------------- AUTOGENERATED START -------------------------
///CLOVER:OFF
/**
* The meta-bean for {@code EngineConfigurationComponentFactory}.
* @return the meta-bean, not null
*/
public static EngineConfigurationComponentFactory.Meta meta() {
return EngineConfigurationComponentFactory.Meta.INSTANCE;
}
static {
JodaBeanUtils.registerMetaBean(EngineConfigurationComponentFactory.Meta.INSTANCE);
}
@Override
public EngineConfigurationComponentFactory.Meta metaBean() {
return EngineConfigurationComponentFactory.Meta.INSTANCE;
}
@Override
protected Object propertyGet(String propertyName, boolean quiet) {
switch (propertyName.hashCode()) {
case -281470431: // classifier
return getClassifier();
case -917704420: // fudgeContext
return getFudgeContext();
case -41854233: // logicalServerId
return getLogicalServerId();
}
return super.propertyGet(propertyName, quiet);
}
@Override
protected void propertySet(String propertyName, Object newValue, boolean quiet) {
switch (propertyName.hashCode()) {
case -281470431: // classifier
setClassifier((String) newValue);
return;
case -917704420: // fudgeContext
setFudgeContext((FudgeContext) newValue);
return;
case -41854233: // logicalServerId
setLogicalServerId((String) newValue);
return;
}
super.propertySet(propertyName, newValue, quiet);
}
@Override
protected void validate() {
JodaBeanUtils.notNull(_classifier, "classifier");
JodaBeanUtils.notNull(_fudgeContext, "fudgeContext");
super.validate();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && obj.getClass() == this.getClass()) {
EngineConfigurationComponentFactory other = (EngineConfigurationComponentFactory) obj;
return JodaBeanUtils.equal(getClassifier(), other.getClassifier()) &&
JodaBeanUtils.equal(getFudgeContext(), other.getFudgeContext()) &&
JodaBeanUtils.equal(getLogicalServerId(), other.getLogicalServerId()) &&
super.equals(obj);
}
return false;
}
@Override
public int hashCode() {
int hash = 7;
hash += hash * 31 + JodaBeanUtils.hashCode(getClassifier());
hash += hash * 31 + JodaBeanUtils.hashCode(getFudgeContext());
hash += hash * 31 + JodaBeanUtils.hashCode(getLogicalServerId());
return hash ^ super.hashCode();
}
//-----------------------------------------------------------------------
/**
* Gets the classifier that the factory should publish under.
* @return the value of the property, not null
*/
public String getClassifier() {
return _classifier;
}
/**
* Sets the classifier that the factory should publish under.
* @param classifier the new value of the property, not null
*/
public void setClassifier(String classifier) {
JodaBeanUtils.notNull(classifier, "classifier");
this._classifier = classifier;
}
/**
* Gets the the {@code classifier} property.
* @return the property, not null
*/
public final Property<String> classifier() {
return metaBean().classifier().createProperty(this);
}
//-----------------------------------------------------------------------
/**
* Gets the Fudge context.
* @return the value of the property, not null
*/
public FudgeContext getFudgeContext() {
return _fudgeContext;
}
/**
* Sets the Fudge context.
* @param fudgeContext the new value of the property, not null
*/
public void setFudgeContext(FudgeContext fudgeContext) {
JodaBeanUtils.notNull(fudgeContext, "fudgeContext");
this._fudgeContext = fudgeContext;
}
/**
* Gets the the {@code fudgeContext} property.
* @return the property, not null
*/
public final Property<FudgeContext> fudgeContext() {
return metaBean().fudgeContext().createProperty(this);
}
//-----------------------------------------------------------------------
/**
* Gets the logical server unique identifier. This is defined by the data environment. Clustered servers (that is, they appear suitably identical to any connecting clients) should have the same logical
* identifier to reflect this. Any server backed by a unique data environment must have a correspondingly unique identifier. If a server has a transient or temporary data environment it must
* generate a new logical identifier whenever that environment is flushed.
* <p>
* The default behavior, if this is not specified in the configuration file, is to generate a unique identifier at start up. This is suitable for most standard installations which include temporary
* (for example, in-memory) masters or other data stores.
* @return the value of the property
*/
public String getLogicalServerId() {
return _logicalServerId;
}
/**
* Sets the logical server unique identifier. This is defined by the data environment. Clustered servers (that is, they appear suitably identical to any connecting clients) should have the same logical
* identifier to reflect this. Any server backed by a unique data environment must have a correspondingly unique identifier. If a server has a transient or temporary data environment it must
* generate a new logical identifier whenever that environment is flushed.
* <p>
* The default behavior, if this is not specified in the configuration file, is to generate a unique identifier at start up. This is suitable for most standard installations which include temporary
* (for example, in-memory) masters or other data stores.
* @param logicalServerId the new value of the property
*/
public void setLogicalServerId(String logicalServerId) {
this._logicalServerId = logicalServerId;
}
/**
* Gets the the {@code logicalServerId} property.
* identifier to reflect this. Any server backed by a unique data environment must have a correspondingly unique identifier. If a server has a transient or temporary data environment it must
* generate a new logical identifier whenever that environment is flushed.
* <p>
* The default behavior, if this is not specified in the configuration file, is to generate a unique identifier at start up. This is suitable for most standard installations which include temporary
* (for example, in-memory) masters or other data stores.
* @return the property, not null
*/
public final Property<String> logicalServerId() {
return metaBean().logicalServerId().createProperty(this);
}
//-----------------------------------------------------------------------
/**
* The meta-bean for {@code EngineConfigurationComponentFactory}.
*/
public static class Meta extends AbstractComponentFactory.Meta {
/**
* The singleton instance of the meta-bean.
*/
static final Meta INSTANCE = new Meta();
/**
* The meta-property for the {@code classifier} property.
*/
private final MetaProperty<String> _classifier = DirectMetaProperty.ofReadWrite(
this, "classifier", EngineConfigurationComponentFactory.class, String.class);
/**
* The meta-property for the {@code fudgeContext} property.
*/
private final MetaProperty<FudgeContext> _fudgeContext = DirectMetaProperty.ofReadWrite(
this, "fudgeContext", EngineConfigurationComponentFactory.class, FudgeContext.class);
/**
* The meta-property for the {@code logicalServerId} property.
*/
private final MetaProperty<String> _logicalServerId = DirectMetaProperty.ofReadWrite(
this, "logicalServerId", EngineConfigurationComponentFactory.class, String.class);
/**
* The meta-properties.
*/
private final Map<String, MetaProperty<?>> _metaPropertyMap$ = new DirectMetaPropertyMap(
this, (DirectMetaPropertyMap) super.metaPropertyMap(),
"classifier",
"fudgeContext",
"logicalServerId");
/**
* Restricted constructor.
*/
protected Meta() {
}
@Override
protected MetaProperty<?> metaPropertyGet(String propertyName) {
switch (propertyName.hashCode()) {
case -281470431: // classifier
return _classifier;
case -917704420: // fudgeContext
return _fudgeContext;
case -41854233: // logicalServerId
return _logicalServerId;
}
return super.metaPropertyGet(propertyName);
}
@Override
public BeanBuilder<? extends EngineConfigurationComponentFactory> builder() {
return new DirectBeanBuilder<EngineConfigurationComponentFactory>(new EngineConfigurationComponentFactory());
}
@Override
public Class<? extends EngineConfigurationComponentFactory> beanType() {
return EngineConfigurationComponentFactory.class;
}
@Override
public Map<String, MetaProperty<?>> metaPropertyMap() {
return _metaPropertyMap$;
}
//-----------------------------------------------------------------------
/**
* The meta-property for the {@code classifier} property.
* @return the meta-property, not null
*/
public final MetaProperty<String> classifier() {
return _classifier;
}
/**
* The meta-property for the {@code fudgeContext} property.
* @return the meta-property, not null
*/
public final MetaProperty<FudgeContext> fudgeContext() {
return _fudgeContext;
}
/**
* The meta-property for the {@code logicalServerId} property.
* @return the meta-property, not null
*/
public final MetaProperty<String> logicalServerId() {
return _logicalServerId;
}
}
///CLOVER:ON
//-------------------------- AUTOGENERATED END --------------------------
}