Skip to content


echo " "; Profile Profile

Gmail Contextual Gadgets with GWT

Today we’ll create Gmail Contextual gadget with the help of GWT.
According to Google definition, Contextual gadget is “a gadget that is triggered by clues in Gmail, such as the contents of Subject lines and email messages. For example, Gmail already provides a YouTube contextual gadget. If the body of an email contains a link to a YouTube video, a clickable thumbnail view of the video appears at the bottom of the email.”

Gmail Contextual gadgets can work only with Google Apps for domain, you can’t set it up with your *@gmail.com account, because gadgets are available via Google Apps Marketplace only.

There are very good specs from Google about creating gadgets with pure JavaScript http://code.google.com/intl/en/apis/gmail/gadgets/contextual. I recommend to look through it, to understand, what we are doing.
GWT version has some differences with native JavaScript, we will take look at this.

Manifest

{code type=xml}








Demo Contextual Gadget
Simple GWT gadget



Email Body
google.com:EmailBodyExtractor






Demo Gmail Contextual Gadget

http://mozgoweb.com/gadget/CtxGadgetEntryPoint.gadget.xml



tag:google.com,2010:auth/contextual/extractor/BODY
Body


{/code}

I take only Body Extractor, you can add any other.
Now, we need to define extractors for GWT application, create Extractors.xml in the same folder, where placed your main class with following lines:{code type=xml}



google.com:EmailBodyExtractor


{/code}
To add multiply extractors use:{code type=xml}…

google.com:SenderEmailExtractor,google.com:RecipientEmailExtractor,google.com:EmailBodyExtractor


{/code}

GWT application code

{code type=php}
package com.mozgoweb.demo.client;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.gadgets.client.Gadget;
import com.google.gwt.gadgets.client.Gadget.AllowHtmlQuirksMode;
import com.google.gwt.gadgets.client.Gadget.InjectModulePrefs;
import com.google.gwt.gadgets.client.Gadget.ModulePrefs;
import com.google.gwt.gadgets.client.Gadget.UseLongManifestName;
import com.google.gwt.gadgets.client.GadgetFeature;
import com.google.gwt.gadgets.client.UserPreferences;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.ui.HTML;

