<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Jeff Douglas - Technology, Coding and Bears... OH MY! &#187; GAE/J</title>
	<atom:link href="http://blog.jeffdouglas.com/category/technology/gaej/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.jeffdouglas.com</link>
	<description>Get your head out of your #@! and into the clouds!</description>
	<lastBuildDate>Thu, 02 Feb 2012 11:57:20 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>Any Interest in a Chatter / Spring / App Engine Tutorial?</title>
		<link>http://blog.jeffdouglas.com/2010/06/30/any-interest-in-a-chatter-spring-app-engine-tutorial/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=any-interest-in-a-chatter-spring-app-engine-tutorial</link>
		<comments>http://blog.jeffdouglas.com/2010/06/30/any-interest-in-a-chatter-spring-app-engine-tutorial/#comments</comments>
		<pubDate>Wed, 30 Jun 2010 11:35:44 +0000</pubDate>
		<dc:creator>Jeff Douglas</dc:creator>
				<category><![CDATA[GAE/J]]></category>
		<category><![CDATA[Salesforce]]></category>
		<category><![CDATA[VMforce]]></category>

		<guid isPermaLink="false">http://blog.jeffdouglas.com/?p=2735</guid>
		<description><![CDATA[Following up on yesterday&#8217;s post, Get Ready for VMForce &#8211; Here&#8217;s How!, I was thinking about putting together a series of tutorials on building a Chatter app on App Engine using Spring MVC with JDO. This will take considerable effort and I&#8217;m just wondering if it would be valuable to the Salesforce.com community. Tell me [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2010%2F06%2F30%2Fany-interest-in-a-chatter-spring-app-engine-tutorial%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2010%2F06%2F30%2Fany-interest-in-a-chatter-spring-app-engine-tutorial%2F&amp;source=jeffdonthemic&amp;style=normal&amp;service=bit.ly&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>Following up on yesterday&#8217;s post, <a href="http://blog.jeffdouglas.com/2010/06/29/get-ready-for-vmforce/" target="_blank">Get Ready for VMForce &#8211; Here&#8217;s How!</a>, I was thinking about putting together a series of tutorials on building a Chatter app on App Engine using Spring MVC with JDO. This will take considerable effort and I&#8217;m just wondering if it would be valuable to the Salesforce.com community.
<div>Tell me what you think.</div>
<p/>
<a href="http://polldaddy.com/poll/3412057/">View This Poll</a></p>
<p>UPDATE: I spoke with <a href="http://twitter.com/umityalcinalp" target="_blank">Umit Yalcinalp</a> at Salesforce.com and they are planning some webinars on this topic the end of August. I don&#8217;t want to steal their thunder so I&#8217;ll possibly look at doing this mid-September sometime.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jeffdouglas.com/2010/06/30/any-interest-in-a-chatter-spring-app-engine-tutorial/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Integrate Chatter &amp; Twitter on Google App Engine using OAuth</title>
		<link>http://blog.jeffdouglas.com/2010/03/17/integrate-chatter-twitter-on-google-app-engine-using-oauth/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=integrate-chatter-twitter-on-google-app-engine-using-oauth</link>
		<comments>http://blog.jeffdouglas.com/2010/03/17/integrate-chatter-twitter-on-google-app-engine-using-oauth/#comments</comments>
		<pubDate>Wed, 17 Mar 2010 11:37:01 +0000</pubDate>
		<dc:creator>Jeff Douglas</dc:creator>
				<category><![CDATA[Code Sample]]></category>
		<category><![CDATA[GAE/J]]></category>
		<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Salesforce]]></category>

		<guid isPermaLink="false">http://blog.jeffdouglas.com/?p=2340</guid>
		<description><![CDATA[Cross-posted at the Appirio Tech Blog. At Appirio we&#8217;ve been excited about Salesforce Chatter for quite a while. We firmly believe that Chatter has the potential to bridge the gap between enterprise applications and the way people work. We were luckily enough to receive special prerelease access to Chatter to develop our Social PS Enterprise [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2010%2F03%2F17%2Fintegrate-chatter-twitter-on-google-app-engine-using-oauth%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2010%2F03%2F17%2Fintegrate-chatter-twitter-on-google-app-engine-using-oauth%2F&amp;source=jeffdonthemic&amp;style=normal&amp;service=bit.ly&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p style="clear: both">Cross-posted at the <a href="http://techblog.appirio.com/2010/03/integrate-chatter-twitter-on-google-app.html">Appirio Tech Blog</a>.</p>
<p style="clear: both">At <a href="http://www.appirio.com" target="_blank">Appirio</a> we&#8217;ve been excited about Salesforce Chatter for quite a while. We firmly believe that Chatter has the potential to bridge the gap between enterprise applications and the way people work. We were luckily enough to receive special prerelease access to Chatter to develop our Social PS Enterprise for the Dreamforce &#8217;09 Chatter Keynote and if you missed the demo at Dreamforce &#8217;09 you can <a href="http://www.youtube.com/watch?v=Xu-2ZgrmBhs&#038;feature=player_embedded#" target="_blank">find it here</a>.</p>
<p style="clear: both">Chatter is now in private beta for 100 companies and it is enabled in our production org. We&#8217;ve been using it for couple of weeks now and I find myself logging into our org more and more to check the status of other employees, projects and opportunities. As a developer I really wanted to get my hands on the code and test drive Chatter&#8217;s functionality. Luckily Quinton Wall has a great <a href="http://wiki.developerforce.com/index.php/An_Introduction_to_Salesforce_Chatter" target="_blank">Intro to Chatter</a> on developer.force.com to get me started. Sure, I could have developed an Apex and Visualforce application for Chatter but I naturally wanted to integrate Chatter with Twitter. So what I came up with is a Chatter/Twitter app running on Google App Engine using OAuth for Twitter authentication.</p>
<p><span class="youtube">
<object width="560" height="340">
<param name="movie" value="http://www.youtube.com/v/FqeGxAuFqSM?color1=d6d6d6&amp;color2=f0f0f0&amp;border=0&amp;fs=1&amp;hl=en&amp;loop=0&amp;showinfo=0&amp;iv_load_policy=3&amp;showsearch=0&amp;rel=1&amp;hd=1&#038;w=550" />
<param name="allowFullScreen" value="true" />
<embed wmode="opaque" src="http://www.youtube.com/v/FqeGxAuFqSM?color1=d6d6d6&amp;color2=f0f0f0&amp;border=0&amp;fs=1&amp;hl=en&amp;loop=0&amp;showinfo=0&amp;iv_load_policy=3&amp;showsearch=0&amp;rel=1&amp;hd=1&#038;w=550" type="application/x-shockwave-flash" allowfullscreen="true" width="560" height="340"></embed>
<param name="wmode" value="opaque" />
</object>
</span><p><a href="http://www.youtube.com/watch?v=FqeGxAuFqSM&fmt=18">www.youtube.com/watch?v=FqeGxAuFqSM</a></p></p>
<p style="clear: both"><strong>Understanding Chatter</strong></p>
<p style="clear: both">Initially I was under the assumption that Salesforce.com would release some sort of API for Chatter. However, they&#8217;ve done something even better. Instead of a new API to learn, Salesforce.com exposed Chatter as a series of sObjects allowing you to query for records using the same SOQL that you know and love and manipulate records using DML. Once you get a grip on the Chatter object model and where data lives, developing applications for Chatter is essentially the same as using the Sales or Service Could.</p>
<p style="clear: both"><a href="http://blog.jeffdouglas.com/wp-content/uploads/2010/03/600px-DataModel2.png" class="image-link" rel="lightbox"><img class="linked-to-original" src="http://blog.jeffdouglas.com/wp-content/uploads/2010/03/600px-DataModel2-thumb.png" height="308" align="left" width="550" style=" display: inline; float: left; margin: 0 10px 10px 0;" /></a><br style="clear: both" />The Chatter model is based upon familiar social networking &#8220;Feed Posts&#8221;. These posts are made up of a series of Feed Items and Feed Types. The FeedPost stores most of the information that you are concerned about such as the body, title and any content related data. The FeedPost object also contains the information for all posts for the User object including profile statuses, news feeds and entity updates (accounts, contacts or custom objects). The Feed Types are dependent on what actions you are performing:</p>
<ul style="clear: both">
<li> UserStatus - this is the user status update (e.g., &#8220;What are you working on?&#8221;)</li>
<li> TextPost - a post you make from a record</li>
<li> LinkPost - a post that contains a URL link (when you click on the link icon)</li>
<li> ContentPost - a post that contains some type of uploaded content such as a document or graphic</li>
<li> TrackedChanges - whenever a field on a record (set up during Chatter Feed Tracking configuration) is updated</li>
</ul>
<p style="clear: both">One thing to understand from the beginning is that you do not query for Feed Posts directly. You must query via the Feed Item which contains a reference to the details of the post. So to get the last status update for the current user, you would issue the following SOQL:</p>
<p style="clear: both"  style="font-family:courier new;">SELECT Id, FeedPost.Body FROM UserFeed WHERE ParentId = :Userinfo.getUserId()<br />And Type = &#8216;UserStatus&#8217; ORDER BY CreatedDate DESC LIMIT 1</p>
<p style="clear: both">For more sample Chatter code, check out the <a href="http://wiki.developerforce.com/index.php/Chatter_Code_Recipes" target="_blank">Chatter Code Recipes</a>.</p>
<p style="clear: both"><strong>Functional Design</strong></p>
<p style="clear: both">From a high-level overview, the application is fairly simple. When it initially loads the user is prompted to log into Twitter using OAuth.</p>
<p style="clear: both"><a href="http://blog.jeffdouglas.com/wp-content/uploads/2010/03/chatter1.png" class="image-link" rel="lightbox"><img class="linked-to-original" src="http://blog.jeffdouglas.com/wp-content/uploads/2010/03/chatter1-thumb.png" height="326" align="left" width="550" style=" display: inline; float: left; margin: 0 10px 10px 0;" /></a><br style="clear: both" />Twitter asks you to grant the App Engine application the ability to access and update your Twitter account. I&#8217;m currently working on OAuth for Salesforce.com and hope to have both sides of the application using OAuth soon. Currently my Salesforce.com sandbox credentials are hard-coded in the application.</p>
<p style="clear: both"><a href="http://blog.jeffdouglas.com/wp-content/uploads/2010/03/chatter2.png" class="image-link" rel="lightbox"><img class="linked-to-original" src="http://blog.jeffdouglas.com/wp-content/uploads/2010/03/chatter2-thumb.png" height="325" align="left" width="547" style=" display: inline; float: left; margin: 0 10px 10px 0;" /></a><br style="clear: both" />Once you authorize access you are redirected back to the application on Google App Engine and presented the following options:</p>
<p style="clear: both">
<ul style="clear: both">
<li>Send your latest tweet to Chatter - fetches your last tweet from your timeline and sends it to Chatter as a status update.</li>
<li>Tweet your latest Chatter status update - queries for you last Chatter update and tweets it. Since Chatter is designed to be private within your org this option isn&#8217;t recommended for production and I only implemented it for academic purposes.</li>
<li>Send a status update to both Chatter and Twitter - presents you with a simple form to enter your status update. Once the form is submitted, your status is sent to both Chatter and Twitter.</li>
</ul>
<p style="clear: both"><a href="http://blog.jeffdouglas.com/wp-content/uploads/2010/03/chatter3.png" class="image-link" rel="lightbox"><img class="linked-to-original" src="http://blog.jeffdouglas.com/wp-content/uploads/2010/03/chatter3-thumb.png" height="325" align="left" width="547" style=" display: inline; float: left; margin: 0 10px 10px 0;" /></a><br style="clear: both" /><strong>Technical Design</strong></p>
<p style="clear: both">The application is developed on Google App Engine using the <a href="http://code.google.com/p/sfdc-wsc/" target="_blank">Force.com Web Service Connector (WSC)</a>, Salesfore.com Partner library, and the <a href="http://twitter4j.org/en/index.html" target="_blank">Twitter4j</a> Java library. Since we are using Google App Engine, download the wsc-gae-16_0.jar and partner-library.jar Jars from the <a href="http://code.google.com/p/sfdc-wsc/downloads/list" target="_blank">WSC project</a>. I used Chatter on one of our sandboxes so I had to do a <a href="http://blog.jeffdouglas.com/2010/03/11/error-compiling-wsc-appengine-partner-jar-for-sandbox/" target="_blank">little tweaking</a> to get the Partner jar running. Now create a new Web Application Project for App Engine and then drop your two jars and the twitter4j jar into the lib directory. You&#8217;ll also need to add them to your project&#8217;s build path in Eclipse.</p>
<p style="clear: both">Next you&#8217;ll have to <a href="http://twitter.com/oauth" target="_blank">register your app</a> with Twitter. This will give you the consumer key, consumer secret and URLs you&#8217;ll need to authenticate and make requests to Twitter. I&#8217;m storing these credentials along with the Salesforce.com sandbox credentials and user id as static variables in a simple credentials class for ease of use.</p>
<p style="clear: both">The<br />
 application is a series of JSPs and Servlets and if you&#8217;d like the code for the entire project, <a href="http://www.twitter.com/jeffdonthemic" target="_blank">send me a message</a>. The interesting parts of the application are described below and hopefully you can extrapolate the rest.</p>
<p style="clear: both"><strong>LoginServlet</strong></p>
<p style="clear: both">This is the initial request for the application. The code uses the Twitter credentials and gets the authorization URL for the app and presents it to the users in the JSP page. The user clicks this link and is taken to Twitter to authorize the application.</p>
<p style="clear: both">

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
</pre></td><td class="code"><pre class="java" style="font-family:monospace;">package com.jeffdouglas;
&nbsp;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import org.apache.log4j.Logger;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.http.RequestToken;
&nbsp;
public class LoginServlet extends HttpServlet {
&nbsp;
  private static final Logger log = Logger.getLogger(LoginServlet.class);
&nbsp;
  public void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws IOException {
&nbsp;
    HttpSession session = req.getSession();
    Twitter twitter = new TwitterFactory().getInstance();
    twitter.setOAuthConsumer(Credentials.TWITTER_CONSUMERKEY,Credentials.TWITTER_CONSUMERSECRET);
    RequestToken requestToken = null;
&nbsp;
    try {
      requestToken = twitter.getOAuthRequestToken();
    } catch (TwitterException e) {
      log.error(e.toString());
    }
&nbsp;
    // get the token and tokenSecret
    String token = (String)requestToken.getToken();
    String tokenSecret = (String)requestToken.getTokenSecret();
    // store the token and tokenSecret in the session
    session.setAttribute(&quot;token&quot;, token);
    session.setAttribute(&quot;tokenSecret&quot;, tokenSecret);
&nbsp;
    // get the url that the user must click to authenticate w/OAuth
    String authUrl = requestToken.getAuthorizationURL();
    req.setAttribute(&quot;authUrl&quot;, authUrl);
    RequestDispatcher rd = req.getRequestDispatcher(&quot;login.jsp&quot;);
&nbsp;
    try {
      rd.forward(req, resp);
    } catch (ServletException e) {
      log.error(e.toString());
    }
&nbsp;
  }
}</pre></td></tr></table></div>

</p>
<p style="clear: both"><strong>SendChatterServlet</strong></p>
<p style="clear: both">This Servlet runs when the user clicks the Twitter -> Chatter link. The code grabs the user&#8217;s last tweet and the uses the Partner Web Services API to submit the sObject with the new Chatter status to Salesforce.com.</p>
<p style="clear: both">

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
</pre></td><td class="code"><pre class="java" style="font-family:monospace;">package com.jeffdouglas;
&nbsp;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.http.AccessToken;
&nbsp;
import com.sforce.ws.*;
import com.sforce.soap.partner.*;
import com.sforce.soap.partner.sobject.SObject;
&nbsp;
public class SendChatterServlet extends HttpServlet {
&nbsp;
  private static final Logger log = Logger.getLogger(SendTweetServlet.class);
&nbsp;
  public void doGet(HttpServletRequest req, HttpServletResponse resp)
  throws IOException {
&nbsp;
    PartnerConnection connection = null;
    // get the user's last tweet
    String tweet = getLastTweet(req,resp);
&nbsp;
    if (tweet != null) {
&nbsp;
      try {
        if (connection == null) {
          ConnectorConfig config = new ConnectorConfig();
          config.setUsername(Credentials.SFDC_USERNAME);
          config.setPassword(Credentials.SFDC_PASSWORD);
          connection = Connector.newConnection(config);
        }
&nbsp;
        // create the sobject to hold the post
        SObject post = new SObject();
        post.setType(&quot;FeedPost&quot;);
        post.setField(&quot;ParentId&quot;, Credentials.SFDC_USERID);
        post.setField(&quot;Body&quot;, tweet);
        // submit the update to Salesforce.com
        connection.create(new SObject[]{post});
&nbsp;
      } catch (ConnectionException ce) {
        log.error(ce.toString());
      }
&nbsp;
      resp.getWriter().println(&quot;Tweet sent to Chatter: &quot;+tweet);
    } else {
      resp.getWriter().println(&quot;Could not fetch the lastes update from Twitter. Nothing sent to Chatter.&quot;);
    }
&nbsp;
  }
&nbsp;
  private String getLastTweet(HttpServletRequest req, HttpServletResponse resp) {
&nbsp;
    String tweet = null;
    HttpSession session = req.getSession();
    Twitter twitter = new TwitterFactory().getInstance();
&nbsp;
    twitter.setOAuthConsumer(Credentials.TWITTER_CONSUMERKEY,
        Credentials.TWITTER_CONSUMERSECRET);
&nbsp;
    // if the access token is present in the session
    if (session.getAttribute(&quot;accessToken&quot;) == null){
        // get the request token from the session
        String token = (String) session.getAttribute(&quot;token&quot;);
        String tokenSecret = (String)session.getAttribute(&quot;tokenSecret&quot;);
&nbsp;
        // get the access token from twitter
        AccessToken accessToken = null;
        try {
          accessToken = twitter.getOAuthAccessToken(token, tokenSecret);
        } catch (TwitterException e) {
          log.error(e.toString());
        }
        twitter.setOAuthAccessToken(accessToken);
&nbsp;
        // save the access token, that are different from request token
        session.setAttribute(&quot;accessToken&quot;, accessToken.getToken());
        session.setAttribute(&quot;accessTokenSecret&quot;, accessToken.getTokenSecret());
&nbsp;
    } else {
        // use the access token from the session
        twitter.setOAuthAccessToken((String)session.getAttribute(&quot;accessToken&quot;),
            (String)session.getAttribute(&quot;accessTokenSecret&quot;));
    }
&nbsp;
    List&lt;status&gt; statuses = null;
    try {
      // get the user's timeline
      statuses = twitter.getUserTimeline();
      // set their last tweet to return
      tweet = statuses.get(0).getText();
    } catch (TwitterException e) {
      log.error(e.toString());
    }
&nbsp;
    return tweet;
&nbsp;
  }
&nbsp;
}</pre></td></tr></table></div>

</p>
<p style="clear: both"><strong>SendTweetServlet</strong></p>
<p style="clear: both">When the user clicks the Chatter -> Twitter link, this Servlet queries Salesforce.com for the user&#8217;s most recent status update, finds the status in the returned XML results and then sends the status out as a tweet.</p>
<p style="clear: both">

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
</pre></td><td class="code"><pre class="java" style="font-family:monospace;">package com.jeffdouglas;
&nbsp;
import java.io.IOException;
import java.util.Iterator;
import javax.servlet.http.*;
import org.apache.log4j.Logger;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.http.AccessToken;
&nbsp;
import com.sforce.ws.*;
import com.sforce.ws.bind.XmlObject;
import com.sforce.soap.partner.*;
import com.sforce.soap.partner.sobject.SObject;
&nbsp;
public class SendTweetServlet extends HttpServlet {
&nbsp;
  private static final Logger log = Logger.getLogger(SendTweetServlet.class);
&nbsp;
  public void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws IOException {
&nbsp;
    PartnerConnection connection = null;
    String feedPost = null;
&nbsp;
    try {
      if (connection == null) {
        ConnectorConfig config = new ConnectorConfig();
        config.setUsername(Credentials.SFDC_USERNAME);
        config.setPassword(Credentials.SFDC_PASSWORD);
        connection = Connector.newConnection(config);
      }
&nbsp;
      QueryResult results = connection
          .query(&quot;SELECT Id, FeedPost.Body FROM UserFeed WHERE &quot;
              + &quot;ParentId = '&quot; + Credentials.SFDC_USERID + &quot;'&quot;
              + &quot; And Type = 'UserStatus' ORDER BY CreatedDate DESC LIMIT 1&quot;);
&nbsp;
      // in this case there will only be 1 record returned, but....
      for (int i = 0; i &lt; results.getRecords().length; i++) {
        SObject feed = results.getRecords()[i];
        feedPost = getFeedBody(feed);
      }
&nbsp;
    } catch (ConnectionException ce) {
      log.error(ce.toString());
    }
&nbsp;
    if (feedPost != null) {
      sendTweet(feedPost, req, resp);
      resp.getWriter().println(&quot;Chatter message sent to Twitter: &quot; + feedPost);
    } else {
      resp.getWriter().println(&quot;Nothing sent to Twitter&quot;);
    }
&nbsp;
  }
&nbsp;
  private void sendTweet(String tweet, HttpServletRequest req, HttpServletResponse resp) {
&nbsp;
    HttpSession session = req.getSession();
    Twitter twitter = new TwitterFactory().getInstance();
&nbsp;
    twitter.setOAuthConsumer(Credentials.TWITTER_CONSUMERKEY,
        Credentials.TWITTER_CONSUMERSECRET);
&nbsp;
    // if the access token is present in the session
    if (session.getAttribute(&quot;accessToken&quot;) == null){
        // get the request token from the session
        String token = (String) session.getAttribute(&quot;token&quot;);
        String tokenSecret = (String)session.getAttribute(&quot;tokenSecret&quot;);
&nbsp;
        // get the access token from twitter
        AccessToken accessToken = null;
        try {
          accessToken = twitter.getOAuthAccessToken(token, tokenSecret);
        } catch (TwitterException e) {
          log.error(e.toString());
        }
        twitter.setOAuthAccessToken(accessToken);
&nbsp;
        // save the access token, that are different from request token
        session.setAttribute(&quot;accessToken&quot;, accessToken.getToken());
        session.setAttribute(&quot;accessTokenSecret&quot;, accessToken.getTokenSecret());
&nbsp;
    } else {
        // use the access token from the session
        twitter.setOAuthAccessToken((String)session.getAttribute(&quot;accessToken&quot;),
            (String)session.getAttribute(&quot;accessTokenSecret&quot;));
    }
&nbsp;
    try {
      // update the user's twitter status
      twitter.updateStatus(tweet);
    } catch (TwitterException e) {
      log.error(e.toString());
    }
&nbsp;
  }
&nbsp;
  private String getFeedBody(SObject feed) {
    String feedBody = &quot;&quot;;
    Iterator&lt;xmlObject&gt; feedPost = feed.getChildren();
    while (feedPost.hasNext()) {
      XmlObject post = feedPost.next();
      if (post.getValue() == null) {
        Iterator&lt;xmlObject&gt; body = post.getChildren();
        while (body.hasNext()) {
          XmlObject child = body.next();
          if (child.getName().toString().equals(
              &quot;{urn:sobject.partner.soap.sforce.com}Body&quot;)) {
            feedBody = child.getValue().toString();
            break;
          }
        }
      }
    }
    return feedBody;
  }
}</pre></td></tr></table></div>

</p>
<p style="clear: both"><strong>SendBothServlet</strong></p>
<p style="clear: both">This Servlet loads the HTML form presenting the user with a textbox to enter their new status. When the form is posted, the status is sent out to both Chatter and Twitter.</p>
<p style="clear: both">

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
</pre></td><td class="code"><pre class="java" style="font-family:monospace;">package com.jeffdouglas;
&nbsp;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.http.AccessToken;
&nbsp;
import com.sforce.soap.partner.Connector;
import com.sforce.soap.partner.PartnerConnection;
import com.sforce.soap.partner.sobject.SObject;
import com.sforce.ws.ConnectionException;
import com.sforce.ws.ConnectorConfig;
&nbsp;
public class SendBothServlet extends HttpServlet {
&nbsp;
  private static final Logger log = Logger.getLogger(SendTweetServlet.class);
&nbsp;
  private void sendToChatter(String status) {
&nbsp;
    PartnerConnection connection = null;
&nbsp;
      try {
        if (connection == null) {
          ConnectorConfig config = new ConnectorConfig();
          config.setUsername(Credentials.SFDC_USERNAME);
          config.setPassword(Credentials.SFDC_PASSWORD);
          connection = Connector.newConnection(config);
        }
&nbsp;
        // create the sobject to hold the post
        SObject post = new SObject();
        post.setType(&quot;FeedPost&quot;);
        post.setField(&quot;ParentId&quot;, Credentials.SFDC_USERID);
        post.setField(&quot;Body&quot;, status);
        // submit the update to Salesforce.com
        connection.create(new SObject[]{post});
&nbsp;
      } catch (ConnectionException ce) {
        log.error(ce.toString());
      }
&nbsp;
  }
&nbsp;
  private void sendToTwitter(String status, HttpServletRequest req) {
&nbsp;
    HttpSession session = req.getSession();
    Twitter twitter = new TwitterFactory().getInstance();
&nbsp;
    twitter.setOAuthConsumer(Credentials.TWITTER_CONSUMERKEY,
        Credentials.TWITTER_CONSUMERSECRET);
&nbsp;
    // if the access token is present in the session
    if (session.getAttribute(&quot;accessToken&quot;) == null){
        // get the request token from the session
        String token = (String) session.getAttribute(&quot;token&quot;);
        String tokenSecret = (String)session.getAttribute(&quot;tokenSecret&quot;);
&nbsp;
        // get the access token from twitter
        AccessToken accessToken = null;
        try {
          accessToken = twitter.getOAuthAccessToken(token, tokenSecret);
        } catch (TwitterException e) {
          log.error(e.toString());
        }
        twitter.setOAuthAccessToken(accessToken);
&nbsp;
        // save the access token, that are different from request token
        session.setAttribute(&quot;accessToken&quot;, accessToken.getToken());
        session.setAttribute(&quot;accessTokenSecret&quot;, accessToken.getTokenSecret());
&nbsp;
    } else {
        // use the access token from the session
        twitter.setOAuthAccessToken((String)session.getAttribute(&quot;accessToken&quot;),
            (String)session.getAttribute(&quot;accessTokenSecret&quot;));
    }
&nbsp;
    try {
      // update the user's twitter status
      twitter.updateStatus(status);
    } catch (TwitterException e) {
      log.error(e.toString());
    }
&nbsp;
  }
&nbsp;
  public void doPost(HttpServletRequest req, HttpServletResponse resp)
  throws IOException {
&nbsp;
    sendToChatter(req.getParameter(&quot;status&quot;));
    sendToTwitter(req.getParameter(&quot;status&quot;),req);
&nbsp;
    resp.getWriter().println(&quot;Sent the following to both Chatter and Twitter: &quot;+req.getParameter(&quot;status&quot;));
&nbsp;
  }
&nbsp;
  public void doGet(HttpServletRequest req, HttpServletResponse resp)
  throws IOException {
&nbsp;
    try {
      RequestDispatcher rd = req.getRequestDispatcher(&quot;post.jsp&quot;);
      rd.forward(req, resp);
    } catch (ServletException e) {
      log.error(e.toString());
    }
&nbsp;
  }
&nbsp;
}</pre></td></tr></table></div>

</p>
<p><br class="final-break" style="clear: both" /></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jeffdouglas.com/2010/03/17/integrate-chatter-twitter-on-google-app-engine-using-oauth/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AppWrench Thinks My Code Needs Help</title>
		<link>http://blog.jeffdouglas.com/2009/12/14/appwrench-thinks-my-code-needs-help/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=appwrench-thinks-my-code-needs-help</link>
		<comments>http://blog.jeffdouglas.com/2009/12/14/appwrench-thinks-my-code-needs-help/#comments</comments>
		<pubDate>Mon, 14 Dec 2009 20:53:39 +0000</pubDate>
		<dc:creator>Jeff Douglas</dc:creator>
				<category><![CDATA[GAE/J]]></category>
		<category><![CDATA[Google App Engine]]></category>

		<guid isPermaLink="false">http://blog.jeffdouglas.com/?p=1854</guid>
		<description><![CDATA[One of my fellow Appirians sent me an email today pointing me to the AppWrench site. AppWrench is a profiling tool for App Engine that runs both locally (for free!) and for applications deployed on App Engine. In addition to profiling, you can also display Data Store and Memcache API calls as well as log files. [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2009%2F12%2F14%2Fappwrench-thinks-my-code-needs-help%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2009%2F12%2F14%2Fappwrench-thinks-my-code-needs-help%2F&amp;source=jeffdonthemic&amp;style=normal&amp;service=bit.ly&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>One of my fellow Appirians sent me an email today pointing me to the <a href="http://blog.jeffdouglas.com//appwrench.onpositive.com/" target="_blank">AppWrench</a> site. AppWrench is a profiling tool for App Engine that runs both locally (for free!) and for applications deployed on App Engine. In addition to profiling, you can also display Data Store and Memcache API calls as well as log files.</p>
<p>The one thing that caught his attention is that the screenshots on the site reference my code! Apparently my code is so crappy that it needs profiling to improve it.</p>
<p>Perhaps I should buy my own book, <a href="http://links.jeffdouglas.com/book" target="_blank">Beginning Jave Google App Engine</a>, so that I can learn something.</p>
<p><a href="http://blog.jeffdouglas.com/wp-content/uploads/2009/12/appwrenchscreenshot.png" rel="lightbox[1854]"><img class="alignnone size-full wp-image-1855" title="AppWrenchScreenshot" src="http://blog.jeffdouglas.com/wp-content/uploads/2009/12/appwrenchscreenshot.png" alt="" width="544" height="208" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jeffdouglas.com/2009/12/14/appwrench-thinks-my-code-needs-help/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Virtual File System for Google App Engine</title>
		<link>http://blog.jeffdouglas.com/2009/10/23/virtual-file-system-for-google-app-engine/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=virtual-file-system-for-google-app-engine</link>
		<comments>http://blog.jeffdouglas.com/2009/10/23/virtual-file-system-for-google-app-engine/#comments</comments>
		<pubDate>Fri, 23 Oct 2009 13:01:25 +0000</pubDate>
		<dc:creator>Jeff Douglas</dc:creator>
				<category><![CDATA[Cloud Computing]]></category>
		<category><![CDATA[GAE/J]]></category>
		<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://blog.jeffdouglas.com/?p=1518</guid>
		<description><![CDATA[App Engine has a number of security restrictions that it imposes on applications. One that is often cited as a major source of frustration is the inability to read/write to a local file system. It looks like the developer community may have developed a workaround. Kyle Roche sent me a link to a GaeVFS, a [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2009%2F10%2F23%2Fvirtual-file-system-for-google-app-engine%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2009%2F10%2F23%2Fvirtual-file-system-for-google-app-engine%2F&amp;source=jeffdonthemic&amp;style=normal&amp;service=bit.ly&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>App Engine has a number of <a href="http://code.google.com/appengine/docs/whatisgoogleappengine.html" target="_blank">security restrictions</a> that it imposes on applications. One that is often cited as a major source of frustration is the inability to read/write to a local file system. It looks like the developer community may have developed a workaround.</p>
<p><a href="http://www.kyleroche.com" target="_blank">Kyle Roche</a> sent me a link to a <a href="http://code.google.com/p/gaevfs/" target="_blank">GaeVFS</a>, a distributed, writeable virtual file system for Google App Engine Java. It doesn&#8217;t actually use the local file system but simulates it using the datastore and memcache.</p>
<blockquote><p>&#8220;GaeVFS is an Apache Commons VFS plug-in that implements a distributed, writeable virtual file system for Google App Engine (GAE) for Java. GaeVFS is implemented using the GAE datastore and memcache APIs. The primary goal of GaeVFS is to provide a portability layer that allows you to write application code to access the file system&#8211;both reads and writes&#8211;that runs unmodified in either GAE or non-GAE servlet environments.&#8221;</p></blockquote>
<p>I&#8217;m not sure if we have enough time to get this added to <a href="http://links.jeffdouglas.com/book" target="_blank">our book</a> but it may be interesting.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jeffdouglas.com/2009/10/23/virtual-file-system-for-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Creating a Salesforce.com Robot with Google Wave and GAE/J</title>
		<link>http://blog.jeffdouglas.com/2009/09/15/creating-a-salesforce-com-robot-with-google-wave-and-app-engine/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=creating-a-salesforce-com-robot-with-google-wave-and-app-engine</link>
		<comments>http://blog.jeffdouglas.com/2009/09/15/creating-a-salesforce-com-robot-with-google-wave-and-app-engine/#comments</comments>
		<pubDate>Tue, 15 Sep 2009 09:01:25 +0000</pubDate>
		<dc:creator>Jeff Douglas</dc:creator>
				<category><![CDATA[GAE/J]]></category>
		<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Salesforce]]></category>

		<guid isPermaLink="false">http://blog.jeffdouglas.com/?p=1250</guid>
		<description><![CDATA[My buddy Kyle Roche has an awesome post today on how to create a Google Wave robot on App Engine that contributes to the conversation with information from Salesforce.com using the Force.com toolkit for Google App Engine. If you are not familiar with Google Wave check out this great intro video from Google I/O. You [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2009%2F09%2F15%2Fcreating-a-salesforce-com-robot-with-google-wave-and-app-engine%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2009%2F09%2F15%2Fcreating-a-salesforce-com-robot-with-google-wave-and-app-engine%2F&amp;source=jeffdonthemic&amp;style=normal&amp;service=bit.ly&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p><a href="http://blog.jeffdouglas.com/wp-content/uploads/2009/09/wavelogo.png" rel="lightbox[1250]"><img class="alignleft size-full wp-image-1251" style="padding-right:10px;" title="wavelogo" src="http://blog.jeffdouglas.com/wp-content/uploads/2009/09/wavelogo.png" alt="wavelogo" width="154" height="154" /></a> My buddy Kyle Roche has an <a href="http://www.kyleroche.com/2009/09/creating-salesforce-robot-for-google.html" target="_blank">awesome post</a> today on how to create a Google Wave robot on App Engine that contributes to the conversation with information from Salesforce.com using the <a href="http://developer.force.com/appengine" target="_blank">Force.com toolkit for Google App Engine</a>.</p>
<p>If you are not familiar with Google Wave check out <a href="http://www.youtube.com/watch?v=v_UyVmITiYQ" target="_blank">this great intro video</a> from Google I/O.</p>
<p><a href="http://www.kyleroche.com/2009/09/creating-salesforce-robot-for-google.html" target="_blank">You can read Kyle&#8217;s entire article here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jeffdouglas.com/2009/09/15/creating-a-salesforce-com-robot-with-google-wave-and-app-engine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rich Internet Applications Using Flex, Salesforce.com and Google App Engine</title>
		<link>http://blog.jeffdouglas.com/2009/08/20/rich-internet-applications-using-flex-salesforce-com-and-google-app-engine/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rich-internet-applications-using-flex-salesforce-com-and-google-app-engine</link>
		<comments>http://blog.jeffdouglas.com/2009/08/20/rich-internet-applications-using-flex-salesforce-com-and-google-app-engine/#comments</comments>
		<pubDate>Thu, 20 Aug 2009 15:06:27 +0000</pubDate>
		<dc:creator>Jeff Douglas</dc:creator>
				<category><![CDATA[Cloud Computing]]></category>
		<category><![CDATA[Code Sample]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[GAE/J]]></category>
		<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Salesforce]]></category>

		<guid isPermaLink="false">http://blog.jeffdouglas.com/?p=1113</guid>
		<description><![CDATA[Cross-posted at the Appirio Technology Blog It’s fairly common these days to see Flex applications running inside Salesforce.com. But what if you&#8217;d like to run your Flex applications on another SaaS provider like Google App Engine or Amazon EC2. We are going to set up a simple Flex application that fetches Accounts and Opportunities from [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2009%2F08%2F20%2Frich-internet-applications-using-flex-salesforce-com-and-google-app-engine%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2009%2F08%2F20%2Frich-internet-applications-using-flex-salesforce-com-and-google-app-engine%2F&amp;source=jeffdonthemic&amp;style=normal&amp;service=bit.ly&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>Cross-posted at the <a href="http://techblog.appirio.com/2009/08/rich-internet-applications-using-flex.html" target="_blank">Appirio Technology Blog</a></p>
<p>It’s fairly common these days to see Flex applications running inside Salesforce.com. But what if you&#8217;d like to run your Flex applications on another SaaS provider like Google App Engine or Amazon EC2. We are going to set up a simple Flex application that fetches Accounts and Opportunities from Salesforce.com using an open source remoting library that runs on Google App Engine. Here a quick peak at the final application:</p>
<p><a href="http://blog.jeffdouglas.com/wp-content/uploads/2009/08/flex-graniteds-screenshot.png" rel="lightbox[1113]"><img class="alignnone size-medium wp-image-1116" title="flex-graniteDS-screenshot" src="http://blog.jeffdouglas.com/wp-content/uploads/2009/08/flex-graniteds-screenshot.png?w=300" alt="flex-graniteDS-screenshot" width="300" height="211" /></a></p>
<p><strong>You can run this demo at: </strong><a href="http://jeffdouglas-sfdc-graniteds.appspot.com/main.html" target="_blank"><strong>http://jeffdouglas-sfdc-graniteds.appspot.com/main.html</strong></a></p>
<p>Flex can communicate with a Java backend using HTTP, SOAP-based web services or Adobe’s proprietary AMF format. There are a number of open source AMF implementations including Aodobe BlazeDS, WebORB and GraniteDS.  These implementations allow you to communicate via JMS or Flex remoting and are more efficient and exponentially faster than using XML across the wire. For this application we are going to be utilizing <a href="http://sourceforge.net/projects/granite/" target="_blank">GraniteDS</a>. The GraniteDS remoting service is a high performance data transfer service that allows your Flex applications to directly invoke Java object methods on your application and consume the return values natively. The objects returned from the server-side methods are automatically deserialized into either dynamic or typed ActionScript objects.</p>
<p><strong>Setting Up Your Environment</strong></p>
<p>To get started you&#8217;ll need to <a href="http://www.adobe.com/cfusion/entitlement/index.cfm?e=flexbuilder3" target="_blank">download</a> the Adobe Flex 3.0 Builder 3 or Flex 3 Builder Plug-In. There’s a 60 day trial if you don’t already have a license. Installation is pretty straight-forward if you are familiar with the Eclipse installation process. The plug-in is a little easier and quicker to install.</p>
<p>If you don&#8217;t have a Google App Engine account, you can create one <a href="http://appengine.google.com" target="_blank">here</a>. You&#8217;ll also need to download and install the Google App Engine SDK and Eclipse plug-in. You can find details on this process <a href="http://code.google.com/appengine/downloads.html" target="_blank">here</a>.</p>
<p><strong>Creating Your Project</strong></p>
<p>Now that our environment is setup, create a new Web Application Project and uncheck “Use Google Web Toolkit”. Since we are going to be using Flex as the front end for our application you will want to add the Flex Project Nature to your project. Right click on the project name in the left panel and select Flex Project Nature -&gt; Add Flex Project Nature. Choose “Other” as the application server and click Next and then Finish. This will automatically create your Flex main.mxml file for you in the src directory.</p>
<p>After the file has been created you should see the following error message in the Eclipse Problems tab, &#8220;Cannot create HTML wrapper. Right-click here to recreate folder html-template.&#8221; To fix this simply right click on the error message and select “Recreate HTML Templates”.</p>
<p><strong>Adding Required Libraries</strong></p>
<p>There are a number of libraries that we are going to need for Salesforce.com and GraniteDS. Download the latest version of GraniteDS from <a href="http://sourceforge.net/projects/granite/files/" target="_blank">here</a>, unzip the files, find the granite.jar in the graniteds/build/ directory and place it into your project’s /WEB-INF/lib/ directory.</p>
<p>You’ll also need to get the latest version of Xalan-J from <a href="http://www.apache.org/dyn/closer.cgi/xml/xalan-j" target="_blank">here</a>. Unzip the files and copy serializer.jar and xalan.jar into your project’s /WEB-INF/lib directory.</p>
<p>There are two jar files you will need for the Salesforce.com integration. Download the Force.com Web Service Connector files from <a href="http://code.google.com/p/sfdc-wsc/downloads/list" target="_blank">here</a> and copy them to your project&#8217;s /WEB-INF/lib directory:</p>
<ul>
<li>partner-library.jar &#8211; the objects and methods from the Force.com partner WSDL</li>
<li>wsc-gae-16_0.jar &#8211; the Web Service parsing and transport for GAE-Java</li>
</ul>
<p>You&#8217;ll need to add these new jar files to your Java build path in Eclipse by right clicking on the project name and selecting Properties. Select Java Build Path -&gt; Libraries and add your jars from the lib directory.</p>
<p><strong>Server Configuration</strong></p>
<p>So now that we have all of requirements in place we can start configuring our application. Place the following code into your /WEB-INF/web-xml file between the  tags to tell App Engine which classes GraniteDS utilizes.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</pre></td><td class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">&lt;!-- GraniteDS --&gt;</span>
	<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;listener<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
	<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;listener-class<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>org.granite.config.GraniteConfigListener<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/listener-class<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/listener<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
&nbsp;
<span style="color: #808080; font-style: italic;">&lt;!-- handle AMF requests serialization and deserialization --&gt;</span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;filter<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;filter-name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>AMFMessageFilter<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/filter-name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;filter-class<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>org.granite.messaging.webapp.AMFMessageFilter<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/filter-class<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/filter<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;filter-mapping<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;filter-name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>AMFMessageFilter<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/filter-name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;url-pattern<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>/graniteamf/*<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/url-pattern<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/filter-mapping<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
&nbsp;
<span style="color: #808080; font-style: italic;">&lt;!-- processes AMF requests --&gt;</span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;servlet<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;servlet-name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>AMFMessageServlet<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/servlet-name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;servlet-class<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>org.granite.messaging.webapp.AMFMessageServlet<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/servlet-class<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;load-on-startup<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>1<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/load-on-startup<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/servlet<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;servlet-mapping<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;servlet-name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>AMFMessageServlet<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/servlet-name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;url-pattern<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>/graniteamf/*<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/url-pattern<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/servlet-mapping<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></td></tr></table></div>

<p>You&#8217;ll also want to change the welcome-file to specify main.html instead of the generated index.html file.</p>
<p>GraniteDS communicates with the servlet container via a remoting destination. A Remoting destination exposes a Java class that your Flex application can invoke remotely. The destination id is the logical name that your Flex application uses to refer to the remote class. This eliminates the need to hardcode a reference to the fully qualified Java class name. This logical name is mapped to the Java class name as part of the destination configuration in services-config.xml. Create a new folder under /WEB-INF/ called “flex” and create the following services-config.xml file.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre></td><td class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;?xml</span> <span style="color: #000066;">version</span>=<span style="color: #ff0000;">&quot;1.0&quot;</span> <span style="color: #000066;">encoding</span>=<span style="color: #ff0000;">&quot;UTF-8&quot;</span><span style="color: #000000; font-weight: bold;">?&gt;</span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;services-config<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;services<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
          <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;service</span></span>
<span style="color: #009900;">               <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;granite-service&quot;</span></span>
<span style="color: #009900;">               <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;flex.messaging.services.RemotingService&quot;</span></span>
<span style="color: #009900;">               <span style="color: #000066;">messageTypes</span>=<span style="color: #ff0000;">&quot;flex.messaging.messages.RemotingMessage&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
               <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;destination</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;Gateway&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
                    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;channels<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
                         <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;channel</span> <span style="color: #000066;">ref</span>=<span style="color: #ff0000;">&quot;my-graniteamf&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span>
                    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/channels<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;properties<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
                         <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;scope<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>application<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/scope<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
                         <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;source<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>com.appirio.Gateway<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/source<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
                    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/properties<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
               <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/destination<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
          <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/service<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/services<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
&nbsp;
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;channels<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
          <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;channel-definition</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;my-graniteamf&quot;</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;mx.messaging.channels.AMFChannel&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
               <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;endpoint</span></span>
<span style="color: #009900;">                    <span style="color: #000066;">uri</span>=<span style="color: #ff0000;">&quot;/graniteamf/amf&quot;</span></span>
<span style="color: #009900;">                    <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;flex.messaging.endpoints.AMFEndpoint&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span>
          <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/channel-definition<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/channels<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/services-config<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></td></tr></table></div>

<p>Our remoting destination points to a class called Gateway that we will create shortly. Now we need to tell the Flex compiler where to find the services file that defines our remoting destination. Right click on the project name in the left panel and select Properties -&gt; Flex Compiler. Replace your compiler arguments with the following:</p>
<blockquote><p>-locale en_US -services ../war/WEB-INF/flex/services-config.xml</p></blockquote>
<p><strong>Client-Side Code</strong></p>
<p>Now that our server is configured we can start working on the Flex client. Our client is fairly simple and allows a user select an Account and view/create Opportunities for it. The Flex portion of the application is represented by a single MXML file. For larger applications you would typically break your application into an MVC model and possibly use some sort of Flex framework like Cairngorm, Mate or PureMVC. Since our application is so small there is really no need to implement any sort of framework.</p>
<p>One of the most important parts of this file is the RemoteObject tag at the top. The id of the tag (gateway) is used by the application to reference the object while the destination (Gateway) is same destination we set up in our services-config.xml file specifying our remoting destination of com.appirio.Gateway.</p>
<p>The individual methods specified by the RemoteObject tag map directly to methods in the Gateway class that we are about to define.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
</pre></td><td class="code"><pre class="java" style="font-family:monospace;">&nbsp;
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;mx:Application xmlns:mx=&quot;http://www.adobe.com/2006/mxml&quot;
	layout=&quot;absolute&quot;
	creationComplete=&quot;gateway.getAccounts()&quot;&gt;
&nbsp;
     &lt;mx:RemoteObject id=&quot;gateway&quot; destination=&quot;Gateway&quot; fault=&quot;Alert.show(event.fault.toString());&quot;&gt;
          &lt;mx:method name=&quot;getAccounts&quot; result=&quot;storeAccounts(event)&quot; fault=&quot;Alert.show(event.fault.faultString);&quot; /&gt;
          &lt;mx:method name=&quot;getOpportunities&quot; result=&quot;storeOpportunities(event)&quot; fault=&quot;Alert.show(event.fault.faultString);&quot; /&gt;
          &lt;mx:method name=&quot;createOpportunity&quot; result=&quot;fetchOpportunities(event)&quot; fault=&quot;Alert.show(event.fault.faultString);&quot; /&gt;
     &lt;/mx:RemoteObject&gt;
&nbsp;
	&lt;mx:Script&gt;
	&lt;![CDATA[
		import mx.controls.Alert;
		import mx.collections.ArrayCollection;
		import mx.rpc.events.ResultEvent;
&nbsp;
		[Bindable] private var accounts:ArrayCollection;
		[Bindable] private var opportunities:ArrayCollection;
		[Bindable] private var selectedAccount:Account;
&nbsp;
		// store the accounts returned from Salesforce.com, select the first account as
		// the currently selected one and then fetch its opportunities from Salesforce.com
		private function storeAccounts(event:ResultEvent):void {
			accounts = event.result as ArrayCollection;
			selectedAccount = accounts.getItemAt(0) as Account;
			gateway.getOpportunities(selectedAccount.id);
		}
&nbsp;
		// store the list of opportunities returned from Salesforce.com
		private function storeOpportunities(event:ResultEvent):void {
			opportunities = event.result as ArrayCollection;
		}
&nbsp;
		// fetch the opportunities from Salesforce.com after a new opportunities has been created
		// and returned from the createOpportunity() remote object method
		private function fetchOpportunities(event:ResultEvent):void {
			gateway.getOpportunities(selectedAccount.id);
		}
&nbsp;
		private function changeAccount():void {
			opportunities = null;
			selectedAccount = cbxAccount.selectedItem as Account;
			gateway.getOpportunities(selectedAccount.id);
		}
&nbsp;
		private function saveOpportunity():void {
&nbsp;
			var opp:Opportunity = new Opportunity();
			opp.accountId = selectedAccount.id;
			opp.probability = frmProbability.text;
			opp.stageName = frmStage.text;
			opp.amount = frmAmount.text;
			opp.closeDate = dateFormatter.format(frmCloseDate.selectedDate);
			opp.name = frmName.text;
			opp.orderNumber = frmOrder.text;
&nbsp;
			// create the opportunity
			gateway.createOpportunity(opp);
&nbsp;
			// reset the form
			frmProbability.selectedIndex = 0;
			frmStage.selectedIndex = 0;
			frmAmount.text = null;
			frmCloseDate.selectedDate = null;
			frmName.text = null;
			frmOrder.text = null;
&nbsp;
		}
&nbsp;
	]]&gt;
	&lt;/mx:Script&gt;
&nbsp;
	&lt;mx:DateFormatter id=&quot;dateFormatter&quot; formatString=&quot;MM/DD/YYYY&quot;/&gt;
&nbsp;
	&lt;mx:Label x=&quot;10&quot; y=&quot;14&quot; text=&quot;Telesales Demo with Salesforce.com, GraniteDS and Google App Engine&quot; fontSize=&quot;18&quot; color=&quot;#FFFFFF&quot;/&gt;
&nbsp;
	&lt;mx:VBox top=&quot;50&quot; bottom=&quot;10&quot; left=&quot;10&quot; right=&quot;10&quot;&gt;
		&lt;mx:HBox width=&quot;100%&quot; height=&quot;50%&quot;&gt;
			&lt;mx:Panel width=&quot;50%&quot; height=&quot;100%&quot; layout=&quot;absolute&quot; title=&quot;Account Display&quot;&gt;
				&lt;mx:Form width=&quot;100%&quot; height=&quot;100%&quot;&gt;
					&lt;mx:FormItem label=&quot;Account&quot;&gt;
						&lt;mx:ComboBox
							id=&quot;cbxAccount&quot;
							dataProvider=&quot;{accounts}&quot;
							labelField=&quot;name&quot;
							change=&quot;changeAccount()&quot;/&gt;
					&lt;/mx:FormItem&gt;
					&lt;mx:FormItem label=&quot;City&quot;&gt;
						&lt;mx:Text text=&quot;{selectedAccount.city}&quot;/&gt;
					&lt;/mx:FormItem&gt;
					&lt;mx:FormItem label=&quot;State&quot;&gt;
						&lt;mx:Text text=&quot;{selectedAccount.state}&quot;/&gt;
					&lt;/mx:FormItem&gt;
					&lt;mx:FormItem label=&quot;Phone&quot;&gt;
						&lt;mx:Text text=&quot;{selectedAccount.phone}&quot;/&gt;
					&lt;/mx:FormItem&gt;
					&lt;mx:FormItem label=&quot;Website&quot;&gt;
						&lt;mx:Text text=&quot;{selectedAccount.website}&quot;/&gt;
					&lt;/mx:FormItem&gt;
				&lt;/mx:Form&gt;
			&lt;/mx:Panel&gt;
			&lt;mx:Panel width=&quot;50%&quot; height=&quot;100%&quot; layout=&quot;absolute&quot; title=&quot;New Opportunity for {selectedAccount.name}&quot;&gt;
				&lt;mx:Form width=&quot;100%&quot; height=&quot;100%&quot;&gt;
					&lt;mx:FormItem label=&quot;Name&quot;&gt;
						&lt;mx:TextInput id=&quot;frmName&quot;/&gt;
					&lt;/mx:FormItem&gt;
					&lt;mx:FormItem label=&quot;Amount&quot;&gt;
						&lt;mx:TextInput id=&quot;frmAmount&quot;/&gt;
					&lt;/mx:FormItem&gt;
					&lt;mx:FormItem label=&quot;Stage&quot;&gt;
						&lt;mx:ComboBox id=&quot;frmStage&quot;&gt;
							&lt;mx:dataProvider&gt;
								&lt;mx:String&gt;Prospecting&lt;/mx:String&gt;
								&lt;mx:String&gt;Qualifications&lt;/mx:String&gt;
								&lt;mx:String&gt;Value Proposition&lt;/mx:String&gt;
							&lt;/mx:dataProvider&gt;
						&lt;/mx:ComboBox&gt;
					&lt;/mx:FormItem&gt;
					&lt;mx:FormItem label=&quot;Probability&quot;&gt;
						&lt;mx:ComboBox id=&quot;frmProbability&quot;&gt;
							&lt;mx:dataProvider&gt;
								&lt;mx:String&gt;10&lt;/mx:String&gt;
								&lt;mx:String&gt;25&lt;/mx:String&gt;
								&lt;mx:String&gt;50&lt;/mx:String&gt;
								&lt;mx:String&gt;75&lt;/mx:String&gt;
							&lt;/mx:dataProvider&gt;
						&lt;/mx:ComboBox&gt;
					&lt;/mx:FormItem&gt;
					&lt;mx:FormItem label=&quot;Close Date&quot;&gt;
						&lt;mx:DateField id=&quot;frmCloseDate&quot;/&gt;
					&lt;/mx:FormItem&gt;
					&lt;mx:FormItem label=&quot;Order&quot;&gt;
						&lt;mx:TextInput id=&quot;frmOrder&quot;/&gt;
					&lt;/mx:FormItem&gt;
					&lt;mx:FormItem&gt;
						&lt;mx:Button label=&quot;Save Opportunity&quot; click=&quot;saveOpportunity()&quot;/&gt;
					&lt;/mx:FormItem&gt;
				&lt;/mx:Form&gt;
			&lt;/mx:Panel&gt;
		&lt;/mx:HBox&gt;
		&lt;mx:Panel width=&quot;100%&quot; height=&quot;50%&quot; layout=&quot;absolute&quot; title=&quot;Opportunities for {selectedAccount.name}&quot;&gt;
			&lt;mx:DataGrid width=&quot;100%&quot; height=&quot;100%&quot; id=&quot;dgOpps&quot; dataProvider=&quot;{opportunities}&quot;&gt;
				&lt;mx:columns&gt;
					&lt;mx:DataGridColumn headerText=&quot;Name&quot; dataField=&quot;name&quot;/&gt;
					&lt;mx:DataGridColumn headerText=&quot;Amount&quot; dataField=&quot;amount&quot;/&gt;
					&lt;mx:DataGridColumn headerText=&quot;Stage&quot; dataField=&quot;stageName&quot;/&gt;
					&lt;mx:DataGridColumn headerText=&quot;Probability&quot; dataField=&quot;probability&quot;/&gt;
					&lt;mx:DataGridColumn headerText=&quot;Close Date&quot; dataField=&quot;closeDate&quot;/&gt;
					&lt;mx:DataGridColumn headerText=&quot;Order&quot; dataField=&quot;orderNumber&quot;/&gt;
				&lt;/mx:columns&gt;
			&lt;/mx:DataGrid&gt;
		&lt;/mx:Panel&gt;
	&lt;/mx:VBox&gt;
&nbsp;
&lt;/mx:Application&gt;</pre></td></tr></table></div>

<p>One last thing we need for the front end are Account and Opportunity value objects. Right-click the src folder and select New -&gt; ActionScript Class. Enter the class name as and click Finish. Add the following code to these classes. Notice that the code uses the [RemoteClass(alias=" com.appirio.Account")] annotation to map the ActionScript version of the Account class (Account.as) to the Java version (Account.java). As a result, Account objects returned by methods of the Java object in the service layer that are deserialized into instances of the ActionScript Account class automatically for you.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre class="java" style="font-family:monospace;">&nbsp;
package
{
	[Bindable]
	[RemoteClass(alias=&quot;com.appirio.Account&quot;)]
	public class Account
	{
&nbsp;
		public var id:String;
		public var name:String;
		public var city:String;
		public var state:String;
		public var phone:String;
		public var website:String;
&nbsp;
	}
}</pre></td></tr></table></div>


<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre class="java" style="font-family:monospace;">&nbsp;
package
{
	[Bindable]
	[RemoteClass(alias=&quot;com.appirio.Opportunity&quot;)]
	public class Opportunity
	{
&nbsp;
		public var id:String;
		public var name:String;
		public var amount:String;
		public var stageName:String;
		public var probability:String;
		public var closeDate:String;
		public var orderNumber:String;
		public var accountId:String;
&nbsp;
	}
}</pre></td></tr></table></div>

<p><strong>Server-Side Code</strong></p>
<p>So now back to the server-side to finish up our application. We need to add the POJOs to model our Account and Opportunity object returned from Salesforce.com. These classes will consist of the same members as the ActionScript classes so that GraniteDS can translate them back and forth for us.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
</pre></td><td class="code"><pre class="java" style="font-family:monospace;">&nbsp;
package com.appirio;
&nbsp;
public class Account {
&nbsp;
	private String id;
	private String name;
	private String city;
	private String state;
	private String phone;
	private String website;
&nbsp;
	public Account(String id, String name, String city, String state, String phone, String website) {
		this.id = id;
		this.name = name;
		this.city = city;
		this.state = state;
		this.phone = phone;
		this.website = website;
	}
&nbsp;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getState() {
		return state;
	}
	public void setState(String state) {
		this.state = state;
	}
	public String getPhone() {
		return phone;
	}
	public void setPhone(String phone) {
		this.phone = phone;
	}
	public String getWebsite() {
		return website;
	}
	public void setWebsite(String website) {
		this.website = website;
	}
}</pre></td></tr></table></div>


<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
</pre></td><td class="code"><pre class="java" style="font-family:monospace;">&nbsp;
package com.appirio;
&nbsp;
public class Opportunity {
&nbsp;
	private String id;
	private String name;
	private String amount;
	private String stageName;
	private String probability;
	private String closeDate;
	private String orderNumber;
	private String accountId;
&nbsp;
	public Opportunity(String id, String name, String amount, String stageName, String probability, String closeDate, String orderNumber) {
		this.id = id;
		this.name = name;
		this.amount = amount;
		this.stageName = stageName;
		this.probability = probability;
		this.closeDate = closeDate;
		this.orderNumber = orderNumber;
	}
&nbsp;
	public String getId() {
		return id;
	}
&nbsp;
	public void setId(String id) {
		this.id = id;
	}
&nbsp;
	public String getName() {
		return name;
	}
&nbsp;
	public void setName(String name) {
		this.name = name;
	}
&nbsp;
	public String getAmount() {
		return amount;
	}
&nbsp;
	public void setAmount(String amount) {
		this.amount = amount;
	}
&nbsp;
	public String getStageName() {
		return stageName;
	}
&nbsp;
	public void setStageName(String stageName) {
		this.stageName = stageName;
	}
&nbsp;
	public String getProbability() {
		return probability;
	}
&nbsp;
	public void setProbability(String probability) {
		this.probability = probability;
	}
&nbsp;
	public String getCloseDate() {
		return closeDate;
	}
&nbsp;
	public void setCloseDate(String closeDate) {
		this.closeDate = closeDate;
	}
&nbsp;
	public String getOrderNumber() {
		return orderNumber;
	}
&nbsp;
	public void setOrderNumber(String orderNumber) {
		this.orderNumber = orderNumber;
	}
&nbsp;
	public String getAccountId() {
		return accountId;
	}
&nbsp;
	public void setAccountId(String accountId) {
		this.accountId = accountId;
	}
&nbsp;
}</pre></td></tr></table></div>

<p>The last part of our application is the Gateway class that GraniteDS uses as the remoting endpoint. This class contains the methods that our Flex front-end calls via the RemoveObject tag.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
</pre></td><td class="code"><pre class="java" style="font-family:monospace;">&nbsp;
package com.appirio;
&nbsp;
import java.text.DateFormat;
import java.util.logging.Logger;
import java.util.Date;
import java.util.Vector;
&nbsp;
import com.sforce.soap.partner.Connector;
import com.sforce.soap.partner.PartnerConnection;
import com.sforce.soap.partner.QueryResult;
import com.sforce.ws.ConnectionException;
import com.sforce.ws.ConnectorConfig;
import com.sforce.soap.partner.sobject.SObject;
&nbsp;
public class Gateway {
&nbsp;
	private static final Logger log = Logger.getLogger(Gateway.class.getName());
	private String username = &quot;YOUR-SALESFORCE-USERNAME&quot;;
	private String password = &quot;YOUR-SALESFORCE-PASSWORD-AND-TOKEN&quot;;
	private PartnerConnection connection;
&nbsp;
	// query for 10 accounts in Salesforce.com
	public Vector&lt;account&gt; getAccounts() {
&nbsp;
		// get a new connection to Salesforce.com ising the Force.com Web Service Connector (WSC) toolkit
		getConnection();
&nbsp;
		QueryResult result = null;
		Vector&lt;account&gt; accounts = new Vector&lt;account&gt;();
&nbsp;
		try {
			result = connection.query(&quot;Select Id, Name, Phone, BillingCity, BillingState, website from Account LIMIT 10&quot;);
&nbsp;
			if (result.getSize() &gt; 0) {
&nbsp;
				for (SObject account : result.getRecords()) {
					Account a = new Account(
							(String)account.getField(&quot;Id&quot;),
							(String)account.getField(&quot;Name&quot;),
							(String)account.getField(&quot;BillingCity&quot;),
							(String)account.getField(&quot;BillingState&quot;),
							(String)account.getField(&quot;Phone&quot;),
							(String)account.getField(&quot;website&quot;)
						);
					accounts.add(a);
&nbsp;
				}
&nbsp;
			}
&nbsp;
		} catch (ConnectionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
&nbsp;
		return accounts;
&nbsp;
	}
&nbsp;
	// query for all opportunities for an account
	public Vector&lt;opportunity&gt; getOpportunities(String accountId) {
&nbsp;
		// get a new connection to Salesforce.com ising the Force.com Web Service Connector (WSC) toolkit
		getConnection();
&nbsp;
		QueryResult result = null;
		Vector&lt;opportunity&gt; opportunities = new Vector&lt;opportunity&gt;();
&nbsp;
		try {
			result = connection.query(&quot;Select id, name, stagename, amount, closeDate, probability, ordernumber__c from Opportunity where AccountId = '&quot;+accountId+&quot;'&quot;);
&nbsp;
			if (result.getSize() &gt; 0) {
&nbsp;
				for (SObject opp : result.getRecords()) {
					Opportunity o = new Opportunity(
							(String)opp.getField(&quot;Id&quot;),
							(String)opp.getField(&quot;Name&quot;),
							(String)opp.getField(&quot;Amount&quot;),
							(String)opp.getField(&quot;StageName&quot;),
							(String)opp.getField(&quot;Probability&quot;),
							(String)opp.getField(&quot;CloseDate&quot;),
							(String)opp.getField(&quot;OrderNumber__c&quot;)
						);
					opportunities.add(o);
				}
&nbsp;
			}
&nbsp;
		} catch (ConnectionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
&nbsp;
		return opportunities;
&nbsp;
	}
&nbsp;
	// create a new opportunity in Salesforce.com
	public Boolean createOpportunity(Opportunity o) {
&nbsp;
		// get a new connection to Salesforce.com ising the Force.com Web Service Connector (WSC) toolkit
		getConnection();
&nbsp;
		Boolean success = true;
		Date closeDate = new Date();
&nbsp;
		// try and parse the date
        try {
        	DateFormat df = DateFormat.getDateInstance(3);
        	closeDate = df.parse(o.getCloseDate());
        } catch(java.text.ParseException pe) {
            System.out.println(&quot;Exception &quot; + pe);
        }
&nbsp;
        // populate the new opportunity
        SObject opp = new SObject();
        opp.setType(&quot;Opportunity&quot;);
        opp.setField(&quot;Name&quot;, o.getName());
        opp.setField(&quot;Amount&quot;, new Double(o.getAmount()).doubleValue());
        opp.setField(&quot;StageName&quot;, o.getStageName());
        opp.setField(&quot;Probability&quot;, new Double(o.getProbability()).doubleValue());
        opp.setField(&quot;CloseDate&quot;, closeDate);
        opp.setField(&quot;OrderNumber__c&quot;, o.getOrderNumber());
        opp.setField(&quot;AccountId&quot;, o.getAccountId());
&nbsp;
        SObject[] opportunities = {opp};
&nbsp;
		try {
			connection.create(opportunities);
		} catch (ConnectionException e) {
			// TODO Auto-generated catch block
        	success = false;
			e.printStackTrace();
		}
&nbsp;
		return success;
&nbsp;
	}
&nbsp;
 	void getConnection() {
		try {
		   if ( connection == null ) {
			   log.info(&quot;Fetching new connection....&quot;);
			   // login to salesforce
			   ConnectorConfig config = new ConnectorConfig();
			   config.setUsername(username);
			   config.setPassword(password);
			   connection = Connector.newConnection(config);
		   } else {
			   log.info(&quot;Reusing existing connection....&quot;);
		   }
		} catch ( ConnectionException ce) {
			log.warning(&quot;ConnectionException &quot; +ce.getMessage());
		}
&nbsp;
	}
&nbsp;
}</pre></td></tr></table></div>

<p>Our last step before uploading our application to Google App Engine is to build and export for deployment. Right click on the project name in the left panel and select Export. Choose Flex Builder folder -&gt; Release Build -&gt; Next. In the Export to folder section browse to your war for the project. We want to build our Flex application to this folder so that the App Engine plug-in will deploy this code to App Engine along with our Java code.</p>
<p>Our last step it is to create a new App Engine application and upload our code to Google&#8217;s servers. There is detailed help for uploading with the Eclipse plug-in <a href="http://code.google.com/appengine/docs/java/tools/eclipse.html" target="_blank">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jeffdouglas.com/2009/08/20/rich-internet-applications-using-flex-salesforce-com-and-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Need Input for Demo App using GWT and GAE/J</title>
		<link>http://blog.jeffdouglas.com/2009/07/27/need-input-for-demo-app-using-gwt-and-gaej/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=need-input-for-demo-app-using-gwt-and-gaej</link>
		<comments>http://blog.jeffdouglas.com/2009/07/27/need-input-for-demo-app-using-gwt-and-gaej/#comments</comments>
		<pubDate>Mon, 27 Jul 2009 19:34:42 +0000</pubDate>
		<dc:creator>Jeff Douglas</dc:creator>
				<category><![CDATA[GAE/J]]></category>
		<category><![CDATA[GWT]]></category>
		<category><![CDATA[Google App Engine]]></category>

		<guid isPermaLink="false">http://blog.jeffdouglas.com/?p=1058</guid>
		<description><![CDATA[A couple of the guys and myself at Appirio are writing a beginning Google App Engine with Java book to be released this fall. We&#8217;ve locked down the functionality of the app we are going to be discussing in the book but I would like to put the app out to everyone and get any [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2009%2F07%2F27%2Fneed-input-for-demo-app-using-gwt-and-gaej%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.jeffdouglas.com%2F2009%2F07%2F27%2Fneed-input-for-demo-app-using-gwt-and-gaej%2F&amp;source=jeffdonthemic&amp;style=normal&amp;service=bit.ly&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>A couple of the guys and myself at Appirio are writing a beginning Google App Engine with Java book to be released this fall. We&#8217;ve locked down the functionality of the app we are going to be discussing in the book but I would like to put the app out to everyone and get any feedback regarding the user interaction.</p>
<p><strong>You can run the application at: </strong><a href="http://book-timeentry.appspot.com/" target="_blank"><strong>http://book-timeentry.appspot.com</strong></a></p>
<p><a href="http://book-timeentry.appspot.com/"><img title="timecard-app" src="http://blog.jeffdouglas.com/wp-content/uploads/2009/07/timecard-app.png" alt="timecard-app" width="544" height="235" /></a></p>
<p>The use case is fairly simple. This is a time card application where users login with their Google credentials and enter time against a project and milestone. The user&#8217;s time is persisted to BigTable and can be viewed by selecting the &#8220;Current Entries&#8221; tab.</p>
<p>Please let me know if you have any issues, recommendation or general feedback on functionality. This is a beginners level book so the app won&#8217;t go into too much depth. Any feedback would be greatly appreciated.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jeffdouglas.com/2009/07/27/need-input-for-demo-app-using-gwt-and-gaej/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

