001    package org.findata.blpwrapper;
002    
003    import com.bloomberglp.blpapi.*;
004    
005    import java.util.Arrays;
006    import java.util.ArrayList;
007    import java.util.HashSet;
008    import java.util.regex.Pattern;
009    
010    import java.util.logging.Level;
011    import java.util.logging.Logger;
012    import java.util.logging.FileHandler;
013    import java.util.logging.SimpleFormatter;
014    
015    public class Connection {
016      private SessionOptions session_options;
017      private Session session;
018    
019      private Logger logger;
020    
021      public ArrayList response_cache;
022    
023      // Session options defaults.
024      private String server_host = "localhost";
025      private int server_port = 8194;
026    
027      private String refdata_service_name = "//blp/refdata";
028      private boolean refdata_service_open = false;
029      private String refdata_request_name = "ReferenceDataRequest";
030      private String histdata_request_name = "HistoricalDataRequest";
031      private String intraday_tick_request_name = "IntradayTickRequest";
032      private String intraday_bar_request_name = "IntradayBarRequest";
033    
034      private String apifields_service_name = "//blp/apiflds";
035      private boolean apifields_service_open = false;
036    
037      private boolean throw_invalid_ticker_error = true;
038    
039      public static final int REFERENCE_DATA_RESULT = 1;
040      public static final int BULK_DATA_RESULT = 2;
041      public static final int HISTORICAL_DATA_RESULT = 3;
042      public static final int FIELD_INFO_RESULT = 4;
043      public static final int INTRADAY_TICK_RESULT = 5;
044      public static final int INTRADAY_BAR_RESULT = 6;
045    
046      public static final int MB = 1048576;
047    
048      public static final String DATETIME_OPTION_NAMES[] = {
049        "startDateTime", 
050        "endDateTime"
051      };
052    
053      public static final String BOOLEAN_OPTION_NAMES[] = {
054        "useUTCTime", 
055        "returnRelativeDate", 
056        "adjustmentNormal",
057        "adjustmentAbnormal",
058        "adjustmentSplit",
059        "adjustmentFollowDPDF",
060        "returnEids",
061        "includeConditionCodes",
062        "includeNonPlottableEvents",
063        "includeExchangeCodes"
064      };
065    
066      public Connection() throws java.io.IOException, java.lang.InterruptedException, WrapperException {
067        this(Level.FINEST);
068      }
069    
070      public Connection(Level logLevel) throws java.io.IOException, java.lang.InterruptedException, WrapperException {
071        response_cache = new ArrayList();
072        setupLogger(logLevel);
073        connect();
074      }
075    
076      public Connection(Level logLevel, String serverHost, int serverPort) throws java.io.IOException, java.lang.InterruptedException, WrapperException {
077        server_host = serverHost;
078        server_port = serverPort;
079        response_cache = new ArrayList();
080        setupLogger(logLevel);
081        connect();
082      }
083    
084      private void setupLogger(Level log_level) throws java.io.IOException {
085        logger = Logger.getLogger("org.findata.blpwrapper");
086        logger.setUseParentHandlers(false);
087        logger.setLevel(log_level);
088    
089        if (logger.getHandlers().length == 0) {
090          FileHandler handler = new FileHandler("%h/org.findata.blpwrapper.%g.log", 100*MB, 100, true);
091          handler.setFormatter(new SimpleFormatter());
092          logger.addHandler(handler);
093        }
094      }
095    
096      private void connect() throws java.io.IOException, java.lang.InterruptedException, WrapperException {
097        setupSessionOptions();
098        setupSession();
099        processEventLoop();
100      }
101    
102      public void close() throws java.io.IOException, java.lang.InterruptedException {
103        session.stop();
104      }
105    
106      private void setupSessionOptions() {
107        session_options = new SessionOptions();
108        session_options.setServerHost(server_host);
109        session_options.setServerPort(server_port);
110      }
111    
112      private void setupSession() throws java.io.IOException, java.lang.InterruptedException {
113        session = new Session(session_options);
114        session.start();
115      }
116    
117      public void setThrowInvalidTickerError(boolean arg) {
118        throw_invalid_ticker_error = arg;
119      }
120    
121      public CorrelationID nextCorrelationID(int result_type, String[] securities, String[] fields) throws Exception {
122        DataResult result;
123        switch(result_type) {
124          case REFERENCE_DATA_RESULT:           result = new ReferenceDataResult(securities, fields); break;
125          case BULK_DATA_RESULT:                result = new BulkDataResult(securities, fields); break;
126          case HISTORICAL_DATA_RESULT:          result = new HistoricalDataResult(securities, fields); break;
127          case FIELD_INFO_RESULT:               result = new FieldInfoResult(securities); break;
128          case INTRADAY_TICK_RESULT:    result = new IntradayTickDataResult(securities, fields); break;
129          case INTRADAY_BAR_RESULT:    result = new IntradayBarDataResult(securities, fields); break;
130          default: throw new WrapperException("unknown result_type " + result_type);
131        }
132        if (response_cache.add(result)) {
133          return(new CorrelationID(response_cache.size()-1));
134        } else {
135          throw new Exception("unable to add to response_cache");
136        }
137      }
138    
139      private Service getRefDataService() throws java.io.IOException, java.lang.InterruptedException {
140        if (!refdata_service_open) {
141          refdata_service_open = session.openService(refdata_service_name);
142        }
143        return(session.getService(refdata_service_name));
144      }
145    
146      private Service getApiDataService() throws java.io.IOException, java.lang.InterruptedException {
147        if (!apifields_service_open) {
148          apifields_service_open = session.openService(apifields_service_name);
149        }
150        return(session.getService(apifields_service_name));
151      }
152    
153      private CorrelationID sendApiDataRequest(int result_type, String[] field_identifiers) throws Exception {
154        Service service = getApiDataService();
155        Request request = service.createRequest("FieldInfoRequest");
156    
157        for (int i = 0; i < field_identifiers.length; i++) {
158          request.append("id", field_identifiers[i]);
159        }
160    
161        String[] mock_fields = {""};
162        CorrelationID correlation_id = nextCorrelationID(result_type, field_identifiers, mock_fields);
163        session.sendRequest(request, correlation_id);
164        return(correlation_id);
165      }
166    
167      private CorrelationID sendRefDataRequest(int result_type, String request_name, String[] securities, String[] fields, String[] override_fields, String[] override_values, String[] option_names, String[] option_values) throws Exception {
168        String[] event_types = new String[0];
169        return(sendRefDataRequest(result_type, request_name, securities, fields, override_fields, override_values, option_names, option_values, event_types));
170      }
171    
172      private CorrelationID sendRefDataRequest(int result_type, String request_name, String[] securities, String[] fields, String[] override_fields, String[] override_values, String[] option_names, String[] option_values, String[] event_types) throws Exception {
173        Service service = getRefDataService();
174        Request request = service.createRequest(request_name);
175        
176        if (request.hasElement("securities")) {
177          Element securities_element = request.getElement("securities");
178          for (int i = 0; i < securities.length; i++) {
179            securities_element.appendValue(securities[i]);
180          }
181        }
182        
183        if (request.hasElement("fields")) {
184          Element fields_element = request.getElement("fields");
185          for (int i = 0; i < fields.length; i++) {
186            fields_element.appendValue(fields[i]);
187          }
188        }
189    
190        if (override_fields.length > 0) {
191          Element override_values_element = request.getElement("overrides");
192          for (int i = 0; i < override_fields.length; i++) {
193            if (!override_fields[i].equals("IGNORE")) {
194              Element override = override_values_element.appendElement();
195              override.setElement("fieldId", override_fields[i]);
196              override.setElement("value", override_values[i]);
197              logger.fine("override " + override_fields[i] + " set to " + override_values[i]);
198            }
199          }
200        }
201    
202        if (event_types.length > 0) {
203          for (int i = 0; i < event_types.length; i++) {
204            request.append("eventTypes", event_types[i]);
205          }
206        }
207    
208        for (int i = 0; i < option_names.length; i++) {
209          String n = option_names[i];
210    
211          HashSet datetime_option_names = new HashSet(Arrays.asList(DATETIME_OPTION_NAMES));
212          HashSet boolean_option_names = new HashSet(Arrays.asList(BOOLEAN_OPTION_NAMES));
213          
214          if (datetime_option_names.contains(n)) {
215            Pattern p = Pattern.compile(":|-|\\.|\\s"); // Expecting e.g. 2010-01-01 09:00:00.000
216            String[] time_parts = p.split(option_values[i]);
217    
218            int year = new Integer(time_parts[0]).intValue();
219            int month = new Integer(time_parts[1]).intValue();
220            int day_of_month = new Integer(time_parts[2]).intValue();
221            int hour = new Integer(time_parts[3]).intValue();
222            int minute = new Integer(time_parts[4]).intValue();
223            int second = new Integer(time_parts[5]).intValue();
224            int millisecond = new Integer(time_parts[6]).intValue();
225    
226            Datetime d = new Datetime(year, month, day_of_month, hour, minute, second, millisecond);
227            logger.fine("option " + n + " set to Datetime value " + d + ".");
228            request.set(n, d); 
229          } else if (boolean_option_names.contains(n)) {
230            boolean option_value;
231    
232            String true_value_elements[] = {"true", "TRUE", "True", "t", "T"};
233            HashSet true_values = new HashSet(Arrays.asList(true_value_elements));
234    
235            String false_value_elements[] = {"false", "FALSE", "False", "f", "F"};
236            HashSet false_values = new HashSet(Arrays.asList(false_value_elements));
237            
238            if (true_values.contains(option_values[i])) {
239              option_value = true;
240            } else if (false_values.contains(option_values[i])) {
241              option_value = false;
242            } else {
243              throw new WrapperException("Unable to convert this string '" + option_values[i] + "' to a boolean.");
244            }
245    
246            logger.fine("option " + n + " set to boolean value " + option_value + " original string '" + option_values[i] + "'.");
247            request.set(n, option_value);
248          } else {
249            logger.fine("option " + n + " set to string value '" + option_values[i] + "'.");
250            request.set(n, option_values[i]);
251          }
252        }
253    
254        CorrelationID correlation_id = nextCorrelationID(result_type, securities, fields);
255        session.sendRequest(request, correlation_id);
256        return(correlation_id);
257      }
258    
259      private void processEventLoop() throws java.lang.InterruptedException, WrapperException {
260        processEventLoop(0);
261      }
262    
263      private void processEventLoop(int result_type) throws java.lang.InterruptedException, WrapperException {
264        boolean await_response = (result_type > 0);
265        boolean cont = true;
266        while (cont) {
267          Event event = session.nextEvent();
268    
269          switch (event.eventType().intValue()) {
270            case Event.EventType.Constants.SESSION_STATUS:       processSessionStatusEvent(event); cont=await_response; break;
271            case Event.EventType.Constants.SERVICE_STATUS:       processServiceStatusEvent(event); cont=await_response; break;
272            case Event.EventType.Constants.RESPONSE:             processResponseEvent(result_type, event); cont=false; break;
273            case Event.EventType.Constants.PARTIAL_RESPONSE:     processResponseEvent(result_type, event); break;
274            default: throw new WrapperException(event.eventType());
275          }
276        }
277      }
278    
279      private void processSessionStatusEvent(Event event) throws WrapperException {
280        MessageIterator msgIter = event.messageIterator();
281    
282        while(msgIter.hasNext()) {
283          Message message = msgIter.next();
284          Element response = message.asElement();
285    
286          if (response.name().equals("SessionStarted")) {
287            logger.info("Session Started");
288          } else if (response.name().equals("SessionStartupFailure")) {
289            logger.warning("" + response);
290            Element reason = response.getElement(0);
291            throw new WrapperException("Session not started because: " + reason.getElementAsString("description"));
292          } else {
293            logger.warning("" + response);
294            throw new WrapperException("Session not started. See logs. Please report this to blpwrapper maintainer.");
295          }
296        }
297      }
298    
299      private void processServiceStatusEvent(Event event) throws WrapperException {
300        MessageIterator msgIter = event.messageIterator();
301    
302        while(msgIter.hasNext()) {
303          Message message = msgIter.next();
304          Element response = message.asElement();
305    
306          if (response.name().equals("ServiceOpened")) {
307            logger.info("Service Started");
308          } else {
309            logger.warning("" + response);
310            throw new WrapperException("Service not started. See logs. Please report this to blpwrapper maintainer.");
311          }
312        }
313      }
314    
315      private void processResponseEvent(int result_type, Event event) throws WrapperException {
316        MessageIterator msgIter = event.messageIterator();
317    
318        while (msgIter.hasNext()) {
319          Message message = msgIter.next();
320          int response_id = (int)message.correlationID().value();
321          logger.fine("Response id " + response_id);
322          DataResult result;
323    
324          switch(result_type) {
325            case REFERENCE_DATA_RESULT:     result = (ReferenceDataResult)response_cache.get(response_id); break;
326            case BULK_DATA_RESULT:          result = (BulkDataResult)response_cache.get(response_id); break;
327            case HISTORICAL_DATA_RESULT:    result = (HistoricalDataResult)response_cache.get(response_id); break;
328            case FIELD_INFO_RESULT:         result = (FieldInfoResult)response_cache.get(response_id); break;
329            case INTRADAY_TICK_RESULT:      result = (IntradayTickDataResult)response_cache.get(response_id); break;
330            case INTRADAY_BAR_RESULT:       result = (IntradayBarDataResult)response_cache.get(response_id); break;
331            default: throw new WrapperException("unknown result_type " + result_type);
332          }
333    
334          Element response = message.asElement();
335    
336          if (response.hasElement("responseError")) {
337            Element response_error = response.getElement("responseError");
338            logger.warning(response_error.toString());
339            throw new WrapperException("response error: " + response_error.getElementAsString("message"));
340          }
341          logger.fine("Processing response:\n" + response);
342          result.processResponse(response, logger, throw_invalid_ticker_error);
343        }
344      }
345    
346      public DataResult fieldInfo(String[] fields) throws Exception {
347        int response_id = (int)sendApiDataRequest(FIELD_INFO_RESULT, fields).value();
348        processEventLoop(FIELD_INFO_RESULT);
349        return((DataResult)response_cache.get(response_id));
350      }
351    
352      public DataResult blp(String[] securities, String[] fields) throws Exception {
353        String[] override_fields = new String[0];
354        String[] override_values = new String[0];
355        String[] option_names = new String[0];
356        String[] option_values = new String[0];
357        return(blp(securities, fields, override_fields, override_values, option_names, option_values));
358      }
359    
360      public DataResult blp(String[] securities, String[] fields, String[] override_fields, String[] override_values) throws Exception {
361        String[] option_names = new String[0];
362        String[] option_values = new String[0];
363        return(blp(securities, fields, override_fields, override_values, option_names, option_values));
364      }
365    
366      public DataResult blp(String[] securities, String[] fields, String[] override_fields, String[] override_values, String[] option_names, String[] option_values) throws Exception {
367        int response_id = (int)sendRefDataRequest(REFERENCE_DATA_RESULT, refdata_request_name, securities, fields, override_fields, override_values, option_names, option_values).value();
368        processEventLoop(REFERENCE_DATA_RESULT);
369        return((DataResult)response_cache.get(response_id));
370      }
371    
372      public DataResult blh(String security, String[] fields, String start_date, String end_date) throws Exception {
373        String[] override_fields = new String[0];
374        String[] override_values = new String[0];
375    
376        String[] option_names = {"startDate", "endDate"};
377        String[] option_values = new String[2];
378        option_values[0] = start_date;
379        option_values[1] = end_date;
380    
381        return(blh(security, fields, override_fields, override_values, option_names, option_values));
382      }
383    
384      public DataResult blh(String security, String[] fields, String start_date) throws Exception {
385        String[] override_fields = new String[0];
386        String[] override_values = new String[0];
387    
388        String[] option_names = {"startDate"};
389        String[] option_values = new String[1];
390        option_values[0] = start_date;
391    
392        return(blh(security, fields, override_fields, override_values, option_names, option_values));
393      }
394    
395      public DataResult blh(String security, String[] fields, String start_date, String end_date, String[] override_fields, String[] override_values) throws Exception {
396        String[] option_names = {"startDate", "endDate"};
397        String[] option_values = new String[2];
398        option_values[0] = start_date;
399        option_values[1] = end_date;
400    
401        return(blh(security, fields, override_fields, override_values, option_names, option_values));
402      }
403    
404      public DataResult blh(String security, String[] fields, String start_date, String[] override_fields, String[] override_values) throws Exception {
405        String[] option_names = {"startDate"};
406        String[] option_values = new String[1];
407        option_values[0] = start_date;
408    
409        return(blh(security, fields, override_fields, override_values, option_names, option_values));
410      }
411    
412      public DataResult blh(String security, String[] fields, String start_date, String[] override_fields, String[] override_values, String[] option_names, String[] option_values) throws Exception {
413    
414        int len = option_names.length;
415        String[] option_names_with_start = new String[len + 1];
416        String[] option_values_with_start = new String[len + 1];
417    
418        for (int i = 0; i < len; i++) {
419          option_names_with_start[i] = option_names[i];
420          option_values_with_start[i] = option_values[i];
421        }
422    
423        option_names_with_start[len] = "startDate";
424        option_values_with_start[len] = start_date;
425    
426        return(blh(security, fields, override_fields, override_values, option_names_with_start, option_values_with_start));
427      }
428    
429      public DataResult blh(String security, String[] fields, String start_date, String end_date, String[] override_fields, String[] override_values, String[] option_names, String[] option_values) throws Exception {
430    
431        int len = option_names.length;
432        String[] option_names_with_start = new String[len + 2];
433        String[] option_values_with_start = new String[len + 2];
434    
435        for (int i = 0; i < len; i++) {
436          option_names_with_start[i] = option_names[i];
437          option_values_with_start[i] = option_values[i];
438        }
439    
440        option_names_with_start[len] = "startDate";
441        option_values_with_start[len] = start_date;
442    
443        option_names_with_start[len+1] = "endDate";
444        option_values_with_start[len+1] = end_date;
445        
446        return(blh(security, fields, override_fields, override_values, option_names_with_start, option_values_with_start));
447      }
448    
449      public DataResult blh(String security, String[] fields, String[] override_fields, String[] override_values, String[] option_names, String[] option_values) throws Exception {
450        String[] securities = new String[1];
451        securities[0] = security;
452    
453        int response_id = (int)sendRefDataRequest(HISTORICAL_DATA_RESULT, histdata_request_name, securities, fields, override_fields, override_values, option_names, option_values).value();
454        processEventLoop(HISTORICAL_DATA_RESULT);
455        return((DataResult)response_cache.get(response_id));
456      }
457      
458      /**
459       * Request bulk data from [name redacted]. Shortcut method which allows you to call bls simply by passing a security and field.
460       * @param security A string containing security ticker.
461       * @param field A string containing field mnemonic.
462       */
463      public DataResult bls(String security, String field) throws Exception {
464        String[] override_fields = new String[0];
465        String[] override_values = new String[0];
466        String[] option_names = new String[0];
467        String[] option_values = new String[0];
468        return(bls(security, field, override_fields, override_values, option_names, option_values));
469      }
470    
471      /**
472       * Request bulk data from [name redacted]. Bulk data may return several different fields for a single requested field.
473       * @param security A string containing security ticker.
474       * @param field A string containing field mnemonic.
475       * @param override_fields Array of strings with field mnemonics for override fields.
476       * @param override_values Array of strings with override values, must be in same order as override_fields.
477       */
478      public DataResult bls(String security, String field, String[] override_fields, String[] override_values) throws Exception {
479        String[] option_names = new String[0];
480        String[] option_values = new String[0];
481        return(bls(security, field, override_fields, override_values, option_names, option_values));
482      }
483    
484      /**
485       * Request bulk data from [name redacted]. Bulk data may return several different fields for a single requested field.
486       * @param security A string containing security ticker.
487       * @param field A string containing field mnemonic.
488       * @param override_fields Array of strings with field mnemonics for override fields.
489       * @param override_values Array of strings with override values, must be in same order as override_fields.
490       * @param option_names Array of strings with option names.
491       * @param option_values Array of strings with option values, must be in same order as option_names.
492       */
493      public DataResult bls(String security, String field, String[] override_fields, String[] override_values, String[] option_names, String[] option_values) throws Exception {
494        String[] securities = new String[1];
495        securities[0] = security;
496    
497        String[] fields = new String[1];
498        fields[0] = field;
499    
500        int response_id = (int)sendRefDataRequest(BULK_DATA_RESULT, refdata_request_name, securities, fields, override_fields, override_values, option_names, option_values).value();
501        processEventLoop(BULK_DATA_RESULT);
502        return((DataResult)response_cache.get(response_id));
503      }
504    
505      public DataResult tick(String security, String[] event_types, String start_date_time, String end_date_time) throws Exception {
506        String[] option_names = new String[0];
507        String[] option_values = new String[0];
508    
509        return(tick(security, event_types, start_date_time, end_date_time, option_names, option_values));
510      }
511    
512      public DataResult tick(String security, String[] event_types, String start_date_time, String end_date_time, String[] option_names, String[] option_values) throws Exception {
513        String[] securities = new String[0];
514        String[] fields = new String[0];
515      
516        int len = option_names.length;
517        String[] option_names_with_start = new String[len + 3];
518        String[] option_values_with_start = new String[len + 3];
519        
520        for (int i = 0; i < option_names.length; i++) {
521          option_names_with_start[i] = option_names[i];
522          option_values_with_start[i] = option_values[i];
523        }
524    
525        option_names_with_start[len] = "security";
526        option_values_with_start[len] = security;
527        option_names_with_start[len+1] = "startDateTime";
528        option_values_with_start[len+1] = start_date_time;
529        option_names_with_start[len+2] = "endDateTime";
530        option_values_with_start[len+2] = end_date_time;
531    
532        String[] override_fields = new String[0];
533        String[] override_values = new String[0];
534    
535        int response_id = (int)sendRefDataRequest(INTRADAY_TICK_RESULT, intraday_tick_request_name, securities, fields, override_fields, override_values, option_names_with_start, option_values_with_start, event_types).value();
536        processEventLoop(INTRADAY_TICK_RESULT);
537        return((DataResult)response_cache.get(response_id));
538      }
539    
540      public DataResult bar(String security, String event_type, String start_date_time, String end_date_time, String interval) throws Exception {
541        String[] securities = new String[0];
542        String[] fields = new String[0];
543    
544        String[] option_names = new String[0];
545        String[] option_values = new String[0];
546        String[] override_fields = new String[0];
547        String[] override_values = new String[0];
548      
549        int len = option_names.length;
550        String[] option_names_with_start = new String[len + 5];
551        String[] option_values_with_start = new String[len + 5];
552        
553        for (int i = 0; i < option_names.length; i++) {
554          option_names_with_start[i] = option_names[i];
555          option_values_with_start[i] = option_values[i];
556        }
557    
558        option_names_with_start[len] = "security";
559        option_values_with_start[len] = security;
560        option_names_with_start[len+1] = "startDateTime";
561        option_values_with_start[len+1] = start_date_time;
562        option_names_with_start[len+2] = "endDateTime";
563        option_values_with_start[len+2] = end_date_time;
564        option_names_with_start[len+3] = "eventType";
565        option_values_with_start[len+3] = event_type;
566        option_names_with_start[len+4] = "interval";
567        option_values_with_start[len+4] = interval;
568    
569        int response_id = (int)sendRefDataRequest(INTRADAY_BAR_RESULT, intraday_bar_request_name, securities, fields, override_fields, override_values, option_names_with_start, option_values_with_start).value();
570        processEventLoop(INTRADAY_BAR_RESULT);
571        return((DataResult)response_cache.get(response_id));
572      }
573    }
574