Our Blog

Let us find tech solutions together

Apr 15

Here at Mystic, we do a lot of remote work using IRC. And due to the heavy use of best of breed tools under OSX and Linux, we find ourselves working in a shell a lot of the day, along with our IDE. We like to keep in the Flow state for as much of the time working as possible, so we turn off things like email notifications, twitter popups, and other such annoyances.

Since our communication is important, but we don’t want it to be distracting, going back and forth to an IRC window became, tedious. After searching high and low, we have what appears to be a pretty nice solution to the problem at hand. It uses SSH tunneling, socat, Growl for notifications, screen for staying online, and Weechat as the irc client. Any client that uses Net::Growl should perform adequately, Weechat isn’t a requirement, just the console-based irc client we prefer.

Initial setup

A couple of required items before we begin:

  • A Mac with Growl installed (the basics would have to be adapted for growl-like notifiers in linux and windows)
  • An unix-based outside shell account with ssh access and Weechat (or irssi) installed

Setting up the Mac side

Growl

If you don’t have Growl already installed on your Mac, Get it! Open System Preferences and select Growl from under the “Other” panel. After Growl’s preference pane comes up, select the Network tab.

Growl Network Tab

What you’ll need checked here, is “Listen for incoming notifications” and “Allow remote application registration” along with entering in your super-secret password.

socat on the Mac

First thing we’re going to assume, is that you have MacPorts installed. Ready? If it’s not already installed (it probably won’t be), open up Terminal and execute:

sudo port install socat

Follow along the prompts to get that installed. And with that Terminal open, assuming you’ll be using TCP port 9999 to reverse-proxy the Growl data, execute:

socat TCP-LISTEN:9999,reuseaddr,fork UDP:127.0.0.1:9887 &

What this does, is setup a forward so that any packets hitting TCP port 9999 will proxy to UDP port 9887 (the magic Growl UDP port). I would suggest, that you setup this command as an alias in your ~/.bashrc or similar for your shell.

Now we’ll setup our reverse-proxy with SSH and our shell that we run Weechat on:

ssh myserver -R 9999:127.0.0.1:9999

What this command says is, setup a reverse proxy with myserver so all traffic on TCP port 9999 is transmitted and received over our secure SSH connection. We’re also assuming with this command, that you’ve set up SSH certificate authentication, which is really handy for logging into any unix-based servers with SSH.

Setting up the Linux side

socat on Linux

Since there are many many flavors of Linux, I’ll tailor the discussion assuming whatever distro you’re using, it has apt-get or similar. As with the Mac, we do a simple

apt-get install socat

Follow along the prompts to get that installed. And now we’ll setup socat to proxy in the opposite direction

socat UDP-LISTEN:9887,reuseaddr,fork TCP:127.0.0.1:9999 &

And this will listen for UDP connections on 9887 and proxy those through TCP 9999. If you’re really astute, you’ll realize, that we’ve just completed the networking end-to-end. And again, I would set the above socat command up in a .bashrc or similar.

Weechat Setup

When we run Weechat, we like to always be on, which is the biggest reason for having it running in a Linux shell. To that end, we have screen running on the server with Weechat always-on. This is quite simple, after logging into your linux shell:

% screen
% weechat-curses

When we’re not on the shell or in irc, we detach from that screen session using Ctrl-A + D and exit. Our IRC session is always running, and we can get back to it with:

screen -r

If you have multiple screens, or accidentally killed your shell without properly detaching, we’re going to assume that you can man screen and read.

We took many clues and a lot of sample code from growl-notify, modified it heavily and made changes to support Net::Growl. So the next step is to head over here and download growl-net-notify.pl into your ~/.weechat/perl or ~/.weechat/perl/autoload directory.

[EDIT] Now wait a minute, maybe you’re not groking what Net::Growl is, and the other dependency, Parse::IRC. No problem, just make sure you have perl installed, and type:

% cpan -i Net::Growl
% cpan -i Parse::IRC

If this is your first time using cpan, you’ll have a bunch of questions to answer, most of the defaults in here are okay.

After you’ve loaded the script, you’ll need to run the setup:

/growl setup [host] [password] [port]

You’ll have to enter at least host and password, port is already the default 9887. To test that it works, use the /growl test [message] command.

Let us know either on IRC or via our Contact page if everything worked out, if you have any questions or feature requests.

Have fun! And get back to working in Flow!

A big thanks goes out to:

Conky on irc.freenode.net #basementcoders - the socat over ssh underpinnings originated with him sf11 and jgenender on irc.freenode.net - early testing on the script was a great help

Read more
Mar 30

