package com.produban.openbus.storm; import java.io.BufferedReader; import java.io.FileReader; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import org.apache.log4j.Logger; import org.codehaus.jackson.map.ObjectMapper; import com.produban.openbus.webservice.CamposOrigen; import com.produban.openbus.webservice.MetricaOnLine; import backtype.storm.Config; import backtype.storm.Constants; import backtype.storm.task.OutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.base.BaseRichBolt; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; public class Tuple2Stream extends BaseRichBolt { /** * */ private static final long serialVersionUID = 1L; private List<MetricaOnLine> totalMetricas=null; private Map <String,ArrayList<MetricaOnLine>> metricasPorOrigen=new HashMap<String,ArrayList<MetricaOnLine>>(); private Map<String,Integer> posicionCampos=new HashMap<String,Integer>(); private String SEPARADOR = "\001"; private int tickFrequencyInSecs=5; private String urlServicioGetMetadata; public String metadataJson ; private boolean firstMetadataSinc; private OutputCollector collector; private static Logger LOG =Logger.getLogger(Tuple2Stream.class); public Tuple2Stream(int metadataSincroSecs,String urlServicioGetMetadata,String metadataJson){ tickFrequencyInSecs=metadataSincroSecs; this.urlServicioGetMetadata=urlServicioGetMetadata; this.metadataJson=metadataJson; init(); } public void init() { //inicializar la lectura de metadata actualizaMetadata(); firstMetadataSinc=true; } @Override public Map<String, Object> getComponentConfiguration() { Config conf = new Config(); if(tickFrequencyInSecs>0){ conf.put(Config.TOPOLOGY_TICK_TUPLE_FREQ_SECS, tickFrequencyInSecs); } return conf; } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public void execute(Tuple tupla) { // TODO Auto-generated method stub if(firstMetadataSinc){ sendMetricTuples(); firstMetadataSinc=false; } if(isTickTuple(tupla)){ //Sincronizamos la metadata con el servicio REST actualizaMetadata(); sendMetricTuples(); }else{ LOG.debug("Registro de datos"); //Es un registro de datos if(this.metricasPorOrigen !=null && !this.metricasPorOrigen.isEmpty()){ //obtenemos el origen de la tupla String origen = null; String[] camposOrigen= null; List<Object> objetos =tupla.getValues(); if (objetos.get(0) instanceof String){ camposOrigen=tupla.getString(0).split(SEPARADOR); }else if(objetos.get(0) instanceof byte[]){ camposOrigen = (new String((byte[]) tupla.getValues().get(0)).split(SEPARADOR)); } if(camposOrigen!= null && camposOrigen.length>2){ origen=camposOrigen[0]; MetricaOnLine metrica = null; List<MetricaOnLine> metricList = this.metricasPorOrigen.get(origen); if(metricList!=null){ LOG.debug("Hay una o m�s de una m�trica para el origen"); Iterator<MetricaOnLine> it = metricList.iterator(); //por cada m�trica asociada al origen, generamos un a tupla con la estructura necesaria while(it.hasNext()){ metrica=it.next(); String streamID=metrica.getStreamCep().getStreamName(); LOG.debug("Salida al stream: "+streamID); String[] camposMetrica = metrica.getStreamCep().getStreamFields().split(","); //Tama�o para el stream + todos los campos List valoresEnvio = new ArrayList(); List datos = new ArrayList(); List salida = new ArrayList<String>(); datos.add(streamID); for(int i=0;i<camposMetrica.length;i++){ //obtenemos el tipo de dato para darle tratamiento adecuado String formato=camposMetrica[i].replace("\t", " ").trim().toUpperCase().split(" ")[1]; camposMetrica[i]=camposMetrica[i].replace("\t", " ").trim().toUpperCase().split(" ")[0]; //los campos en la METADATA tienen que empezar por 1!!!! sino a�adimos un +1 String valor=camposOrigen[posicionCampos.get(origen+"-"+camposMetrica[i])]; if ((formato.toUpperCase().equals("INT") || formato.toUpperCase().equals("LONG") || formato.toUpperCase().equals("FLOAT") || formato.toUpperCase().equals("DOUBLE")) && valor.toUpperCase().equals("NULL") ){ valor="0"; } if(formato.toUpperCase().equals("INT")) { valoresEnvio.add(Integer.parseInt(valor)); }else if(formato.toUpperCase().equals("LONG")){ valoresEnvio.add(Long.parseLong(valor)); }else if(formato.toUpperCase().equals("FLOAT")){ valoresEnvio.add(Float.parseFloat(valor)); }else{ //como String valoresEnvio.add(valor); } //si esto da error ser� porque el usuario pone un campo que no existe } datos.add(valoresEnvio); salida.add("DATOS"); salida.add(datos); collector.emit(salida); } } } } collector.ack(tupla); } } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { // TODO Auto-generated method stub declarer.declare(new Fields("tipo","datos")); //Si el tipo es METADATA entonces datos es un objeto tipo MetricaOnLine //Si el tipo es DATOS entnces datos es un objeto tipo List[] con todos los campos; } private void actualizaMetadata(){ try { String json=""; if(this.urlServicioGetMetadata!=null ){ HttpClient httpClient = new DefaultHttpClient(); HttpGet requestGet = new HttpGet(this.urlServicioGetMetadata); HttpResponse response; metricasPorOrigen = new HashMap<String, ArrayList<MetricaOnLine>>();; response = httpClient.execute(requestGet); HttpEntity entity = response.getEntity(); json = EntityUtils.toString(entity); }else{ BufferedReader entrada; entrada = new BufferedReader(new FileReader(this.metadataJson)); String linea=entrada.readLine(); while(linea!=null){ json+=linea; linea=entrada.readLine(); } entrada.close(); } ObjectMapper mapper = new ObjectMapper(); this.totalMetricas = mapper.readValue(json,mapper.getTypeFactory().constructCollectionType(List.class, MetricaOnLine.class)); Iterator<MetricaOnLine> it = this.totalMetricas.iterator(); ArrayList<MetricaOnLine> lista; //Clasificamos las m�tricas por origen para recorrerlas de manera m�s eficiente MetricaOnLine metrica; while(it.hasNext()){ metrica = it.next(); //organizamos los campos por origen para agilizar las b�squedas String origen = metrica.getStreamCep().getOrigenEstructurado().getTopologyName(); if(metrica.getStreamCep().getOrigenEstructurado().getHsCamposOrigen()!=null && !metrica.getStreamCep().getOrigenEstructurado().getHsCamposOrigen().isEmpty()){ Iterator<CamposOrigen> itCampo = metrica.getStreamCep().getOrigenEstructurado().getHsCamposOrigen().iterator(); while(itCampo.hasNext()){ CamposOrigen campo = itCampo.next(); posicionCampos.put(origen+"-"+campo.getNombreCampo().toUpperCase(),campo.getOrdenEnTabla().intValue()); } } //si la lista de m�tricas no existe, la creamos a vac�a lista = metricasPorOrigen.get(origen); if(lista==null){ lista= new ArrayList<MetricaOnLine>(); } //insertamos la m�trica para el origende la iteraci�n lista.add(metrica); metricasPorOrigen.put(origen, lista); } } catch (Exception e) { //e.printStackTrace(); LOG.error("Error al acceder a la metadata"); //NOTA: Mostrar mensaje de error y continuar con la lista como estaba antes de la llamada } } private void sendMetricTuples(){ Iterator<MetricaOnLine> it = this.totalMetricas.iterator(); MetricaOnLine met ; while(it.hasNext()){ met = it.next(); List<Object> tupla = new ArrayList<Object>(); tupla.add("METADATA"); tupla.add(met); collector.emit(tupla); } } private static boolean isTickTuple(Tuple tuple) { return tuple.getSourceComponent().equals(Constants.SYSTEM_COMPONENT_ID) && tuple.getSourceStreamId().equals(Constants.SYSTEM_TICK_STREAM_ID); } @SuppressWarnings("rawtypes") @Override public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { // TODO Auto-generated method stub this.collector=collector; } }