fluo-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ktur...@apache.org
Subject incubator-fluo-website git commit: Update excercise 1 to add CFM and instructions on running in live instance.
Date Wed, 30 Nov 2016 00:25:22 GMT
Repository: incubator-fluo-website
Updated Branches:
  refs/heads/gh-pages e0d01dc3d -> 31abb2c21


Update excercise 1 to add CFM and instructions on running in live instance.


Project: http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/commit/31abb2c2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/tree/31abb2c2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/diff/31abb2c2

Branch: refs/heads/gh-pages
Commit: 31abb2c210c9050dbedd3383accfc686675e4f4c
Parents: e0d01dc
Author: Keith Turner <kturner@apache.org>
Authored: Mon Nov 28 18:27:52 2016 -0500
Committer: Keith Turner <kturner@apache.org>
Committed: Tue Nov 29 19:20:27 2016 -0500

----------------------------------------------------------------------
 tour/exercise-1.md | 544 ++++++++++++++++++++++++++++++++++--------------
 1 file changed, 392 insertions(+), 152 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/31abb2c2/tour/exercise-1.md
----------------------------------------------------------------------
diff --git a/tour/exercise-1.md b/tour/exercise-1.md
index ff16126..39d28f7 100644
--- a/tour/exercise-1.md
+++ b/tour/exercise-1.md
@@ -1,5 +1,5 @@
 ---
-title: Word count Exercise
+title: Word counts for unique documents exercise
 ---
 
 This exercise gives you an opportunity to use everything you have learned so
@@ -138,11 +138,8 @@ Add the following to the ft.Main class.
     //line.
     mini.waitForObservers();
 