Don’t get me wrong, Mystic Paste is great! However if I’m on ##wicket and someone asks a question about some code, and I know I’ve written that code, I want to be the first to respond. I don’t want some n00b making me look like a n00b by responding first :)

So instead of:

  1. copying code to the clipboard
  2. opening up my browser
  3. navigating to Mystic Paste
  4. pasting my code
  5. selecting the syntax highlighting language
  6. clicking submit
  7. copying the url for the pasted code to my clipboard
  8. pasting that to irc
  9. bang my head against the wall cause someone else beat me to it...

I can instead:

  1. right click on the text I have selected in my editor and choose "Add to Mystic Paste"
  2. paste url to irc

That’s what the plugin does, it will take that text, figure out which type of editor it resides in and use that information to determine the syntax, post the code to Mystic Paste and copy the url for the code to the clipboard. Boom, now steps a) through i) become steps a) to b) .

You are simply here for the Plugin

If you don’t actually care to know how the plugin was written, but want to start using it, you may download it from http://www.mysticpaste.com/plugin the directions are there to show you how to get it to work with Eclipse.

Setting up the project

Ok, for the two of you who stayed to learn, here’s how the Mystic Paste Eclipse plugin was created. Firstly, launch Eclipse and choose File->New->Project and choose “Plug-in Project”.

Eclipse Plug-in Project

This will guide you through a series of dialogs, I’ll only go over the ones which are meaningful. We named our plugin project MysticPasteEclipseProject and we gave it the plugin ID of com.mysticcoders.mysticpaste. The ID of the plugin is important to Eclipse because when your plugin is loaded, it’s the ID which uniquely identifies it amongst the thousands of other plugins which comprise the Eclipse platform.

The plug-in project wizard also lets you pick a template to start a new project from. Our plugin is going to be one that extends the “Popup Menu” feature of Eclipse, this is any feature which shows a context menu. The project template we choose is not exactly what we want, but it will be close enough so for now, choose the project template for “Plug-in with popup menu” and proceed.

Eclipse Popup Menu Template

Next the project wizard will ask us some questions about the “Action” for our plugin. The Action, in Eclipses-speak , is the class which houses the actual code that will be executed when the user clicks on your menu item.

Eclipse Action Setup

Click the Finish button and let Eclipse build the skeleton project for you.

Plugin.xml

plugin.xml is the file which Eclipse itself will read to determine what exactly your plugin does. Is it a new editor? Is it a toolbar? That’s determined by the extension point you list in this file. For context menus, the extension point is org.eclipse.ui.popupMenus and the plug-in project wizard conveniently set this for us.

However, remember above I was mentioning that the project template we picked was not “exactly what we want”? The template Eclipse uses will assume that the context menu should be applicable to resource types, typically these are the nodes you see in your navigation window on the left hand side of the screen when you are browsing through your project files. Instead, we want to extend the context menu which shows up when you right click inside an editor.

So, instead of our plugin.xml file looking like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   <extension
         point="org.eclipse.ui.popupMenus">
      <objectContribution
            objectClass="#CompilationUnitEditorContext"
            id="com.mysticcoders.mysticpaste.contribution1">
         <menu
               label="group.add"
               path="additions"
               id="com.mysticcoders.mysticpaste.menu1">
            <separator
                  name="group1">
            </separator>
         </menu>
         <action
               label="Add to Mystic Paste"
               class="com.mysticcoders.mysticpaste.popup.actions.MysticPasteAction"
               menubarPath="a.menu1/group1"
               enablesFor="1"
               id="com.mysticcoders.mysticpaste.newAction">
         </action>
      </objectContribution>
   </extension>

We want to instead change the “objectContribution” tag to a “viewerContribution” tag and all together remove the “menu” tag like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
   <extension
         point="org.eclipse.ui.popupMenus">
		<viewerContribution
			targetID="#CompilationUnitEditorContext"
			id="com.mysticcoders.mysticpaste.actions.compilationuniteditor">
			<action
				label="%Add_to_MysticPaste.name"
				icon="$nl$/icons/mystic16.png"
				class="com.mysticcoders.mysticpaste.popup.actions.MysticPasteAction"
				menubarPath="group.add"
				id="com.mysticcoders.mysticpaste.popup.actions.MysticPasteAction.JAVA5" />
		</viewerContribution>
   </extension>

Now what I am showing you above is the actual finished product, and if you are not familiar with Eclipse plugins it probably doesn’t amount to a whole lot. Here’s a dissection of the elements:

1
2
   <extension
         point="org.eclipse.ui.popupMenus">

Extension points are places in Eclipse which can be “Extended”. Basically, they are just the IDs of plugins in Eclipse that allow themselves to be extended. The ID for context menus is “org.eclipse.ui.popupMenus”.

