Use Mule to sync WordPress sites

11 Jul

I recently thought about to create a process with which I could keep two instances of WordPress in sync. I wanted to see if I could do this with Mule. I came up with the following solution:

  • Receive the RSS feed of a new post in Mule flow
  • Use the content of the RSS feed to post to the other wordpress instance by using XML-RPC
  • Have it running on Mule iON so I don’t have to setup my own Muleserver


So lets start a new Mule iON project. Since I am a big fan of Netbeans I use the Maven based approach (I must admit I have tried to use MuleStudio for this but it didn’t bring me the productivityboost I hoped for).
I created the Mule project with:

mvn org.mule.tools:mule-project-archetype:create -DartifactId=wordpress-copy-service -DgroupId=net.pascalalma.mule-ion -Dinteractive=false -Dmodules=rss -DmuleVersion=3.1.1

The Mule config looks like:

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:http="http://www.mulesoft.org/schema/mule/http"
      xmlns:rss="http://www.mulesoft.org/schema/mule/rss"
      xmlns:spring="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="
        http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/3.1/mule.xsd
        http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/3.1/mule-http.xsd
        http://www.mulesoft.org/schema/mule/rss http://www.mulesoft.org/schema/mule/rss/3.1/mule-rss.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <spring:bean name="xmlRpcHelper" class="net.pascalalma.rss.XmlRpcHelper">
        <spring:constructor-arg value="http://www.pragmatic-integrators.com/xmlrpc.php" type="java.lang.String"/>
        <spring:constructor-arg value="UserName" type="java.lang.String"/>
        <spring:constructor-arg value="Password" type="java.lang.String"/>
    </spring:bean>
    <spring:bean name="entryReceiver" class="net.pascalalma.rss.EntryReceiver">
        <spring:constructor-arg ref="xmlRpcHelper"/>
    </spring:bean>
    <flow name="poll-and-post">
        <poll frequency="36000000">
            <http:outbound-endpoint address="http://www.redstream.nl/feed/" />
        </poll>
        <rss:feed-splitter />
        <component>
            <spring-object bean="entryReceiver"/>
        </component>
    </flow>
</mule>

As you can see I poll the website ‘www.redstream.nl’ once each 10 hours. The RSS feed I receive is split and sent to my custom made component ‘entryReceiver’. It would be nice to use a XML-RPC endpoint here but this isn’t created yet (and actually I don’t feel like it neither).

The EntryReceiver looks like:

package net.pascalalma.rss;

import org.mule.api.annotations.param.Payload;
import com.sun.syndication.feed.synd.SyndEntry;

public class EntryReceiver {

    XmlRpcHelper xmlRpcHelper = null;

    public EntryReceiver(XmlRpcHelper helper) {
        xmlRpcHelper = helper;
    }
    public void readEntry(@Payload SyndEntry entry) throws Exception {

        // Get the title
        String title = entry.getTitle();

        // Check if title exist in current blogs on the new blogsite
       if (!xmlRpcHelper.titleExists(title)) {
            xmlRpcHelper.post(entry);

        } else {
            // Skip this post
        }
    }
}

Disclaimer: just wanted to see if it worked, I did not try to build a robust solution that had to work for every WordPress site, so you are warned!

And the bean where the dirty work is done:

package net.pascalalma.rss;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;

import com.sun.syndication.feed.synd.SyndContentImpl;
import com.sun.syndication.feed.synd.SyndEntry;

public class XmlRpcHelper {

    private String url;
    private String userName;
    private String password;
    XmlRpcClient client = new XmlRpcClient();

    public XmlRpcHelper(String url, String u, String p) {
        this.url = url;
        this.userName = u;
        this.password = p;

        XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
        try {
            config.setServerURL(new URL(url));
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        client.setConfig(config);
    }

    public boolean titleExists(String title) {
        return getRecentTitles().contains(title);
    }

    public void post(SyndEntry entry) {
        StringBuilder content = new StringBuilder("<content><title>").append(entry.getTitle()).append("</title>");
        List<SyndContentImpl> contents = entry.getContents();

        for (SyndContentImpl syndContent : contents) {
            content.append(syndContent.getValue());
        }
        content.append("</content>");
        try {
            client.execute("blogger.newPost", new Object[]{"bla", 1,
                        userName, password, content.toString(), true});
        } catch (XmlRpcException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private List<String> getRecentTitles() {
        List<String> titles = new ArrayList<String>();
        try {
            Object token = client.execute("blogger.getRecentPosts", new Object[]{"bla", 1,
                        userName, password, 10});

            Object[] result = (Object[]) token;
            for (int i = 0; i < result.length; i++) {
                titles.add(getTitle((HashMap<String, Object>) result[i]));
            }
        } catch (XmlRpcException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            // return null;
        }
        return titles;
    }

    private static String getTitle(Map response) {
        String title = null;
        String content = (String) response.get("content");

        if (content != null) {
            title = content.substring(content.indexOf("<title>") + 7,
                    content.indexOf("</title>"));
        }
        return title;
    }
}

One thing that kept me busy for a while was that the first String in the Object array that is supplied with the XmlRpc call may contain anything as long as it contains something. An empty String “” is ignored in the call and will lead to an invalid Username/Password message.

If all this is in place you can build the project with: ‘mvn clean install’. This will create a zip file in the target directory. This zip file can then be uploaded with your dashboard to your Mule iON instance and that’s it! As you can see the two sites ‘www.redstream.nl’ and ‘www.pragmatic-integrators.com’ are know kept in sync (with a max. gap of 10 hrs). Of course the copied post is not an exact clone of the original (I am loosing info about the Author, Categories, etc. and images are referred to at the original location) but it shows that the main idea works!

Follow

Get every new post delivered to your Inbox.

Join 111 other followers

%d bloggers like this: