/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.bbg;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bloomberglp.blpapi.EventHandler;
import com.bloomberglp.blpapi.Session;
import com.bloomberglp.blpapi.SessionOptions;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.bbg.referencedata.statistics.BloombergReferenceDataStatistics;
import com.opengamma.bbg.referencedata.statistics.NullBloombergReferenceDataStatistics;
import com.opengamma.bbg.util.SessionOptionsUtils;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.Connector;
/**
* Connector used to access Bloomberg.
* <p>
* This class performs only minimal session connections; the caller must
* configure them and attach them to Bloomberg services.
* This is mainly a data holder for connectivity.
* <p>
* This class is usually configured using the associated factory bean.
*/
public class BloombergConnector implements Connector {
/** Logger. */
private static final Logger s_logger = LoggerFactory.getLogger(BloombergConnector.class);
/**
* Callback interface for listeners which wish to be notified when the Bloomberg connection is available.
*/
public interface AvailabilityListener {
void bloombergAvailable();
}
/**
* The configuration name.
*/
private final String _name;
/**
* The Bloomberg Session Options.
*/
private final SessionOptions _sessionOptions;
/**
* The Bloomberg statistics.
*/
private final BloombergReferenceDataStatistics _referenceDataStatistics;
/**
* The listeners that wish to be notified whenever Bloomberg is available.
*/
private final Collection<AvailabilityListener> _listeners = new CopyOnWriteArrayList<AvailabilityListener>();
/**
* Creates an instance.
*
* @param name the configuration name, not null
* @param sessionOptions the Bloomberg session options, not null
*/
public BloombergConnector(String name, SessionOptions sessionOptions) {
this(name, sessionOptions, NullBloombergReferenceDataStatistics.INSTANCE);
}
/**
* Creates an instance.
*
* @param name the configuration name, not null
* @param sessionOptions the Bloomberg session options, not null
* @param statistics the Bloomberg statistics, not null
*/
public BloombergConnector(String name, SessionOptions sessionOptions, BloombergReferenceDataStatistics statistics) {
ArgumentChecker.notNull(name, "name");
ArgumentChecker.notNull(sessionOptions, "sessionOptions");
ArgumentChecker.notNull(statistics, "statistics");
_name = name;
_sessionOptions = sessionOptions;
_referenceDataStatistics = statistics;
}
/**
* Creates an instance.
* <p>
* Subclasses must override the session options getter.
*
* @param name the configuration name, not null
* @param statistics the Bloomberg statistics, not null
*/
protected BloombergConnector(String name, BloombergReferenceDataStatistics statistics) {
ArgumentChecker.notNull(name, "name");
ArgumentChecker.notNull(statistics, "statistics");
_name = name;
_sessionOptions = null;
_referenceDataStatistics = statistics;
}
//-------------------------------------------------------------------------
@Override
public final String getName() {
return _name;
}
@Override
public final Class<? extends Connector> getType() {
return BloombergConnector.class;
}
//-------------------------------------------------------------------------
/**
* Gets the Bloomberg session options.
* <p>
* DO NOT MODIFY this object.
*
* @return the Bloomberg session options, not null
*/
public SessionOptions getSessionOptions() {
return _sessionOptions;
}
/**
* Gets the Bloomberg reference data statistics.
* <p>
* This is used to capture statistics about Bloomberg use.
*
* @return the Bloomberg statistics recorder, not null
*/
public BloombergReferenceDataStatistics getReferenceDataStatistics() {
return _referenceDataStatistics;
}
/**
* Creates and starts a new Bloomberg {@code Session} in Synchronous mode.
* <p>
*
* @return the started Bloomberg session, not null
* @throws RuntimeException if an error occurs
*/
public Session createOpenSession() {
return createOpenSession(null);
}
//-------------------------------------------------------------------------
/**
* Creates and starts a new Bloomberg {@code Session}.
* <p>
* The connector does not retain the state of the session, thus the caller is responsible for its lifecycle.
* If the specified eventHandler is not null then this Session will operate in asynchronous mode, otherwise the Session will operate in Synchronous mode.
*
* @param eventHandler the event handler if provided
* @return the started Bloomberg session, not null
* @throws RuntimeException if an error occurs
*/
public Session createOpenSession(final EventHandler eventHandler) {
Session session = createSession(eventHandler);
try {
if (session.start() == false) {
throw new OpenGammaRuntimeException("Bloomberg session failed to start: " + SessionOptionsUtils.toString(getSessionOptions()));
}
} catch (InterruptedException ex) {
// Interruption may mean that threads have still been created which must be killed. See PLAT-5309.
try {
s_logger.debug("Attempting to stop session which was created but not started");
session.stop();
} catch (Exception e) {
s_logger.error("Can't stop session", e);
}
Thread.interrupted();
throw new OpenGammaRuntimeException("Bloomberg session failed to start: " + SessionOptionsUtils.toString(getSessionOptions()), ex);
} catch (Exception ex) {
// Failure from "start" to connect may mean that threads have still been created which must be killed. See PLAT-5309.
try {
s_logger.debug("Attempting to stop session which was created but not started");
session.stop();
} catch (Exception e) {
s_logger.error("Can't stop session", e);
}
throw new OpenGammaRuntimeException("Bloomberg session failed to start: " + SessionOptionsUtils.toString(getSessionOptions()), ex);
}
return session;
}
/**
* Creates a Bloomberg session in Synchronous mode that uses the session options.
* <p>
* The session is not opened.
*
* @return the Bloomberg session, not null
*/
public Session createSession() {
return createSession(null);
}
/**
* Creates a Bloomberg session that uses the session options.
*
* <p>
* The session is not opened.
* If the specified eventHandler is not null then this Session will operate in asynchronous mode, otherwise the Session will operate in Synchronous mode.
* @param eventHandler the event handler if provided
*
* @return the Bloomberg session, not null
*/
public Session createSession(final EventHandler eventHandler) {
return new Session(getSessionOptions(), eventHandler);
}
//-------------------------------------------------------------------------
@Override
public void close() {
// no action, as the connector holds no closeable state
}
//-------------------------------------------------------------------------
/**
* Returns a description of this object suitable for debugging.
*
* @return the description, not null
*/
@Override
public String toString() {
return getClass().getSimpleName() + "[" + _name + "]";
}
/**
* Registers a callback to be notified when {@link #notifyAvailabilityListeners} gets called.
*
* @param listener the callback to register, not null
*/
public void addAvailabilityListener(final AvailabilityListener listener) {
ArgumentChecker.notNull(listener, "listener");
_listeners.add(listener);
}
/**
* Removes a callback previously registered with {@link #addAvailabilityListener}.
*
* @param listener the listener to remove, not null
*/
public void removeAvailabilityListener(final AvailabilityListener listener) {
_listeners.remove(listener);
}
/**
* Calls the {@link AvailabilityListener#bloombergAvailable()} method on all registered listeners.
* <p>
* These calls are made inline - callers should take care not to be holding locks that may cause potential deadlocks and be aware that they too will be called if they are also registered as a
* listener.
*/
public void notifyAvailabilityListeners() {
for (AvailabilityListener listener : _listeners) {
s_logger.debug("Notifying availability to {}", listener);
listener.bloombergAvailable();
}
}
/**
* Checks if the session needs authentication
*
* @return true if authentication options is set, otherwise false
*/
public boolean requiresAuthentication() {
if (getSessionOptions() != null) {
String authenticationOptions = StringUtils.trimToNull(getSessionOptions().authenticationOptions());
return authenticationOptions != null;
}
return false;
}
/**
* Returns the application name if available, otherwise null.
*
* @return the application name if available.
*/
public String getApplicationName() {
String applicationName = null;
if (getSessionOptions() != null && getSessionOptions().authenticationOptions() != null) {
if (getSessionOptions().authenticationOptions().startsWith(BloombergConstants.AUTH_APP_PREFIX)) {
applicationName = getSessionOptions().authenticationOptions().substring(BloombergConstants.AUTH_APP_PREFIX.length());
}
}
return applicationName;
}
}