1
2
3
<viewerContribution
	targetID="#CompilationUnitEditorContext"
	id="com.mysticcoders.mysticpaste.actions.compilationuniteditor">

Viewer contributions (as opposed to object contributions) determine which type of “thing” in Eclipse will be contributed to. In our case, we want a viewer contribution because Editors are a type of viewer. The targetID might seem a little weird, but it’s actually a predefined constant used by Eclipse to indicate “any editor that has code which can be compiled”. To us, this means a “Java editor”, so our contribution to the popup menu extension is for Java Editors. The id, is a unique id which our contribution is known to Eclipse by, it doesn’t have to correspond to a package in your project or anything, it just needs to be unique across plugins.

1
2
3
4
5
6
<action
	label="%Add_to_MysticPaste.name"
	icon="$nl$/icons/mystic16.png"
	class="com.mysticcoders.mysticpaste.popup.actions.MysticPasteAction"
	menubarPath="group.add"
	id="com.mysticcoders.mysticpaste.popup.actions.MysticPasteAction.JAVA5" />

An “Action” is going to be the class which is loaded by Eclipse when someone clicks on the menu item we have added. You can see that the class “executed” is determined by the fully qualified class you enter as the value for the class attribute. It must extend IEditorActionDelegate, this is not the same class that the plug-in project wizard setup for you, so you’ll have to change it.

menubarPath is a pre-canned designation setup for an area of the context menu which holds the “Add to Snippets” menu item, I figured Add to Mystic Paste did a very similar job, so decided to have the menu item live there.

You’ll notice weird things like %Add_to_MysticPaste.name and $nl$/icons/mystic16.png. You’ll find all sorts of unconventional syntax like that in the Eclipse platform and usually you have to dig deep to find out what exactly it all means. Well, in the case of %Add_to_MysticPaste.name, this tells Eclipse to look in a plugin.properties file bundled with your plugin and insert the value for the key Add_to_MysticPaste.name. For $nl$/icons/mystic16.png this tells Eclipse to replace the $nl$ token with the path to your plugin’s base directory when loaded into Eclipse. Actually, it points to the “internationalized” base path, but don’t worry about that for now, Mystic Paste’s icon isn’t language dependent. Important: in order to use values from plugin.properties in your plugin.xml file you must add the following line to META-INF/MANIFEST.INF: Bundle-Localization: plugin

Finally we have the id for the action, again just make something unique. In our case, I added .JAVA5 to the end of my id so that when the user clicks it, I know what language syntax to use when pasting the selected code to Mystic Paste by querying the action’s ID at runtime. What isn’t depicted above is the fact that the actual project has several viewerContribution sections for different types of editors in Eclipse and each action has a .<lang type> appended to it’s ID to help me determine what language the editor supports.

The Code

The code for Mystic Paste is really quite simple. We take the selected text in the editor and do an HTTP POST to a Servlet which is setup in the Mystic Paste webapplication. The Servlet accepts the content of the paste as well as the language type.

The only thing special which needs to be done, is to add the dependencies for Commons Http Client. This is done by using the plugin.xml editor and adding the dependency jars to your project as follows:

Build Depenencies Build Dependencies

The actual code itself is pretty straight forward especially since the author actually documented the code!

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
package com.mysticcoders.mysticpaste.popup.actions;

/**
 * Implements an Action Delegate which responds to a context menu item click<br/>
 * <br/>
 * The delegate posts the selected text to the Mystic Paste webapplication, then places the url for the page
 * where the user can view their paste onto the clipboard.  A "Balloon Tip" is shown after a successful paste.
 *
 * @author Craig Tataryn
 *
 */
public class MysticPasteAction implements IEditorActionDelegate {

	private String selectionText = null;
	private int selectionStart = 0;
	private Shell shell;
	ResourceBundle bundle = null;

	public MysticPasteAction() {
		super();
		bundle = ResourceBundle.getBundle("plugin");
	}

	public void setActiveEditor(IAction action, IEditorPart editorPart) {
		shell = editorPart.getSite().getShell();
	}

	/**
	 * Code which executes when the menu item is clicked.  Code selected is pasted to the Mystic Paste
	 * web application, the view URL is put on the clipboard and an informational message is shown in a
	 * balloon tip.
	 */
	public void run(IAction action) {
		System.out.println("run called");
		if (this.selectionText != null && !this.selectionText.trim().equals("")) {
			//the action is setup in plugin.xml with an ID that ends in .<lang type>
			String type = action.getId().substring(action.getId().lastIndexOf('.') + 1);
			String url = submitPaste(this.selectionText, type);
			Clipboard cb = new Clipboard(this.shell.getDisplay());
			cb.setContents(new Object[] {url}, new Transfer[] {TextTransfer.getInstance()});
			cb.dispose();
			showUrlBox();
		}
	}

