I’m Addicted to Play! on Heroku

October 25th, 2011

I’ve been using Play! since Dreamforce 11 when Heroku announced support for it and I have to say that I’m addicted to it. If you love Java (and who doesn’t) but hate developing Java applications with all of the crap that goes along with it (Maven, XML config files, deployments, etc.) then the Play! framework my just be your savior.

I did a small demo with salesforce.com integration with Heroku and Play! if you would like some sample code or to run it for yourself.

Here’s what I like the most about Play!: it makes me productive. The development style is similar to Rails and I can simply get stuff done. I don’t have to save my Java code, restart Tomcat, wait for Hibernate to fire up and then see if my code runs as expected. With Play! I make my modifications, hit refresh in the browser and presto! my new app is either up and running or there is a nice, pretty error message.

Running Play! is a breeze. The framework comes bundled with Jetty so I don’t have to install and maintain Tomcat locally and when I’m ready to put the app into production, I just push it to Heroku and they take care of the rest.

If you want a great intro to Play! and why you might to use it, check out the first 10 minutes of this video from Dreamforce 11, “Introducing Play! Framework: Painless Java and Scala Web Applications”. The reset of the preso gets into actually building an app so if you want to see some code, feel free to continue watching.

Categories: Heroku, Play!, Salesforce

No Comments

Video – Debugging Apex Tools and Techniques (DF11)

October 24th, 2011

The new System Log (aka Apex CSI) is a cool new part of Winter 12 that significantly reduces the pain of debugging Force.com applications. This is a great video from Dreamforce 11 showing you how to use the new console to debug code, set “breakpoints” and examine heap variables and more.

Categories: Apex, Salesforce

No Comments

Getting Salesforce Field Metadata the Easy Way

October 20th, 2011

I’m working on some of the Apex REST services for our CloudSpokes org and needed some code to fetch field level metadata using Apex Describe. I poked around and realized that there isn’t really much out there. So I decided to write something up and hopefully people find it useful or instructional. Perhaps it should be part of apex-lang?

If you’ve ever worked with Apex Describe before you’ll quickly realize that it’s not the easiest thing to work with. You’ll want to take a peek at the docs. Don’t get me wrong, it’s power, fast and very handy. But it is rather confusing and cumbersome to work with at first. So I wanted some code that returns the metadata for specific fields in an object so that I could look at field types and lengths and perform “stuff” accordingly.

So here’s what I can up with. You pass the method the DescribeFieldResult for an object and a collection of fields that you want metadata for. The method returns a map where the field names are the keys and the values contain the metadata for the corresponding field. It looks pretty hard but that’s the great thing about utility methods, they encapsulate and hide the complexity of the implementation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static Map<String, Schema.DescribeFieldResult> getFieldMetaData(
  Schema.DescribeSObjectResult dsor, Set<String> fields) {
 
  // the map to be returned with the final data
  Map<String,Schema.DescribeFieldResult> finalMap = 
    new Map<String, Schema.DescribeFieldResult>();
  // map of all fields in the object
  Map<String, Schema.SObjectField> objectFields = dsor.fields.getMap();
 
  // iterate over the requested fields and get the describe info for each one. 
  // add it to a map with field name as key
  for(String field : fields){
    // skip fields that are not part of the object
    if (objectFields.containsKey(field)) {
      Schema.DescribeFieldResult dr = objectFields.get(field).getDescribe();
      // add the results to the map to be returned
      finalMap.put(field, dr); 
    }
  }
  return finalMap;
}

So now in my code I can call this static method in my Utils class and access the field metadata easily by fetching it from the map by it’s field name:

1
2
3
4
5
6
7
8
9
10
11
12
// field to return -- skips fields not actually part of the sobject
Set<String> fields = new Set<String>{'name','annualrevenue','BADFIELD'};
 
Map<String, Schema.DescribeFieldResult> finalMap = 
  Utils.getFieldMetaData(Account.getSObjectType().getDescribe(), fields);
 
// only print out the 'good' fields
for (String field : new Set<String>{'name','annualrevenue'}) {
  System.debug(finalMap.get(field).getName()); // field name
  System.debug(finalMap.get(field).getType()); // field type
  System.debug(finalMap.get(field).getLength()); // field length
}

So if you run this, you’ll see something like:

DEBUG|Name
DEBUG|STRING
DEBUG|255
DEBUG|AnnualRevenue
DEBUG|CURRENCY
DEBUG|0

Categories: Code Sample, Salesforce

1 Comment

Demo App – Salesforce on Heroku (Java) with Play! Framework

September 26th, 2011

At Dreamforce a couple of weeks ago, Heroku announced the public beta for the Play! Framework on their cedar stack. So if you love Java but hate the pain associated with developing web apps, then the Play Framework is for you. It’s essentially a Ruby on Rails framework for Java. It makes Java development fun again!