/**
* Main entry point.
*
* @author Denis Lunev
*
* Twitter functionality based on “Cross-Site Scripting with GWT and Twitter”
* http://jectbd.com/?p=406 by Alex Moffat
*
*/
@ModulePrefs(title = “DemoGadget”, author = “Denis Lunev”, author_email = “den@mozogweb.com”)
@UseLongManifestName(false)
@AllowHtmlQuirksMode(false)
@GadgetFeature.FeatureName(value = “google.contentmatch”)
@InjectModulePrefs(files = {“Extractors.xml”})
public class CtxGadgetEntryPoint extends Gadget {
public CtxGadgetEntryPoint() {}

/*
For gadget use init() instead of onModuleLoad
*/
@Override
protected void init(UserPreferences preferences) {

final JsArray matches = jsGetContentMatches();
if (matches != null) {
final int numMatches = matches.length();
result = new JSONObject[numMatches];
for (int i = 0; i < numMatches; i++) { //Do something with email body matches.get(i); } } } /* Native JS method to invoke extractors */ native JsArray jsGetContentMatches() /*-{
return google.contentmatch.getContentMatches();
}-*/;
}
{/code}

Then build the project and open xml file with main class, in my case it’s build/web/com.mozgoweb.demo.gadget/CtxGadgetEntryPoint.gadget.xml
Make sure, that your extractors are presented and add view=”card” to Content tag:
{code type=xml}

{/code}
Card view is required for Contextual gadgets.
Now you need to upload builded gadgets (all files from folder, where located CtxGadgetEntryPoint.gadget.xml) to FTP or any other web storage, which we specified in Manifest.

Apps Marketplace

Login to Marketplace:
“My Vendor Profile” -> “Create a new listing”;
Check “My product may be directly installed into Google Apps domains”;
Fill “Application Manifest” with content from our Manifest.xml.

So, you’ve created application, now we need to connect it to domain.
Hit “Add it now” button and write your domain, then login with your domain admin credentials and activate application.
Thats all! Open any email and see how it works. Remember, that gadgets have very solid cache and it can take some time for your changes to be accepted, even with ?nogadgetcache=1 parameter.

My sample application searches words, starting with “@” in email body and shows twitter info for these users.

I used Twitter + GWT example by Alex Moffat for this Demo Gadget.
You can find source code here.
Enjoy!

Posted in Uncategorized. Tagged with , , .

Google App Engine plugin for Netbeans 6.9

I tried to find GAE plugin for netbeans 6.9, but there is only for 6.8. Last Netbeans version doesn’t work with plugin for 6.8, GAE projects just don’t open.
So, I built plugin from source, seems it works, at least project can be opened, built, deployed to App Engine.
You can download it here http://www.mozgoweb.com/netbeans-6.9-gae-plugin.zip

Posted in Uncategorized. Tagged with .

Jar to exe. Tools overview.

Well, you can run java application as: java -jar app.jar in console.
But I’m sure, you agree, that it’s not easy-to-use for users.
So, we can make exe from jar and run it as usual application. I found following tools for this:

JEXECreator
This tool costs $75 and has 30-day trial version.
Application converted fine, but when I run the application, there appears message box with thanking for using JEXECreator and suggesting to buy a full version.

Jar2Exe
Free version works only for console application, Standard Edition costs $14.95. Free trial period is 30 days.
Simple wizard, without many settings. Generated application doesn’t work, a dll is missing, but I didn’t search for it.

Launch4j
Open Source, BSD license. Crossplatform.
It has many settings for all needs. Minimal settings for working application are: exe/jar paths, minimal JRE version, type: console/GUI.
You can make launch4j config and generate exe automatically using ant or maven.

My choice is launche4j! It’s Open Source and allows to automate converting process.

Posted in Uncategorized. Tagged with , .

How to parse mime message using mime4j library

There is example how to use mime4j lib. See comments below.

package com.mozgoweb.mail.test;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import org.apache.james.mime4j.message.BinaryBody;
import org.apache.james.mime4j.message.BodyPart;
import org.apache.james.mime4j.message.Entity;
import org.apache.james.mime4j.message.Message;
import org.apache.james.mime4j.message.Multipart;
import org.apache.james.mime4j.message.TextBody;
import org.apache.james.mime4j.parser.Field;

/**
 *
 * @author Denis Lunev <den@mozgoweb.com>
 */

public class TestParser {

    private StringBuffer txtBody;
    private StringBuffer htmlBody;
    private ArrayList<BodyPart> attachments;

    /**
     *
     * @param fileName
     */
    public void parseMessage(String fileName) {
        FileInputStream fis = null;

        txtBody = new StringBuffer();
        htmlBody = new StringBuffer();
        attachments<BodyPart> = new ArrayList();

        try {
            //Get stream from file
            fis = new FileInputStream(fileName);
            //Create message with stream from file
            //If you want to parse String, you can use:
            //Message mimeMsg = new Message(new ByteArrayInputStream(mimeSource.getBytes()));
            Message mimeMsg = new Message(fis);

            //Get some standard headers
            System.out.println("To: " + mimeMsg.getTo().toString());
            System.out.println("From: " + mimeMsg.getFrom().toString());
            System.out.println("Subject: " + mimeMsg.getSubject());

            //Get custom header by name
            Field priorityFld = mimeMsg.getHeader().getField("X-Priority");
            //If header doesn't found it returns null
            if (priorityFld != null) {
                //Print header value
                System.out.println("Priority: " + priorityFld.getBody());
            }

            //If message contains many parts - parse all parts
            if (mimeMsg.isMultipart()) {
                Multipart multipart = (Multipart) mimeMsg.getBody();
                parseBodyParts(multipart);
            } else {
                //If it's single part message, just get text body
                String text = getTxtPart(mimeMsg);
                txtBody.append(text);
            }

            //Print text and HTML bodies
            System.out.println("Text body: " + txtBody.toString());
            System.out.println("Html body: " + htmlBody.toString());

            for (BodyPart attach : attachments) {
                String attName = attach.getFilename();
                //Create file with specified name
                FileOutputStream fos = new FileOutputStream(attName);
                try {
                    //Get attach stream, write it to file
                    BinaryBody bb = (BinaryBody) attach.getBody();
                    bb.writeTo(fos);
                } finally {
                    fos.close();
                }
            }

        } catch (IOException ex) {
            ex.fillInStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    /**
     * This method classifies bodyPart as text, html or attached file
     *
     * @param multipart
     * @throws IOException
     */
    private void parseBodyParts(Multipart multipart) throws IOException {
        for (BodyPart part : multipart.getBodyParts()) {
            if (part.isMimeType("text/plain")) {
                String txt = getTxtPart(part);
                txtBody.append(txt);
            } else if (part.isMimeType("text/html")) {
                String html = getTxtPart(part);
                htmlBody.append(html);
            } else if (part.getDispositionType() != null && !part.getDispositionType().equals("")) {
                //If DispositionType is null or empty, it means that it's multipart, not attached file
                attachments.add(part);
            }

            //If current part contains other, parse it again by recursion
            if (part.isMultipart()) {
                parseBodyParts((Multipart) part.getBody());
            }
        }
    }

    /**
     *
     * @param part
     * @return
     * @throws IOException
     */
    private String getTxtPart(Entity part) throws IOException {
        //Get content from body
        TextBody tb = (TextBody) part.getBody();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        tb.writeTo(baos);
        return new String(baos.toByteArray());
    }

    /**
     *
     * @param args
     */
    public static void main(String[] args) {

        String eml = "message.eml";

        TestParser parser = new TestParser();
        parser.parseMessage(eml);
    }
}

Posted in Uncategorized. Tagged with , .

How to fix GWT-ext: java.lang.OutOfMemoryError: Java heap space

If you try to run application with gwt-ext on Netbeans, you can face memory problem: GWT-ext: java.lang.OutOfMemoryError: Java heap space
We need to increase java heap for the project.
Simple to fix it:
in %project_home/nbproject/build-gwt.xml after this line:

<arg value="${gwt.module}"/>

just put following (you can put more, then 512mb):

<jvmarg value="-Xmx512m"/>

Enjoy!

Posted in Uncategorized. Tagged with .

How to switch off axis logging?

If you working with axis, you know, that it generates huge batch of debug/logging information into console or log file.

You can switch it off just putting log4j.logger.org.apache=ERROR into log4j.properties.

Enjoy!

Posted in Uncategorized. Tagged with .