-    System.out.println("**** begin table dump ****");
-    try (Snapshot snap = client.newSnapshot()) {
-      snap.scanner().build().forEach(rcv -> System.out.println("  " + rcv));
-    }
-    System.out.println("**** end table dump ****\n");
+    FluoITHelper.printFluoTable(client);
+    System.out.println();
   }
 
   private static void excercise(MiniFluo mini, FluoClient client) {
@@ -155,46 +152,46 @@ Add the following to the ft.Main class.
 Once the TODOs in the DocLoader class are implemented, running Main should print out the
following.
 
 ```
-**** begin table dump ****
-  d:a6c4d1f doc content  Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
-  d:a6c4d1f doc refc  1
-  d:a6c4d1f doc refs  referenced
-  d:cf8ddc0 doc content  Bill plans to rescue Jebediah after taking tourist to Minimus.
-  d:cf8ddc0 doc refc  1
-  d:cf8ddc0 doc refs  referenced
-  u:http://news.com/a23 uri hash  a6c4d1f
-  u:http://news.com/a24 uri hash  cf8ddc0
-**** end table dump ****
-
-**** begin table dump ****
-  d:a6c4d1f doc content  Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
-  d:a6c4d1f doc refc  2
-  d:a6c4d1f doc refs  referenced
-  d:cf8ddc0 doc content  Bill plans to rescue Jebediah after taking tourist to Minimus.
-  d:cf8ddc0 doc refc  1
-  d:cf8ddc0 doc refs  referenced
-  u:http://news.com/a23 uri hash  a6c4d1f
-  u:http://news.com/a24 uri hash  cf8ddc0
-  u:http://oldnews.com/a23 uri hash  a6c4d1f
-**** end table dump ****
-
-**** begin table dump ****
-  d:2732ebc doc content  Crisis at KSC.  Tourist stuck at Minimus.  Bill forgot solar panels.
-  d:2732ebc doc refc  1
-  d:2732ebc doc refs  referenced
-  d:6658252 doc content  Jebediah orbits Mun for 38 days.  No power, forgot solar panels.
-  d:6658252 doc refc  1
-  d:6658252 doc refs  referenced
-  d:a6c4d1f doc content  Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
-  d:a6c4d1f doc refc  1
-  d:a6c4d1f doc refs  referenced
-  d:cf8ddc0 doc content  Bill plans to rescue Jebediah after taking tourist to Minimus.
-  d:cf8ddc0 doc refc  0
-  d:cf8ddc0 doc refs  unreferenced
-  u:http://news.com/a23 uri hash  6658252
-  u:http://news.com/a24 uri hash  2732ebc
-  u:http://oldnews.com/a23 uri hash  a6c4d1f
-**** end table dump ****
+== fluo start ==
+d:a6c4d1f doc content	Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
+d:a6c4d1f doc refc	1
+d:a6c4d1f doc refs	referenced
+d:cf8ddc0 doc content	Bill plans to rescue Jebediah after taking tourist to Minimus.
+d:cf8ddc0 doc refc	1
+d:cf8ddc0 doc refs	referenced
+u:http://news.com/a23 uri hash	a6c4d1f
+u:http://news.com/a24 uri hash	cf8ddc0
+=== fluo end ===
+
+== fluo start ==
+d:a6c4d1f doc content	Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
+d:a6c4d1f doc refc	2
+d:a6c4d1f doc refs	referenced
+d:cf8ddc0 doc content	Bill plans to rescue Jebediah after taking tourist to Minimus.
+d:cf8ddc0 doc refc	1
+d:cf8ddc0 doc refs	referenced
+u:http://news.com/a23 uri hash	a6c4d1f
+u:http://news.com/a24 uri hash	cf8ddc0
+u:http://oldnews.com/a23 uri hash	a6c4d1f
+=== fluo end ===
+
+== fluo start ==
+d:2732ebc doc content	Crisis at KSC.  Tourist stuck at Minimus.  Bill forgot solar panels.
+d:2732ebc doc refc	1
+d:2732ebc doc refs	referenced
+d:6658252 doc content	Jebediah orbits Mun for 38 days.  No power, forgot solar panels.
+d:6658252 doc refc	1
+d:6658252 doc refs	referenced
+d:a6c4d1f doc content	Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
+d:a6c4d1f doc refc	1
+d:a6c4d1f doc refs	referenced
+d:cf8ddc0 doc content	Bill plans to rescue Jebediah after taking tourist to Minimus.
+d:cf8ddc0 doc refc	0
+d:cf8ddc0 doc refs	unreferenced
+u:http://news.com/a23 uri hash	6658252
+u:http://news.com/a24 uri hash	2732ebc
+u:http://oldnews.com/a23 uri hash	a6c4d1f
+=== fluo end ===
 ```
 
 ## Part 2 : Computing word counts.
@@ -240,14 +237,15 @@ public class ContentObserver extends AbstractObserver {
   /**
    * Utility method to tokenize the content of a document into unique words.
    */
-  private Set<String> tokenize(String content) {
-    return new HashSet<String>(Arrays.asList(content.split("[ .!,]+")));
+  private List<String> tokenize(String content) {
+    return Arrays.asList(content.split("[\\W]+"));
   }
 
   /**
    *  Adds the passed to delta to the values for each word.
    */
-  private void adjustCounts(TransactionBase tx, int delta, Set<String> words) {
+  private void adjustCounts(TransactionBase tx, int delta, List<String> words) {
+     Set<String> uniqueWords = new HashSet<String>(words);
     // TODO make a single call to get all of the current word counts.  Could use
     //tx.gets(Collection<RowColumn>)
 
@@ -297,113 +295,355 @@ observer as follows.
 After implementing the Observer, the output of the program should look like the following.
 
 ```
-**** begin table dump ****
-  d:a6c4d1f doc content  Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
-  d:a6c4d1f doc processed  true
-  d:a6c4d1f doc refc  1
-  d:a6c4d1f doc refs  referenced
-  d:cf8ddc0 doc content  Bill plans to rescue Jebediah after taking tourist to Minimus.
-  d:cf8ddc0 doc processed  true
-  d:cf8ddc0 doc refc  1
-  d:cf8ddc0 doc refs  referenced
-  u:http://news.com/a23 uri hash  a6c4d1f
-  u:http://news.com/a24 uri hash  cf8ddc0
-  w:35 word docCount  1
-  w:Bill word docCount  1
-  w:Jebediah word docCount  2
-  w:Minimus word docCount  1
-  w:Mun word docCount  1
-  w:No word docCount  1
-  w:after word docCount  1
-  w:days word docCount  1
-  w:for word docCount  1
-  w:forgot word docCount  1
-  w:orbits word docCount  1
-  w:panels word docCount  1
-  w:plans word docCount  1
-  w:power word docCount  1
-  w:rescue word docCount  1
-  w:solar word docCount  1
-  w:taking word docCount  1
-  w:to word docCount  1
-  w:tourist word docCount  1
-**** end table dump ****
-
-**** begin table dump ****
-  d:a6c4d1f doc content  Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
-  d:a6c4d1f doc processed  true
-  d:a6c4d1f doc refc  2
-  d:a6c4d1f doc refs  referenced
-  d:cf8ddc0 doc content  Bill plans to rescue Jebediah after taking tourist to Minimus.
-  d:cf8ddc0 doc processed  true
-  d:cf8ddc0 doc refc  1
-  d:cf8ddc0 doc refs  referenced
-  u:http://news.com/a23 uri hash  a6c4d1f
-  u:http://news.com/a24 uri hash  cf8ddc0
-  u:http://oldnews.com/a23 uri hash  a6c4d1f
-  w:35 word docCount  1
-  w:Bill word docCount  1
-  w:Jebediah word docCount  2
-  w:Minimus word docCount  1
-  w:Mun word docCount  1
-  w:No word docCount  1
-  w:after word docCount  1
-  w:days word docCount  1
-  w:for word docCount  1
-  w:forgot word docCount  1
-  w:orbits word docCount  1
-  w:panels word docCount  1
-  w:plans word docCount  1
-  w:power word docCount  1
-  w:rescue word docCount  1
-  w:solar word docCount  1
-  w:taking word docCount  1
-  w:to word docCount  1
-  w:tourist word docCount  1
-**** end table dump ****
-
-**** begin table dump ****
-  d:2732ebc doc content  Crisis at KSC.  Tourist stuck at Minimus.  Bill forgot solar panels.
-  d:2732ebc doc processed  true
-  d:2732ebc doc refc  1
-  d:2732ebc doc refs  referenced
-  d:6658252 doc content  Jebediah orbits Mun for 38 days.  No power, forgot solar panels.
-  d:6658252 doc processed  true
-  d:6658252 doc refc  1
-  d:6658252 doc refs  referenced
-  d:a6c4d1f doc content  Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
-  d:a6c4d1f doc processed  true
-  d:a6c4d1f doc refc  1
-  d:a6c4d1f doc refs  referenced
-  u:http://news.com/a23 uri hash  6658252
-  u:http://news.com/a24 uri hash  2732ebc
-  u:http://oldnews.com/a23 uri hash  a6c4d1f
-  w:35 word docCount  1
-  w:38 word docCount  1
-  w:Bill word docCount  1
-  w:Crisis word docCount  1
-  w:Jebediah word docCount  2
-  w:KSC word docCount  1
-  w:Minimus word docCount  1
-  w:Mun word docCount  2
-  w:No word docCount  2
-  w:Tourist word docCount  1
-  w:at word docCount  1
-  w:days word docCount  2
-  w:for word docCount  2
-  w:forgot word docCount  3
-  w:orbits word docCount  2
-  w:panels word docCount  3
-  w:power word docCount  2
-  w:solar word docCount  3
-  w:stuck word docCount  1
-**** end table dump ****
+== fluo start ==
+d:a6c4d1f doc content	Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
+d:a6c4d1f doc processed	true
+d:a6c4d1f doc refc	1
+d:a6c4d1f doc refs	referenced
+d:cf8ddc0 doc content	Bill plans to rescue Jebediah after taking tourist to Minimus.
+d:cf8ddc0 doc processed	true
+d:cf8ddc0 doc refc	1
+d:cf8ddc0 doc refs	referenced
+u:http://news.com/a23 uri hash	a6c4d1f
+u:http://news.com/a24 uri hash	cf8ddc0
+w:35 word docCount	1
+w:Bill word docCount	1
+w:Jebediah word docCount	2
+w:Minimus word docCount	1
+w:Mun word docCount	1
+w:No word docCount	1
+w:after word docCount	1
+w:days word docCount	1
+w:for word docCount	1
+w:forgot word docCount	1
+w:orbits word docCount	1
+w:panels word docCount	1
+w:plans word docCount	1
+w:power word docCount	1
+w:rescue word docCount	1
+w:solar word docCount	1
+w:taking word docCount	1
+w:to word docCount	1
+w:tourist word docCount	1
+=== fluo end ===
+
+== fluo start ==
+d:a6c4d1f doc content	Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
+d:a6c4d1f doc processed	true
+d:a6c4d1f doc refc	2
+d:a6c4d1f doc refs	referenced
+d:cf8ddc0 doc content	Bill plans to rescue Jebediah after taking tourist to Minimus.
+d:cf8ddc0 doc processed	true
+d:cf8ddc0 doc refc	1
+d:cf8ddc0 doc refs	referenced
+u:http://news.com/a23 uri hash	a6c4d1f
+u:http://news.com/a24 uri hash	cf8ddc0
+u:http://oldnews.com/a23 uri hash	a6c4d1f
+w:35 word docCount	1
+w:Bill word docCount	1
+w:Jebediah word docCount	2
+w:Minimus word docCount	1
+w:Mun word docCount	1
+w:No word docCount	1
+w:after word docCount	1
+w:days word docCount	1
+w:for word docCount	1
+w:forgot word docCount	1
+w:orbits word docCount	1
+w:panels word docCount	1
+w:plans word docCount	1
+w:power word docCount	1
+w:rescue word docCount	1
+w:solar word docCount	1
+w:taking word docCount	1
+w:to word docCount	1
+w:tourist word docCount	1
+=== fluo end ===
+
+== fluo start ==
+d:2732ebc doc content	Crisis at KSC.  Tourist stuck at Minimus.  Bill forgot solar panels.
+d:2732ebc doc processed	true
+d:2732ebc doc refc	1
+d:2732ebc doc refs	referenced
+d:6658252 doc content	Jebediah orbits Mun for 38 days.  No power, forgot solar panels.
+d:6658252 doc processed	true
+d:6658252 doc refc	1
+d:6658252 doc refs	referenced
+d:a6c4d1f doc content	Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
+d:a6c4d1f doc processed	true
+d:a6c4d1f doc refc	1
+d:a6c4d1f doc refs	referenced
+u:http://news.com/a23 uri hash	6658252
+u:http://news.com/a24 uri hash	2732ebc
+u:http://oldnews.com/a23 uri hash	a6c4d1f
+w:35 word docCount	1
+w:38 word docCount	1
+w:Bill word docCount	1
+w:Crisis word docCount	1
+w:Jebediah word docCount	2
+w:KSC word docCount	1
+w:Minimus word docCount	1
+w:Mun word docCount	2
+w:No word docCount	2
+w:Tourist word docCount	1
+w:at word docCount	1
+w:days word docCount	2
+w:for word docCount	2
+w:forgot word docCount	3
+w:orbits word docCount	2
+w:panels word docCount	3
+w:power word docCount	2
+w:solar word docCount	3
+w:stuck word docCount	1
+=== fluo end ===
 ```
 
 ## Part 3 : Using Fluo Recipes
 
 The way to compute word counts above is very prone to transactional collisions. One way to
avoid
-these collisions is to use the CollisionFreeMap provided in Fluo Recipes. Currently Fluo
Recipes is
-not released, this section will be updated with more information once it is.
+these collisions is to use the CollisionFreeMap(CFM) provided in Fluo Recipes.  The CFM will
queue
+updates for words and notify another observer to process the queued updates.  The updates
are queued
+in a way that will not cause collisions.  The CFM has its own Observer which will call two
functions
+you provide.  The code below shows an example of these two functions and how to configure
the CFM to
+call them.
+
+To try using a CFM, first add the following class.
+
+```java
+package ft;
+
+import java.util.*;
+
+import org.apache.fluo.api.client.TransactionBase;
+import org.apache.fluo.api.config.FluoConfiguration;
+import org.apache.fluo.api.config.SimpleConfiguration;
+import org.apache.fluo.recipes.core.map.CollisionFreeMap;
+import org.apache.fluo.recipes.core.map.Combiner;
+import org.apache.fluo.recipes.core.map.Update;
+import org.apache.fluo.recipes.core.map.UpdateObserver;
+
+import static ft.ContentObserver.WORD_COUNT;
+
+/**
+ * This class contains all of the code related to the {@link CollisionFreeMap} that keeps
track of
+ * word counts.  It also generates an inverted index of word counts as an example follow
on action.
+ */
+public class WordCounter {
+
+  /**
+   * the {@link CollisionFreeMap} Observer calls this combiner to processes the queued updates
for
+   * a word.
+   */
+  public static class LongCombiner implements Combiner<String, Long>{
+
+    @Override
+    public Optional<Long> combine(String k, Iterator<Long> counts) {
+      long sum = 0;
+      while(counts.hasNext()) {
+        sum += counts.next();
+      }
+
+      if(sum == 0) {
+        return Optional.empty();
+      } else {
+        return Optional.of(sum);
+      }
+    }
+  }
+
+  /**
+   * The {@link CollisionFreeMap} Observer will call this class when the counts for a word
change.
+   */
+  public static class WordObserver extends UpdateObserver<String, Long> {
+    @Override
+    public void updatingValues(TransactionBase tx, Iterator<Update<String, Long>>
updates) {
+      System.out.println("== begin CFM updates ==");  //this print to show per bucket processing
+      while(updates.hasNext()) {
+        Update<String, Long> u = updates.next();
+
+        long oldCount = u.getOldValue().orElse(0l);
+        long newCount = u.getNewValue().orElse(0l);
+
+        //create an inverted index of word counts
+        if(u.getOldValue().isPresent()) {
+          tx.delete(String.format("ic:%06d:%s", oldCount, u.getKey()), WORD_COUNT);
+        }
+
+        if(u.getNewValue().isPresent()) {
+          tx.set(String.format("ic:%06d:%s", newCount, u.getKey()), WORD_COUNT, "");
+        }
+
+        System.out.printf("  update %s %d -> %d\n", u.getKey(), oldCount , newCount);
+      }
+      System.out.println("== end CFM updates ==");
+    }
+  }
+
+  /**
+   * Code to setup a CFM's Observer and configure it to call your functions.
+   */
+  public static void configure(FluoConfiguration fluoConfig, int numBuckets) {
+    CollisionFreeMap.configure(fluoConfig, new CollisionFreeMap.Options("wc", LongCombiner.class,
+        WordObserver.class, String.class, Long.class, 3));
+  }
+
+  private CollisionFreeMap<String, Long> cfm;
+
+  WordCounter(SimpleConfiguration appConfig){
+    cfm = CollisionFreeMap.getInstance("wc", appConfig);
+  }
+
+  /**
+   * This method will queue updates for each word to be processed later by the CFM Observer.
+   */
+  void adjustCounts(TransactionBase tx, int delta, List<String> words){
+    HashMap<String, Long> wcUpdates = new HashMap<>();
+
+    for (String word : words) {
+      wcUpdates.put(word, (long)delta);
+    }
+
+    cfm.update(tx, wcUpdates);
+  }
+}
+```
+
+Then modify `preInit()` in `Main` to the following.
+
+```java
+  private static void preInit(FluoConfiguration fluoConfig) {
+    fluoConfig.addObserver(new ObserverSpecification(ContentObserver.class.getName()));
+    WordCounter.configure(fluoConfig, 3);
+  }
+```
+
+After that add the following `init()` method to `ContentObserver` and modify `adjustCounts()`
to the
+following.
+
+```java
+  private WordCounter wordCounter;
+
+  @Override
+  public void init(Context context) throws Exception {
+    //get an instance of the CFM based on application config
+    wordCounter = new WordCounter(context.getAppConfiguration());
+  }
+
+  private void adjustCounts(TransactionBase tx, int delta, String[] words) {
+    wordCounter.adjustCounts(tx, delta, words);
+  }
+```
+
+The CFM groups key values into buckets for efficiency and processes the updates for entire
bucket in
+a single transaction.  When you run this new code, that is why `== begin CFM updates ==`
is seen at
+least three times for each group of documents loaded.
+
+When you run this example you will also notice two new prefixes in the output of the table
scan.  First
+the `wc:` prefix is where the CFM stores its data.  By default the CFM uses Kryo for serialization
+and therefore the key values with this prefix contain non-ASCII characters.  The utility
function
+`FluoITHelper.printFluoTable()` escapes non-ASCII characters with `\x<HEX>`.   Second
the `ic:`
+prefix contains an inverted index of word counts.  This was created simply to show an example
of a
+follow on action when word counts change.  Ideally this follow on action would have a low
chance of
+collisions.  Creating the inverted index will not cause collisions because each word is in
a single
+CFM bucket and each bucket is processed independently.
+
+## Part 4 : Running this example on a real instance.
+
+Everything in the tour so far has used MiniFluo to run code.  The following
+instructions show how to run the code in this excercise on a real Fluo
+instance.  [Uno] can be used to quickly setup Fluo on a single node.
+
+The following two helper classes will be needed to run on a real instance.
+
+```java
+package ft;
+
+import org.apache.fluo.api.config.FluoConfiguration;
+
+/**
+ * Generates application config.
+ */
+public class GenConfig {
+  public static void main(String[] args) {
+    FluoConfiguration conf = new FluoConfiguration();
+    Main.preInit(conf);
+    conf.save(System.out);
+  }
+}
+```
+
+
+```java
+package ft;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.file.*;
+import javax.inject.Inject;
+import org.apache.fluo.api.client.*;
+import org.apache.fluo.api.config.FluoConfiguration;
+
+/**
+ * Loads one or more document passed in on the command line.
+ */
+public class Load {
+  // when run with fluo exec command, the applications configuration will be injected
+  @Inject
+  private static FluoConfiguration fluoConfig;
+
+  public static void main(String[] args) throws Exception {
+    try (FluoClient client = FluoFactory.newClient(fluoConfig);
+        LoaderExecutor loaderExecutor = client.newLoaderExecutor()) 
+    {
+      for (String filename : args) {
+        Path path = Paths.get(filename);
+        byte[] encoded = Files.readAllBytes(path);
+        String docContent = new String(encoded, StandardCharsets.UTF_8);
+        String uri = path.toAbsolutePath().normalize().toUri().toString();
+        Document doc = new Document(uri, docContent);
+        loaderExecutor.execute(new DocLoader(doc));
+      }
+    }
+  }
+}
+```
+
+The following command will run this application on a Fluo instance, assuming
+`$FLUO_HOME` is set and `fluo` is on the path.
+
+```bash
+cd <your fluo tour dir>
+
+#create a new Fluo application named wordCount
+fluo new wordCount
+
+#populate applications lib directory
+mvn clean package
+cp target/fluo-tour-0.0.1-SNAPSHOT.jar $FLUO_HOME/apps/wordCount/lib
+mvn dependency:copy-dependencies -DincludeArtifactIds="fluo-recipes-core,fluo-recipes-accumulo,fluo-recipes-kryo,kryo,minlog,reflectasm,objenesis"
-DoutputDirectory=$FLUO_HOME/apps/wordCount/lib
+
+#add app specific config to properties file that Fluo init will use
+fluo exec wordCount ft.GenConfig >> $FLUO_HOME/apps/wordCount/conf/fluo.properties
+
+#initialize and start Fluo application
+fluo init wordCount
+fluo start wordCount
+fluo info wordCount
+
+#load some text files
+fluo exec wordCount ft.Load <some filename> <some filename> ...
+
+#wait for all notifications to process
+fluo wait wordCount
+
+#scan data in Fluo
+fluo scan wordCount
+fluo scan wordCount -p ic:
+
+#Could try changing a file and reloading it
+
+#stop Fluo application
+fluo stop wordCount
+```
 
 [aft]: https://twitter.com/hashtag/apachefluotour
+[Uno]: https://github.com/astralway/uno


Mime
View raw message