├── .gitignore ├── src └── main │ ├── resources │ └── words.txt │ └── java │ ├── countword │ ├── spouts │ │ ├── SignalsSpout.java │ │ └── WordReader.java │ ├── TopologyMain.java │ └── bolts │ │ ├── WordNormalizer.java │ │ └── WordCounter.java │ └── drpc │ ├── AdderBolt.java │ └── DRPCTopologyMain.java ├── .settings ├── org.eclipse.jdt.core.prefs └── org.maven.ide.eclipse.prefs ├── .project └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.classpath 3 | -------------------------------------------------------------------------------- /src/main/resources/words.txt: -------------------------------------------------------------------------------- 1 | storm 2 | test 3 | are 4 | great 5 | is 6 | an 7 | storm 8 | simple 9 | application 10 | but 11 | very 12 | powerfull 13 | really 14 | StOrm 15 | is 16 | great 17 | 18 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | #Sun Apr 22 00:24:03 ART 2012 2 | eclipse.preferences.version=1 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 4 | org.eclipse.jdt.core.compiler.compliance=1.6 5 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 6 | org.eclipse.jdt.core.compiler.source=1.6 7 | -------------------------------------------------------------------------------- /.settings/org.maven.ide.eclipse.prefs: -------------------------------------------------------------------------------- 1 | #Wed Feb 01 00:58:33 ART 2012 2 | activeProfiles= 3 | eclipse.preferences.version=1 4 | fullBuildGoals=process-test-resources 5 | includeModules=false 6 | resolveWorkspaceProjects=true 7 | resourceFilterGoals=process-resources resources\:testResources 8 | skipCompilerPlugin=true 9 | version=1 10 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ch03-topologies 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.maven.ide.eclipse.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.maven.ide.eclipse.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/countword/spouts/SignalsSpout.java: -------------------------------------------------------------------------------- 1 | package countword.spouts; 2 | 3 | import java.util.Map; 4 | 5 | import backtype.storm.spout.SpoutOutputCollector; 6 | import backtype.storm.task.TopologyContext; 7 | import backtype.storm.topology.OutputFieldsDeclarer; 8 | import backtype.storm.topology.base.BaseRichSpout; 9 | import backtype.storm.tuple.Fields; 10 | import backtype.storm.tuple.Values; 11 | 12 | public class SignalsSpout extends BaseRichSpout{ 13 | 14 | private SpoutOutputCollector collector; 15 | 16 | 17 | @Override 18 | public void nextTuple() { 19 | collector.emit("signals",new Values("refreshCache")); 20 | try { 21 | Thread.sleep(1000); 22 | } catch (InterruptedException e) {} 23 | } 24 | 25 | @Override 26 | public void open(Map conf, TopologyContext context, 27 | SpoutOutputCollector collector) { 28 | this.collector = collector; 29 | } 30 | 31 | @Override 32 | public void declareOutputFields(OutputFieldsDeclarer declarer) { 33 | declarer.declareStream("signals",new Fields("action")); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | storm.book 4 | Topologies 5 | 0.0.1-SNAPSHOT 6 | 7 | 8 | 9 | 10 | org.apache.maven.plugins 11 | maven-compiler-plugin 12 | 2.3.2 13 | 14 | 1.6 15 | 1.6 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | clojars.org 26 | http://clojars.org/repo 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | storm 36 | storm 37 | 0.7.1 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/countword/TopologyMain.java: -------------------------------------------------------------------------------- 1 | package countword; 2 | 3 | import countword.spouts.SignalsSpout; 4 | import countword.spouts.WordReader; 5 | import countword.bolts.WordCounter; 6 | import countword.bolts.WordNormalizer; 7 | import backtype.storm.Config; 8 | import backtype.storm.LocalCluster; 9 | import backtype.storm.topology.TopologyBuilder; 10 | import backtype.storm.tuple.Fields; 11 | 12 | 13 | public class TopologyMain { 14 | public static void main(String[] args) throws InterruptedException { 15 | 16 | //Topology definition 17 | TopologyBuilder builder = new TopologyBuilder(); 18 | builder.setSpout("word-reader",new WordReader()); 19 | builder.setSpout("signals-spout",new SignalsSpout()); 20 | builder.setBolt("word-normalizer", new WordNormalizer()) 21 | .shuffleGrouping("word-reader"); 22 | 23 | builder.setBolt("word-counter", new WordCounter(),2) 24 | .fieldsGrouping("word-normalizer",new Fields("word")) 25 | .allGrouping("signals-spout","signals"); 26 | 27 | 28 | //Configuration 29 | Config conf = new Config(); 30 | conf.put("wordsFile", args[0]); 31 | conf.setDebug(true); 32 | //Topology run 33 | conf.put(Config.TOPOLOGY_MAX_SPOUT_PENDING, 1); 34 | LocalCluster cluster = new LocalCluster(); 35 | cluster.submitTopology("Count-Word-Toplogy-With-Refresh-Cache", conf, builder.createTopology()); 36 | Thread.sleep(5000); 37 | cluster.shutdown(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/drpc/AdderBolt.java: -------------------------------------------------------------------------------- 1 | package drpc; 2 | 3 | import java.security.InvalidParameterException; 4 | import java.util.Map; 5 | 6 | import backtype.storm.task.OutputCollector; 7 | import backtype.storm.task.TopologyContext; 8 | import backtype.storm.topology.BasicOutputCollector; 9 | import backtype.storm.topology.IRichBolt; 10 | import backtype.storm.topology.OutputFieldsDeclarer; 11 | import backtype.storm.topology.base.BaseBasicBolt; 12 | import backtype.storm.topology.base.BaseRichBolt; 13 | import backtype.storm.tuple.Fields; 14 | import backtype.storm.tuple.Tuple; 15 | import backtype.storm.tuple.Values; 16 | 17 | public class AdderBolt extends BaseBasicBolt{ 18 | 19 | private static final Object NULL = "NULL"; 20 | private OutputCollector collector; 21 | 22 | @Override 23 | public void execute(Tuple input, BasicOutputCollector collector) { 24 | //Parse the add expression 25 | String[] numbers = (String[]) input.getString(1).split("\\+"); 26 | Integer added = 0; 27 | try{ 28 | if(numbers.length<2){ 29 | throw new InvalidParameterException("Should be at least 2 numbers"); 30 | } 31 | for(String num : numbers){ 32 | //Add each member 33 | added += Integer.parseInt((java.lang.String) num); 34 | } 35 | }catch(Exception e){ 36 | //On error emit null 37 | collector.emit(new Values(input.getValue(0),NULL)); 38 | } 39 | //Emit the result 40 | collector.emit(new Values(input.getValue(0),added)); 41 | } 42 | 43 | @Override 44 | public void declareOutputFields(OutputFieldsDeclarer declarer) { 45 | declarer.declare(new Fields("id","result")); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/drpc/DRPCTopologyMain.java: -------------------------------------------------------------------------------- 1 | package drpc; 2 | 3 | import backtype.storm.Config; 4 | import backtype.storm.LocalCluster; 5 | import backtype.storm.LocalDRPC; 6 | import backtype.storm.drpc.LinearDRPCTopologyBuilder; 7 | import backtype.storm.utils.DRPCClient; 8 | 9 | /** 10 | * DRPC example 11 | * 12 | * @author Storm-Book 13 | * 14 | */ 15 | public class DRPCTopologyMain { 16 | 17 | public static void main(String[] args) { 18 | //Create the local drpc client/server 19 | LocalDRPC drpc = new LocalDRPC(); 20 | 21 | //Create the drpc topology 22 | LinearDRPCTopologyBuilder builder = new LinearDRPCTopologyBuilder("add"); 23 | builder.addBolt(new AdderBolt(),2); 24 | 25 | Config conf = new Config(); 26 | conf.setDebug(true); 27 | 28 | //Create cluster and submit the topology 29 | LocalCluster cluster = new LocalCluster(); 30 | cluster.submitTopology("drpc-adder-topology", conf, builder.createLocalTopology(drpc)); 31 | 32 | //Test the topology 33 | String result = drpc.execute("add", "1+-1"); 34 | checkResult(result,0); 35 | result = drpc.execute("add", "1+1+5+10"); 36 | 37 | //Finish and shutdown 38 | checkResult(result,17); 39 | cluster.shutdown(); 40 | drpc.shutdown(); 41 | } 42 | 43 | private static boolean checkResult(String result, int expected) { 44 | if(result != null && !result.equals("NULL")){ 45 | if(Integer.parseInt(result) == expected){ 46 | System.out.println("Add valid [result: "+result+"]"); 47 | return true; 48 | }else{ 49 | System.err.print("Invalid result ["+result+"]"); 50 | } 51 | }else{ 52 | System.err.println("There was an error running the drpc call"); 53 | } 54 | return false; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/countword/bolts/WordNormalizer.java: -------------------------------------------------------------------------------- 1 | package countword.bolts; 2 | 3 | import java.util.Map; 4 | 5 | import backtype.storm.task.OutputCollector; 6 | import backtype.storm.task.TopologyContext; 7 | import backtype.storm.topology.OutputFieldsDeclarer; 8 | import backtype.storm.topology.base.BaseRichBolt; 9 | import backtype.storm.tuple.Fields; 10 | import backtype.storm.tuple.Tuple; 11 | import backtype.storm.tuple.Values; 12 | 13 | public class WordNormalizer extends BaseRichBolt { 14 | 15 | private OutputCollector collector; 16 | int numCounterTasks=0; 17 | public void cleanup() {} 18 | 19 | /** 20 | * The bolt will receive the line from the 21 | * words file and process it to Normalize this line 22 | * 23 | * The normalize will be put the words in lower case 24 | * and split the line to get all words in this 25 | */ 26 | public void execute(Tuple input) { 27 | String sentence = input.getString(0); 28 | String[] words = sentence.split(" "); 29 | for(String word : words){ 30 | word = word.trim(); 31 | if(!word.isEmpty()){ 32 | word = word.toLowerCase(); 33 | collector.emit(new Values(word)); 34 | } 35 | } 36 | // Acknowledge the tuple 37 | collector.ack(input); 38 | } 39 | public void prepare(Map stormConf, TopologyContext context, 40 | OutputCollector collector) { 41 | this.collector = collector; 42 | this.numCounterTasks = context.getComponentTasks("word-counter").size(); 43 | } 44 | 45 | /** 46 | * The bolt will only emit the field "word" 47 | */ 48 | public void declareOutputFields(OutputFieldsDeclarer declarer) { 49 | declarer.declare(new Fields("word")); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/countword/bolts/WordCounter.java: -------------------------------------------------------------------------------- 1 | package countword.bolts; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import backtype.storm.task.OutputCollector; 7 | import backtype.storm.task.TopologyContext; 8 | import backtype.storm.topology.OutputFieldsDeclarer; 9 | import backtype.storm.topology.base.BaseRichBolt; 10 | import backtype.storm.tuple.Tuple; 11 | 12 | public class WordCounter extends BaseRichBolt { 13 | 14 | Integer id; 15 | String name; 16 | Map counters; 17 | private OutputCollector collector; 18 | 19 | /** 20 | * At the end of the spout (when the cluster is shutdown 21 | * We will show the word counters 22 | */ 23 | @Override 24 | public void cleanup() { 25 | System.out.println("-- Word Counter ["+name+"-"+id+"] --"); 26 | for(Map.Entry entry : counters.entrySet()){ 27 | System.out.println(entry.getKey()+": "+entry.getValue()); 28 | } 29 | } 30 | 31 | /** 32 | * On each word We will count 33 | */ 34 | @Override 35 | public void execute(Tuple input) { 36 | String str = null; 37 | try{ 38 | str = input.getStringByField("word"); 39 | }catch (IllegalArgumentException e) { 40 | //Do nothing 41 | } 42 | 43 | if(str!=null){ 44 | /** 45 | * If the word dosn't exist in the map we will create 46 | * this, if not We will add 1 47 | */ 48 | if(!counters.containsKey(str)){ 49 | counters.put(str, 1); 50 | }else{ 51 | Integer c = counters.get(str) + 1; 52 | counters.put(str, c); 53 | } 54 | }else{ 55 | if(input.getSourceStreamId().equals("signals")){ 56 | str = input.getStringByField("action"); 57 | if("refreshCache".equals(str)) 58 | counters.clear(); 59 | } 60 | } 61 | //Set the tuple as Acknowledge 62 | collector.ack(input); 63 | } 64 | 65 | /** 66 | * On create 67 | */ 68 | @Override 69 | public void prepare(Map stormConf, TopologyContext context, 70 | OutputCollector collector) { 71 | this.counters = new HashMap(); 72 | this.collector = collector; 73 | this.name = context.getThisComponentId(); 74 | this.id = context.getThisTaskId(); 75 | } 76 | 77 | @Override 78 | public void declareOutputFields(OutputFieldsDeclarer declarer) {} 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/countword/spouts/WordReader.java: -------------------------------------------------------------------------------- 1 | package countword.spouts; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileNotFoundException; 5 | import java.io.FileReader; 6 | import java.util.Map; 7 | import backtype.storm.spout.SpoutOutputCollector; 8 | import backtype.storm.task.TopologyContext; 9 | import backtype.storm.topology.IRichSpout; 10 | import backtype.storm.topology.OutputFieldsDeclarer; 11 | import backtype.storm.topology.base.BaseRichSpout; 12 | import backtype.storm.tuple.Fields; 13 | import backtype.storm.tuple.Values; 14 | 15 | public class WordReader extends BaseRichSpout { 16 | 17 | private SpoutOutputCollector collector; 18 | private FileReader fileReader; 19 | private boolean completed = false; 20 | private TopologyContext context; 21 | 22 | public void ack(Object msgId) { 23 | System.out.println("OK:"+msgId); 24 | } 25 | public void close() {} 26 | public void fail(Object msgId) { 27 | System.out.println("FAIL:"+msgId); 28 | } 29 | 30 | /** 31 | * The only thing that the methods will do It is emit each 32 | * file line 33 | */ 34 | public void nextTuple() { 35 | /** 36 | * The nextuple it is called forever, so if we have been readed the file 37 | * we will wait and then return 38 | */ 39 | if(completed){ 40 | try { 41 | Thread.sleep(1000); 42 | } catch (InterruptedException e) { 43 | //Do nothing 44 | } 45 | return; 46 | } 47 | String str; 48 | //Open the reader 49 | BufferedReader reader = new BufferedReader(fileReader); 50 | try{ 51 | //Read all lines 52 | while((str = reader.readLine()) != null){ 53 | /** 54 | * By each line emmit a new value with the line as a their 55 | */ 56 | this.collector.emit(new Values(str),str); 57 | } 58 | }catch(Exception e){ 59 | throw new RuntimeException("Error reading tuple",e); 60 | }finally{ 61 | completed = true; 62 | } 63 | } 64 | 65 | /** 66 | * We will create the file and get the collector object 67 | */ 68 | public void open(Map conf, TopologyContext context, 69 | SpoutOutputCollector collector) { 70 | try { 71 | this.context = context; 72 | this.fileReader = new FileReader(conf.get("wordsFile").toString()); 73 | } catch (FileNotFoundException e) { 74 | throw new RuntimeException("Error reading file ["+conf.get("wordFile")+"]"); 75 | } 76 | this.collector = collector; 77 | } 78 | 79 | /** 80 | * Declare the output field "word" 81 | */ 82 | public void declareOutputFields(OutputFieldsDeclarer declarer) { 83 | declarer.declare(new Fields("line")); 84 | } 85 | } 86 | --------------------------------------------------------------------------------