To get started, check out the Play! on Heroku blog post as it has everything you need to get started with a simple Hello World app. There’s also a Getting Started with Play! on Heroku/Cedar article for more info.

Once again, this is a demo Java app running on Heroku using the Play! Framework. It uses the Force.com Web Service Connector (WSC) and the Partner jar to connect to a DE salesforce.com org. It uses the web services API to query for records, retrieve records to display, create new records and update existing ones. It should be good fodder for anyone wanting to start out with Play! and Force.com.

You can run the app for yourself here.

All of the code for this app is at github so fork awey. I’ve pulled out some of the more important parts of the app for a quick peek. There’s also an overview of one of the important classes to give you something to look at.

app/controllers/Account.java

The account controller contains all of the business logic for integration with Force.com and then packages up the returns for the views.

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
package controllers;
 
import play.*;
import play.mvc.*;
import java.util.*;
 
import models.*;
import service.*;
import com.sforce.soap.partner.*;
import com.sforce.ws.ConnectionException;
import com.sforce.soap.partner.sobject.SObject;
 
public class Account extends Controller {
 
  public static void create() {
    render();
  }
 
  // create the record in salesforce
  public static void createSubmit() {
 
    SaveResult[] results = null;
 
    // populate the new opportunity
    SObject a = new SObject();
    a.setType("Account");
    a.setField("Name", params.get("name"));
    a.setField("BillingCity", params.get("city"));
    a.setField("BillingState", params.get("state"));
    a.setField("Phone", params.get("phone"));
    a.setField("Website", params.get("website"));
 
    SObject[] accounts = {a};
 
    // get a reference to the connection
    PartnerConnection connection = ConnectionManager.getConnectionManager().getConnection();
 
    try {
      results = connection.create(accounts);
 
      // check for any errors
      if ( results[0].isSuccess() ) {
         System.out.println("Successfully created Account: " + results[0].getId());
      } else {
         System.out.println("Error: could not create Accout: " + results[0].getErrors()[0].getMessage());
      }
 
    } catch (ConnectionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
 
    redirect("/account/"+results[0].getId() );
  }
 
  // fetch the account and all related opportunities to display
  public static void display() {
 
    if (params.get("id") != null) {
 
      // add the account id to the array to be retrieved
      String[] accountIds = { params.get("id") };
 
      SObject[] accounts = null;
      QueryResult result = null;
 
      // get a reference to the connection
      PartnerConnection connection = ConnectionManager.getConnectionManager().getConnection();
 
      try {
        accounts = connection.retrieve("Id, Name, Phone, BillingCity, BillingState, Website","Account", accountIds);
        result = connection.query("Select id, name, stagename, amount, closeDate, probability, ordernumber__c from Opportunity where AccountId = '"+params.get("id")+"'");
      } catch (ConnectionException e) {
        e.printStackTrace();
      } catch (NullPointerException npe) {
        System.out.println("NullPointerException: "+npe.getCause().toString());
      }
 
      renderArgs.put("account", accounts[0]);
      renderArgs.put("opportunities", result.getRecords());
 
    }
 
    render();
  }
 
  // fetch the account so we can show the edit form with data
  public static void edit() {
 
    if (params.get("id") != null) {
 
      // add the account id to the array to be retrieved
      String[] accountIds = { params.get("id") };
      SObject[] accounts = null;
 
      // get a reference to the connection
      PartnerConnection connection = ConnectionManager.getConnectionManager().getConnection();
 
      try {
        accounts = connection.retrieve("Id, Name, Phone, BillingCity, BillingState, Website","Account", accountIds);
      } catch (ConnectionException e) {
        e.printStackTrace();
      } catch (NullPointerException npe) {
        System.out.println("NullPointerException: "+npe.getCause().toString());
      }
 
      renderArgs.put("account", accounts[0]);
 
    }
 
    render();
  }
 
  // update the record in salesforce
  public static void editSubmit() {
 
    SaveResult[] results = null;
 
    // populate the account to update
    SObject a = new SObject();
    a.setType("Account");
    a.setId(params.get("id"));
    a.setField("Billingcity", params.get("BillingCity"));
 
    SObject[] accounts = {a};
 
    // get a reference to the connection
    PartnerConnection connection = ConnectionManager.getConnectionManager().getConnection();
 
    try {
      results = connection.update(accounts);
 
      // check for any errors
      if ( results[0].isSuccess() ) {
         System.out.println("Successfully updated Account: " + results[0].getId());
      } else {
         System.out.println("Error: could not update Account: " + results[0].getErrors()[0].getMessage());
      }
 
    } catch (ConnectionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
 
    redirect("/account/"+params.get("id") );
  }
 
}

app/views/display.html

The account display view displays the account details and any related opportunities.

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
#{extends 'main.html' /}
#{set title:'Account Details' /}
 
<span class="nav"><a href="/opportunity">Search</a></span><p/>
<span class="title">Account Display</span>
<p/>
 
<table id="fancytable">
  <tr>
    <td class="label">Name</td>
    <td>${ account.getField("Name") }</td>
  </tr>
  <tr>
    <td class="label">City</td>
    <td>${ account.getField("BillingCity") }</td>
  </tr>
  <tr>
    <td class="label">State</td>
    <td>${ account.getField("BillingState") }</td>
  </tr>
  <tr>
    <td class="label">Phone</td>
    <td>${ account.getField("Phone") }</td>
  </tr>
  <tr>
    <td class="label">Website</td>
    <td>${ account.getField("Website") }</td>
  </tr>  
</table>
 
<br><a href="/opportunity/create/${ account.getField("Id") }">Create a new Opportunity</a> 
or <a href="/account/${ account.getField("Id") }/edit">edit this Account</a><p/>  
 
%{ if (opportunities.length > 0) { }%
 
  <p/><span class="heading">Opportunities for ${ account.getField("Name") }</span><br><p/>
 
  <table id="fancytable">
  <tr>
    <td class="label">Name</td>
    <td class="label">Amount</td>
    <td class="label">Stage</td>
    <td class="label">Probability</td>
    <td class="label">Close Date</td>
    <td class="label">Order</td>
  </tr>
  #{list items:opportunities, as:'o' }
    <tr>
      <td nowrap>${ o.getField("Name") }</td>
      <td>$${ o.getField("Amount") }</td>
      <td>${ o.getField("StageName") }</td>
      <td>${ o.getField("Probability") }</td>
      <td>${ o.getField("CloseDate") }</td>
      <td>${ o.getField("OrderNumber__c") }</td>
    </tr>
  #{/list}
  </table>
 
%{ } else { }%
  <p/><span class="heading">No Opportunities found for ${ account.getField("Name") }</span>
%{ } }%

Categories: Heroku, Salesforce

3 Comments

Video – Connect Your Clouds with Force.com from Dreamforce 11

September 24th, 2011

Here’s the video from my Dreamforce 11 session!

Abstract: As more and more applications move to the Cloud, Force.com integration is no longer just about connecting with on-premise systems and databases. You now also need to effectively connect with other cloud platforms. This session will show you how to integrate your Force.com application with other cloud platforms such as Amazon’s S3 storage, Google App Engine, and Microsoft Azure. The session will feature demos and code walkthroughs of the various cloud integrations.

Categories: Salesforce

No Comments

Twilio Client for Force.com BETA

September 24th, 2011

Twilio just released a new beta version of their Apex toolkit. You can fork it here on GitHub. It includes some new stuff, mainly the Capabilities class for Twilio Client. The majority of it was written by Kyle Roche so it’s pretty top notch stuff.

If you are interested in getting your hands dirty with the new toolkit, we have a Twilio Client for Salesforce Customer Portal development challenge over at CloudSpokes with $1000 up for grabs.

Categories: Salesforce, Twilio

No Comments

Session Recap – Connect Your Clouds with Force.com

September 3rd, 2011

Dreamforce 11 is over and I can finally catch my breath. Despite the un-godly start time of 8:30am Friday morning, my session went fairly well. I had a good turnout and some interesting questions.

I’ve posted the PDF of my deck to the session’s Chatter group, but it may be easier to access them from my blog. Here is the PDF and PPT version of my presentation along with the source code.

Categories: Salesforce

2 Comments

It’s Official! Heroku Loves Java

August 26th, 2011

It’s official! The Heroku blog proudly announced a couple of hours ago that “Java is the fourth official language available on the Cedar stack”. There’s even a nifty little “heroku for java in 5 minutes tutorial” that you can walk through to get up and running.

Categories: Heroku, Salesforce

No Comments

Build a Bulk Emailer for Salesforce with App Engine

August 25th, 2011

Sometimes you just want to send a crapload of email from Salesforce.com. However, like every PaaS platform there are limits baked into the multi-tenant environment so you don’t stomp on other tenants’ resources. Salesforce.com limits you to 2000 emails per day for each Salesforce license. So if you don’t have a lot of Salesforce licenses or a different kind of license, you may be out of luck if you want to send out large volumes. There are a few AppExchange products but they seem more targeted towards marketing purposes.

Google App Engine may be a good solution in this case. With Google App Engine quotas you get 7,000 Mail API calls per day free and can bump that up as high as 1.7M with a paid account.

Here’s how to roll your own basic bulk emailer using Google App Engine. Take a look at the video below, but it essentially queries Force.com for records, uses the Google Mail Java API to send out individual emails and then send an administrator a notification via Jabber (Google Talk). You can schedule your application (essentially a Servlet) to run on a timed basis using the cron service.

All of this code is at this GitHub repo.

Most of the important code is in the MailServlet class.

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
package com.jeffdouglas.emailer;
 
[removed imports]
 
/**
 * MailServlet.java - a simple, schedulable servlet for sending mail
 * with salesforce.com
 * @author Jeff Douglas
 * @version 1.0
 * @see http://code.google.com/appengine/docs/java/mail/overview.html
 * for more details on using Mail with App Engine
 */
 
@SuppressWarnings("serial")
public class MailServlet extends HttpServlet {
 
    private static final Logger logger = Logger.getLogger(ConnectionManager.class.getName());
    private String jabberRecipient = "jeffdonthemic@gmail.com";
 
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
 
        resp.setContentType("text/html");
        String mailerMsg = "No contact found to email!!";
        QueryResult result = null;
 
        // get a reference to the salesforce connection
        PartnerConnection connection = ConnectionManager.getConnectionManager().getConnection();
 
        try {
            // query for contacts based upon some criteria -- emailNotSent boolean
            result = connection.query("Select Id, FirstName, LastName, Email " +
            		"FROM Contact Where Email = 'jeff@jeffdouglas.com' Limit 1");
        } catch (ConnectionException e) {
            e.printStackTrace();
            logger.severe(e.getCause().toString());
        } catch (NullPointerException npe) {
            npe.printStackTrace();
            logger.severe(npe.getCause().toString());
        }
 
        // if records were returned then send out email
        if (result != null) {
 
          for (SObject contact : result.getRecords()) {
 
            // construct the 'name' for the email recipient
            String contactName = contact.getField("FirstName").toString() + " " + 
              contact.getField("LastName").toString();
 
            logger.info("Sending emil to " + contactName + " at " + 
              contact.getField("Email").toString());
 
            /// send the email
            mailerMsg = sendMail(contact.getField("Email").toString(), contactName);
 
            // send a jabber notification of the status
            sendJabberNotification(jabberRecipient, mailerMsg);
 
          }
 
          // TODO - make a call back into salesforce and update these records
          // as having their emails sent. Implementation is up to you.
 
        } else {
          logger.warning("No results returned from salesforce");
        }
 
        resp.getOutputStream().println(mailerMsg);
 
    }  
 
    /**  
     * Sends an email
     * @param toAddress the email address of the recipient
     * @param toName the name that appears for the recipeint in their email client  
     * @return A String representing the status of the email sent  
     */  
    private String sendMail(String toAddress, String toName) {
 
      String msg = "Email sent successfully to " + toAddress;
      Properties props = new Properties();
      Session session = Session.getDefaultInstance(props, null); 
 
      String messageBody = "This is the body of my email";
 
      try {
 
          Message emailMessage = new MimeMessage(session);
          //  must be the email address of an administrator for the application. see docs
          emailMessage.setFrom(new InternetAddress("jeffdonthemic@gmail.com","Jeff Douglas"));
          emailMessage.addRecipient(Message.RecipientType.TO, 
            new InternetAddress(toAddress, toName));
          emailMessage.setSubject("My Email Subject");
          emailMessage.setText(messageBody);
          Transport.send(emailMessage);
 
      } catch (AddressException e) {
          msg = e.toString();
      } catch (MessagingException e) {
          msg = e.toString();
      } catch (UnsupportedEncodingException e) {
          msg = e.toString();
      }
 
      return msg;
    }
 
    /**  
     * Sends a message to any XMPP-compatible chat messaging service (google talk). 
     * See http://code.google.com/appengine/docs/java/xmpp/overview.html
     * for more detils
     * @param recipient the jid of the jabber recipient of the notification
     * @param msgBody the body of the message to be sent  
     */  
    private void sendJabberNotification(String recipient, String msgBody) {
 
      JID jid = new JID(recipient);
 
      com.google.appengine.api.xmpp.Message msg = new MessageBuilder()
          .withRecipientJids(jid)
          .withBody(msgBody)
          .build();
 
      boolean messageSent = false;
      XMPPService xmpp = XMPPServiceFactory.getXMPPService();
 
      if (xmpp.getPresence(jid).isAvailable()) {
          SendResponse status = xmpp.sendMessage(msg);
          messageSent = (status.getStatusMap().get(jid) == SendResponse.Status.SUCCESS);
      }
 
      logger.info("Jabber notifiation sent: " + messageSent);
 
    }
 
}

Categories: Google App Engine, Salesforce

3 Comments

How I’m Getting Ready for Dreamforce

August 23rd, 2011

Categories: Salesforce

1 Comment

Feed

http://blog.jeffdouglas.com /

WordPress Appliance - Powered by TurnKey Linux