You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streams.apache.org by sb...@apache.org on 2014/07/21 06:49:16 UTC

[01/18] bootstrap of module

Repository: incubator-streams
Updated Branches:
  refs/heads/master b7e4c3467 -> 7c5e29007


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/test/resources/testtweets.txt
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/resources/testtweets.txt b/streams-contrib/streams-provider-instagram/src/test/resources/testtweets.txt
new file mode 100644
index 0000000..6cb73b6
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/test/resources/testtweets.txt
@@ -0,0 +1,695 @@
+{"contributors":null,"text":"@JasonCalacanis Blade Runner is my favorite movie of all time.  The \"Final Cut\" in HD/BluRay is amazing!","geo":null,"retweeted":false,"in_reply_to_screen_name":"Jason","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":1004,"name":"JasonMC","indices":[0,15],"screen_name":"Jasoncalacanis","id_str":"1004"}]},"in_reply_to_status_id_str":"801450173","id":801455863,"source":"web","in_reply_to_user_id_str":"3840","favorited":false,"in_reply_to_status_id":801450173,"retweet_count":0,"created_at":"Thu May 01 20:53:44 +0000 2008","in_reply_to_user_id":3840,"favorite_count":0,"id_str":"801455863","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology e
 xecutive, and an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFE
 C","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Installed fish finder on my kayak over the weekend.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":803894383,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Mon May 05 14:32:28 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"803894383","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","pro
 file_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"
 is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Flying (back) to Boston today.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":804623799,"source":"<a href=\"http://www.32hours.com/betwitteredinfo/\" rel=\"nofollow\">BeTwittered<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue May 06 12:31:14 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"804623799","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_side
 bar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images
 /1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Why can't I see my tweets in Twinkle?!  Is anyone else having issues with Twinkle since the last update a few days ago?","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":804863183,"source":"<a href=\"http://twitter.com/twinkleking\" rel=\"nofollow\">Twinkle<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue May 06 17:52:40 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"804863183","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologet
 ic techno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD"
 ,"id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"In South Florida for two days of meetings.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":822771775,"source":"<a href=\"http://twitter.com/twinkleking\" rel=\"nofollow\">Twinkle<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu May 29 20:18:52 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"822771775","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_sideb
 ar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/
 1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"If you've never been to the Breakers in Palm Beach, I highly recommend it.  The poolside cabanas are unbelievable!","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":837953391,"source":"<a href=\"http://twitter.com/twinkleking\" rel=\"nofollow\">Twinkle<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Wed Jun 18 17:22:39 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"837953391","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic te
 chno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_
 str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Leaving for Majorca, Spain to speak at a health care informatics conference.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":842661503,"source":"<a href=\"http://twitter.com/twinkleking\" rel=\"nofollow\">Twinkle<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue Jun 24 18:32:13 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"842661503","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic techno-geek.","verified":false,"contribu
 tors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"ht
 tp://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"iPhone firmware 2.0 is buggy for me. ActiveSync deleting appointments from my Outlook calendar and some of the app store apps don't work!","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":856160351,"source":"<a href=\"http://twitterrific.com\" rel=\"nofollow\">Twitterrific<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Sat Jul 12 00:52:22 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"856160351","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, a
 nd an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_
 name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@Paulflevy Your special contribution is Transparency as a foundation for safety/quality. Give some fresh cuts from your unique gift there.","geo":null,"retweeted":false,"in_reply_to_screen_name":"Paulflevy","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":17668347,"name":"Paul Levy","indices":[0,10],"screen_name":"Paulflevy","id_str":"17668347"}]},"in_reply_to_status_id_str":"1042274173","id":1042786575,"source":"web","in_reply_to_user_id_str":"17668347","favorited":false,"in_reply_to_status_id":1042274173,"retweet_count":0,"created_at":"Sun Dec 07 01:25:27 +0000 2008","in_reply_to_user_id":17668347,"favorite_count":0,"id_str":"1042786575","place":null,"user":{"location":"Southwest Desert - Arizona USA","default_profile":false,"profile_background_tile":false,"statuses_count":83,"lang":"en","profile_link_color":"FF3300","id":16030226,"following":false,"protected":false,"favourites_count":6,"profile_text
 _color":"333333","description":"MBA Physician - pathology, informatics and Lean continuous improvement","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"86A4A6","name":"Peter P Patterson MD","profile_background_color":"8E9770","created_at":"Thu Aug 28 18:00:06 +0000 2008","default_profile_image":false,"followers_count":229,"profile_image_url_https":"https://pbs.twimg.com/profile_images/723673074/P652-LTPP0652117307JCP-2_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme6/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme6/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":52,"profile_sidebar_fill_color":"A0B1C7","screen_name":"IDrPete","id_str":"16030226","profile_image_url":"http://pbs.twimg.com/pr
 ofile_images/723673074/P652-LTPP0652117307JCP-2_normal.jpg","listed_count":4,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"I am enjoying some time off with the family.  Hoping all of you have a safe and joyous holiday season.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1071035551,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Sun Dec 21 20:38:32 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1071035551","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_bord
 er_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730
 845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@anitasamarth Clinovations web site seems to promise more than it can deliver. I would suggest toning down some of the rhetoric.","geo":null,"retweeted":false,"in_reply_to_screen_name":"anitasamarth","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":16257434,"name":"Anita Samarth","indices":[0,13],"screen_name":"anitasamarth","id_str":"16257434"}]},"in_reply_to_status_id_str":"1106542273","id":1106752071,"source":"web","in_reply_to_user_id_str":"16257434","favorited":false,"in_reply_to_status_id":1106542273,"retweet_count":0,"created_at":"Fri Jan 09 14:14:42 +0000 2009","in_reply_to_user_id":16257434,"favorite_count":0,"id_str":"1106752071","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_co
 lor":"333333","description":"Semi-retired University of Michigan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)
 ","notifications":false,"profile_use_background_image":true,"friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"An academic pathologist, Dr. Tony Killeen, comments on how lab test reference ranges are created (http://twurl.cc/bxe)","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1111123095,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Sun Jan 11 16:13:35 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1111123095","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michigan Medical School pathologist & informatician. Author of clinical lab & pathology blog
  called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","i
 d_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"UnitedHealth Settles Suit with New York Attorney General Cuomo: I have posted a number of previous notes about t.. http://tinyurl.com/6u8q2x","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1118315383,"source":"<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Wed Jan 14 14:20:20 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1118315383","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michi
 gan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"
 friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Free Exercise and Nutrition Program Offered in Parks in Brazil: In a previous post, I discussed the very high co.. http://tinyurl.com/86v7kx","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1120906383,"source":"<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu Jan 15 13:20:26 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1120906383","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michi
 gan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"
 friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Extracting Value from Twitter Messages Using Filters: As a relatively recent convert to Twitter, a microblogging.. http://tinyurl.com/73qchn","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1133231679,"source":"<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue Jan 20 14:24:17 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1133231679","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michi
 gan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"
 friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Book Premium for Early Registrants to Lab InfoTech Summit: We are offering a book premium to the first 100 regis.. http://tinyurl.com/c5kpfb","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1152789015,"source":"<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue Jan 27 18:23:33 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1152789015","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michi
 gan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"
 friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Anxiously awaiting the arrival of my new netbook...tomorrow.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1160434255,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Fri Jan 30 01:59:23 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1160434255","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez
  M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed
 _count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@jenmccabegorman Lab information systems  (LISs) manage manage about 80% of all health information. Also exploring digital pathology.","geo":null,"retweeted":false,"in_reply_to_screen_name":"jensmccabe","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":"1176600518","id":1177499983,"source":"web","in_reply_to_user_id_str":"14258044","favorited":false,"in_reply_to_status_id":1176600518,"retweet_count":0,"created_at":"Wed Feb 04 20:40:32 +0000 2009","in_reply_to_user_id":14258044,"favorite_count":0,"id_str":"1177499983","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michigan Medical School pathologist & informa
 tician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":242,"profile_sidebar_fill
 _color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@scottshreeve If large health systems endorse home health physiologic monitoring, it can succeed -- they can provide needed infrastructure.","geo":null,"retweeted":false,"in_reply_to_screen_name":"scottshreeve","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":16059834,"name":"Scott Shreeve, MD","indices":[0,13],"screen_name":"scottshreeve","id_str":"16059834"}]},"in_reply_to_status_id_str":"1183578679","id":1184110279,"source":"web","in_reply_to_user_id_str":"16059834","favorited":false,"in_reply_to_status_id":1183578679,"retweet_count":0,"created_at":"Fri Feb 06 19:00:43 +0000 2009","in_reply_to_user_id":16059834,"favorite_count":0,"id_str":"1184110279","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"
 profile_text_color":"333333","description":"Semi-retired University of Michigan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Tim
 e (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Live Tweeting from Molecular Summit. For coronary artery stenosis dx, moving from lumen-ology to plaque-ology.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1195927703,"source":"<a href=\"http://twitter.com/devices\" rel=\"nofollow\">txt<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue Feb 10 16:30:30 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1195927703","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michigan Medical School pathologist & i
 nformatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":242,"profile_sideba
 r_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Live Tweeting from Molecular Summit. Jared Schwartz: Many blockbuster drugs show limited efficacy in as many as 70% of pts.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1197125583,"source":"<a href=\"http://twitter.com/devices\" rel=\"nofollow\">txt<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue Feb 10 22:35:27 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1197125583","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michigan Medical School pa
 thologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":242,"p
 rofile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Diagosed today: Unusual presentation. Pt w/ 1st symptom being visual disturbance. Brain biopsy of primary visual cortex: metastatic lung ca.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1197925487,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Wed Feb 11 03:34:13 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1197925487","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_count":247,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"F2E195","nam
 e":"Brian Moore","profile_background_color":"BADFCD","created_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":523,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name":"neuropathology","id_str":"19749157","profile_image_url":"http://pbs.t
 wimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","listed_count":27,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"NPR reports on challenge of switching to digital medical records: http://tinyurl.com/cl4a4p","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1216667127,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Mon Feb 16 21:20:41 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1216667127","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_count":247,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"F2E195","name":"Brian Moore","profile_background_color":"BADF
 CD","created_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":523,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name":"neuropathology","id_str":"19749157","profile_image_url":"http://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10
 _June_2011_for_twitter_normal.jpg","listed_count":27,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"just signed up for twitter","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1225092359,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu Feb 19 01:23:25 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1225092359","place":null,"user":{"location":"Houston, TX","default_profile":true,"profile_background_tile":false,"statuses_count":2,"lang":"en","profile_link_color":"0084B4","id":21263297,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"Ultrarunner, marathoner, neuropathologist","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Kent Heck","profile_background_color":"C0DEED","created_at":"Thu Feb 19 01:20:47 +0000 2009"
 ,"default_profile_image":true,"followers_count":14,"profile_image_url_https":"https://abs.twimg.com/sticky/default_profile_images/default_profile_6_normal.png","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":null,"time_zone":null,"notifications":false,"profile_use_background_image":true,"friends_count":17,"profile_sidebar_fill_color":"DDEEF6","screen_name":"nemaline","id_str":"21263297","profile_image_url":"http://abs.twimg.com/sticky/default_profile_images/default_profile_6_normal.png","listed_count":0,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Twitted that is ,  sorry and good night","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1225678863,"source":"<a href=\"http://m.twitter.com/\" rel=\"nofollow\">mobile web<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu Feb 19 04:53:54 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1225678863","place":null,"user":{"location":"Seattle","default_profile":false,"profile_background_tile":false,"statuses_count":194,"lang":"en","profile_link_color":"1F98C7","id":21274091,"following":false,"protected":false,"favourites_count":49,"profile_text_color":"663B12","description":"Christian seeker. Loving husband. Proud father. Practicing physician (surgical pathology, hematopathology), Musician (pianist). Chess player. Dem. Apple"
 ,"verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C6E2EE","name":"Charles Simrell","profile_background_color":"C6E2EE","created_at":"Thu Feb 19 04:01:18 +0000 2009","default_profile_image":false,"followers_count":17,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1173141302/image_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme2/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme2/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":94,"profile_sidebar_fill_color":"DAECF4","screen_name":"ChuckSimrell","id_str":"21274091","profile_image_url":"http://pbs.twimg.com/profile_images/1173141302/image_normal.jpg","listed_count":0,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Soup is done, ready for tomorrow.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1235740735,"source":"<a href=\"http://m.twitter.com/\" rel=\"nofollow\">mobile web<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Sun Feb 22 00:24:52 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1235740735","place":null,"user":{"location":"Seattle","default_profile":false,"profile_background_tile":false,"statuses_count":194,"lang":"en","profile_link_color":"1F98C7","id":21274091,"following":false,"protected":false,"favourites_count":49,"profile_text_color":"663B12","description":"Christian seeker. Loving husband. Proud father. Practicing physician (surgical pathology, hematopathology), Musician (pianist). Chess player. Dem. Apple","veri
 fied":false,"contributors_enabled":false,"profile_sidebar_border_color":"C6E2EE","name":"Charles Simrell","profile_background_color":"C6E2EE","created_at":"Thu Feb 19 04:01:18 +0000 2009","default_profile_image":false,"followers_count":17,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1173141302/image_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme2/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme2/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":94,"profile_sidebar_fill_color":"DAECF4","screen_name":"ChuckSimrell","id_str":"21274091","profile_image_url":"http://pbs.twimg.com/profile_images/1173141302/image_normal.jpg","listed_count":0,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Just finished clean up from feeding 160+ church folk and homeless. Very good gathering.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1238561103,"source":"<a href=\"http://m.twitter.com/\" rel=\"nofollow\">mobile web<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Sun Feb 22 23:14:50 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1238561103","place":null,"user":{"location":"Seattle","default_profile":false,"profile_background_tile":false,"statuses_count":194,"lang":"en","profile_link_color":"1F98C7","id":21274091,"following":false,"protected":false,"favourites_count":49,"profile_text_color":"663B12","description":"Christian seeker. Loving husband. Proud father. Practicing physician (surgical pathology, hematopathology
 ), Musician (pianist). Chess player. Dem. Apple","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C6E2EE","name":"Charles Simrell","profile_background_color":"C6E2EE","created_at":"Thu Feb 19 04:01:18 +0000 2009","default_profile_image":false,"followers_count":17,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1173141302/image_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme2/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme2/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":94,"profile_sidebar_fill_color":"DAECF4","screen_name":"ChuckSimrell","id_str":"21274091","profile_image_url":"http://pbs.twimg.com/profile_images/1173141302/image_normal.jpg","listed_count":0,"is_translator
 ":false},"coordinates":null}
+{"contributors":null,"text":"I guess I'm an official \"TWIT\" now.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1241067119,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Mon Feb 23 15:16:03 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1241067119","place":null,"user":{"location":"Northeast Kingdom of VT","default_profile":false,"profile_background_tile":false,"statuses_count":200,"lang":"en","profile_link_color":"0084B4","id":21651866,"following":false,"protected":false,"favourites_count":3,"profile_text_color":"333333","description":"Feral pathologist. (In the wild...)","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"FFFFFF","name":"Robert W. McDowell","profile_background_color":"9AE4E8","created_at":"Mo
 n Feb 23 13:26:56 +0000 2009","default_profile_image":false,"followers_count":14,"profile_image_url_https":"https://pbs.twimg.com/profile_images/531893711/100_3967a_normal.JPG","geo_enabled":true,"profile_background_image_url":"http://a0.twimg.com/profile_background_images/378800000138452440/NG96U6hr.jpeg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/378800000138452440/NG96U6hr.jpeg","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":12,"profile_sidebar_fill_color":"DDFFCC","screen_name":"msie01","id_str":"21651866","profile_image_url":"http://pbs.twimg.com/profile_images/531893711/100_3967a_normal.JPG","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@UMHealthSystem Community benefit. I am still timing out on server www2.med.umich.edu 24 hours later.","geo":null,"retweeted":false,"in_reply_to_screen_name":"UMHealthSystem","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":15457383,"name":"U-M Health System","indices":[0,15],"screen_name":"UMHealthSystem","id_str":"15457383"}]},"in_reply_to_status_id_str":"1241366607","id":1243544607,"source":"web","in_reply_to_user_id_str":"15457383","favorited":false,"in_reply_to_status_id":1241366607,"retweet_count":0,"created_at":"Tue Feb 24 03:40:14 +0000 2009","in_reply_to_user_id":15457383,"favorite_count":0,"id_str":"1243544607","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","desc
 ription":"Semi-retired University of Michigan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":f
 alse,"profile_use_background_image":true,"friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@hiphopoptamus Korean named president of Dartmouth College: http://www.dartmouth.edu/presidentelect/","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":129898249,"name":"KingAdobo","indices":[0,14],"screen_name":"Hiphopoptamus","id_str":"129898249"}]},"in_reply_to_status_id_str":null,"id":1275422599,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue Mar 03 20:37:14 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1275422599","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_count":247,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"contribut
 ors_enabled":false,"profile_sidebar_border_color":"F2E195","name":"Brian Moore","profile_background_color":"BADFCD","created_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":523,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name":"neurop
 athology","id_str":"19749157","profile_image_url":"http://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","listed_count":27,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@cosmeticclinic Tummy Tuck 101: http://tinyurl.com/cnyjdu._Lee","geo":null,"retweeted":false,"in_reply_to_screen_name":"cosmeticclinic","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":22731442,"name":"Mississauga Cosmetic","indices":[0,15],"screen_name":"cosmeticclinic","id_str":"22731442"}]},"in_reply_to_status_id_str":"1277166229","id":1278479255,"source":"web","in_reply_to_user_id_str":"22731442","favorited":false,"in_reply_to_status_id":1277166229,"retweet_count":0,"created_at":"Wed Mar 04 13:43:27 +0000 2009","in_reply_to_user_id":22731442,"favorite_count":0,"id_str":"1278479255","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_count":247,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"c
 ontributors_enabled":false,"profile_sidebar_border_color":"F2E195","name":"Brian Moore","profile_background_color":"BADFCD","created_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":523,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name"
 :"neuropathology","id_str":"19749157","profile_image_url":"http://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","listed_count":27,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"RT @jamesbyers: RT @CRAIGLSANDERS  \"One of the greatest success secrets is uninterrupted time given to a specific task.\" - Steve Chandler","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":17050074,"name":"James Byers","indices":[3,14],"screen_name":"jamesbyers","id_str":"17050074"},{"id":20658282,"name":"Craig L. Sanders","indices":[19,33],"screen_name":"CRAIGLSANDERS","id_str":"20658282"}]},"in_reply_to_status_id_str":null,"id":1279318623,"source":"<a href=\"http://www.tweetdeck.com/\" rel=\"nofollow\">TweetDeck<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Wed Mar 04 17:09:24 +0000 2009","in_reply_to_user_id":null,"favorite_count":1,"id_str":"1279318623","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_co
 unt":247,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"F2E195","name":"Brian Moore","profile_background_color":"BADFCD","created_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":523,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.
 co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name":"neuropathology","id_str":"19749157","profile_image_url":"http://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","listed_count":27,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Using an iPhone for Review or Diagnosis of Pathology Images: In a recent blog note (see: iPhone, the Fifth Most .. http://tinyurl.com/aszn4f","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1283235239,"source":"<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu Mar 05 13:26:18 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1283235239","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michi
 gan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"
 friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@JayOatway Totally agree. One other reason. Twitter positioning itself to supply local news linked to revenue-generating classifieds.","geo":null,"retweeted":false,"in_reply_to_screen_name":"JayOatway","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":616173,"name":"Jay Oatway","indices":[0,10],"screen_name":"JayOatway","id_str":"616173"}]},"in_reply_to_status_id_str":"1306339770","id":1306463087,"source":"web","in_reply_to_user_id_str":"616173","favorited":false,"in_reply_to_status_id":1306339770,"retweet_count":0,"created_at":"Tue Mar 10 17:56:46 +0000 2009","in_reply_to_user_id":616173,"favorite_count":0,"id_str":"1306463087","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333
 ","description":"Semi-retired University of Michigan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notificat
 ions":false,"profile_use_background_image":true,"friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Few Vendors Deliver an Integrated RIS/PACS Solution According to KLAS: I have been a strong advocate for the mer.. http://tinyurl.com/cpu5nj","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1310116663,"source":"<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Wed Mar 11 11:28:55 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1310116663","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michi
 gan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"
 friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Why lucid interval after epidural hematoma trauma? Initial blow causes concussion.  Pt wakes.  Bleed pressure: loss of consciousness again.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1321360735,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Fri Mar 13 12:23:12 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1321360735","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_count":247,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"F2E195","name
 ":"Brian Moore","profile_background_color":"BADFCD","created_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":523,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name":"neuropathology","id_str":"19749157","profile_image_url":"http://pbs.tw
 img.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","listed_count":27,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Finishing a gel.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1324179463,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Fri Mar 13 21:51:44 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1324179463","place":null,"user":{"location":"","default_profile":true,"profile_background_tile":false,"statuses_count":3,"lang":"en","profile_link_color":"0084B4","id":24262686,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bradley Miller","profile_background_color":"C0DEED","created_at":"Fri Mar 13 21:50:32 +0000 2009","default_profile_image":true,"followers_count":6,"profil
 e_image_url_https":"https://abs.twimg.com/sticky/default_profile_images/default_profile_0_normal.png","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":null,"time_zone":null,"notifications":false,"profile_use_background_image":true,"friends_count":1,"profile_sidebar_fill_color":"DDEEF6","screen_name":"miller2865","id_str":"24262686","profile_image_url":"http://abs.twimg.com/sticky/default_profile_images/default_profile_0_normal.png","listed_count":0,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Reviewing a case.  Just did a frozen. Going back to the dentist today","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1388548023,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Wed Mar 25 15:39:16 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1388548023","place":null,"user":{"location":"Indiana/ North Carolina","default_profile":false,"profile_background_tile":false,"statuses_count":487,"lang":"en","profile_link_color":"9EDEF5","id":26316026,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"333333","description":"I am an American Forensic Pathologist, and Fitness Activist, who likes to write and race bicycles.","verified":false,"contributors_enabled":false,"profile_sidebar_border_c
 olor":"9CEE72","name":"Scott Wagner M.D.","profile_background_color":"C9FAFD","created_at":"Tue Mar 24 20:29:03 +0000 2009","default_profile_image":false,"followers_count":85,"profile_image_url_https":"https://pbs.twimg.com/profile_images/139026389/At_work2.1_normal.JPG","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://Pathfor.com","indices":[0,22],"display_url":"Pathfor.com","url":"http://t.co/ckrct3sKAd"}]}},"url":"http://t.co/ckrct3sKAd","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":239,"profile_sidebar_fill_color":"EFFFCC","screen_name":"sawagnermd","id_str":"26316026","profile_image_url":"http://pbs.twimg.com/profile_images/139026389/At_work2.1_nor
 mal.JPG","listed_count":2,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Thinking about sleep.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1391418135,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu Mar 26 00:27:06 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1391418135","place":null,"user":{"location":"","default_profile":true,"profile_background_tile":false,"statuses_count":3,"lang":"en","profile_link_color":"0084B4","id":24262686,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bradley Miller","profile_background_color":"C0DEED","created_at":"Fri Mar 13 21:50:32 +0000 2009","default_profile_image":true,"followers_count":6,"p
 rofile_image_url_https":"https://abs.twimg.com/sticky/default_profile_images/default_profile_0_normal.png","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":null,"time_zone":null,"notifications":false,"profile_use_background_image":true,"friends_count":1,"profile_sidebar_fill_color":"DDEEF6","screen_name":"miller2865","id_str":"24262686","profile_image_url":"http://abs.twimg.com/sticky/default_profile_images/default_profile_0_normal.png","listed_count":0,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Mayo Clinic Medical Education Reform Summit April 26-28 http://tinyurl.com/dj3uwl","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1391798423,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu Mar 26 01:41:14 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1391798423","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_count":248,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"F2E195","name":"Brian Moore","profile_background_color":"BADFCD","creat
 ed_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":526,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name":"neuropathology","id_str":"19749157","profile_image_url":"http://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011
 _for_twitter_normal.jpg","listed_count":28,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"On call...first case delayed already!","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1400537143,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Fri Mar 27 12:25:36 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1400537143","place":null,"user":{"location":"Indiana/ North Carolina","default_profile":false,"profile_background_tile":false,"statuses_count":487,"lang":"en","profile_link_color":"9EDEF5","id":26316026,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"333333","description":"I am an American Forensic Pathologist, and Fitness Activist, who likes to write and race bicycles.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"9CEE72","name":"Scott Wag
 ner M.D.","profile_background_color":"C9FAFD","created_at":"Tue Mar 24 20:29:03 +0000 2009","default_profile_image":false,"followers_count":85,"profile_image_url_https":"https://pbs.twimg.com/profile_images/139026389/At_work2.1_normal.JPG","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://Pathfor.com","indices":[0,22],"display_url":"Pathfor.com","url":"http://t.co/ckrct3sKAd"}]}},"url":"http://t.co/ckrct3sKAd","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":239,"profile_sidebar_fill_color":"EFFFCC","screen_name":"sawagnermd","id_str":"26316026","profile_image_url":"http://pbs.twimg.com/profile_images/139026389/At_work2.1_normal.JPG","listed_count":2,"is_tr
 anslator":false},"coordinates":null}
+{"contributors":null,"text":"Biked 12.00 miles\r\n in 1h 00m. Spinning Class","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1402341623,"source":"<a href=\"http://www.beginnertriathlete.com\" rel=\"nofollow\">BeginnerTriathlete.com<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Fri Mar 27 17:51:33 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1402341623","place":null,"user":{"location":"Seattle Area","default_profile":false,"profile_background_tile":false,"statuses_count":560,"lang":"en","profile_link_color":"088253","id":16283736,"following":false,"protected":false,"favourites_count":89,"profile_text_color":"634047","description":"Pathologist developing innovative digital pathology solutions to connect patients around the world with the highest 
 quality diagnosticians.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"D3D2CF","name":"Gregory S Henderson","profile_background_color":"EDECE9","created_at":"Sun Sep 14 15:19:36 +0000 2008","default_profile_image":false,"followers_count":1349,"profile_image_url_https":"https://pbs.twimg.com/profile_images/339777194/Henderson-Gregory-IMG_4461B_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme3/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme3/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"url

<TRUNCATED>

[03/18] git commit: Added mapping:

Posted by sb...@apache.org.
Added mapping:


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/0df4fa1c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/0df4fa1c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/0df4fa1c

Branch: refs/heads/master
Commit: 0df4fa1c8477f559c154a71b5a9ea108c3daef3d
Parents: 537b97a
Author: mfranklin <mf...@apache.org>
Authored: Tue Jul 1 10:25:53 2014 -0400
Committer: mfranklin <mf...@apache.org>
Committed: Tue Jul 1 10:25:53 2014 -0400

----------------------------------------------------------------------
 .../metadata/instagram_to_activity_mapping.png   | Bin 0 -> 1123684 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/0df4fa1c/streams-contrib/streams-provider-instagram/metadata/instagram_to_activity_mapping.png
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/metadata/instagram_to_activity_mapping.png b/streams-contrib/streams-provider-instagram/metadata/instagram_to_activity_mapping.png
new file mode 100644
index 0000000..a3b7050
Binary files /dev/null and b/streams-contrib/streams-provider-instagram/metadata/instagram_to_activity_mapping.png differ


[14/18] git commit: STREAMS-121 | Merged apache instagram branch

Posted by sb...@apache.org.
STREAMS-121 | Merged apache instagram branch


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/1e233007
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/1e233007
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/1e233007

Branch: refs/heads/master
Commit: 1e233007d0adcff9459bda9bad8dea00e2f27be4
Parents: 95f6d5d 14f7050
Author: rebanks <re...@w2odigital.com>
Authored: Fri Jul 11 16:41:12 2014 -0500
Committer: rebanks <re...@w2odigital.com>
Committed: Fri Jul 11 16:41:12 2014 -0500

----------------------------------------------------------------------
 streams-contrib/pom.xml                         |   1 +
 .../processor/InstagramTypeConverter.java       | 116 ++----------
 .../InstagramJsonActivitySerializer.java        |  22 ++-
 .../serializer/util/InstagramActivityUtil.java  | 187 ++++++++++++++++---
 .../serializer/util/InstagramDeserializer.java  |  33 ++++
 .../test/InstagramActivitySerDeTest.java        |  88 +++++++++
 .../src/test/resources/testMediaFeedObjects.txt |   2 +
 7 files changed, 323 insertions(+), 126 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/1e233007/streams-contrib/pom.xml
----------------------------------------------------------------------


[10/18] git commit: STREAMS-122 | Added header

Posted by sb...@apache.org.
STREAMS-122 | Added header


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/957e13f9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/957e13f9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/957e13f9

Branch: refs/heads/master
Commit: 957e13f9d0bcf330d40e47691ce2c9b8a038781a
Parents: 23bfcac
Author: Robert Douglas <rd...@w2odigital.com>
Authored: Wed Jul 2 11:19:57 2014 -0500
Committer: Robert Douglas <rd...@w2odigital.com>
Committed: Wed Jul 2 11:19:57 2014 -0500

----------------------------------------------------------------------
 .../serializer/util/InstagramDeserializer.java  | 21 +++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/957e13f9/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
index b4aee2d..d4280bf 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
@@ -1,11 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
 package org.apache.streams.instagram.serializer.util;
 
 import org.jinstagram.Instagram;
 import org.jinstagram.exceptions.InstagramException;
 
-/**
- * Created by rdouglas on 7/1/14.
- */
 public class InstagramDeserializer extends Instagram{
     public InstagramDeserializer(String test) {
         super(test);


[04/18] git commit: Adding MUP source

Posted by sb...@apache.org.
Adding MUP source


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/a3f443fe
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/a3f443fe
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/a3f443fe

Branch: refs/heads/master
Commit: a3f443fe76ee3ae99925c88b17a18954a3662edd
Parents: 0df4fa1
Author: mfranklin <mf...@apache.org>
Authored: Tue Jul 1 10:27:28 2014 -0400
Committer: mfranklin <mf...@apache.org>
Committed: Tue Jul 1 10:27:28 2014 -0400

----------------------------------------------------------------------
 .../InstagramMediaDataActivitySerializer.mup    | 894 +++++++++++++++++++
 1 file changed, 894 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/a3f443fe/streams-contrib/streams-provider-instagram/metadata/InstagramMediaDataActivitySerializer.mup
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/metadata/InstagramMediaDataActivitySerializer.mup b/streams-contrib/streams-provider-instagram/metadata/InstagramMediaDataActivitySerializer.mup
new file mode 100644
index 0000000..deddd26
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/metadata/InstagramMediaDataActivitySerializer.mup
@@ -0,0 +1,894 @@
+{
+  "title": "InstagramMediaDataActivitySerializer",
+  "id": 1,
+  "formatVersion": 2,
+  "ideas": {
+    "1": {
+      "title": "activity",
+      "id": 2,
+      "ideas": {
+        "1": {
+          "title": "published",
+          "id": 25
+        },
+        "2": {
+          "title": "id:instagram:post:<id>",
+          "id": 26
+        },
+        "3": {
+          "title": "provider",
+          "id": 27,
+          "ideas": {
+            "1": {
+              "title": "id",
+              "id": 28,
+              "ideas": {
+                "1": {
+                  "title": "id:providers:instagram",
+                  "id": 31
+                }
+              }
+            },
+            "2": {
+              "title": "displayName",
+              "id": 29,
+              "ideas": {
+                "1": {
+                  "title": "Instagram",
+                  "id": 30
+                }
+              }
+            }
+          }
+        },
+        "6": {
+          "title": "url",
+          "id": 40
+        },
+        "7": {
+          "title": "object",
+          "id": 46,
+          "ideas": {
+            "1": {
+              "title": "objectType",
+              "id": 38,
+              "attr": {
+                "style": {}
+              }
+            },
+            "2": {
+              "title": "attachments",
+              "id": 51,
+              "ideas": {
+                "1": {
+                  "title": "object",
+                  "id": 52,
+                  "ideas": {
+                    "1": {
+                      "title": "objectType",
+                      "id": 53,
+                      "ideas": {
+                        "1": {
+                          "title": "\"image\"",
+                          "id": 81
+                        }
+                      }
+                    },
+                    "2": {
+                      "title": "image",
+                      "id": 54,
+                      "ideas": {
+                        "1": {
+                          "title": "url",
+                          "id": 55
+                        },
+                        "2": {
+                          "title": "height",
+                          "id": 56
+                        },
+                        "3": {
+                          "title": "width",
+                          "id": 57
+                        }
+                      }
+                    }
+                  }
+                },
+                "2": {
+                  "title": "object",
+                  "ideas": {
+                    "1": {
+                      "title": "objectType",
+                      "ideas": {
+                        "1": {
+                          "title": "\"image\"",
+                          "id": 101
+                        }
+                      },
+                      "id": 100
+                    },
+                    "2": {
+                      "title": "image",
+                      "ideas": {
+                        "1": {
+                          "title": "url",
+                          "id": 103
+                        },
+                        "2": {
+                          "title": "height",
+                          "id": 104
+                        },
+                        "3": {
+                          "title": "width",
+                          "id": 105
+                        }
+                      },
+                      "id": 102
+                    }
+                  },
+                  "id": 99,
+                  "attr": {
+                    "style": {}
+                  }
+                },
+                "3": {
+                  "title": "object",
+                  "ideas": {
+                    "1": {
+                      "title": "objectType",
+                      "ideas": {
+                        "1": {
+                          "title": "\"video\"",
+                          "id": 108
+                        }
+                      },
+                      "id": 107
+                    },
+                    "2": {
+                      "title": "image",
+                      "ideas": {
+                        "1": {
+                          "title": "url",
+                          "id": 110
+                        },
+                        "2": {
+                          "title": "height",
+                          "id": 111
+                        },
+                        "3": {
+                          "title": "width",
+                          "id": 112
+                        }
+                      },
+                      "id": 109
+                    }
+                  },
+                  "id": 106,
+                  "attr": {
+                    "style": {}
+                  }
+                }
+              }
+            },
+            "3": {
+              "title": "image\n\nIF objectType == video\nimage = data.videos.standard_resolution\nELSE \nimage = data.images.standard_resolution",
+              "id": 62,
+              "attr": {
+                "position": [
+                  74.5,
+                  74,
+                  1
+                ],
+                "style": {}
+              },
+              "ideas": {
+                "1": {
+                  "title": "url",
+                  "id": 64
+                },
+                "2": {
+                  "title": "height",
+                  "id": 65
+                },
+                "3": {
+                  "title": "width",
+                  "id": 66
+                }
+              }
+            },
+            "4": {
+              "title": "content",
+              "id": 113
+            }
+          }
+        },
+        "8": {
+          "title": "extensions",
+          "id": 75,
+          "ideas": {
+            "1": {
+              "title": "instagram",
+              "id": 91,
+              "attr": {
+                "style": {}
+              }
+            },
+            "2": {
+              "title": "location",
+              "id": 77,
+              "ideas": {
+                "1": {
+                  "title": "coordinates",
+                  "id": 78,
+                  "ideas": {
+                    "1": {
+                      "title": "type",
+                      "id": 79,
+                      "ideas": {
+                        "1": {
+                          "title": "\"Point\"",
+                          "id": 80
+                        }
+                      }
+                    },
+                    "0.5": {
+                      "title": "coordinates\n\n[longitude,latitude]",
+                      "id": 82,
+                      "attr": {
+                        "style": {}
+                      }
+                    }
+                  }
+                }
+              }
+            },
+            "12": {
+              "title": "hashtags",
+              "id": 76,
+              "attr": {
+                "position": [
+                  93.5,
+                  125.5,
+                  1
+                ],
+                "style": {}
+              }
+            },
+            "13": {
+              "title": "user_mentions",
+              "id": 92,
+              "ideas": {
+                "1": {
+                  "title": "id",
+                  "id": 93
+                },
+                "2": {
+                  "title": "handle",
+                  "id": 94
+                }
+              }
+            },
+            "9.5": {
+              "title": "rebroadcasts",
+              "id": 88,
+              "attr": {
+                "style": {}
+              }
+            },
+            "10.75": {
+              "title": "likes",
+              "id": 87,
+              "attr": {
+                "style": {}
+              },
+              "ideas": {
+                "1": {
+                  "title": "count",
+                  "id": 90
+                }
+              }
+            }
+          },
+          "attr": {}
+        },
+        "9": {
+          "title": "content",
+          "id": 114
+        },
+        "0.5": {
+          "title": "actor",
+          "id": 32,
+          "ideas": {
+            "1": {
+              "title": "id:instagram:<id>",
+              "id": 35
+            },
+            "2": {
+              "title": "image",
+              "id": 49,
+              "ideas": {
+                "1": {
+                  "title": "url",
+                  "id": 50
+                }
+              }
+            },
+            "0.5": {
+              "title": "handle",
+              "id": 36,
+              "attr": {
+                "position": [
+                  63.5,
+                  -66,
+                  1
+                ],
+                "style": {}
+              }
+            },
+            "0.25": {
+              "title": "extensions",
+              "id": 47,
+              "ideas": {
+                "1": {
+                  "title": "screenName",
+                  "id": 48,
+                  "attr": {
+                    "style": {}
+                  }
+                }
+              },
+              "attr": {
+                "position": [
+                  60.5,
+                  -126.5,
+                  2
+                ],
+                "style": {}
+              }
+            }
+          }
+        }
+      }
+    },
+    "-1": {
+      "title": "data",
+      "id": 4,
+      "ideas": {
+        "1": {
+          "title": "comments",
+          "id": 5,
+          "ideas": {
+            "1": {
+              "title": "data",
+              "id": 44
+            }
+          }
+        },
+        "3": {
+          "title": "caption",
+          "id": 7,
+          "ideas": {}
+        },
+        "4": {
+          "title": "links",
+          "id": 9
+        },
+        "5": {
+          "title": "link",
+          "id": 10
+        },
+        "6": {
+          "title": "user",
+          "id": 11,
+          "ideas": {
+            "1": {
+              "title": "username",
+              "id": 33
+            },
+            "2": {
+              "title": "profile_picture",
+              "id": 34
+            },
+            "3": {
+              "title": "id",
+              "id": 37
+            }
+          }
+        },
+        "9": {
+          "title": "type",
+          "id": 21
+        },
+        "12": {
+          "title": "videos",
+          "id": 41,
+          "ideas": {
+            "1": {
+              "title": "low_resolution",
+              "id": 42,
+              "ideas": {
+                "1": {
+                  "title": "url",
+                  "id": 67
+                },
+                "2": {
+                  "title": "height",
+                  "id": 68
+                },
+                "3": {
+                  "title": "width",
+                  "id": 69
+                }
+              }
+            },
+            "2": {
+              "title": "standard_resolution",
+              "id": 43,
+              "ideas": {
+                "1": {
+                  "title": "height",
+                  "id": 70
+                },
+                "2": {
+                  "title": "width",
+                  "id": 71
+                },
+                "3": {
+                  "title": "url",
+                  "id": 72
+                }
+              }
+            }
+          }
+        },
+        "13": {
+          "title": "tags",
+          "id": 45,
+          "ideas": {}
+        },
+        "14": {
+          "title": "users_in_photo",
+          "id": 95,
+          "ideas": {
+            "1": {
+              "title": "id",
+              "id": 96,
+              "ideas": {}
+            },
+            "2": {
+              "title": "profile_picture",
+              "id": 98
+            },
+            "3": {
+              "title": "username",
+              "id": 97,
+              "attr": {
+                "style": {}
+              }
+            }
+          }
+        },
+        "11.5": {
+          "title": "images",
+          "id": 14,
+          "ideas": {
+            "1": {
+              "title": "low_resolution",
+              "id": 15,
+              "ideas": {
+                "1": {
+                  "title": "url",
+                  "id": 18
+                },
+                "2": {
+                  "title": "height",
+                  "id": 58
+                },
+                "3": {
+                  "title": "width",
+                  "id": 59
+                }
+              }
+            },
+            "2": {
+              "title": "thumbnail",
+              "id": 16,
+              "ideas": {
+                "1": {
+                  "title": "url",
+                  "id": 19
+                },
+                "2": {
+                  "title": "height",
+                  "id": 60
+                },
+                "3": {
+                  "title": "width",
+                  "id": 61
+                }
+              }
+            },
+            "3": {
+              "title": "standard_resolution",
+              "id": 17,
+              "ideas": {
+                "1": {
+                  "title": "url",
+                  "id": 20
+                },
+                "2": {
+                  "title": "height",
+                  "id": 73
+                },
+                "3": {
+                  "title": "width",
+                  "id": 74
+                }
+              }
+            }
+          }
+        },
+        "6.5": {
+          "title": "id",
+          "id": 23
+        },
+        "6.25": {
+          "title": "created_time",
+          "id": 12
+        },
+        "12.5": {
+          "title": "likes",
+          "id": 6,
+          "ideas": {
+            "1": {
+              "title": "count",
+              "id": 89
+            }
+          },
+          "attr": {
+            "style": {}
+          }
+        },
+        "12.25": {
+          "title": "location",
+          "id": 24,
+          "ideas": {
+            "1": {
+              "title": "latitude",
+              "id": 83
+            },
+            "2": {
+              "title": "longitude",
+              "id": 84
+            }
+          },
+          "attr": {
+            "style": {}
+          }
+        }
+      }
+    }
+  },
+  "links": [
+    {
+      "ideaIdFrom": 23,
+      "ideaIdTo": 26,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 33,
+      "ideaIdTo": 36,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 37,
+      "ideaIdTo": 35,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 12,
+      "ideaIdTo": 25,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 21,
+      "ideaIdTo": 38,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 33,
+      "ideaIdTo": 48,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 50,
+      "ideaIdTo": 34,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 83,
+      "ideaIdTo": 82,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 84,
+      "ideaIdTo": 82,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 45,
+      "ideaIdTo": 76,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 89,
+      "ideaIdTo": 90,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 91,
+      "ideaIdTo": 4,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 40,
+      "ideaIdTo": 10,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 97,
+      "ideaIdTo": 94,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 96,
+      "ideaIdTo": 93,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 64,
+      "ideaIdTo": 20,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 65,
+      "ideaIdTo": 73,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 66,
+      "ideaIdTo": 74,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 66,
+      "ideaIdTo": 71,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 65,
+      "ideaIdTo": 70,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 64,
+      "ideaIdTo": 72,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 55,
+      "ideaIdTo": 19,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 56,
+      "ideaIdTo": 60,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 61,
+      "ideaIdTo": 57,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 103,
+      "ideaIdTo": 18,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 104,
+      "ideaIdTo": 58,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 59,
+      "ideaIdTo": 105,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 110,
+      "ideaIdTo": 67,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 111,
+      "ideaIdTo": 68,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 112,
+      "ideaIdTo": 69,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 7,
+      "ideaIdTo": 114,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    },
+    {
+      "ideaIdFrom": 113,
+      "ideaIdTo": 7,
+      "attr": {
+        "style": {
+          "color": "#FF0000",
+          "lineStyle": "dashed"
+        }
+      }
+    }
+  ]
+}
\ No newline at end of file


[02/18] git commit: bootstrap of module

Posted by sb...@apache.org.
bootstrap of module


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/537b97aa
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/537b97aa
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/537b97aa

Branch: refs/heads/master
Commit: 537b97aa0adb1bf396b51cc12214722bf4f95c6c
Parents: 34c95a6
Author: Steve Blackmon <sb...@w2odigital.com>
Authored: Thu Jun 26 18:58:14 2014 -0500
Committer: Steve Blackmon <sb...@w2odigital.com>
Committed: Thu Jun 26 18:58:14 2014 -0500

----------------------------------------------------------------------
 .../streams-provider-instagram/README.md        |  17 +
 .../streams-provider-instagram/pom.xml          | 139 ++++
 .../instagram/InstagramConfigurator.java        |  78 +++
 .../processor/InstagramTypeConverter.java       | 190 +++++
 .../provider/InstagramTimelineProvider.java     | 409 +++++++++++
 .../InstagramJsonActivitySerializer.java        |  60 ++
 .../serializer/util/InstagramActivityUtil.java  | 142 ++++
 .../com/instagram/InstagramConfiguration.json   |  20 +
 .../InstagramUserInformationConfiguration.json  |  17 +
 .../src/main/resources/reference.conf           |   5 +
 .../test/InstagramActivitySerDeTest.java        |  92 +++
 .../src/test/resources/testtweets.txt           | 695 +++++++++++++++++++
 12 files changed, 1864 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/README.md
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/README.md b/streams-contrib/streams-provider-instagram/README.md
new file mode 100644
index 0000000..3bca23b
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/README.md
@@ -0,0 +1,17 @@
+streams-provider-instagram
+
+Purpose                  
+
+  Module connects to instagram API, collects events, converts to activity, and passes each activity downstream.
+
+Example configuration
+
+    "instagram": {
+        "version": "v1",
+        "endpoint": "media/recent",
+        "accessToken": "",
+        "info": [
+            "3",
+            "kevin"        
+        ]
+    }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/pom.xml
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/pom.xml b/streams-contrib/streams-provider-instagram/pom.xml
new file mode 100644
index 0000000..e356a7a
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/pom.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.apache.streams</groupId>
+        <artifactId>streams-contrib</artifactId>
+        <version>0.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>streams-provider-instagram</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.typesafe</groupId>
+            <artifactId>config</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-joda</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jsonschema2pojo</groupId>
+            <artifactId>jsonschema2pojo-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.streams</groupId>
+            <artifactId>streams-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.streams</groupId>
+            <artifactId>streams-pojo</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.streams</groupId>
+            <artifactId>streams-config</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.streams</groupId>
+            <artifactId>streams-util</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.jayway.jsonpath</groupId>
+            <artifactId>json-path</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.jayway.jsonpath</groupId>
+            <artifactId>json-path-assert</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sachinhandiekar</groupId>
+            <artifactId>jInstagram</artifactId>
+            <version>1.0.7</version>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-all</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <sourceDirectory>src/main/java</sourceDirectory>
+        <testSourceDirectory>src/test/java</testSourceDirectory>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+        </resources>
+        <testResources>
+            <testResource>
+                <directory>src/test/resources</directory>
+            </testResource>
+        </testResources>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>1.8</version>
+                <executions>
+                    <execution>
+                        <id>add-source</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>target/generated-sources/jsonschema2pojo</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.jsonschema2pojo</groupId>
+                <artifactId>jsonschema2pojo-maven-plugin</artifactId>
+                <configuration>
+                    <addCompileSourceRoot>true</addCompileSourceRoot>
+                    <generateBuilders>true</generateBuilders>
+                    <sourcePaths>
+                        <sourcePath>src/main/jsonschema/com/instagram/InstagramConfiguration.json</sourcePath>
+                        <sourcePath>src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json</sourcePath>
+                    </sourcePaths>
+                    <outputDirectory>target/generated-sources/jsonschema2pojo</outputDirectory>
+                    <targetPackage>org.apache.streams.instagram.pojo</targetPackage>
+                    <useLongIntegers>true</useLongIntegers>
+                    <useJodaDates>true</useJodaDates>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
new file mode 100644
index 0000000..f771856
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.streams.instagram;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Preconditions;
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigRenderOptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import java.io.IOException;
+
+/**
+ * Created by sblackmon on 12/10/13.
+ */
+public class InstagramConfigurator {
+
+    private final static Logger LOGGER = LoggerFactory.getLogger(InstagramConfigurator.class);
+    private final static ObjectMapper mapper = new ObjectMapper();
+
+
+    public static InstagramConfiguration detectInstagramConfiguration(Config config) {
+
+        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+        Validator validator = factory.getValidator();
+
+        InstagramConfiguration instagramConfiguration = null;
+        try {
+            instagramConfiguration = mapper.readValue(config.root().render(ConfigRenderOptions.concise()), InstagramConfiguration.class);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        Preconditions.checkNotNull(instagramConfiguration);
+
+        Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
+
+        return instagramConfiguration;
+    }
+
+    public static InstagramUserInformationConfiguration detectInstagramUserInformationConfiguration(Config config) {
+
+        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+        Validator validator = factory.getValidator();
+
+        InstagramUserInformationConfiguration instagramConfiguration = null;
+        try {
+            instagramConfiguration = mapper.readValue(config.root().render(ConfigRenderOptions.concise()), InstagramUserInformationConfiguration.class);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        Preconditions.checkNotNull(instagramConfiguration);
+
+        Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
+
+        return instagramConfiguration;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
new file mode 100644
index 0000000..14260e3
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.streams.instagram.processor;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Lists;
+import org.apache.streams.core.StreamsDatum;
+import org.apache.streams.core.StreamsProcessor;
+import org.apache.streams.exceptions.ActivitySerializerException;
+import org.apache.streams.instagram.serializer.InstagramJsonActivitySerializer;
+import org.apache.streams.jackson.StreamsJacksonMapper;
+import org.apache.streams.pojo.json.Activity;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * Created by sblackmon on 12/10/13.
+ */
+public class InstagramTypeConverter implements StreamsProcessor {
+
+    public final static String STREAMS_ID = "InstagramTypeConverter";
+
+    private final static Logger LOGGER = LoggerFactory.getLogger(InstagramTypeConverter.class);
+
+    private ObjectMapper mapper;
+
+    private Queue<MediaFeedData> inQueue;
+    private Queue<StreamsDatum> outQueue;
+
+    private Class inClass;
+    private Class outClass;
+
+    private InstagramJsonActivitySerializer instagramJsonActivitySerializer;
+
+    private int count = 0;
+
+    public final static String TERMINATE = new String("TERMINATE");
+
+    public InstagramTypeConverter(Class inClass, Class outClass) {
+        this.inClass = inClass;
+        this.outClass = outClass;
+    }
+
+    public Queue<StreamsDatum> getProcessorOutputQueue() {
+        return outQueue;
+    }
+
+    public void setProcessorInputQueue(Queue<MediaFeedData> inputQueue) {
+        inQueue = inputQueue;
+    }
+
+    public Object convert(ObjectNode event, Class inClass, Class outClass) throws ActivitySerializerException, JsonProcessingException {
+
+        Object result = null;
+
+        if( outClass.equals( Activity.class )) {
+            LOGGER.debug("ACTIVITY");
+            result = instagramJsonActivitySerializer.deserialize(
+                    mapper.writeValueAsString(event));
+        } else if( outClass.equals( ObjectNode.class )) {
+            LOGGER.debug("OBJECTNODE");
+            result = mapper.convertValue(event, ObjectNode.class);
+        } else if( outClass.equals( String.class )) {
+            LOGGER.debug("OBJECTNODE");
+            result = mapper.writeValueAsString(event);
+        }
+
+
+    // no supported conversion were applied
+        if( result != null ) {
+            count ++;
+            return result;
+        }
+
+        LOGGER.debug("CONVERT FAILED");
+
+        return null;
+
+    }
+
+    public boolean validate(Object document, Class klass) {
+
+        // TODO
+        return true;
+    }
+
+    public boolean isValidJSON(final String json) {
+        boolean valid = false;
+        try {
+            final JsonParser parser = new ObjectMapper().getJsonFactory()
+                    .createJsonParser(json);
+            while (parser.nextToken() != null) {
+            }
+            valid = true;
+        } catch (JsonParseException jpe) {
+            LOGGER.warn("validate: {}", jpe);
+        } catch (IOException ioe) {
+            LOGGER.warn("validate: {}", ioe);
+        }
+
+        return valid;
+    }
+
+    @Override
+    public List<StreamsDatum> process(StreamsDatum entry) {
+
+        StreamsDatum result = null;
+
+        try {
+
+            Object item = entry.getDocument();
+            ObjectNode node;
+
+            LOGGER.debug("{} processing {}", STREAMS_ID, item.getClass());
+
+            if( item instanceof String ) {
+
+                // if the target is string, just pass-through
+                if( String.class.equals(outClass)) {
+                    result = entry;
+                }
+                else {
+                    // first check for valid json
+                    node = (ObjectNode)mapper.readTree((String)item);
+
+                    Object out = convert(node, String.class, outClass);
+
+                    if( out != null && validate(out, outClass))
+                        result = new StreamsDatum(out);
+                }
+
+            } else if( item instanceof ObjectNode ) {
+
+                // first check for valid json
+                node = (ObjectNode)mapper.valueToTree(item);
+
+                Object out = convert(node, ObjectNode.class, outClass);
+
+                if( out != null && validate(out, outClass))
+                    result = new StreamsDatum(out);
+
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        if( result != null )
+            return Lists.newArrayList(result);
+        else
+            return Lists.newArrayList();
+    }
+
+    @Override
+    public void prepare(Object o) {
+        mapper = new StreamsJacksonMapper();
+        instagramJsonActivitySerializer = new InstagramJsonActivitySerializer();
+    }
+
+    @Override
+    public void cleanUp() {
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java
new file mode 100644
index 0000000..d3e7179
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java
@@ -0,0 +1,409 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.streams.instagram.provider;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Queues;
+import com.typesafe.config.Config;
+import org.apache.streams.config.StreamsConfigurator;
+import org.apache.streams.core.DatumStatusCounter;
+import org.apache.streams.core.StreamsDatum;
+import org.apache.streams.core.StreamsProvider;
+import org.apache.streams.core.StreamsResultSet;
+import org.apache.streams.instagram.InstagramConfigurator;
+import org.apache.streams.instagram.InstagramUserInformationConfiguration;
+import org.apache.streams.jackson.StreamsJacksonMapper;
+import org.jinstagram.Instagram;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class InstagramTimelineProvider implements StreamsProvider, Serializable {
+
+    public final static String STREAMS_ID = "InstagramTimelineProvider";
+
+    private final static Logger LOGGER = LoggerFactory.getLogger(InstagramTimelineProvider.class);
+    public static final int MAX_NUMBER_WAITING = 10000;
+
+    private static StreamsJacksonMapper mapper = StreamsJacksonMapper.getInstance();
+    private InstagramUserInformationConfiguration config;
+
+    private Class klass;
+    protected final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+    public InstagramUserInformationConfiguration getConfig() {
+        return config;
+    }
+
+    public void setConfig(InstagramUserInformationConfiguration config) {
+        this.config = config;
+    }
+
+    protected Iterator<Long[]> idsBatches;
+    protected Iterator<String[]> screenNameBatches;
+
+    protected volatile Queue<StreamsDatum> providerQueue;
+
+    protected int idsCount;
+    protected Instagram client;
+
+
+    protected ExecutorService executor;
+
+    protected DateTime start;
+    protected DateTime end;
+
+    protected final AtomicBoolean running = new AtomicBoolean();
+
+    private static ExecutorService getExecutor() {
+        return Executors.newSingleThreadExecutor();
+    }
+
+    public InstagramTimelineProvider() {
+        Config config = StreamsConfigurator.config.getConfig("instagram");
+        this.config = InstagramConfigurator.detectInstagramUserInformationConfiguration(config);
+    }
+
+    public InstagramTimelineProvider(InstagramUserInformationConfiguration config) {
+        this.config = config;
+    }
+
+    public InstagramTimelineProvider(Class klass) {
+        Config config = StreamsConfigurator.config.getConfig("instagram");
+        this.config = InstagramConfigurator.detectInstagramUserInformationConfiguration(config);
+        this.klass = klass;
+    }
+
+    public InstagramTimelineProvider(InstagramUserInformationConfiguration config, Class klass) {
+        this.config = config;
+        this.klass = klass;
+    }
+
+
+    public Queue<StreamsDatum> getProviderQueue() {
+        return this.providerQueue;
+    }
+
+    @Override
+    public void startStream() {
+        LOGGER.debug("{} startStream", STREAMS_ID);
+
+        Preconditions.checkArgument(idsBatches.hasNext() || screenNameBatches.hasNext());
+
+        LOGGER.info("readCurrent");
+
+        while(idsBatches.hasNext())
+            loadBatch(idsBatches.next());
+
+        while(screenNameBatches.hasNext())
+            loadBatch(screenNameBatches.next());
+
+        executor.shutdown();
+    }
+
+    private void loadBatch(Long[] ids) {
+
+        // twitter4j implementation below - replace with jInstagram
+
+//        Twitter client = getTwitterClient();
+//        int keepTrying = 0;
+//
+//        // keep trying to load, give it 5 attempts.
+//        //while (keepTrying < 10)
+//        while (keepTrying < 1)
+//        {
+//            try
+//            {
+//                long[] toQuery = new long[ids.length];
+//                for(int i = 0; i < ids.length; i++)
+//                    toQuery[i] = ids[i];
+//
+//                for (User tStat : client.lookupUsers(toQuery)) {
+//
+//                    TwitterTimelineProviderTask providerTask = new TwitterTimelineProviderTask(this, client, tStat.getId());
+//                    executor.submit(providerTask);
+//
+//                }
+//                keepTrying = 10;
+//            }
+//            catch(TwitterException twitterException) {
+//                keepTrying += TwitterErrorHandler.handleTwitterError(client, twitterException);
+//            }
+//            catch(Exception e) {
+//                keepTrying += TwitterErrorHandler.handleTwitterError(client, e);
+//            }
+//        }
+    }
+
+    private void loadBatch(String[] ids) {
+
+        // twitter4j implementation below - replace with jInstagram
+//
+//        Twitter client = getTwitterClient();
+//        int keepTrying = 0;
+//
+//        // keep trying to load, give it 5 attempts.
+//        //while (keepTrying < 10)
+//        while (keepTrying < 1)
+//        {
+//            try
+//            {
+//                for (User tStat : client.lookupUsers(ids)) {
+//
+//                    TwitterTimelineProviderTask providerTask = new TwitterTimelineProviderTask(this, client, tStat.getId());
+//                    executor.submit(providerTask);
+//
+//                }
+//                keepTrying = 10;
+//            }
+//            catch(TwitterException twitterException) {
+//                keepTrying += TwitterErrorHandler.handleTwitterError(client, twitterException);
+//            }
+//            catch(Exception e) {
+//                keepTrying += TwitterErrorHandler.handleTwitterError(client, e);
+//            }
+//        }
+    }
+
+    public class InstagramTimelineProviderTask implements Runnable {
+
+        // twitter4j implementation below - replace with jInstagram
+
+        private final Logger LOGGER = LoggerFactory.getLogger(InstagramTimelineProvider.class);
+
+        private InstagramTimelineProvider provider;
+        private Instagram client;
+        private Long id;
+
+        public InstagramTimelineProviderTask(InstagramTimelineProvider provider, Instagram client, Long id) {
+            this.provider = provider;
+            this.client = client;
+            this.id = id;
+        }
+
+        @Override
+        public void run() {
+
+            // twitter4j implementation below - replace with jInstagram
+
+//            Paging paging = new Paging(1, 200);
+//            List<Status> statuses = null;
+//            boolean KeepGoing = true;
+//            boolean hadFailure = false;
+//
+//            do
+//            {
+//                int keepTrying = 0;
+//
+//                // keep trying to load, give it 5 attempts.
+//                //This value was chosen because it seemed like a reasonable number of times
+//                //to retry capturing a timeline given the sorts of errors that could potentially
+//                //occur (network timeout/interruption, faulty client, etc.)
+//                while (keepTrying < 5)
+//                {
+//
+//                    try
+//                    {
+//                        statuses = client.getUserTimeline(id, paging);
+//
+//                        for (Status tStat : statuses)
+//                        {
+//                            String json = TwitterObjectFactory.getRawJSON(tStat);
+//
+//                            try {
+//                                provider.lock.readLock().lock();
+//                                ComponentUtils.offerUntilSuccess(new StreamsDatum(json), provider.providerQueue);
+//                            } finally {
+//                                provider.lock.readLock().unlock();
+//                            }
+//                        }
+//
+//                        paging.setPage(paging.getPage() + 1);
+//
+//                        keepTrying = 10;
+//                    }
+//                    catch(TwitterException twitterException) {
+//                        keepTrying += TwitterErrorHandler.handleTwitterError(client, twitterException);
+//                    }
+//                    catch(Exception e) {
+//                        keepTrying += TwitterErrorHandler.handleTwitterError(client, e);
+//                    }
+//                }
+//            }
+//            while (provider.shouldContinuePulling(statuses));
+
+            LOGGER.info(id + " Thread Finished");
+
+        }
+
+    }
+
+    private Map<Long, Long> userPullInfo;
+
+    protected boolean shouldContinuePulling(List<MediaFeedData> statuses) {
+        return (statuses != null) && (statuses.size() > 0);
+    }
+
+    private void sleep()
+    {
+        Thread.yield();
+        try {
+            // wait one tenth of a millisecond
+            Thread.yield();
+            Thread.sleep(1);
+            Thread.yield();
+        }
+        catch(IllegalArgumentException e) {
+            // passing in static values, this will never happen
+        }
+        catch(InterruptedException e) {
+            // noOp, there must have been an issue sleeping
+        }
+        Thread.yield();
+    }
+
+    public StreamsResultSet readCurrent() {
+
+        LOGGER.info("Providing {} docs", providerQueue.size());
+
+        StreamsResultSet result;
+
+        try {
+            lock.writeLock().lock();
+            result = new StreamsResultSet(providerQueue);
+            result.setCounter(new DatumStatusCounter());
+            providerQueue = constructQueue();
+        } finally {
+            lock.writeLock().unlock();
+        }
+
+        if( providerQueue.isEmpty() && executor.isTerminated()) {
+            LOGGER.info("Finished.  Cleaning up...");
+
+            running.set(false);
+
+            LOGGER.info("Exiting");
+        }
+
+        return result;
+
+    }
+
+    protected Queue<StreamsDatum> constructQueue() {
+        return Queues.synchronizedQueue(new LinkedBlockingQueue<StreamsDatum>(MAX_NUMBER_WAITING));
+    }
+
+    public StreamsResultSet readNew(BigInteger sequence) {
+        LOGGER.debug("{} readNew", STREAMS_ID);
+        throw new NotImplementedException();
+    }
+
+    public StreamsResultSet readRange(DateTime start, DateTime end) {
+        LOGGER.debug("{} readRange", STREAMS_ID);
+        throw new NotImplementedException();
+    }
+
+    @Override
+    public boolean isRunning() {
+        return running.get();
+    }
+
+    void shutdownAndAwaitTermination(ExecutorService pool) {
+        pool.shutdown(); // Disable new tasks from being submitted
+        try {
+            // Wait a while for existing tasks to terminate
+            if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
+                pool.shutdownNow(); // Cancel currently executing tasks
+                // Wait a while for tasks to respond to being cancelled
+                if (!pool.awaitTermination(10, TimeUnit.SECONDS))
+                    System.err.println("Pool did not terminate");
+            }
+        } catch (InterruptedException ie) {
+            // (Re-)Cancel if current thread also interrupted
+            pool.shutdownNow();
+            // Preserve interrupt status
+            Thread.currentThread().interrupt();
+        }
+    }
+
+
+    @Override
+    public void prepare(Object o) {
+
+        executor = getExecutor();
+        running.set(true);
+        try {
+            lock.writeLock().lock();
+            providerQueue = constructQueue();
+        } finally {
+            lock.writeLock().unlock();
+        }
+
+        Preconditions.checkNotNull(providerQueue);
+
+        Preconditions.checkNotNull(this.klass);
+        Preconditions.checkNotNull(config.getAccessToken());
+
+        //idsCount = config.getFollow().size();
+
+        client = getInstagramClient();
+    }
+
+    protected Instagram getInstagramClient()
+    {
+        // twitter4j -> jInstagram
+//        String baseUrl = "https://api.instagram.com:443/1.1/";
+//
+//        ConfigurationBuilder builder = new ConfigurationBuilder()
+//                .setOAuthConsumerKey(config.getOauth().getConsumerKey())
+//                .setOAuthConsumerSecret(config.getOauth().getConsumerSecret())
+//                .setOAuthAccessToken(config.getOauth().getAccessToken())
+//                .setOAuthAccessTokenSecret(config.getOauth().getAccessTokenSecret())
+//                .setIncludeEntitiesEnabled(includeEntitiesEnabled)
+//                .setJSONStoreEnabled(jsonStoreEnabled)
+//                .setAsyncNumThreads(3)
+//                .setRestBaseURL(baseUrl)
+//                .setIncludeMyRetweetEnabled(Boolean.TRUE)
+//                .setPrettyDebugEnabled(Boolean.TRUE);
+//
+//        return new InstagramFactory(builder.build()).getInstance();
+        return null;
+    }
+
+    @Override
+    public void cleanUp() {
+        shutdownAndAwaitTermination(executor);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
new file mode 100644
index 0000000..8d92641
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.streams.instagram.serializer;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.streams.data.ActivitySerializer;
+import org.apache.streams.exceptions.ActivitySerializerException;
+import org.apache.streams.pojo.json.Activity;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class InstagramJsonActivitySerializer implements ActivitySerializer<String>, Serializable
+{
+
+    public InstagramJsonActivitySerializer() {
+
+    }
+
+    @Override
+    public String serializationFormat() {
+        return null;
+    }
+
+    @Override
+    public String serialize(Activity deserialized) throws ActivitySerializerException {
+        throw new NotImplementedException();
+    }
+
+    @Override
+    public Activity deserialize(String serialized) throws ActivitySerializerException {
+
+        Activity activity = null;
+
+        // implement
+
+        return activity;
+    }
+
+    @Override
+    public List<Activity> deserializeAll(List<String> serializedList) {
+        throw new NotImplementedException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
new file mode 100644
index 0000000..e71c43e
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.streams.instagram.serializer.util;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import org.apache.streams.exceptions.ActivitySerializerException;
+import org.apache.streams.pojo.json.Activity;
+import org.apache.streams.pojo.json.ActivityObject;
+import org.apache.streams.pojo.json.Actor;
+import org.apache.streams.pojo.json.Provider;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.streams.data.util.ActivityUtil.ensureExtensions;
+
+/**
+ * Provides utilities for working with Activity objects within the context of Instagram
+ */
+public class InstagramActivityUtil {
+
+    /**
+     * Updates the given Activity object with the values from the item
+     * @param item the object to use as the source
+     * @param activity the target of the updates.  Will receive all values from the tweet.
+     * @throws ActivitySerializerException
+     */
+    public static void updateActivity(MediaFeedData item, Activity activity) throws ActivitySerializerException {
+
+    }
+
+    /**
+     * Builds the actor
+     * @param item the item
+     * @return a valid Actor
+     */
+    public static  Actor buildActor(MediaFeedData item) {
+        Actor actor = new Actor();
+        return actor;
+    }
+
+    /**
+     * Builds the ActivityObject
+     * @param item the item
+     * @return a valid Activity Object
+     */
+    public static ActivityObject buildActivityObject(MediaFeedData item) {
+        ActivityObject actObj = new ActivityObject();
+        return actObj;
+    }
+
+
+    /**
+     * Updates the content, and associated fields, with those from the given tweet
+     * @param activity the target of the updates.  Will receive all values from the tweet.
+     * @param item the object to use as the source
+     * @param verb the verb for the given activity's type
+     */
+    public static void updateActivityContent(Activity activity, MediaFeedData item, String verb) {
+
+    }
+
+    /**
+     * Gets the links from the Instagram event
+     * @param item the object to use as the source
+     * @return a list of links corresponding to the expanded URL
+     */
+    public static List<String> getLinks(MediaFeedData item) {
+        List<String> links = Lists.newArrayList();
+        return links;
+    }
+
+    /**
+     * Adds the location extension and populates with teh instagram data
+     * @param activity the Activity object to update
+     * @param item the object to use as the source
+     */
+    public static void addLocationExtension(Activity activity, MediaFeedData item) {
+        Map<String, Object> extensions = ensureExtensions(activity);
+        Map<String, Object> location = new HashMap<String, Object>();
+
+    }
+
+    /**
+     * Gets the common instagram {@link org.apache.streams.pojo.json.Provider} object
+     * @return a provider object representing Instagram
+     */
+    public static Provider getProvider() {
+        Provider provider = new Provider();
+        provider.setId("id:providers:instagram");
+        provider.setDisplayName("Instagram");
+        return provider;
+    }
+    /**
+     * Adds the given Instagram event to the activity as an extension
+     * @param activity the Activity object to update
+     * @param event the Instagram event to add as the extension
+     */
+    public static void addInstagramExtension(Activity activity, ObjectNode event) {
+        Map<String, Object> extensions = org.apache.streams.data.util.ActivityUtil.ensureExtensions(activity);
+        extensions.put("instagram", event);
+    }
+    /**
+     * Formats the ID to conform with the Apache Streams activity ID convention
+     * @param idparts the parts of the ID to join
+     * @return a valid Activity ID in format "id:instagram:part1:part2:...partN"
+     */
+    public static String formatId(String... idparts) {
+        return Joiner.on(":").join(Lists.asList("id:instagram", idparts));
+    }
+
+    /**
+     * Takes various parameters from the instagram object that are currently not part of teh
+     * activity schema and stores them in a generic extensions attribute
+     * @param activity
+     * @param item
+     */
+    public static void addInstagramExtensions(Activity activity, MediaFeedData item) {
+        Map<String, Object> extensions = ensureExtensions(activity);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
new file mode 100644
index 0000000..18a59b9
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
@@ -0,0 +1,20 @@
+{
+    "type": "object",
+    "$schema": "http://json-schema.org/draft-03/schema",
+    "id": "#",
+    "javaType" : "org.apache.streams.instagram.InstagramConfiguration",
+    "javaInterfaces": ["java.io.Serializable"],
+    "properties": {
+        "version": {
+            "type": "string",
+            "description": "The version"
+        },
+        "endpoint": {
+            "type": "string",
+            "description": "The endpoint"
+        },
+        "accessToken": {
+            "type": "string"
+        }
+   }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
new file mode 100644
index 0000000..4b75ee4
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
@@ -0,0 +1,17 @@
+{
+    "type": "object",
+    "$schema": "http://json-schema.org/draft-03/schema",
+    "id": "#",
+    "javaType" : "org.apache.streams.instagram.InstagramUserInformationConfiguration",
+    "extends": {"$ref":"InstagramConfiguration.json"},
+    "javaInterfaces": ["java.io.Serializable"],
+    "properties": {
+        "info": {
+            "type": "array",
+            "description": "A list of user IDs, indicating the users whose posts should be delivered on the stream",
+            "items": {
+                "type": "string"
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/resources/reference.conf
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/resources/reference.conf b/streams-contrib/streams-provider-instagram/src/main/resources/reference.conf
new file mode 100644
index 0000000..9a01bf6
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/resources/reference.conf
@@ -0,0 +1,5 @@
+instagram {
+    version = "v1"
+    endpoint = "sample"
+    accessToken = ""
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
new file mode 100644
index 0000000..fcf5e81
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.streams.twitter.test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.lang.StringUtils;
+import org.apache.streams.instagram.serializer.InstagramJsonActivitySerializer;
+import org.apache.streams.jackson.StreamsJacksonMapper;
+import org.apache.streams.pojo.json.Activity;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertThat;
+
+/**
+* Created with IntelliJ IDEA.
+* User: sblackmon
+* Date: 8/20/13
+* Time: 5:57 PM
+* To change this template use File | Settings | File Templates.
+*/
+public class InstagramActivitySerDeTest {
+
+    private final static Logger LOGGER = LoggerFactory.getLogger(InstagramActivitySerDeTest.class);
+    private ObjectMapper mapper = StreamsJacksonMapper.getInstance();
+
+    private InstagramJsonActivitySerializer instagramJsonActivitySerializer = new InstagramJsonActivitySerializer();
+
+    // remove @Ignore after implementation
+    @Ignore
+    @Test
+    public void Tests()
+    {
+        InputStream is = InstagramActivitySerDeTest.class.getResourceAsStream("/test.txt");
+        InputStreamReader isr = new InputStreamReader(is);
+        BufferedReader br = new BufferedReader(isr);
+
+        try {
+            while (br.ready()) {
+                String line = br.readLine();
+                if(!StringUtils.isEmpty(line))
+                {
+                    LOGGER.info("raw: {}", line);
+
+                    // convert to MediaFeedData?
+                    Activity activity = instagramJsonActivitySerializer.deserialize(line);
+
+                    String activitystring = mapper.writeValueAsString(activity);
+
+                    LOGGER.info("activity: {}", activitystring);
+
+                    assertThat(activity, is(not(nullValue())));
+
+                    assertThat(activity.getId(), is(not(nullValue())));
+                    assertThat(activity.getActor(), is(not(nullValue())));
+                    assertThat(activity.getActor().getId(), is(not(nullValue())));
+                    assertThat(activity.getVerb(), is(not(nullValue())));
+                    assertThat(activity.getProvider(), is(not(nullValue())));
+
+                }
+            }
+        } catch( Exception e ) {
+            System.out.println(e);
+            e.printStackTrace();
+            Assert.fail();
+        }
+    }
+}


[17/18] git commit: Fixed merge issue for rbnks/STREAMS-121

Posted by sb...@apache.org.
Fixed merge issue for rbnks/STREAMS-121


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/2f0a20f3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/2f0a20f3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/2f0a20f3

Branch: refs/heads/master
Commit: 2f0a20f30eb8878b74126190435819a02d714452
Parents: 110dddb
Author: mfranklin <mf...@apache.org>
Authored: Tue Jul 15 12:46:15 2014 -0400
Committer: mfranklin <mf...@apache.org>
Committed: Tue Jul 15 12:46:15 2014 -0400

----------------------------------------------------------------------
 streams-contrib/pom.xml | 1 -
 1 file changed, 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/2f0a20f3/streams-contrib/pom.xml
----------------------------------------------------------------------
diff --git a/streams-contrib/pom.xml b/streams-contrib/pom.xml
index e225af1..699274e 100644
--- a/streams-contrib/pom.xml
+++ b/streams-contrib/pom.xml
@@ -59,7 +59,6 @@
         <module>streams-provider-sysomos</module>
         <module>streams-provider-rss</module>
         <module>streams-processor-regex</module>
-        <module>streams-provider-instagram</module>
     </modules>
 
     <dependencyManagement>


[16/18] git commit: STREAMS-121 | Change volatile booleans to AtomicBooleans per pull request feedbask

Posted by sb...@apache.org.
STREAMS-121 | Change volatile booleans to AtomicBooleans per pull request feedbask


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/110dddbd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/110dddbd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/110dddbd

Branch: refs/heads/master
Commit: 110dddbd25eefbce5ec959103f6004cca5864f28
Parents: 45510b2
Author: rebanks <re...@w2odigital.com>
Authored: Mon Jul 14 14:05:37 2014 -0500
Committer: rebanks <re...@w2odigital.com>
Committed: Mon Jul 14 14:05:37 2014 -0500

----------------------------------------------------------------------
 .../apache/streams/instagram/InstagramConfigurator.java   |  2 +-
 .../instagram/provider/InstagramRecentMediaCollector.java |  9 +++++----
 .../instagram/provider/InstagramRecentMediaProvider.java  | 10 +++++-----
 .../provider/InstagramRecentMediaProviderTest.java        |  3 ++-
 4 files changed, 13 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/110dddbd/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
index cab072d..11e6d79 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
@@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory;
 import java.io.IOException;
 
 /**
- * 
+ *
  */
 public class InstagramConfigurator {
 

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/110dddbd/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
index e459a0a..4f27e49 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
@@ -25,6 +25,7 @@ import org.slf4j.LoggerFactory;
 
 import java.util.Queue;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Executes on all of the Instagram requests to collect the media feed data.
@@ -43,14 +44,14 @@ public class InstagramRecentMediaCollector implements Runnable {
     protected Queue dataQueue; //exposed for testing
     private InstagramUserInformationConfiguration config;
     private Instagram instagramClient;
-    private volatile boolean isCompleted;
+    private AtomicBoolean isCompleted;
 
 
     public InstagramRecentMediaCollector(Queue<MediaFeedData> queue, InstagramUserInformationConfiguration config) {
         this.dataQueue = queue;
         this.config = config;
         this.instagramClient = new Instagram(this.config.getClientId());
-        this.isCompleted = false;
+        this.isCompleted = new AtomicBoolean(false);
     }
 
     /**
@@ -151,7 +152,7 @@ public class InstagramRecentMediaCollector implements Runnable {
      * @return true when the collector has queued all of available media feed data for the provided users.
      */
     public boolean isCompleted() {
-        return this.isCompleted;
+        return this.isCompleted.get();
     }
 
     @Override
@@ -159,6 +160,6 @@ public class InstagramRecentMediaCollector implements Runnable {
         for(Long userId : getUserIds()) {
             getUserMedia(userId);
         }
-        this.isCompleted = true;
+        this.isCompleted.set(true);
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/110dddbd/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
index 3d67a48..30ddda4 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
@@ -29,6 +29,7 @@ import java.util.Queue;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Instagram {@link org.apache.streams.core.StreamsProvider} that provides the recent media data for a group of users
@@ -39,7 +40,7 @@ public class InstagramRecentMediaProvider implements StreamsProvider {
     private InstagramRecentMediaCollector dataCollector;
     protected Queue<MediaFeedData> mediaFeedQueue; //exposed for testing
     private ExecutorService executorService;
-    private volatile boolean isCompleted;
+    private AtomicBoolean isCompleted;
 
     public InstagramRecentMediaProvider() {
         this(InstagramConfigurator.detectInstagramUserInformationConfiguration(StreamsConfigurator.config.getConfig("instagram")));
@@ -48,7 +49,6 @@ public class InstagramRecentMediaProvider implements StreamsProvider {
     public InstagramRecentMediaProvider(InstagramUserInformationConfiguration config) {
         this.config = config;
         this.mediaFeedQueue = Queues.newConcurrentLinkedQueue();
-        this.isCompleted = false;
     }
 
     @Override
@@ -77,7 +77,7 @@ public class InstagramRecentMediaProvider implements StreamsProvider {
                 batch.add(new StreamsDatum(data, data.getId()));
             }
         }
-        this.isCompleted = batch.size() == 0 && this.mediaFeedQueue.isEmpty() && this.dataCollector.isCompleted();
+        this.isCompleted.set(batch.size() == 0 && this.mediaFeedQueue.isEmpty() && this.dataCollector.isCompleted());
         return new StreamsResultSet(batch);
     }
 
@@ -93,12 +93,12 @@ public class InstagramRecentMediaProvider implements StreamsProvider {
 
     @Override
     public boolean isRunning() {
-        return !this.isCompleted;
+        return !this.isCompleted.get();
     }
 
     @Override
     public void prepare(Object configurationObject) {
-
+        this.isCompleted = new AtomicBoolean(false);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/110dddbd/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
index 59e90b4..d81f85a 100644
--- a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
@@ -63,7 +63,7 @@ public class InstagramRecentMediaProviderTest {
                 return collectorStub;
             }
         };
-
+        provider.prepare(null);
         provider.startStream();
 
         latch.await();
@@ -141,6 +141,7 @@ public class InstagramRecentMediaProviderTest {
                 };
             }
         };
+        provider.prepare(null);
         provider.startStream();
         while(provider.isRunning()) {
             try {


[06/18] git commit: STREAMS-122 | Updated the InstagramActivityUtil class to fully map Instagram MediaFeedData objects to Activities. Updated tests so that this deserialization and mapping can be tested

Posted by sb...@apache.org.
STREAMS-122 | Updated the InstagramActivityUtil class to fully map Instagram MediaFeedData objects to Activities. Updated tests so that this deserialization and mapping can be tested


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/11636535
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/11636535
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/11636535

Branch: refs/heads/master
Commit: 116365355dad985da52092c85378b5bd7497e907
Parents: 7b301ce
Author: Robert Douglas <rd...@w2odigital.com>
Authored: Tue Jul 1 17:12:57 2014 -0500
Committer: Robert Douglas <rd...@w2odigital.com>
Committed: Tue Jul 1 17:12:57 2014 -0500

----------------------------------------------------------------------
 streams-contrib/pom.xml                         |   1 +
 .../InstagramJsonActivitySerializer.java        |  22 ++-
 .../serializer/util/InstagramActivityUtil.java  | 170 ++++++++++++++++---
 .../test/InstagramActivitySerDeTest.java        |  24 ++-
 .../src/test/resources/testMediaFeedObjects.txt |   2 +
 5 files changed, 181 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/11636535/streams-contrib/pom.xml
----------------------------------------------------------------------
diff --git a/streams-contrib/pom.xml b/streams-contrib/pom.xml
index 620f68e..699274e 100644
--- a/streams-contrib/pom.xml
+++ b/streams-contrib/pom.xml
@@ -47,6 +47,7 @@
 		<module>streams-amazon-aws</module>
         <!--<module>streams-processor-lucene</module>-->
         <!--<module>streams-processor-tika</module>-->
+        <module>streams-provider-instagram</module>
         <module>streams-processor-json</module>
         <module>streams-processor-urls</module>
         <module>streams-provider-datasift</module>

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/11636535/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
index 8d92641..c5bbdf1 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
@@ -18,14 +18,21 @@
 
 package org.apache.streams.instagram.serializer;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.commons.lang.NotImplementedException;
 import org.apache.streams.data.ActivitySerializer;
 import org.apache.streams.exceptions.ActivitySerializerException;
+import org.apache.streams.jackson.StreamsJacksonMapper;
 import org.apache.streams.pojo.json.Activity;
+import org.jinstagram.entity.users.feed.MediaFeedData;
 
+import java.io.IOException;
 import java.io.Serializable;
 import java.util.List;
 
+import static org.apache.streams.instagram.serializer.util.InstagramActivityUtil.updateActivity;
+
 public class InstagramJsonActivitySerializer implements ActivitySerializer<String>, Serializable
 {
 
@@ -46,9 +53,20 @@ public class InstagramJsonActivitySerializer implements ActivitySerializer<Strin
     @Override
     public Activity deserialize(String serialized) throws ActivitySerializerException {
 
-        Activity activity = null;
+        ObjectMapper mapper = StreamsJacksonMapper.getInstance();
+        MediaFeedData mediaFeedData = null;
+
+        try {
+            mediaFeedData = mapper.readValue(serialized, MediaFeedData.class);
+        } catch (JsonProcessingException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        Activity activity = new Activity();
 
-        // implement
+        updateActivity(mediaFeedData, activity);
 
         return activity;
     }

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/11636535/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
index e71c43e..0561ba7 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
@@ -19,16 +19,21 @@
 
 package org.apache.streams.instagram.serializer.util;
 
-import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
 import com.google.common.collect.Lists;
 import org.apache.streams.exceptions.ActivitySerializerException;
-import org.apache.streams.pojo.json.Activity;
-import org.apache.streams.pojo.json.ActivityObject;
-import org.apache.streams.pojo.json.Actor;
-import org.apache.streams.pojo.json.Provider;
+import org.apache.streams.pojo.json.*;
+import org.jinstagram.entity.common.ImageData;
+import org.jinstagram.entity.common.Images;
+import org.jinstagram.entity.common.VideoData;
+import org.jinstagram.entity.common.Videos;
 import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -39,7 +44,7 @@ import static org.apache.streams.data.util.ActivityUtil.ensureExtensions;
  * Provides utilities for working with Activity objects within the context of Instagram
  */
 public class InstagramActivityUtil {
-
+    private static final Logger LOGGER = LoggerFactory.getLogger(InstagramActivityUtil.class);
     /**
      * Updates the given Activity object with the values from the item
      * @param item the object to use as the source
@@ -47,7 +52,22 @@ public class InstagramActivityUtil {
      * @throws ActivitySerializerException
      */
     public static void updateActivity(MediaFeedData item, Activity activity) throws ActivitySerializerException {
+        activity.setActor(buildActor(item));
+        activity.setPublished(new DateTime(Long.parseLong(item.getCreatedTime()) * 1000));
+
+        activity.setId(formatId(activity.getVerb(),
+            Optional.fromNullable(
+                    item.getId())
+                        .orNull()));
+
+        activity.setProvider(getProvider());
+        activity.setUrl(item.getLink());
+        activity.setObject(buildActivityObject(item));
 
+        if(item.getCaption() != null)
+            activity.setContent(item.getCaption().getText());
+
+        addInstagramExtensions(activity, item);
     }
 
     /**
@@ -57,6 +77,18 @@ public class InstagramActivityUtil {
      */
     public static  Actor buildActor(MediaFeedData item) {
         Actor actor = new Actor();
+
+        Image image = new Image();
+        image.setUrl(item.getUser().getProfilePictureUrl());
+
+        Map<String, Object> extensions = new HashMap<String, Object>();
+        extensions.put("screenName", item.getUser().getUserName());
+
+        actor.setId(formatId(String.valueOf(item.getUser().getId())));
+        actor.setImage(image);
+        actor.setAdditionalProperty("extensions", extensions);
+        actor.setAdditionalProperty("handle", item.getUser().getUserName());
+
         return actor;
     }
 
@@ -67,18 +99,90 @@ public class InstagramActivityUtil {
      */
     public static ActivityObject buildActivityObject(MediaFeedData item) {
         ActivityObject actObj = new ActivityObject();
+
+        actObj.setObjectType(item.getType());
+        actObj.setAttachments(buildActivityObjectAttachments(item));
+
         return actObj;
     }
 
+    /**
+     * Builds all of the attachments associated with a MediaFeedData object
+     *
+     * @param item
+     * @return
+     */
+    public static List<ActivityObject> buildActivityObjectAttachments(MediaFeedData item) {
+        List<ActivityObject> attachments = new ArrayList<ActivityObject>();
+
+        addImageObjects(attachments, item);
+        addVideoObjects(attachments, item);
+
+        return attachments;
+    }
 
     /**
-     * Updates the content, and associated fields, with those from the given tweet
-     * @param activity the target of the updates.  Will receive all values from the tweet.
-     * @param item the object to use as the source
-     * @param verb the verb for the given activity's type
+     * Adds any image objects to the attachment field
+     * @param attachments
+     * @param item
+     */
+    public static void addImageObjects(List<ActivityObject> attachments, MediaFeedData item) {
+        Images images = item.getImages();
+
+        if(images != null) {
+            try {
+                ImageData thumbnail = images.getThumbnail();
+                ImageData lowResolution = images.getLowResolution();
+
+                ActivityObject thumbnailObject = new ActivityObject();
+                Image thumbnailImage = new Image();
+                thumbnailImage.setUrl(thumbnail.getImageUrl());
+                thumbnailImage.setHeight((double) thumbnail.getImageHeight());
+                thumbnailImage.setWidth((double) thumbnail.getImageWidth());
+                thumbnailObject.setImage(thumbnailImage);
+                thumbnailObject.setObjectType("image");
+
+                ActivityObject lowResolutionObject = new ActivityObject();
+                Image lowResolutionImage = new Image();
+                lowResolutionImage.setUrl(lowResolution.getImageUrl());
+                lowResolutionImage.setHeight((double) lowResolution.getImageHeight());
+                lowResolutionImage.setWidth((double) lowResolution.getImageWidth());
+                lowResolutionObject.setImage(lowResolutionImage);
+                lowResolutionObject.setObjectType("image");
+
+                attachments.add(thumbnailObject);
+                attachments.add(lowResolutionObject);
+            } catch (Exception e) {
+                LOGGER.error("Failed to add image objects: {}", e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Adds any video objects to the attachment field
+     * @param attachments
+     * @param item
      */
-    public static void updateActivityContent(Activity activity, MediaFeedData item, String verb) {
+    public static void addVideoObjects(List<ActivityObject> attachments, MediaFeedData item) {
+        Videos videos = item.getVideos();
+
+        if(videos != null) {
+            try {
+                VideoData lowResolutionVideo = videos.getLowResolution();
+
+                ActivityObject lowResolutionVideoObject = new ActivityObject();
+                Image lowResolutionVideoImage = new Image();
+                lowResolutionVideoImage.setUrl(lowResolutionVideo.getUrl());
+                lowResolutionVideoImage.setHeight((double) lowResolutionVideo.getHeight());
+                lowResolutionVideoImage.setWidth((double) lowResolutionVideo.getWidth());
+                lowResolutionVideoObject.setImage(lowResolutionVideoImage);
+                lowResolutionVideoObject.setObjectType("video");
 
+                attachments.add(lowResolutionVideoObject);
+            } catch (Exception e) {
+                LOGGER.error("Failed to add video objects: {}", e.getMessage());
+            }
+        }
     }
 
     /**
@@ -98,7 +202,14 @@ public class InstagramActivityUtil {
      */
     public static void addLocationExtension(Activity activity, MediaFeedData item) {
         Map<String, Object> extensions = ensureExtensions(activity);
-        Map<String, Object> location = new HashMap<String, Object>();
+
+        if(item.getLocation() != null) {
+            Map<String, Object> coordinates = new HashMap<String, Object>();
+            coordinates.put("type", "Point");
+            coordinates.put("coordinates", "[" + item.getLocation().getLatitude() + "," + item.getLocation().getLongitude() + "]");
+
+            extensions.put("coordinates", coordinates);
+        }
 
     }
 
@@ -112,15 +223,7 @@ public class InstagramActivityUtil {
         provider.setDisplayName("Instagram");
         return provider;
     }
-    /**
-     * Adds the given Instagram event to the activity as an extension
-     * @param activity the Activity object to update
-     * @param event the Instagram event to add as the extension
-     */
-    public static void addInstagramExtension(Activity activity, ObjectNode event) {
-        Map<String, Object> extensions = org.apache.streams.data.util.ActivityUtil.ensureExtensions(activity);
-        extensions.put("instagram", event);
-    }
+
     /**
      * Formats the ID to conform with the Apache Streams activity ID convention
      * @param idparts the parts of the ID to join
@@ -138,5 +241,28 @@ public class InstagramActivityUtil {
      */
     public static void addInstagramExtensions(Activity activity, MediaFeedData item) {
         Map<String, Object> extensions = ensureExtensions(activity);
+
+        addLocationExtension(activity, item);
+
+        Map<String, Object> likes = new HashMap<String, Object>();
+        likes.put("count", item.getLikes().getCount());
+        extensions.put("likes", likes);
+
+        extensions.put("hashtags", item.getTags());
+
+        Image standardResolution = new Image();
+        if(item.getType() == "image" && item.getImages() != null) {
+            ImageData standardResolutionData = item.getImages().getStandardResolution();
+            standardResolution.setHeight((double)standardResolutionData.getImageHeight());
+            standardResolution.setWidth((double)standardResolutionData.getImageWidth());
+            standardResolution.setUrl(standardResolutionData.getImageUrl());
+        } else if(item.getType() == "video" && item.getVideos() != null) {
+            VideoData standardResolutionData = item.getVideos().getStandardResolution();
+            standardResolution.setHeight((double)standardResolutionData.getHeight());
+            standardResolution.setWidth((double)standardResolutionData.getWidth());
+            standardResolution.setUrl(standardResolutionData.getUrl());
+        }
+
+        extensions.put("image", standardResolution);
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/11636535/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
index fcf5e81..075da80 100644
--- a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
@@ -20,11 +20,12 @@ package org.apache.streams.twitter.test;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.commons.lang.StringUtils;
+import org.apache.streams.instagram.serializer.util.InstagramDeserializer;
 import org.apache.streams.instagram.serializer.InstagramJsonActivitySerializer;
 import org.apache.streams.jackson.StreamsJacksonMapper;
 import org.apache.streams.pojo.json.Activity;
+import org.jinstagram.entity.users.feed.MediaFeedData;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,6 +34,7 @@ import java.io.BufferedReader;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 
+import static org.apache.streams.instagram.serializer.util.InstagramActivityUtil.updateActivity;
 import static org.hamcrest.CoreMatchers.*;
 import static org.junit.Assert.assertThat;
 
@@ -46,16 +48,11 @@ import static org.junit.Assert.assertThat;
 public class InstagramActivitySerDeTest {
 
     private final static Logger LOGGER = LoggerFactory.getLogger(InstagramActivitySerDeTest.class);
-    private ObjectMapper mapper = StreamsJacksonMapper.getInstance();
 
-    private InstagramJsonActivitySerializer instagramJsonActivitySerializer = new InstagramJsonActivitySerializer();
-
-    // remove @Ignore after implementation
-    @Ignore
     @Test
-    public void Tests()
-    {
-        InputStream is = InstagramActivitySerDeTest.class.getResourceAsStream("/test.txt");
+    public void Tests() {
+        InstagramDeserializer instagramDeserializer = new InstagramDeserializer("");
+        InputStream is = InstagramActivitySerDeTest.class.getResourceAsStream("/testMediaFeedObjects.txt");
         InputStreamReader isr = new InputStreamReader(is);
         BufferedReader br = new BufferedReader(isr);
 
@@ -66,13 +63,13 @@ public class InstagramActivitySerDeTest {
                 {
                     LOGGER.info("raw: {}", line);
 
-                    // convert to MediaFeedData?
-                    Activity activity = instagramJsonActivitySerializer.deserialize(line);
+                    MediaFeedData mediaFeedData = instagramDeserializer.createObjectFromResponse(MediaFeedData.class, line);
 
-                    String activitystring = mapper.writeValueAsString(activity);
+                    Activity activity = new Activity();
 
-                    LOGGER.info("activity: {}", activitystring);
+                    LOGGER.info("activity: {}", activity.toString());
 
+                    updateActivity(mediaFeedData, activity);
                     assertThat(activity, is(not(nullValue())));
 
                     assertThat(activity.getId(), is(not(nullValue())));
@@ -80,7 +77,6 @@ public class InstagramActivitySerDeTest {
                     assertThat(activity.getActor().getId(), is(not(nullValue())));
                     assertThat(activity.getVerb(), is(not(nullValue())));
                     assertThat(activity.getProvider(), is(not(nullValue())));
-
                 }
             }
         } catch( Exception e ) {

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/11636535/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt b/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
index e69de29..b62e599 100644
--- a/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
+++ b/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
@@ -0,0 +1,2 @@
+{ "attribution":null, "tags":[ ], "type":"image", "location":null, "comments":{ "count":0, "data":[ ] }, "filter":"X-Pro II", "created_time":"1404162054", "link":"http://instagram.com/p/p4ez__syJi/", "likes":{ "count":0, "data":[ ] }, "images":{ "low_resolution":{ "url":"http://scontent-a.cdninstagram.com/hphotos-xpa1/t51.2885-15/10518155_283164271863608_1534480525_a.jpg", "width":306, "height":306 }, "thumbnail":{ "url":"http://scontent-a.cdninstagram.com/hphotos-xpa1/t51.2885-15/10518155_283164271863608_1534480525_s.jpg", "width":150, "height":150 }, "standard_resolution":{ "url":"http://scontent-a.cdninstagram.com/hphotos-xpa1/t51.2885-15/10518155_283164271863608_1534480525_n.jpg", "width":640, "height":640 } }, "users_in_photo":[ ], "caption":{ "created_time":"1404162054", "text":"Testing streams", "from":{ "username":"ktsafford", "profile_picture":"http://images.ak.instagram.com/profiles/anonymousUser.jpg", "id":"1412068271", "full_name":"ktsafford" }, "id":"754488452958068751"
  }, "user_has_liked":false, "id":"754488452387644002_1412068271", "user":{ "username":"ktsafford", "website":"", "profile_picture":"http://images.ak.instagram.com/profiles/anonymousUser.jpg", "full_name":"ktsafford", "bio":"", "id":"1412068271" } }
+{ "type":"image", "users_in_photo":[ { "user":{ "username":"kevin", "full_name":"Kevin S", "id":"3", "profile_picture":"..." }, "position":{ "x":0.315, "y":0.9111 } } ], "filter":"Walden", "tags":[ ], "comments":{ "data":[ { "created_time":"1279332030", "text":"Love the sign here", "from":{ "username":"mikeyk", "full_name":"Mikey Krieger", "id":"4", "profile_picture":"http://distillery.s3.amazonaws.com/profiles/profile_1242695_75sq_1293915800.jpg" }, "id":"8" }, { "created_time":"1279341004", "text":"Chilako taco", "from":{ "username":"kevin", "full_name":"Kevin S", "id":"3", "profile_picture":"..." }, "id":"3" } ], "count":2 }, "caption":null, "likes":{ "count":1, "data":[ { "username":"mikeyk", "full_name":"Mikeyk", "id":"4", "profile_picture":"..." } ] }, "link":"http://instagr.am/p/D/", "user":{ "username":"kevin", "full_name":"Kevin S", "profile_picture":"...", "bio":"...", "website":"...", "id":"3" }, "created_time":"1279340983", "images":{ "low_resolution":{ "url":"http://dis
 tillery.s3.amazonaws.com/media/2010/07/16/4de37e03aa4b4372843a7eb33fa41cad_6.jpg", "width":306, "height":306 }, "thumbnail":{ "url":"http://distillery.s3.amazonaws.com/media/2010/07/16/4de37e03aa4b4372843a7eb33fa41cad_5.jpg", "width":150, "height":150 }, "standard_resolution":{ "url":"http://distillery.s3.amazonaws.com/media/2010/07/16/4de37e03aa4b4372843a7eb33fa41cad_7.jpg", "width":612, "height":612 } }, "id":"3", "location":null }


[18/18] git commit: Merge branch 'instagram'

Posted by sb...@apache.org.
Merge branch 'instagram'


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/7c5e2900
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/7c5e2900
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/7c5e2900

Branch: refs/heads/master
Commit: 7c5e29007b27f59cdf7120c26a00b59da3ce931e
Parents: b7e4c34 2f0a20f
Author: Steve Blackmon <sb...@w2odigital.com>
Authored: Sun Jul 20 23:46:56 2014 -0500
Committer: Steve Blackmon <sb...@w2odigital.com>
Committed: Sun Jul 20 23:46:56 2014 -0500

----------------------------------------------------------------------
 streams-contrib/pom.xml                         |   1 +
 .../streams-provider-instagram/README.md        |  17 +
 .../InstagramMediaDataActivitySerializer.mup    | 894 +++++++++++++++++++
 .../metadata/instagram_to_activity_mapping.png  | Bin 0 -> 1123684 bytes
 .../streams-provider-instagram/pom.xml          | 145 +++
 .../instagram/InstagramConfigurator.java        |  65 ++
 .../processor/InstagramTypeConverter.java       | 102 +++
 .../provider/InstagramRecentMediaCollector.java | 165 ++++
 .../provider/InstagramRecentMediaProvider.java  | 115 +++
 .../InstagramJsonActivitySerializer.java        |  78 ++
 .../serializer/util/InstagramActivityUtil.java  | 285 ++++++
 .../serializer/util/InstagramDeserializer.java  |  33 +
 .../com/instagram/InstagramConfiguration.json   |  21 +
 .../InstagramUserInformationConfiguration.json  |  17 +
 .../src/main/resources/reference.conf           |   5 +
 .../InstagramRecentMediaCollectorTest.java      | 175 ++++
 .../InstagramRecentMediaProviderTest.java       | 160 ++++
 .../test/InstagramActivitySerDeTest.java        |  88 ++
 .../src/test/resources/testMediaFeedObjects.txt |   2 +
 .../src/test/resources/testtweets.txt           | 695 ++++++++++++++
 20 files changed, 3063 insertions(+)
----------------------------------------------------------------------



[12/18] git commit: STREAMS 121 | Added instagram provider and tests

Posted by sb...@apache.org.
STREAMS 121 | Added instagram provider and tests


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/77603934
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/77603934
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/77603934

Branch: refs/heads/master
Commit: 77603934e41ac78ba68f73b3f80ea6852f0acb46
Parents: 815ce2a
Author: rebanks <re...@w2odigital.com>
Authored: Fri Jul 11 16:09:59 2014 -0500
Committer: rebanks <re...@w2odigital.com>
Committed: Fri Jul 11 16:09:59 2014 -0500

----------------------------------------------------------------------
 streams-contrib/pom.xml                         |   1 +
 .../streams-provider-instagram/pom.xml          |   6 +
 .../instagram/InstagramConfigurator.java        |  15 +-
 .../provider/InstagramRecentMediaCollector.java | 120 ++++++
 .../provider/InstagramRecentMediaProvider.java  |  97 +++++
 .../provider/InstagramTimelineProvider.java     | 409 -------------------
 .../com/instagram/InstagramConfiguration.json   |  13 +-
 .../InstagramUserInformationConfiguration.json  |   2 +-
 .../InstagramRecentMediaCollectorTest.java      | 161 ++++++++
 .../InstagramRecentMediaProviderTest.java       | 145 +++++++
 .../test/InstagramActivitySerDeTest.java        |  92 -----
 11 files changed, 544 insertions(+), 517 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/pom.xml
----------------------------------------------------------------------
diff --git a/streams-contrib/pom.xml b/streams-contrib/pom.xml
index 620f68e..b06c276 100644
--- a/streams-contrib/pom.xml
+++ b/streams-contrib/pom.xml
@@ -58,6 +58,7 @@
         <module>streams-provider-sysomos</module>
         <module>streams-provider-rss</module>
         <module>streams-processor-regex</module>
+        <module>streams-provider-instagram</module>
     </modules>
 
     <dependencyManagement>

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/pom.xml
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/pom.xml b/streams-contrib/streams-provider-instagram/pom.xml
index e356a7a..a96ff1c 100644
--- a/streams-contrib/streams-provider-instagram/pom.xml
+++ b/streams-contrib/streams-provider-instagram/pom.xml
@@ -76,6 +76,12 @@
             <version>1.3</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.9.5</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
index f771856..4d01605 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
@@ -25,9 +25,6 @@ import com.typesafe.config.ConfigRenderOptions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.validation.Validation;
-import javax.validation.Validator;
-import javax.validation.ValidatorFactory;
 import java.io.IOException;
 
 /**
@@ -41,8 +38,8 @@ public class InstagramConfigurator {
 
     public static InstagramConfiguration detectInstagramConfiguration(Config config) {
 
-        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
-        Validator validator = factory.getValidator();
+//        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+//        Validator validator = factory.getValidator();
 
         InstagramConfiguration instagramConfiguration = null;
         try {
@@ -52,15 +49,15 @@ public class InstagramConfigurator {
         }
         Preconditions.checkNotNull(instagramConfiguration);
 
-        Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
+//        Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
 
         return instagramConfiguration;
     }
 
     public static InstagramUserInformationConfiguration detectInstagramUserInformationConfiguration(Config config) {
 
-        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
-        Validator validator = factory.getValidator();
+//        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+//        Validator validator = factory.getValidator();
 
         InstagramUserInformationConfiguration instagramConfiguration = null;
         try {
@@ -70,7 +67,7 @@ public class InstagramConfigurator {
         }
         Preconditions.checkNotNull(instagramConfiguration);
 
-        Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
+//        Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
 
         return instagramConfiguration;
     }

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
new file mode 100644
index 0000000..7eb3fcd
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
@@ -0,0 +1,120 @@
+package org.apache.streams.instagram.provider;
+
+import com.google.common.collect.Sets;
+import org.apache.streams.instagram.InstagramUserInformationConfiguration;
+import org.jinstagram.Instagram;
+import org.jinstagram.entity.users.feed.MediaFeed;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.jinstagram.exceptions.InstagramException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Queue;
+import java.util.Set;
+
+/**
+ *
+ */
+public class InstagramRecentMediaCollector implements Runnable {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(InstagramRecentMediaCollector.class);
+    protected static final int MAX_ATTEMPTS = 5;
+    protected static final int SLEEP_SECS = 5; //5 seconds
+
+    protected Queue dataQueue; //exposed for testing
+    private InstagramUserInformationConfiguration config;
+    private Instagram instagramClient;
+    private volatile boolean isCompleted;
+
+
+    public InstagramRecentMediaCollector(Queue<MediaFeedData> queue, InstagramUserInformationConfiguration config) {
+        this.dataQueue = queue;
+        this.config = config;
+        this.instagramClient = new Instagram(this.config.getClientId());
+        this.isCompleted = false;
+    }
+
+    protected void setInstagramClient(Instagram instagramClient) {
+        this.instagramClient = instagramClient;
+    }
+
+    protected Set<Long> getUserIds() {
+        Set<Long> userIds = Sets.newHashSet();
+        for(String id : config.getUserIds()) {
+            try {
+                userIds.add(Long.parseLong(id));
+            } catch (NumberFormatException nfe) {
+                LOGGER.error("Failed to parse user id, {}, to a long : {}", id, nfe.getMessage());
+            }
+        }
+        return userIds;
+    }
+
+    protected void handleInstagramException(InstagramException instaExec, int attempt) throws InstagramException {
+        LOGGER.debug("RemainingApiLimitStatus: {}", instaExec.getRemainingLimitStatus());
+        if(instaExec.getRemainingLimitStatus() == 0) { //rate limit exception
+            long sleepTime = Math.round(Math.pow(SLEEP_SECS, attempt)) * 1000;
+            try {
+                LOGGER.debug("Encountered rate limit exception, sleeping for {} ms", sleepTime);
+                Thread.sleep(sleepTime);
+            } catch (InterruptedException ie) {
+                Thread.currentThread().interrupt();
+            }
+        } else {
+            LOGGER.error("Instagram returned an excetpion to the user media request : {}", instaExec.getMessage());
+            throw instaExec;
+        }
+    }
+
+    private void getUserMedia(Long userId) {
+        MediaFeed feed = null;
+        int attempts = 0;
+        int count = 0;
+        do {
+            ++attempts;
+            try {
+                feed = this.instagramClient.getRecentMediaFeed(userId);
+                queueData(feed, userId);
+                count += feed.getData().size();
+                while(feed != null && feed.getPagination() != null && feed.getPagination().hasNextPage()) {
+                    feed = this.instagramClient.getRecentMediaNextPage(feed.getPagination());
+                    queueData(feed, userId);
+                    count += feed.getData().size();
+                }
+            } catch (InstagramException ie) {
+                try {
+                    handleInstagramException(ie, attempts);
+                } catch (InstagramException ie2) { //not a rate limit exception, ignore user
+                    attempts = MAX_ATTEMPTS;
+                }
+            }
+        } while(feed == null && attempts < MAX_ATTEMPTS);
+        LOGGER.debug("For user, {}, received {} MediaFeedData", userId, count);
+    }
+
+    private void queueData(MediaFeed userFeed, Long userId) {
+        if(userFeed == null) {
+            LOGGER.error("User id, {}, returned a NULL media feed from instagram.", userId);
+        } else {
+            for(MediaFeedData data : userFeed.getData()) {
+                synchronized (this.dataQueue) {
+                    while(!this.dataQueue.offer(data)) {
+                        Thread.yield();
+                    }
+                }
+            }
+        }
+    }
+
+    public boolean isCompleted() {
+        return this.isCompleted;
+    }
+
+    @Override
+    public void run() {
+        for(Long userId : getUserIds()) {
+            getUserMedia(userId);
+        }
+        this.isCompleted = true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
new file mode 100644
index 0000000..3354e54
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
@@ -0,0 +1,97 @@
+package org.apache.streams.instagram.provider;
+
+import com.google.common.collect.Queues;
+import org.apache.streams.config.StreamsConfigurator;
+import org.apache.streams.core.StreamsDatum;
+import org.apache.streams.core.StreamsProvider;
+import org.apache.streams.core.StreamsResultSet;
+import org.apache.streams.instagram.InstagramConfigurator;
+import org.apache.streams.instagram.InstagramUserInformationConfiguration;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.joda.time.DateTime;
+
+import java.math.BigInteger;
+import java.util.Queue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by rebanks on 7/9/14.
+ */
+public class InstagramRecentMediaProvider implements StreamsProvider {
+
+    private InstagramUserInformationConfiguration config;
+    private InstagramRecentMediaCollector dataCollector;
+    protected Queue<MediaFeedData> mediaFeedQueue; //exposed for testing
+    private ExecutorService executorService;
+    private volatile boolean isCompleted;
+
+    public InstagramRecentMediaProvider() {
+        this(InstagramConfigurator.detectInstagramUserInformationConfiguration(StreamsConfigurator.config.getConfig("instagram")));
+    }
+
+    public InstagramRecentMediaProvider(InstagramUserInformationConfiguration config) {
+        this.config = config;
+        this.mediaFeedQueue = Queues.newConcurrentLinkedQueue();
+        this.isCompleted = false;
+    }
+
+    @Override
+    public void startStream() {
+        this.dataCollector = getInstagramRecentMediaCollector();
+        this.executorService = Executors.newSingleThreadExecutor();
+        this.executorService.submit(this.dataCollector);
+    }
+
+    protected InstagramRecentMediaCollector getInstagramRecentMediaCollector() {
+        return new InstagramRecentMediaCollector(this.mediaFeedQueue, this.config);
+    }
+
+
+    @Override
+    public StreamsResultSet readCurrent() {
+        Queue<StreamsDatum> batch = Queues.newConcurrentLinkedQueue();
+        MediaFeedData data = null;
+        synchronized (this.mediaFeedQueue) {
+            while(!this.mediaFeedQueue.isEmpty()) {
+                data = this.mediaFeedQueue.poll();
+                batch.add(new StreamsDatum(data, data.getId()));
+            }
+        }
+        this.isCompleted = batch.size() == 0 && this.mediaFeedQueue.isEmpty() && this.dataCollector.isCompleted();
+        return new StreamsResultSet(batch);
+    }
+
+    @Override
+    public StreamsResultSet readNew(BigInteger sequence) {
+        return null;
+    }
+
+    @Override
+    public StreamsResultSet readRange(DateTime start, DateTime end) {
+        return null;
+    }
+
+    @Override
+    public boolean isRunning() {
+        return !this.isCompleted;
+    }
+
+    @Override
+    public void prepare(Object configurationObject) {
+
+    }
+
+    @Override
+    public void cleanUp() {
+        this.executorService.shutdown();
+        try {
+            this.executorService.awaitTermination(5, TimeUnit.SECONDS);
+        } catch (InterruptedException ie) {
+            Thread.currentThread().interrupt();
+        } finally {
+            this.executorService = null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java
deleted file mode 100644
index d3e7179..0000000
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.streams.instagram.provider;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Queues;
-import com.typesafe.config.Config;
-import org.apache.streams.config.StreamsConfigurator;
-import org.apache.streams.core.DatumStatusCounter;
-import org.apache.streams.core.StreamsDatum;
-import org.apache.streams.core.StreamsProvider;
-import org.apache.streams.core.StreamsResultSet;
-import org.apache.streams.instagram.InstagramConfigurator;
-import org.apache.streams.instagram.InstagramUserInformationConfiguration;
-import org.apache.streams.jackson.StreamsJacksonMapper;
-import org.jinstagram.Instagram;
-import org.jinstagram.entity.users.feed.MediaFeedData;
-import org.joda.time.DateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import sun.reflect.generics.reflectiveObjects.NotImplementedException;
-
-import java.io.Serializable;
-import java.math.BigInteger;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-public class InstagramTimelineProvider implements StreamsProvider, Serializable {
-
-    public final static String STREAMS_ID = "InstagramTimelineProvider";
-
-    private final static Logger LOGGER = LoggerFactory.getLogger(InstagramTimelineProvider.class);
-    public static final int MAX_NUMBER_WAITING = 10000;
-
-    private static StreamsJacksonMapper mapper = StreamsJacksonMapper.getInstance();
-    private InstagramUserInformationConfiguration config;
-
-    private Class klass;
-    protected final ReadWriteLock lock = new ReentrantReadWriteLock();
-
-    public InstagramUserInformationConfiguration getConfig() {
-        return config;
-    }
-
-    public void setConfig(InstagramUserInformationConfiguration config) {
-        this.config = config;
-    }
-
-    protected Iterator<Long[]> idsBatches;
-    protected Iterator<String[]> screenNameBatches;
-
-    protected volatile Queue<StreamsDatum> providerQueue;
-
-    protected int idsCount;
-    protected Instagram client;
-
-
-    protected ExecutorService executor;
-
-    protected DateTime start;
-    protected DateTime end;
-
-    protected final AtomicBoolean running = new AtomicBoolean();
-
-    private static ExecutorService getExecutor() {
-        return Executors.newSingleThreadExecutor();
-    }
-
-    public InstagramTimelineProvider() {
-        Config config = StreamsConfigurator.config.getConfig("instagram");
-        this.config = InstagramConfigurator.detectInstagramUserInformationConfiguration(config);
-    }
-
-    public InstagramTimelineProvider(InstagramUserInformationConfiguration config) {
-        this.config = config;
-    }
-
-    public InstagramTimelineProvider(Class klass) {
-        Config config = StreamsConfigurator.config.getConfig("instagram");
-        this.config = InstagramConfigurator.detectInstagramUserInformationConfiguration(config);
-        this.klass = klass;
-    }
-
-    public InstagramTimelineProvider(InstagramUserInformationConfiguration config, Class klass) {
-        this.config = config;
-        this.klass = klass;
-    }
-
-
-    public Queue<StreamsDatum> getProviderQueue() {
-        return this.providerQueue;
-    }
-
-    @Override
-    public void startStream() {
-        LOGGER.debug("{} startStream", STREAMS_ID);
-
-        Preconditions.checkArgument(idsBatches.hasNext() || screenNameBatches.hasNext());
-
-        LOGGER.info("readCurrent");
-
-        while(idsBatches.hasNext())
-            loadBatch(idsBatches.next());
-
-        while(screenNameBatches.hasNext())
-            loadBatch(screenNameBatches.next());
-
-        executor.shutdown();
-    }
-
-    private void loadBatch(Long[] ids) {
-
-        // twitter4j implementation below - replace with jInstagram
-
-//        Twitter client = getTwitterClient();
-//        int keepTrying = 0;
-//
-//        // keep trying to load, give it 5 attempts.
-//        //while (keepTrying < 10)
-//        while (keepTrying < 1)
-//        {
-//            try
-//            {
-//                long[] toQuery = new long[ids.length];
-//                for(int i = 0; i < ids.length; i++)
-//                    toQuery[i] = ids[i];
-//
-//                for (User tStat : client.lookupUsers(toQuery)) {
-//
-//                    TwitterTimelineProviderTask providerTask = new TwitterTimelineProviderTask(this, client, tStat.getId());
-//                    executor.submit(providerTask);
-//
-//                }
-//                keepTrying = 10;
-//            }
-//            catch(TwitterException twitterException) {
-//                keepTrying += TwitterErrorHandler.handleTwitterError(client, twitterException);
-//            }
-//            catch(Exception e) {
-//                keepTrying += TwitterErrorHandler.handleTwitterError(client, e);
-//            }
-//        }
-    }
-
-    private void loadBatch(String[] ids) {
-
-        // twitter4j implementation below - replace with jInstagram
-//
-//        Twitter client = getTwitterClient();
-//        int keepTrying = 0;
-//
-//        // keep trying to load, give it 5 attempts.
-//        //while (keepTrying < 10)
-//        while (keepTrying < 1)
-//        {
-//            try
-//            {
-//                for (User tStat : client.lookupUsers(ids)) {
-//
-//                    TwitterTimelineProviderTask providerTask = new TwitterTimelineProviderTask(this, client, tStat.getId());
-//                    executor.submit(providerTask);
-//
-//                }
-//                keepTrying = 10;
-//            }
-//            catch(TwitterException twitterException) {
-//                keepTrying += TwitterErrorHandler.handleTwitterError(client, twitterException);
-//            }
-//            catch(Exception e) {
-//                keepTrying += TwitterErrorHandler.handleTwitterError(client, e);
-//            }
-//        }
-    }
-
-    public class InstagramTimelineProviderTask implements Runnable {
-
-        // twitter4j implementation below - replace with jInstagram
-
-        private final Logger LOGGER = LoggerFactory.getLogger(InstagramTimelineProvider.class);
-
-        private InstagramTimelineProvider provider;
-        private Instagram client;
-        private Long id;
-
-        public InstagramTimelineProviderTask(InstagramTimelineProvider provider, Instagram client, Long id) {
-            this.provider = provider;
-            this.client = client;
-            this.id = id;
-        }
-
-        @Override
-        public void run() {
-
-            // twitter4j implementation below - replace with jInstagram
-
-//            Paging paging = new Paging(1, 200);
-//            List<Status> statuses = null;
-//            boolean KeepGoing = true;
-//            boolean hadFailure = false;
-//
-//            do
-//            {
-//                int keepTrying = 0;
-//
-//                // keep trying to load, give it 5 attempts.
-//                //This value was chosen because it seemed like a reasonable number of times
-//                //to retry capturing a timeline given the sorts of errors that could potentially
-//                //occur (network timeout/interruption, faulty client, etc.)
-//                while (keepTrying < 5)
-//                {
-//
-//                    try
-//                    {
-//                        statuses = client.getUserTimeline(id, paging);
-//
-//                        for (Status tStat : statuses)
-//                        {
-//                            String json = TwitterObjectFactory.getRawJSON(tStat);
-//
-//                            try {
-//                                provider.lock.readLock().lock();
-//                                ComponentUtils.offerUntilSuccess(new StreamsDatum(json), provider.providerQueue);
-//                            } finally {
-//                                provider.lock.readLock().unlock();
-//                            }
-//                        }
-//
-//                        paging.setPage(paging.getPage() + 1);
-//
-//                        keepTrying = 10;
-//                    }
-//                    catch(TwitterException twitterException) {
-//                        keepTrying += TwitterErrorHandler.handleTwitterError(client, twitterException);
-//                    }
-//                    catch(Exception e) {
-//                        keepTrying += TwitterErrorHandler.handleTwitterError(client, e);
-//                    }
-//                }
-//            }
-//            while (provider.shouldContinuePulling(statuses));
-
-            LOGGER.info(id + " Thread Finished");
-
-        }
-
-    }
-
-    private Map<Long, Long> userPullInfo;
-
-    protected boolean shouldContinuePulling(List<MediaFeedData> statuses) {
-        return (statuses != null) && (statuses.size() > 0);
-    }
-
-    private void sleep()
-    {
-        Thread.yield();
-        try {
-            // wait one tenth of a millisecond
-            Thread.yield();
-            Thread.sleep(1);
-            Thread.yield();
-        }
-        catch(IllegalArgumentException e) {
-            // passing in static values, this will never happen
-        }
-        catch(InterruptedException e) {
-            // noOp, there must have been an issue sleeping
-        }
-        Thread.yield();
-    }
-
-    public StreamsResultSet readCurrent() {
-
-        LOGGER.info("Providing {} docs", providerQueue.size());
-
-        StreamsResultSet result;
-
-        try {
-            lock.writeLock().lock();
-            result = new StreamsResultSet(providerQueue);
-            result.setCounter(new DatumStatusCounter());
-            providerQueue = constructQueue();
-        } finally {
-            lock.writeLock().unlock();
-        }
-
-        if( providerQueue.isEmpty() && executor.isTerminated()) {
-            LOGGER.info("Finished.  Cleaning up...");
-
-            running.set(false);
-
-            LOGGER.info("Exiting");
-        }
-
-        return result;
-
-    }
-
-    protected Queue<StreamsDatum> constructQueue() {
-        return Queues.synchronizedQueue(new LinkedBlockingQueue<StreamsDatum>(MAX_NUMBER_WAITING));
-    }
-
-    public StreamsResultSet readNew(BigInteger sequence) {
-        LOGGER.debug("{} readNew", STREAMS_ID);
-        throw new NotImplementedException();
-    }
-
-    public StreamsResultSet readRange(DateTime start, DateTime end) {
-        LOGGER.debug("{} readRange", STREAMS_ID);
-        throw new NotImplementedException();
-    }
-
-    @Override
-    public boolean isRunning() {
-        return running.get();
-    }
-
-    void shutdownAndAwaitTermination(ExecutorService pool) {
-        pool.shutdown(); // Disable new tasks from being submitted
-        try {
-            // Wait a while for existing tasks to terminate
-            if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
-                pool.shutdownNow(); // Cancel currently executing tasks
-                // Wait a while for tasks to respond to being cancelled
-                if (!pool.awaitTermination(10, TimeUnit.SECONDS))
-                    System.err.println("Pool did not terminate");
-            }
-        } catch (InterruptedException ie) {
-            // (Re-)Cancel if current thread also interrupted
-            pool.shutdownNow();
-            // Preserve interrupt status
-            Thread.currentThread().interrupt();
-        }
-    }
-
-
-    @Override
-    public void prepare(Object o) {
-
-        executor = getExecutor();
-        running.set(true);
-        try {
-            lock.writeLock().lock();
-            providerQueue = constructQueue();
-        } finally {
-            lock.writeLock().unlock();
-        }
-
-        Preconditions.checkNotNull(providerQueue);
-
-        Preconditions.checkNotNull(this.klass);
-        Preconditions.checkNotNull(config.getAccessToken());
-
-        //idsCount = config.getFollow().size();
-
-        client = getInstagramClient();
-    }
-
-    protected Instagram getInstagramClient()
-    {
-        // twitter4j -> jInstagram
-//        String baseUrl = "https://api.instagram.com:443/1.1/";
-//
-//        ConfigurationBuilder builder = new ConfigurationBuilder()
-//                .setOAuthConsumerKey(config.getOauth().getConsumerKey())
-//                .setOAuthConsumerSecret(config.getOauth().getConsumerSecret())
-//                .setOAuthAccessToken(config.getOauth().getAccessToken())
-//                .setOAuthAccessTokenSecret(config.getOauth().getAccessTokenSecret())
-//                .setIncludeEntitiesEnabled(includeEntitiesEnabled)
-//                .setJSONStoreEnabled(jsonStoreEnabled)
-//                .setAsyncNumThreads(3)
-//                .setRestBaseURL(baseUrl)
-//                .setIncludeMyRetweetEnabled(Boolean.TRUE)
-//                .setPrettyDebugEnabled(Boolean.TRUE);
-//
-//        return new InstagramFactory(builder.build()).getInstance();
-        return null;
-    }
-
-    @Override
-    public void cleanUp() {
-        shutdownAndAwaitTermination(executor);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
index 18a59b9..f8f8117 100644
--- a/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
+++ b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
@@ -5,16 +5,17 @@
     "javaType" : "org.apache.streams.instagram.InstagramConfiguration",
     "javaInterfaces": ["java.io.Serializable"],
     "properties": {
-        "version": {
+        "clientId": {
             "type": "string",
-            "description": "The version"
+            "description": "Your Instagram Client Id"
         },
-        "endpoint": {
+        "clientSecret": {
             "type": "string",
-            "description": "The endpoint"
+            "description": "Your Instagram Client secret"
         },
-        "accessToken": {
-            "type": "string"
+        "callbackUrl": {
+            "type": "string",
+            "description": "Your Instagream callback url"
         }
    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
index 4b75ee4..b6a8c6b 100644
--- a/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
+++ b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
@@ -6,7 +6,7 @@
     "extends": {"$ref":"InstagramConfiguration.json"},
     "javaInterfaces": ["java.io.Serializable"],
     "properties": {
-        "info": {
+        "userIds": {
             "type": "array",
             "description": "A list of user IDs, indicating the users whose posts should be delivered on the stream",
             "items": {

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
new file mode 100644
index 0000000..39f607c
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
@@ -0,0 +1,161 @@
+package org.apache.streams.instagram.provider;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Queues;
+import com.google.common.collect.Sets;
+import org.apache.streams.instagram.InstagramUserInformationConfiguration;
+import org.jinstagram.Instagram;
+import org.jinstagram.entity.common.Pagination;
+import org.jinstagram.entity.users.feed.MediaFeed;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.jinstagram.exceptions.InstagramException;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link org.apache.streams.instagram.provider.InstagramRecentMediaCollector}
+ */
+public class InstagramRecentMediaCollectorTest {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(InstagramRecentMediaCollectorTest.class);
+
+    private int expectedDataCount = 0;
+    private long randomSeed = System.currentTimeMillis();
+    private Random rand = new Random(randomSeed);
+    private Map<Pagination, MediaFeed> pageMap = Maps.newHashMap();
+
+    @Test
+    public void testHandleInstagramException1() throws InstagramException {
+        InstagramException ie = mock(InstagramException.class);
+        when(ie.getRemainingLimitStatus()).thenReturn(1);
+        final String message = "Test Message";
+        when(ie.getMessage()).thenReturn(message);
+        InstagramRecentMediaCollector collector = new InstagramRecentMediaCollector(new ConcurrentLinkedQueue<MediaFeedData>(), new InstagramUserInformationConfiguration());
+        try {
+            collector.handleInstagramException(ie, 1);
+            fail("Expected RuntimeException to be thrown");
+        } catch (InstagramException rte) {
+//            assertTrue(rte.getMessage().contains("Mock for InstagramException"));
+            assertEquals(message, rte.getMessage());
+        }
+    }
+
+    @Test
+    public void testHandleInstagramException2() throws InstagramException{
+        InstagramException ie = mock(InstagramException.class);
+        when(ie.getRemainingLimitStatus()).thenReturn(0);
+        InstagramRecentMediaCollector collector = new InstagramRecentMediaCollector(new ConcurrentLinkedQueue<MediaFeedData>(), new InstagramUserInformationConfiguration());
+        long startTime = System.currentTimeMillis();
+        collector.handleInstagramException(ie, 1);
+        long endTime = System.currentTimeMillis();
+        LOGGER.debug("Slept for {} ms", startTime - endTime);
+        assertTrue(endTime - startTime >= 4000); //allow for 1 sec of error
+        startTime = System.currentTimeMillis();
+        collector.handleInstagramException(ie, 2);
+        endTime = System.currentTimeMillis();
+        LOGGER.debug("Slept for {} ms", startTime - endTime);
+        assertTrue(endTime - startTime >= 24000); //allow for 1 sec of error
+    }
+
+    @Test
+    public void testGetUserIds() {
+        InstagramUserInformationConfiguration config = new InstagramUserInformationConfiguration();
+        List<String> userIds = Lists.newLinkedList();
+        userIds.add("1");
+        userIds.add("2");
+        userIds.add("3");
+        userIds.add("4");
+        userIds.add("abcdefg");
+        config.setUserIds(userIds);
+        InstagramRecentMediaCollector collector = new InstagramRecentMediaCollector(new ConcurrentLinkedQueue<MediaFeedData>(), config);
+
+        Set<Long> expected = Sets.newHashSet();
+        expected.add(1L);
+        expected.add(2L);
+        expected.add(3L);
+        expected.add(4L);
+
+        assertEquals(expected, collector.getUserIds());
+    }
+
+    @Test
+    public void testRun() {
+        Queue<MediaFeedData> data = Queues.newConcurrentLinkedQueue();
+        InstagramUserInformationConfiguration config = new InstagramUserInformationConfiguration();
+        List<String> userIds = Lists.newLinkedList();
+        userIds.add("1");
+        userIds.add("2");
+        userIds.add("3");
+        userIds.add("4");
+        config.setUserIds(userIds);
+        InstagramRecentMediaCollector collector = new InstagramRecentMediaCollector(data, config);
+        collector.setInstagramClient(createMockInstagramClient());
+        collector.run();
+        LOGGER.debug("Random seed == {}", randomSeed);
+        assertEquals("Random Seed == " + randomSeed, this.expectedDataCount, data.size());
+    }
+
+    private Instagram createMockInstagramClient() {
+        final Instagram instagramClient = mock(Instagram.class);
+        try {
+            final InstagramException mockException = mock(InstagramException.class);
+            when(mockException.getRemainingLimitStatus()).thenReturn(-1);
+            when(mockException.getMessage()).thenReturn("MockInstagramException message");
+            when(instagramClient.getRecentMediaFeed(any(Long.class))).thenAnswer(new Answer<MediaFeed>() {
+                @Override
+                public MediaFeed answer(InvocationOnMock invocationOnMock) throws Throwable {
+                    long param = (Long) invocationOnMock.getArguments()[0];
+                    if (param == 2L) {
+                        throw mockException;
+                    } else {
+                        return createRandomMockMediaFeed();
+                    }
+                }
+            });
+            when(instagramClient.getRecentMediaNextPage(any(Pagination.class))).thenAnswer(new Answer<MediaFeed>() {
+                @Override
+                public MediaFeed answer(InvocationOnMock invocationOnMock) throws Throwable {
+                    return createRandomMockMediaFeed();
+                }
+            });
+        } catch (InstagramException ie) {
+            fail("Failed to create mock instagram client.");
+        }
+        return instagramClient;
+    }
+
+    private MediaFeed createRandomMockMediaFeed() throws InstagramException {
+        MediaFeed feed = mock(MediaFeed.class);
+        when(feed.getData()).thenReturn(createData(this.rand.nextInt(100)));
+        Pagination pagination = mock(Pagination.class);
+        if(this.rand.nextInt(2) == 0) {
+            when(pagination.hasNextPage()).thenReturn(true);
+        } else {
+            when(pagination.hasNextPage()).thenReturn(false);
+        }
+        when(feed.getPagination()).thenReturn(pagination);
+        return feed;
+    }
+
+    private List<MediaFeedData> createData(int size) {
+        List<MediaFeedData> data = Lists.newLinkedList();
+        for(int i=0; i < size; ++i) {
+            data.add(mock(MediaFeedData.class));
+        }
+        this.expectedDataCount += size;
+        return data;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
new file mode 100644
index 0000000..7d2a47b
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
@@ -0,0 +1,145 @@
+package org.apache.streams.instagram.provider;
+
+import org.apache.streams.core.StreamsResultSet;
+import org.apache.streams.instagram.InstagramUserInformationConfiguration;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Random;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+
+/**
+ *
+ */
+public class InstagramRecentMediaProviderTest {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(InstagramRecentMediaProviderTest.class);
+
+    @Test
+    public void testStartStream() throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final InstagramRecentMediaCollector collectorStub = new InstagramRecentMediaCollector(new ConcurrentLinkedQueue<MediaFeedData>(), new InstagramUserInformationConfiguration()) {
+
+            private volatile boolean isFinished = false;
+
+            @Override
+            public void run() {
+                this.isFinished = true;
+                latch.countDown();
+            }
+
+            @Override
+            public boolean isCompleted() {
+                return this.isFinished;
+            }
+        };
+
+        InstagramRecentMediaProvider provider = new InstagramRecentMediaProvider(null) {
+            @Override
+            protected InstagramRecentMediaCollector getInstagramRecentMediaCollector() {
+                return collectorStub;
+            }
+        };
+
+        provider.startStream();
+
+        latch.await();
+        assertTrue(collectorStub.isCompleted());
+        StreamsResultSet result = provider.readCurrent();
+        assertNotNull(result);
+        assertEquals(0, result.size());
+        assertTrue(!provider.isRunning());
+        try {
+            provider.cleanUp();
+        } catch (Throwable throwable){
+            throwable.printStackTrace();
+            fail("Error durring clean up");
+        }
+    }
+
+    @Test
+    public void testReadCurrent() {
+        final long seed = System.nanoTime();
+        final Random rand = new Random(seed);
+        final CyclicBarrier test = new CyclicBarrier(2);
+        final CyclicBarrier produce = new CyclicBarrier(2);
+        final AtomicInteger batchCount = new AtomicInteger(0);
+        InstagramRecentMediaProvider provider = new InstagramRecentMediaProvider(new InstagramUserInformationConfiguration()) {
+            @Override
+            protected InstagramRecentMediaCollector getInstagramRecentMediaCollector() {
+                return new InstagramRecentMediaCollector(super.mediaFeedQueue, new InstagramUserInformationConfiguration()) {
+
+                    private volatile boolean isFinished = false;
+
+
+
+                    public int getBatchCount() {
+                        return batchCount.get();
+                    }
+
+                    @Override
+                    public boolean isCompleted() {
+                        return isFinished;
+                    }
+
+                    @Override
+                    public void run() {
+                        int randInt = rand.nextInt(5);
+                        while(randInt != 0) {
+                            int batchSize = rand.nextInt(200);
+                            for(int i=0; i < batchSize; ++i) {
+                                while(!super.dataQueue.add(mock(MediaFeedData.class))) {
+                                    Thread.yield();
+                                }
+                            }
+                            batchCount.set(batchSize);
+                            try {
+                                test.await();
+                                produce.await();
+                            } catch (InterruptedException ie ) {
+                                Thread.currentThread().interrupt();
+                            } catch (BrokenBarrierException bbe) {
+                                Thread.currentThread().interrupt();
+                            }
+                            randInt = rand.nextInt(5);
+                        }
+                        batchCount.set(0);
+                        isFinished = true;
+                        try {
+                            test.await();
+                            produce.await();
+                        } catch (InterruptedException ie) {
+                            Thread.currentThread().interrupt();
+                        } catch (BrokenBarrierException bbe) {
+                            Thread.currentThread().interrupt();
+                        }
+                    }
+
+                };
+            }
+        };
+        provider.startStream();
+        while(provider.isRunning()) {
+            try {
+                test.await();
+                assertEquals("Seed == "+seed, batchCount.get(), provider.readCurrent().size());
+                produce.await();
+            } catch (InterruptedException ie) {
+                Thread.currentThread().interrupt();
+            } catch (BrokenBarrierException bbe) {
+                Thread.currentThread().interrupt();
+            }
+
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
deleted file mode 100644
index fcf5e81..0000000
--- a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.streams.twitter.test;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.commons.lang.StringUtils;
-import org.apache.streams.instagram.serializer.InstagramJsonActivitySerializer;
-import org.apache.streams.jackson.StreamsJacksonMapper;
-import org.apache.streams.pojo.json.Activity;
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.assertThat;
-
-/**
-* Created with IntelliJ IDEA.
-* User: sblackmon
-* Date: 8/20/13
-* Time: 5:57 PM
-* To change this template use File | Settings | File Templates.
-*/
-public class InstagramActivitySerDeTest {
-
-    private final static Logger LOGGER = LoggerFactory.getLogger(InstagramActivitySerDeTest.class);
-    private ObjectMapper mapper = StreamsJacksonMapper.getInstance();
-
-    private InstagramJsonActivitySerializer instagramJsonActivitySerializer = new InstagramJsonActivitySerializer();
-
-    // remove @Ignore after implementation
-    @Ignore
-    @Test
-    public void Tests()
-    {
-        InputStream is = InstagramActivitySerDeTest.class.getResourceAsStream("/test.txt");
-        InputStreamReader isr = new InputStreamReader(is);
-        BufferedReader br = new BufferedReader(isr);
-
-        try {
-            while (br.ready()) {
-                String line = br.readLine();
-                if(!StringUtils.isEmpty(line))
-                {
-                    LOGGER.info("raw: {}", line);
-
-                    // convert to MediaFeedData?
-                    Activity activity = instagramJsonActivitySerializer.deserialize(line);
-
-                    String activitystring = mapper.writeValueAsString(activity);
-
-                    LOGGER.info("activity: {}", activitystring);
-
-                    assertThat(activity, is(not(nullValue())));
-
-                    assertThat(activity.getId(), is(not(nullValue())));
-                    assertThat(activity.getActor(), is(not(nullValue())));
-                    assertThat(activity.getActor().getId(), is(not(nullValue())));
-                    assertThat(activity.getVerb(), is(not(nullValue())));
-                    assertThat(activity.getProvider(), is(not(nullValue())));
-
-                }
-            }
-        } catch( Exception e ) {
-            System.out.println(e);
-            e.printStackTrace();
-            Assert.fail();
-        }
-    }
-}


[05/18] git commit: Merge branch 'instagram' into STREAMS-122

Posted by sb...@apache.org.
Merge branch 'instagram' into STREAMS-122


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/7b301cea
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/7b301cea
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/7b301cea

Branch: refs/heads/master
Commit: 7b301ceaa358e6c572b95a0eff754a87cf9f6064
Parents: 3b98df8 a3f443f
Author: Robert Douglas <rd...@w2odigital.com>
Authored: Tue Jul 1 17:11:28 2014 -0500
Committer: Robert Douglas <rd...@w2odigital.com>
Committed: Tue Jul 1 17:11:28 2014 -0500

----------------------------------------------------------------------
 .../streams-provider-instagram/README.md        |  17 +
 .../InstagramMediaDataActivitySerializer.mup    | 894 +++++++++++++++++++
 .../metadata/instagram_to_activity_mapping.png  | Bin 0 -> 1123684 bytes
 .../streams-provider-instagram/pom.xml          | 139 +++
 .../instagram/InstagramConfigurator.java        |  78 ++
 .../processor/InstagramTypeConverter.java       | 190 ++++
 .../provider/InstagramTimelineProvider.java     | 409 +++++++++
 .../InstagramJsonActivitySerializer.java        |  60 ++
 .../serializer/util/InstagramActivityUtil.java  | 142 +++
 .../serializer/util/InstagramDeserializer.java  |  18 +
 .../jsonschema/com/instagram/Instagram.json     |   0
 .../com/instagram/InstagramConfiguration.json   |  20 +
 .../InstagramUserInformationConfiguration.json  |  17 +
 .../src/main/resources/reference.conf           |   5 +
 .../test/InstagramActivitySerDeTest.java        |  92 ++
 .../src/test/resources/testMediaFeedObjects.txt |   0
 .../src/test/resources/testtweets.txt           | 695 ++++++++++++++
 17 files changed, 2776 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/7b301cea/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
----------------------------------------------------------------------
diff --cc streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
index 0000000,0000000..b4aee2d
new file mode 100644
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
@@@ -1,0 -1,0 +1,18 @@@
++package org.apache.streams.instagram.serializer.util;
++
++import org.jinstagram.Instagram;
++import org.jinstagram.exceptions.InstagramException;
++
++/**
++ * Created by rdouglas on 7/1/14.
++ */
++public class InstagramDeserializer extends Instagram{
++    public InstagramDeserializer(String test) {
++        super(test);
++    }
++
++    @Override
++    public <T> T createObjectFromResponse(Class<T> clazz, String response) throws InstagramException {
++        return super.createObjectFromResponse(clazz, response);
++    }
++}

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/7b301cea/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/Instagram.json
----------------------------------------------------------------------
diff --cc streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/Instagram.json
index 0000000,0000000..e69de29
new file mode 100644
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/Instagram.json

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/7b301cea/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
----------------------------------------------------------------------
diff --cc streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
index 0000000,0000000..e69de29
new file mode 100644
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt


[13/18] git commit: STREAMS 121 | Added javadoc comments

Posted by sb...@apache.org.
STREAMS 121 | Added javadoc comments


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/95f6d5d9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/95f6d5d9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/95f6d5d9

Branch: refs/heads/master
Commit: 95f6d5d9bc9069258717a9880e491ce55ae343e2
Parents: 7760393
Author: rebanks <re...@w2odigital.com>
Authored: Fri Jul 11 16:29:55 2014 -0500
Committer: rebanks <re...@w2odigital.com>
Committed: Fri Jul 11 16:29:55 2014 -0500

----------------------------------------------------------------------
 .../provider/InstagramRecentMediaCollector.java | 32 +++++++++++++++++++-
 .../provider/InstagramRecentMediaProvider.java  |  6 +++-
 2 files changed, 36 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/95f6d5d9/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
index 7eb3fcd..928ff9e 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
@@ -13,6 +13,11 @@ import java.util.Queue;
 import java.util.Set;
 
 /**
+ * Executes on all of the Instagram requests to collect the media feed data.
+ *
+ * If errors/exceptions occur when trying to gather data for a particular user, that user is skipped and the collector
+ * move on to the next user.  If a rate limit exception occurs it employs an exponential back off strategy for up to
+ * 5 attempts.
  *
  */
 public class InstagramRecentMediaCollector implements Runnable {
@@ -34,10 +39,19 @@ public class InstagramRecentMediaCollector implements Runnable {
         this.isCompleted = false;
     }
 
+    /**
+     * Set instagram client
+     * @param instagramClient
+     */
     protected void setInstagramClient(Instagram instagramClient) {
         this.instagramClient = instagramClient;
     }
 
+    /**
+     * Gets the user ids from the {@link org.apache.streams.instagram.InstagramUserInformationConfiguration} and
+     * converts them to {@link java.lang.Long}
+     * @return
+     */
     protected Set<Long> getUserIds() {
         Set<Long> userIds = Sets.newHashSet();
         for(String id : config.getUserIds()) {
@@ -50,6 +64,14 @@ public class InstagramRecentMediaCollector implements Runnable {
         return userIds;
     }
 
+    /**
+     * Determins the course of action to take when Instagram returns an exception to a request.  If it is a rate limit
+     * exception, it implements an exponentional back off strategy.  If it is anyother exception, it is logged and
+     * rethrown.
+     * @param instaExec exception to handle
+     * @param attempt number of attempts that have occured to pull this users information
+     * @throws InstagramException
+     */
     protected void handleInstagramException(InstagramException instaExec, int attempt) throws InstagramException {
         LOGGER.debug("RemainingApiLimitStatus: {}", instaExec.getRemainingLimitStatus());
         if(instaExec.getRemainingLimitStatus() == 0) { //rate limit exception
@@ -66,6 +88,10 @@ public class InstagramRecentMediaCollector implements Runnable {
         }
     }
 
+    /**
+     * Gets the MediaFeedData for this particular user and adds it to the share queued.
+     * @param userId
+     */
     private void getUserMedia(Long userId) {
         MediaFeed feed = null;
         int attempts = 0;
@@ -97,7 +123,7 @@ public class InstagramRecentMediaCollector implements Runnable {
             LOGGER.error("User id, {}, returned a NULL media feed from instagram.", userId);
         } else {
             for(MediaFeedData data : userFeed.getData()) {
-                synchronized (this.dataQueue) {
+                synchronized (this.dataQueue) { //unnecessary
                     while(!this.dataQueue.offer(data)) {
                         Thread.yield();
                     }
@@ -106,6 +132,10 @@ public class InstagramRecentMediaCollector implements Runnable {
         }
     }
 
+    /**
+     *
+     * @return true when the collector has queued all of available media feed data for the provided users.
+     */
     public boolean isCompleted() {
         return this.isCompleted;
     }

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/95f6d5d9/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
index 3354e54..e5fa464 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
@@ -17,7 +17,7 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
 /**
- * Created by rebanks on 7/9/14.
+ * Instagram {@link org.apache.streams.core.StreamsProvider} that provides the recent media data for a group of users
  */
 public class InstagramRecentMediaProvider implements StreamsProvider {
 
@@ -44,6 +44,10 @@ public class InstagramRecentMediaProvider implements StreamsProvider {
         this.executorService.submit(this.dataCollector);
     }
 
+    /**
+     * EXPOSED FOR TESTING
+     * @return
+     */
     protected InstagramRecentMediaCollector getInstagramRecentMediaCollector() {
         return new InstagramRecentMediaCollector(this.mediaFeedQueue, this.config);
     }


[08/18] git commit: Merge branch 'master' into instagram

Posted by sb...@apache.org.
Merge branch 'master' into instagram


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/815ce2ab
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/815ce2ab
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/815ce2ab

Branch: refs/heads/master
Commit: 815ce2abdab1be8e279b48a7e38090f5f6948fcb
Parents: a3f443f c1246d9
Author: mfranklin <mf...@apache.org>
Authored: Wed Jul 2 12:13:35 2014 -0400
Committer: mfranklin <mf...@apache.org>
Committed: Wed Jul 2 12:13:35 2014 -0400

----------------------------------------------------------------------
 .../ElasticsearchConfigurator.java              |   4 +
 .../ElasticsearchPersistUpdater.java            |  35 -
 .../ElasticsearchPersistWriter.java             | 705 +++++++------------
 .../ElasticsearchPersistWriterTask.java         |  56 --
 .../ElasticsearchWriterConfiguration.json       |   5 +
 .../provider/SysomosHeartbeatStream.java        |   2 +-
 .../twitter/processor/TwitterTypeConverter.java |   2 +-
 .../TwitterJsonActivitySerializer.java          |   3 +
 .../TwitterJsonUserActivitySerializer.java      |  72 ++
 .../serializer/util/TwitterActivityUtil.java    |  22 +
 .../local/tasks/StreamsProviderTask.java        |  51 +-
 .../local/builders/LocalStreamBuilderTest.java  |  52 +-
 .../local/test/processors/SlowProcessor.java    |  50 ++
 .../test/providers/EmptyResultSetProvider.java  |   2 +-
 .../test/providers/NumericMessageProvider.java  |  63 +-
 .../test/component/FileReaderProvider.java      |  52 +-
 16 files changed, 517 insertions(+), 659 deletions(-)
----------------------------------------------------------------------



[07/18] git commit: STREAMS-105 | Updated the InstagramTypeConverter to use the conversion utility functions provided in InstagramActivityUtil

Posted by sb...@apache.org.
STREAMS-105 | Updated the InstagramTypeConverter to use the conversion utility functions provided in InstagramActivityUtil


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/fa3f9220
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/fa3f9220
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/fa3f9220

Branch: refs/heads/master
Commit: fa3f92200d19605e931954a154e59123d1a36f03
Parents: 1163653
Author: Robert Douglas <rd...@w2odigital.com>
Authored: Wed Jul 2 10:48:35 2014 -0500
Committer: Robert Douglas <rd...@w2odigital.com>
Committed: Wed Jul 2 10:48:35 2014 -0500

----------------------------------------------------------------------
 .../processor/InstagramTypeConverter.java       | 116 +++----------------
 .../serializer/util/InstagramActivityUtil.java  |  32 +++--
 2 files changed, 34 insertions(+), 114 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/fa3f9220/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
index 14260e3..7fb1ec6 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
@@ -18,52 +18,34 @@
 
 package org.apache.streams.instagram.processor;
 
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.Lists;
 import org.apache.streams.core.StreamsDatum;
 import org.apache.streams.core.StreamsProcessor;
-import org.apache.streams.exceptions.ActivitySerializerException;
-import org.apache.streams.instagram.serializer.InstagramJsonActivitySerializer;
-import org.apache.streams.jackson.StreamsJacksonMapper;
+import org.apache.streams.instagram.serializer.util.InstagramActivityUtil;
 import org.apache.streams.pojo.json.Activity;
 import org.jinstagram.entity.users.feed.MediaFeedData;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
 import java.util.List;
 import java.util.Queue;
 
-/**
- * Created by sblackmon on 12/10/13.
- */
 public class InstagramTypeConverter implements StreamsProcessor {
 
     public final static String STREAMS_ID = "InstagramTypeConverter";
 
     private final static Logger LOGGER = LoggerFactory.getLogger(InstagramTypeConverter.class);
 
-    private ObjectMapper mapper;
-
     private Queue<MediaFeedData> inQueue;
     private Queue<StreamsDatum> outQueue;
 
-    private Class inClass;
-    private Class outClass;
-
-    private InstagramJsonActivitySerializer instagramJsonActivitySerializer;
+    private InstagramActivityUtil instagramActivityUtil;
 
     private int count = 0;
 
     public final static String TERMINATE = new String("TERMINATE");
 
-    public InstagramTypeConverter(Class inClass, Class outClass) {
-        this.inClass = inClass;
-        this.outClass = outClass;
+    public InstagramTypeConverter() {
     }
 
     public Queue<StreamsDatum> getProcessorOutputQueue() {
@@ -74,100 +56,31 @@ public class InstagramTypeConverter implements StreamsProcessor {
         inQueue = inputQueue;
     }
 
-    public Object convert(ObjectNode event, Class inClass, Class outClass) throws ActivitySerializerException, JsonProcessingException {
-
-        Object result = null;
-
-        if( outClass.equals( Activity.class )) {
-            LOGGER.debug("ACTIVITY");
-            result = instagramJsonActivitySerializer.deserialize(
-                    mapper.writeValueAsString(event));
-        } else if( outClass.equals( ObjectNode.class )) {
-            LOGGER.debug("OBJECTNODE");
-            result = mapper.convertValue(event, ObjectNode.class);
-        } else if( outClass.equals( String.class )) {
-            LOGGER.debug("OBJECTNODE");
-            result = mapper.writeValueAsString(event);
-        }
-
-
-    // no supported conversion were applied
-        if( result != null ) {
-            count ++;
-            return result;
-        }
-
-        LOGGER.debug("CONVERT FAILED");
-
-        return null;
-
-    }
-
-    public boolean validate(Object document, Class klass) {
-
-        // TODO
-        return true;
-    }
-
-    public boolean isValidJSON(final String json) {
-        boolean valid = false;
-        try {
-            final JsonParser parser = new ObjectMapper().getJsonFactory()
-                    .createJsonParser(json);
-            while (parser.nextToken() != null) {
-            }
-            valid = true;
-        } catch (JsonParseException jpe) {
-            LOGGER.warn("validate: {}", jpe);
-        } catch (IOException ioe) {
-            LOGGER.warn("validate: {}", ioe);
-        }
-
-        return valid;
-    }
-
     @Override
     public List<StreamsDatum> process(StreamsDatum entry) {
 
         StreamsDatum result = null;
 
         try {
-
             Object item = entry.getDocument();
-            ObjectNode node;
 
             LOGGER.debug("{} processing {}", STREAMS_ID, item.getClass());
 
-            if( item instanceof String ) {
-
-                // if the target is string, just pass-through
-                if( String.class.equals(outClass)) {
-                    result = entry;
-                }
-                else {
-                    // first check for valid json
-                    node = (ObjectNode)mapper.readTree((String)item);
+            if(item instanceof MediaFeedData) {
+                //We don't need to use the mapper, since we have a process to convert between
+                //MediaFeedData objects and Activity objects already
+                Activity activity = new Activity();
 
-                    Object out = convert(node, String.class, outClass);
+                instagramActivityUtil.updateActivity((MediaFeedData)item, activity);
 
-                    if( out != null && validate(out, outClass))
-                        result = new StreamsDatum(out);
+                if(activity.getId() != null) {
+                    result = new StreamsDatum(activity);
+                    count++;
                 }
-
-            } else if( item instanceof ObjectNode ) {
-
-                // first check for valid json
-                node = (ObjectNode)mapper.valueToTree(item);
-
-                Object out = convert(node, ObjectNode.class, outClass);
-
-                if( out != null && validate(out, outClass))
-                    result = new StreamsDatum(out);
-
             }
-
         } catch (Exception e) {
             e.printStackTrace();
+            LOGGER.error("Exception while converting MediaFeedData to Activity: {}", e.getMessage());
         }
 
         if( result != null )
@@ -178,13 +91,12 @@ public class InstagramTypeConverter implements StreamsProcessor {
 
     @Override
     public void prepare(Object o) {
-        mapper = new StreamsJacksonMapper();
-        instagramJsonActivitySerializer = new InstagramJsonActivitySerializer();
+        instagramActivityUtil = new InstagramActivityUtil();
     }
 
     @Override
     public void cleanUp() {
-
+        //noop
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/fa3f9220/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
index 0561ba7..bd926d5 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
@@ -53,7 +53,9 @@ public class InstagramActivityUtil {
      */
     public static void updateActivity(MediaFeedData item, Activity activity) throws ActivitySerializerException {
         activity.setActor(buildActor(item));
-        activity.setPublished(new DateTime(Long.parseLong(item.getCreatedTime()) * 1000));
+
+        if(item.getCreatedTime() != null)
+            activity.setPublished(new DateTime(Long.parseLong(item.getCreatedTime()) * 1000));
 
         activity.setId(formatId(activity.getVerb(),
             Optional.fromNullable(
@@ -78,16 +80,20 @@ public class InstagramActivityUtil {
     public static  Actor buildActor(MediaFeedData item) {
         Actor actor = new Actor();
 
-        Image image = new Image();
-        image.setUrl(item.getUser().getProfilePictureUrl());
+        try {
+            Image image = new Image();
+            image.setUrl(item.getUser().getProfilePictureUrl());
 
-        Map<String, Object> extensions = new HashMap<String, Object>();
-        extensions.put("screenName", item.getUser().getUserName());
+            Map<String, Object> extensions = new HashMap<String, Object>();
+            extensions.put("screenName", item.getUser().getUserName());
 
-        actor.setId(formatId(String.valueOf(item.getUser().getId())));
-        actor.setImage(image);
-        actor.setAdditionalProperty("extensions", extensions);
-        actor.setAdditionalProperty("handle", item.getUser().getUserName());
+            actor.setId(formatId(String.valueOf(item.getUser().getId())));
+            actor.setImage(image);
+            actor.setAdditionalProperty("extensions", extensions);
+            actor.setAdditionalProperty("handle", item.getUser().getUserName());
+        } catch (Exception e) {
+            LOGGER.error("Exception trying to build actor object: {}", e.getMessage());
+        }
 
         return actor;
     }
@@ -244,9 +250,11 @@ public class InstagramActivityUtil {
 
         addLocationExtension(activity, item);
 
-        Map<String, Object> likes = new HashMap<String, Object>();
-        likes.put("count", item.getLikes().getCount());
-        extensions.put("likes", likes);
+        if(item.getLikes() != null) {
+            Map<String, Object> likes = new HashMap<String, Object>();
+            likes.put("count", item.getLikes().getCount());
+            extensions.put("likes", likes);
+        }
 
         extensions.put("hashtags", item.getTags());
 


[11/18] git commit: STREAMS-122 | Responded to code review feedback

Posted by sb...@apache.org.
STREAMS-122 | Responded to code review feedback


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/14f7050b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/14f7050b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/14f7050b

Branch: refs/heads/master
Commit: 14f7050ba7de9bd783e6b2bf9c1dcf76da2d9eb2
Parents: 957e13f
Author: Robert Douglas <rd...@w2odigital.com>
Authored: Mon Jul 7 11:08:17 2014 -0500
Committer: Robert Douglas <rd...@w2odigital.com>
Committed: Mon Jul 7 11:08:17 2014 -0500

----------------------------------------------------------------------
 .../serializer/util/InstagramActivityUtil.java  | 45 ++++++++++++--------
 1 file changed, 27 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/14f7050b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
index bd926d5..499d0e7 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
@@ -24,10 +24,8 @@ import com.google.common.base.Optional;
 import com.google.common.collect.Lists;
 import org.apache.streams.exceptions.ActivitySerializerException;
 import org.apache.streams.pojo.json.*;
-import org.jinstagram.entity.common.ImageData;
-import org.jinstagram.entity.common.Images;
-import org.jinstagram.entity.common.VideoData;
-import org.jinstagram.entity.common.Videos;
+import org.jinstagram.entity.comments.CommentData;
+import org.jinstagram.entity.common.*;
 import org.jinstagram.entity.users.feed.MediaFeedData;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
@@ -53,6 +51,7 @@ public class InstagramActivityUtil {
      */
     public static void updateActivity(MediaFeedData item, Activity activity) throws ActivitySerializerException {
         activity.setActor(buildActor(item));
+        activity.setVerb("post");
 
         if(item.getCreatedTime() != null)
             activity.setPublished(new DateTime(Long.parseLong(item.getCreatedTime()) * 1000));
@@ -109,6 +108,21 @@ public class InstagramActivityUtil {
         actObj.setObjectType(item.getType());
         actObj.setAttachments(buildActivityObjectAttachments(item));
 
+        Image standardResolution = new Image();
+        if(item.getType() == "image" && item.getImages() != null) {
+            ImageData standardResolutionData = item.getImages().getStandardResolution();
+            standardResolution.setHeight((double)standardResolutionData.getImageHeight());
+            standardResolution.setWidth((double)standardResolutionData.getImageWidth());
+            standardResolution.setUrl(standardResolutionData.getImageUrl());
+        } else if(item.getType() == "video" && item.getVideos() != null) {
+            VideoData standardResolutionData = item.getVideos().getStandardResolution();
+            standardResolution.setHeight((double)standardResolutionData.getHeight());
+            standardResolution.setWidth((double)standardResolutionData.getWidth());
+            standardResolution.setUrl(standardResolutionData.getUrl());
+        }
+
+        actObj.setImage(standardResolution);
+
         return actObj;
     }
 
@@ -212,11 +226,10 @@ public class InstagramActivityUtil {
         if(item.getLocation() != null) {
             Map<String, Object> coordinates = new HashMap<String, Object>();
             coordinates.put("type", "Point");
-            coordinates.put("coordinates", "[" + item.getLocation().getLatitude() + "," + item.getLocation().getLongitude() + "]");
+            coordinates.put("coordinates", "[" + item.getLocation().getLongitude() + "," + item.getLocation().getLatitude() + "]");
 
             extensions.put("coordinates", coordinates);
         }
-
     }
 
     /**
@@ -258,19 +271,15 @@ public class InstagramActivityUtil {
 
         extensions.put("hashtags", item.getTags());
 
-        Image standardResolution = new Image();
-        if(item.getType() == "image" && item.getImages() != null) {
-            ImageData standardResolutionData = item.getImages().getStandardResolution();
-            standardResolution.setHeight((double)standardResolutionData.getImageHeight());
-            standardResolution.setWidth((double)standardResolutionData.getImageWidth());
-            standardResolution.setUrl(standardResolutionData.getImageUrl());
-        } else if(item.getType() == "video" && item.getVideos() != null) {
-            VideoData standardResolutionData = item.getVideos().getStandardResolution();
-            standardResolution.setHeight((double)standardResolutionData.getHeight());
-            standardResolution.setWidth((double)standardResolutionData.getWidth());
-            standardResolution.setUrl(standardResolutionData.getUrl());
+        Comments comments = item.getComments();
+        String commentsConcat = "";
+        for(CommentData commentData : comments.getComments()) {
+            commentsConcat += " " + commentData.getText();
+        }
+        if(item.getCaption() != null) {
+            commentsConcat += " " + item.getCaption().getText();
         }
 
-        extensions.put("image", standardResolution);
+        extensions.put("keywords", commentsConcat);
     }
 }
\ No newline at end of file


[15/18] git commit: Added license header and remvoed comments

Posted by sb...@apache.org.
Added license header and remvoed comments


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/45510b29
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/45510b29
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/45510b29

Branch: refs/heads/master
Commit: 45510b292b8f1dc5de5611c87abf70b856be4125
Parents: 1e23300
Author: rebanks <re...@w2odigital.com>
Authored: Mon Jul 14 10:22:47 2014 -0500
Committer: rebanks <re...@w2odigital.com>
Committed: Mon Jul 14 10:22:47 2014 -0500

----------------------------------------------------------------------
 .../streams/instagram/InstagramConfigurator.java      | 12 +-----------
 .../provider/InstagramRecentMediaCollector.java       | 14 ++++++++++++++
 .../provider/InstagramRecentMediaProvider.java        | 14 ++++++++++++++
 .../provider/InstagramRecentMediaCollectorTest.java   | 14 ++++++++++++++
 .../provider/InstagramRecentMediaProviderTest.java    | 14 ++++++++++++++
 5 files changed, 57 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/45510b29/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
index 4d01605..cab072d 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
@@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory;
 import java.io.IOException;
 
 /**
- * Created by sblackmon on 12/10/13.
+ * 
  */
 public class InstagramConfigurator {
 
@@ -38,9 +38,6 @@ public class InstagramConfigurator {
 
     public static InstagramConfiguration detectInstagramConfiguration(Config config) {
 
-//        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
-//        Validator validator = factory.getValidator();
-
         InstagramConfiguration instagramConfiguration = null;
         try {
             instagramConfiguration = mapper.readValue(config.root().render(ConfigRenderOptions.concise()), InstagramConfiguration.class);
@@ -49,16 +46,11 @@ public class InstagramConfigurator {
         }
         Preconditions.checkNotNull(instagramConfiguration);
 
-//        Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
-
         return instagramConfiguration;
     }
 
     public static InstagramUserInformationConfiguration detectInstagramUserInformationConfiguration(Config config) {
 
-//        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
-//        Validator validator = factory.getValidator();
-
         InstagramUserInformationConfiguration instagramConfiguration = null;
         try {
             instagramConfiguration = mapper.readValue(config.root().render(ConfigRenderOptions.concise()), InstagramUserInformationConfiguration.class);
@@ -67,8 +59,6 @@ public class InstagramConfigurator {
         }
         Preconditions.checkNotNull(instagramConfiguration);
 
-//        Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
-
         return instagramConfiguration;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/45510b29/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
index 928ff9e..e459a0a 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
@@ -1,3 +1,17 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance *
+http://www.apache.org/licenses/LICENSE-2.0 *
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
 package org.apache.streams.instagram.provider;
 
 import com.google.common.collect.Sets;

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/45510b29/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
index e5fa464..3d67a48 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
@@ -1,3 +1,17 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance *
+http://www.apache.org/licenses/LICENSE-2.0 *
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
 package org.apache.streams.instagram.provider;
 
 import com.google.common.collect.Queues;

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/45510b29/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
index 39f607c..0225b40 100644
--- a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
@@ -1,3 +1,17 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance *
+http://www.apache.org/licenses/LICENSE-2.0 *
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
 package org.apache.streams.instagram.provider;
 
 import com.google.common.collect.Lists;

http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/45510b29/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
index 7d2a47b..59e90b4 100644
--- a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
@@ -1,3 +1,17 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance *
+http://www.apache.org/licenses/LICENSE-2.0 *
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
 package org.apache.streams.instagram.provider;
 
 import org.apache.streams.core.StreamsResultSet;


[09/18] git commit: Merge branch 'instagram' into STREAMS-122

Posted by sb...@apache.org.
Merge branch 'instagram' into STREAMS-122


Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/23bfcac8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/23bfcac8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/23bfcac8

Branch: refs/heads/master
Commit: 23bfcac82884d3280815f1168b82e263f38ef1fb
Parents: fa3f922 815ce2a
Author: Robert Douglas <rd...@w2odigital.com>
Authored: Wed Jul 2 11:17:27 2014 -0500
Committer: Robert Douglas <rd...@w2odigital.com>
Committed: Wed Jul 2 11:17:27 2014 -0500

----------------------------------------------------------------------
 .../twitter/processor/TwitterTypeConverter.java |  2 +-
 .../TwitterJsonActivitySerializer.java          |  3 +
 .../TwitterJsonUserActivitySerializer.java      | 72 ++++++++++++++++++++
 .../serializer/util/TwitterActivityUtil.java    | 22 ++++++
 4 files changed, 98 insertions(+), 1 deletion(-)
----------------------------------------------------------------------