Skip to main content

Tweet Magnolia Tweet

Posted by rah003 on February 22, 2010 at 1:07 PM PST

Originally, I wanted to write about Magnolia-Twitter integration, but at some point I've realized such integration is so simple that there's no point writing about it. So instead, let's look how you can write custom commands for Magnolia.

Anybody who seriously developed on Magnolia for while had to write a Command sooner or later. They are incredibly simple, yet very powerful. Part of it is that you can use commands just anywhere - call them from the context Menu in AdminCentral, invoke them from Groovy or BSH console, call them inside of the workflow, kick start them based on Observation and changes in repository or call them periodically via scheduler.

The whole logic of commands and their execution is based on Apache commons-chain. But you don't need to worry about that at all. The only things you need to know are:

  • Create your command by extending MgnlCommand or one of its subclasses.
  • Command instances are pooled and reused so you can do heavy initialization in constructor and be sure that it will be called just once per instance of command.
  • Any single instance of command is guarantied to be called by one thread only at any given time.
  • Since the commands are pooled and reused, it is important for custom commands to release all unnecessary resources in the release() method to prevent allocation of unnecessary memory, but more importantly to make sure status possibly held by the command instance is not transferred between command instance usages.
  • Content2Bean is used during command instanciation so configuration values can be set for commands via Admin Central.
  • Each module can define its own commands in the config:/modules//commands node, optionally modules can also define their own catalogs for commands. The catalogs names can be anything as long as they are unique and not claimed by other modules yet.
  • And that's really all. Now let's have a look how a simple TweetCommand shown below can look like and where it can be used.

Now lets look at the example of TweetCommand - it's really simple. All you need to do is to provide it your username and password - either in the command configuration node to have a fixed account used or you can provide it dynamically, in the workflow or you can read it from the account of the user invoking the command. Then it will just call twitter using its api to post the message. As a little plus it will try to run the url through the Tinyurl API to shorten it.

Extend the MgnlCommand

public class TweetCommand extends MgnlCommand {

create properties with appropriate setters and getter for anything that you want to have pre-set for you by Magnolia

    private String twitterPassword;
    private String twitterUser;
    private String path;

implement the execute() method

    @Override
    public boolean execute(Context context) throws Exception {

alternatively use the context to read any other property for which you don't have a setter/getter directly, if necessary

        final String repository = (String) context.get(Context.ATTRIBUTE_REPOSITORY);

as promissed, the Twitter integration is really the simplest bit of the whole - just create the tweet and post it

        TwitterFactory factory = new TwitterFactory();
        Twitter twitter = factory.getInstance(this.twitterUser, this.twitterPassword);
        Status status = twitter.updateStatus(this.tweet);

you might also use Tinyurl to shorten the url if you want to post link in your tweet  

    private String shortenUrl(String shorten) {
        final HttpClient client = new HttpClient();
        final GetMethod method = new GetMethod("http://tinyurl.com/api-create.php?url=" + shorten);
        try {
          final int statusCode = client.executeMethod(method);
          if (statusCode != HttpStatus.SC_OK) {
              log.error("Failed to shorten url {} with tinyurl response code: {}", shorten, statusCode);
              return shorten;
          }
          final byte[] responseBody = method.getResponseBody();
          return new String(responseBody);

        } catch (IOException e) {
            log.error("Failed to shorten url {} with exception: {}", shorten, e.getLocalizedMessage());
        } finally {
          method.releaseConnection();
        }
        return shorten;
    }

of course you can also get the full TweetCommand code if you want to.

 

And to be complete, here's the bit to get the twitter4j directly from central repo: 

<dependency>
           <groupId>net.homeip.yusuke</groupId>
           <artifactId>twitter4j-core</artifactId>
           <version>[2.1,)</version>
</dependency>

 

Now that was all for the command creation, here's also the bit of the workflow definition that to invoke the command on activation:

<!-- uncomment if you want to tweet just a simple text --> 
<!-- set field="tweet" value="${userName} activated some content"/--> 

<!-- or uncomment this to tweet the activation comment: -->
<!-- set field="tweet" field-value="comment"/--> 

<!-- or leave it as is to have the command attempt assemble the URL of the activated content and tweet that -->
<participant ref="command-tweet"/>

The above piece of workflow definition (complete listing here) will work under the assumption, you register the command unter the name "tweet" under the path config:/modules//commands. The basic definition contains just the command name and the class name, so it should be simple enough to set it up. If you are not sure, there are other commands defined for example by adminInterface module so you can look it up there. How to register a command in Magnolia and how to use command chain is also described in the user manual.

Any of the properties of the command can be also set directly in the command definition, which I would recommend using for setting up the username, password and public instance base URL.

While the above shows you how to get your command called from the workflow, you can also check the Observation module documentation to see how to use such command from observation, and if there is enough interest I might show up here how to add an entry into context menu of the tree to invoke a command from there.

Enjoy.

 

Comments

Keep password as char[]

Jan,

You should consider keeping the password in a char[] instead of a String, cf. JPasswordField.

http://java.sun.com/javase/6/docs/api/javax/swing/JPasswordField.html#getPassword()

http://java.sun.com/docs/books/tutorial/uiswing/components/passwordfield.html

Karl