	/**
	 * Decides whether or not to enable the mystic paste menu item in the context menu
	 * depending on whether there is selected text.  Unfortunately, because of how eclipse
	 * lazy loads things, this method isn't fired until the menu item is clicked for the
	 * first time, so you can never grey out the item before it is clicked.
	 */
	public void selectionChanged(IAction action, ISelection selection) {
		System.out.println("selectionChanged called");
		if (ITextSelection.class.isAssignableFrom(selection.getClass())) {
			ITextSelection txtSelection = (ITextSelection) selection;
			if (txtSelection == null || txtSelection.isEmpty() || txtSelection.getText().trim().equals("")) {
				action.setEnabled(false);
				this.selectionText = null;
				this.selectionStart = 0;
			} else {
				action.setEnabled(true);
				this.selectionText = txtSelection.getText();
				this.selectionStart = txtSelection.getStartLine();
			}

		}

	}

	/**
	 * Submits the selected text to the Mystic Paste web application.  Web application
	 * urls and other information are stored in the plugin.properties file.<br/>
	 * <br/>
	 * The language type for the selected text is determined by the action's id as setup
	 * in plugin.xml.  For instance, the action for the Java editor will have an id that ends
	 * with .JAVA.  The xml editor's action ID ends in .XML, and so on.  The default is TEXT.
	 * @param content
	 * @param type
	 * @return
	 */
	private String submitPaste(String content, String type) {
		String url = bundle.getString("mysticpaste.url");
		String newPasteContext = bundle.getString("mysticpaste.new");
		String contentParam = bundle.getString("mysticpaste.content.param");
		String langParam = bundle.getString("mysticpaste.language.param");
		String retString = null;

		HttpClient httpClient = new DefaultHttpClient();
		HttpPost post = new HttpPost(url + newPasteContext);
		List <NameValuePair> nvps = new ArrayList <NameValuePair>();
        nvps.add(new BasicNameValuePair(langParam, type));
        nvps.add(new BasicNameValuePair(contentParam, content));

        try {
	        post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
	        HttpResponse response = httpClient.execute(post);
			HttpEntity entity = response.getEntity();
			retString = EntityUtils.toString(entity, HTTP.UTF_8);
			retString = url + bundle.getString("mysticpaste.view") + retString;
			System.out.println(retString);
		} catch (ClientProtocolException e) {
			e.printStackTrace();
			MessageDialog.openInformation(
					shell,
					"MysticPaste Plug-in",
					"Sorry, we couldn't contact Mystic Paste");
		} catch (IOException e) {
			e.printStackTrace();
			MessageDialog.openInformation(
					shell,
					"MysticPaste Plug-in",
					"Sorry, we couldn't contact Mystic Paste");
		}

		return retString;
	}

	/**
	 * Shows an informational "balloon tip" at the bottom of the screen
	 */
	private void showUrlBox() {
		Rectangle bounds = shell.getDisplay().getPrimaryMonitor().getClientArea();
		ToolTip tip = new ToolTip(shell, SWT.BALLOON | SWT.ICON_INFORMATION);
		tip.setAutoHide(true);
		tip.setText("Your selection has been copied to to Mystic Paste");
		tip.setMessage("The Url is on your clipboard");
		tip.setLocation(bounds.width, bounds.height);
		tip.setVisible(true);

	}
}

Building a Plugin Jar

Building the plugin jar is pretty simple. Just go File->Export->Deployable plug-ins and fragments and follow the wizard

Build Jar

Installing the Jar

Locate where on your file system Eclipse is installed. Under this directory there should be a “dropins” folder, this is where the jar you built (or downloaded) for the plugin should reside. Restart Eclipse, and voila!. Note: on a Mac, you’ll have to right click on Eclipse.app and choose “Show Package Contents”, the dropins folder will then be accessible through the Finder window which pops up.

Conclusion

Hopefully this gives you a good idea of how the Mystic Paste Eclipse plugin was built. It probably took more time explaining than it did actually coding the darn thing. That being said, programming for Eclipse is not for the faint of heart. I pretty much “code by debugger” when I have to create Eclipse plugins, a lot of the API is shrouded by interfaces with one method on them, you really never know what the real object is you are dealing with until you inspect it at runtime.

Read more
Mar 20

Thanks again to everyone who made it to Mystic’s presentation on Architecting Applications using Apache Wicket. I enjoyed sharing the information with you, hearing your questions, and offering solutions for them. If you have follow ups that were thought of later, please contact us. Here’s a link to the presentation for the interested:

Architecting Applications using Apache Wicket

See ya Vegas!

Read more