com.jtech.iaf.CityBikesTransport.java Source code

Java tutorial

Introduction

Here is the source code for com.jtech.iaf.CityBikesTransport.java

Source

// Cycle Monitor, Copyright (C) 2015  M.B.Grieve
// This file is part of the Cycle Monitor example application.
//
// Cycle Monitor is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Cycle Monitor is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with City Monitor.  If not, see <http://www.gnu.org/licenses/>.
//
// Contact: moray.grieve@me.com

package com.jtech.iaf;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import it.sauronsoftware.cron4j.Scheduler;

import com.apama.util.Logger;
import com.apama.util.TimestampSet;
import com.apama.iaf.plugin.AbstractEventTransport;
import com.apama.iaf.plugin.EventDecoder;
import com.apama.iaf.plugin.EventTransport;
import com.apama.iaf.plugin.EventTransportProperty;
import com.apama.iaf.plugin.NormalisedEvent;
import com.apama.iaf.plugin.TimestampConfig;
import com.apama.iaf.plugin.TransportException;
import com.apama.iaf.plugin.TransportStatus;

public class CityBikesTransport extends AbstractEventTransport {
    private static final String PROPERTY_CITY_NAME = "cityName";
    private static final String PROPERTY_DATA_URL = "dataURL";
    private static final String PROPERTY_POLLING_SCHEDULE = "pollingSchedule";

    private Logger logger;
    private EventDecoder decoder;
    private String cityName;
    private String dataURL;
    private String pollingSchedule;
    private volatile boolean startRequested;
    private volatile long totalSent;
    private volatile long totalReceived;
    private Scheduler scheduler;
    private ExecutorService exService;;

    public CityBikesTransport(String name, EventTransportProperty[] properties, TimestampConfig timestampConfig)
            throws TransportException {
        super(name, properties, timestampConfig);
        logger = Logger.getLogger(CityBikesTransport.class);
        updateProperties(properties, timestampConfig);
    }

    public synchronized void updateProperties(EventTransportProperty[] properties, TimestampConfig timestampConfig)
            throws TransportException {
        super.updateProperties(properties, timestampConfig);

        for (EventTransportProperty property : properties) {
            String name = property.getName();
            String value = property.getValue();

            if (PROPERTY_CITY_NAME.equals(name)) {
                cityName = value != null ? value : "";
                logger.info("Set city name to " + cityName);
            } else if (PROPERTY_DATA_URL.equals(name)) {
                dataURL = value;
                logger.info("Set data URL to " + dataURL);
            } else if (PROPERTY_POLLING_SCHEDULE.equals(name)) {
                pollingSchedule = value;
                logger.info("Set polling schedule to " + pollingSchedule);
            } else {
                continue;
            }
        }
    }

    @Override
    public int getAPIVersion() {
        return EventTransport.API_VERSION;
    }

    @Override
    public TransportStatus getStatus() {
        String status = (startRequested) ? "Plugin is started" : "Plugin is stopped";
        return new TransportStatus(status, totalReceived, totalSent);
    }

    @Override
    public synchronized void addEventDecoder(String name, EventDecoder decoder) throws TransportException {
        this.decoder = decoder;
    }

    @Override
    public synchronized void removeEventDecoder(String name) throws TransportException {
        decoder = null;
    }

    @Override
    public void sendTransportEvent(Object event, TimestampSet timestampSet) throws TransportException {
        totalReceived++;

        if (!(event instanceof NormalisedEvent)) {
            throw new TransportException("Invalid event type " + event.getClass(),
                    TransportException.DECODINGFAILURE);
        }

        NormalisedEvent normalisedEvent = (NormalisedEvent) event;
        String type = normalisedEvent.findValue("__type");
        if (type != null && type.equals("Start")) {
            if (startRequested)
                return;
            logger.info("Received request to start");
            startRequested = true;
            exService = Executors.newSingleThreadExecutor();
            scheduler = new Scheduler();
            poll();

            if (pollingSchedule != null && !pollingSchedule.equals("none")) {
                scheduler.schedule(pollingSchedule, new Runnable() {
                    public void run() {
                        poll();
                    }
                });
                scheduler.start();
            }
        } else if (type != null && type.equals("Stop")) {
            if (!startRequested)
                return;
            logger.info("Received request to stop");
            startRequested = false;
            exService.shutdownNow();
            scheduler.stop();
        } else if (type != null && type.equals("Poll")) {
            if (!startRequested)
                return;
            logger.info("Received request to poll for data");
            poll();
        }
    }

    @Override
    public void start() throws TransportException {
        logger.info("Adapter start called");
        if (startRequested)
            poll();
    }

    @Override
    public void stop() throws TransportException {
        logger.info("Adapter stop called");
    }

    @Override
    public void cleanup() throws TransportException {
    }

    private synchronized void poll() {
        exService.execute(new Runnable() {
            public void run() {
                JSONArray jsonArray = getJSON();
                processJSON(jsonArray);
            }
        });
    }

    private JSONArray getJSON() {
        try {
            URL page = new URL(dataURL);
            URLConnection urlc = (URLConnection) page.openConnection();
            BufferedReader br = new BufferedReader(new InputStreamReader(urlc.getInputStream()));
            JSONParser parser = new JSONParser();
            return (JSONArray) parser.parse(br);
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return null;
    }

    private void processJSON(JSONArray jsonArray) {
        try {
            for (Object o : jsonArray) {
                JSONObject entry = (JSONObject) o;
                Long id = (Long) entry.get("id");
                String name = (String) entry.get("name");

                Pattern regex = Pattern.compile("^(\\d+)\\s?-\\s?(.*)");
                Matcher regexMatcher = regex.matcher(name);
                while (regexMatcher.find()) {
                    name = regexMatcher.group(2);
                }

                Double lat = Double.valueOf((Long) entry.get("lat")) / 1000000d;
                Double lng = Double.valueOf((Long) entry.get("lng")) / 1000000d;
                Long avail = (Long) entry.get("bikes");
                Long empty = (Long) entry.get("free");
                String updated = (String) entry.get("timestamp");

                NormalisedEvent normalisedEvent = new NormalisedEvent();
                normalisedEvent.addQuick("__type", "__station_update");
                normalisedEvent.addQuick("city", cityName);
                normalisedEvent.addQuick("id", id.toString());
                normalisedEvent.addQuick("name", name);
                normalisedEvent.addQuick("lat", lat.toString());
                normalisedEvent.addQuick("lng", lng.toString());
                normalisedEvent.addQuick("updated", updated);
                normalisedEvent.addQuick("ratio",
                        String.format("%.2f", Double.valueOf(avail) / Double.valueOf((avail + empty))));
                normalisedEvent.addQuick("docked", avail.toString());
                normalisedEvent.addQuick("empty", empty.toString());
                normalisedEvent.addQuick("timestamp", String.valueOf(System.currentTimeMillis() / 1000));

                logger.info("Sending downstream transport event to data codec: " + normalisedEvent);

                TimestampSet timestampSet = new TimestampSet();
                decoder.sendTransportEvent(normalisedEvent, timestampSet);
                totalReceived++;
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }
}