Azureus Plugin Tutorial v0.2

November 30th, 2006 Leave a comment Go to comments

Azureus is my BitTorrent client of choice, but I’ve found on occasion that its functionality is a bit lacking. However, fortunately for me it is extensible via plugins, which has enabled me to add my own functionality to it.

This is a brief tutorial on how to use the Azureus plugin interface to make a simple plugin that aggregates some basic information about your torrents and logs it. Some listeners are used, and some of the basic utilities for creating views and configuration pages are also included. Since this is my first tutorial, I’m not sure it’ll be adequate, but feel free to use this page’s comment section to provide feedback on it and tell me how I went horribly, horribly wrong.

Assumptions

I assume a few things here, although I am trying to minimize them. However, I have not the time or energy to explain classpaths and jar building for every IDE, nor do I have any interest in teaching basic Java. If you need to know more about these things, there are other places to do so.

Getting Started

You’ll need a few things in order to make your plugin. Since I use Eclipse for development, there is obviously going to be a bias towards the use of that tool. It’s not perfect (I’m still trying to get debugging working right) but for simple plugins it works well.

Build Environment (Eclipse)

On the off chance you’re not using it already, I recommend you download Eclipse to start with. Install it however you like.

Libraries (Azureus and SWT)

You’ll need the source code for Azureus, since at this time there is no pure Plugin SDK for it. This leads to a bit of confusion over what parts of the code to use, but I’ll try clear that up. You can get the source here — download the source zip at the bottom of the page. This tutorial is based on the 2.2.0.0 code, but the parts I’m using should be pretty basic across at least a few versions. Unzip the source zip into [dev]/Azureus2.2.0.0source (here’s where the assumptions start — I’ll be assuming a simple and clear folder layout for your files) Also download the actual Azureus jar from the above location, and install it wherever you like — you can use the jarfile from your existing Azureus installation, which is what I did. Although you may or may not need these for your plugin (for this tutorial it’s not strictly necessary) it pays to have SWT(Standard Widget Toolkit) installed. Get it here and unzip the source package into [dev]/swt-3.0.1-win32 (Note that I assume SWT v3.0.1 which is the release version as of this writing. The version number should remain more or less irrelevant as long as it begins with 3)

Eclipse Config

Libraries

First thing you need to do with Eclipse (other IDEs are exercises left to the reader) is configure the above libraries. For each of them, you need open the Classpath Variables preferences page (Window->Preferences: Java -> Build Path -> Classpath Variables) and add the libraries:

Preferences Window

Classpath Preferences

You also have to add:

  • AZUREUS_SRC ([dev]/Azureus_2.2.0.0_source.zip)
  • SWT_LIB ([dev]/swt-3.0.1-win32/swt.jar)
  • SWT_SRC ([dev]/swt-3.0.1-win32/swtsrc.zip)

This assumes that you downloaded the source for SWT as well.

Project Layout

Create a new Java project in Eclipse, and add AZUREUS_LIB and SWT_LIB to it as well (in the Libraries tab). If you wish to have javadoc popups and source code for the SWT/Azureus libraries, you have to add source attachments (the SRC variables were for this) to the jar files. Good luck with that! :) Also, in order to use the SWT you may or may not (depending on how you debug — I didn’t need this) wish to copy the swt-awt-* and swt-* libraries (dll files, on windows) into the project root. My project layout defaults to putting all source files in a src/ subdirectory off of the project root, and this is what I’ll assume. However, if it so happens that you do not do so, it’s in fact easier to export to a plugin jar. I have to jump through one extra hoop with my project layout. So I would recommend creating a flat project layout. However, I’ll just assume that all of your source files are in [src]/[stuff] from here on out.

Down to Brass Tacks

plugin.properties

The first thing that every plugin needs is a plugin.properties file:

  1. plugin.class=edu.azureus.example.plugin
  2. plugin.name=autostop
  3. plugin.langfile=edu.azureus.example.Messages
  4. plugin.id=autostop
The lines are as follows:

  1. plugin.class: (REQUIRED) This is simply the base class of the plugin. This class must implement org.gudy.azureus2.plugins.Plugin.
  2. plugin.name: (REQUIRED) At this time I’m not 100% certain of the difference between this one and plugin.id, so I suggest that they both be the same for now.
  3. plugin.langfile: This optional value indicates the package and name of the file to be used for internationalization. As far as the naming format goes, "Messages" is the basename, adding _[language]_[country code] onto it customizes it for local languages. Bear in mind, I have only worked in english, don’t quote me on that format.
  4. plugin.id: I assume that this must be unique, but I am not sure what its relation to plugin.name is, and as such I keep it the same.

Messages.properties

  1. example.name=Azureus Example Plugin - Status logger
  2. example.prefix=Log prefix
  3. example.defaultPrefix=Status Change
  4. example.enabled=Enabled

The package I am using (edu.azureus.example) can be anything you want it to be, so long as you keep it in sync with the plugin.properties file.

plugin.java

Download: plugin.java

This is a skeleton of a plugin for logging status changes in torrents. It has a few features of immediate interest:

  • Lines 3-8: Import statements

    These are a subset of the plugin library imports. Assuming that you are using Eclipse, you shouldn’t have to worry about these, it’ll take care of it for you. However, I cannot assume that you’re doing the right thing here ;)

  • Line 10,11: Class declaration

    As you can see, the class implements the org.gudy.azureus2.plugins.Plugin, org.gudy.azureus2.plugins.download.DownloadManagerListener, and org.gudy.azureus2.plugins.download.DownloadListener interfaces. These are the most basic of the lot, required for almost any operation on the torrent list.

  • Lines 12-20: Property names

    These constants identify values in Messages.properties. They are used for internationalization and string tables for the application.

The methods are, in order of importance:

  • void initialize(PluginInterface)

    This method initializes the plugin. What’s going on in here is a bit complex, I’ll get to that later.

  • void downloadAdded(Download download)

    This method is called whenever the downloadManager adds another download. All it is doing here is adding this plugin as a download listener for that download, which otherwise doesn’t happen.

  • void downloadRemoved(Download download)

    This should be self-explanatory.

  • void stateChanged(Download download, int old_state, int new_state)

    The actual work takes place here, in this method inherited from DownloadListener. This method checks to see if the enabled property is set to true (See below) and if it is, logs the state change to the logger (Also, see below). Download has several constants regarding states beginning with ST_ and an array of names ST_NAMES that are used here.

  • void positionChanged(Download download, int oldPosition, int newPosition)

    We don’t do anything with this in this example, but it would be used if you wanted to monitor the queue of downloads for changes in ordering.

initialize

As promised, here’s a rundown on what is going on in initialize()

  • (line 43) First, we store the pluginInterface object for future use. Although nothing is done with it in this plugin, you may want subordinate classes to be able to get at it. We may also need it in other parts of this plugin class.

  • (line 44) For convenience (and because I hate typing) I keep the localeUtilities referenced as well. This is necessary for any translates strings that you use manually,.

  • (line 45) Set up the logger.

    Note that log is a LoggerChannel object, which we assign by first getting the Logger for our plugin from the pluginInterface, and then a new channel which we can name whatever we like (although something at least a bit unique is probably a good idea here).

  • (lines 47-51) Set up the plugin log viewer.

    We use BasicPluginViewModel to set up a simple view for our plugin. This is gotten through the UI Manager of the pluginInterface, as should be pretty obious. The lu.getLocalisedMessage(BASENAME + "name") call is the basic format for all string use, this takes the property example.name from Messages.properties and uses that string. vm.getActivity() and vm.getProgress() calls disable elements of the view that we won’t be needing for this plugin. You don’t have to do anything else to make this view show up — by doing this you have already created the view and added it to the Plugins menu in Azureus.

  • (lines 53-63) Set up the log listener.

    Here, we add a LoggerChannelListener to the log we created above. Although we could do fancy things with it, there’s not much point, and an anonymous class does the trick just as well. Either way, the listener must implement LoggerChannelListener at least, and the two methods it provides are pretty basic. In this instance, the listener just writes the message plus an end of line character to the LogArea of the basic view we created a moment ago. the LogArea is something that the basic view model provides, so don’t worry about creating one.

  • (lines 65-74) Configuration.

    We create a BasicPluginConfigModel to act as a config page for our plugin. We use the UI manager for this, and give it a parent heading ("plugins", which you should always use for your plugins) and a lookup name for its own name (BASENAME + "name" in this case, but whatever property you want from your Messages.properties can be substituted here) that Azureus will use to place it in the preferences dialog. At this point, in fact, you have created a preference page (albeit a blank one) and it will already appear in the prefs dialog. Next, create the enabled parameter: call addBooleanParameter2 (the 2 is, I assume, for the more modern versions. There is a version w/0 2 on it, but I believe that it is deprecated) with the ID of the name from your Messages file, a key to store the config value under (this MUST be unique — I recommend prefixing it with your plugin name as I have done here, and then making certain that you have no duplicate values for your key names) and the default value, a boolean parameter. After that, create the prefix string parameter: call addStringParameter2 and the parameters are the same as addBooleanParameter2 in general, although a string parameter using getLocalisedMessage is what the default calls for. Lastly, make the prefix’s enabled state depend on the booleanParameter as in line 74.

  • (line 76) Finally, we add this plugin as a downloadManagerListener and off we go!

Deployment

Deploying your plugin requires that you create a Jar file containing the classes, the plugin.properties file, and any ancillary files that the plugin requires. I do it by right-clicking on my [src] directory (your mileage will vary on this one depending on where you put the source code — q.v. my earlier comment on project layout) and selecting "Export" from the resulting menu. I then export to Jar with ONLY the [src] directory and nothing above it highlighted, and I place that jar file in [azureus install dir]/plugins/[plugin name], although you can call the latter directory whatever you want. Debugging is, however, not working out for me. If anyone knows of a way to debug plugins in a running instance of Azureus, I’d love to know!

Gotchas

The plugin API for Azureus is not terribly well documented. Generally, though, if you stick to public methods of the pluginInterface and classes from the org.gudy.azureus2.plugins package tree, you’re probably going to be okay. If you get stumped, you can post questions as comments to this page, and I’ll try to answer them. Also, there’s an IRC channel #azureus and the sourceforge plugin developer forum to try. Hopefully this helps!

  1. August 14th, 2008 at 10:58 | #1

    That’s an outstanding request; I’ll look at it eventually, but it’s a ways off. Work is keeping me pretty slammed these days. Thank you for the interest, please be patient and I’ll get around to it, I promise!

  2. Steinar
    August 26th, 2008 at 15:34 | #2
  3. Kelsey
    September 17th, 2008 at 05:45 | #3

    Auto Categorizer not saving - I have the same issues the guys above have. When I restart, all of my categories are gone.

  4. Timken
    September 20th, 2008 at 04:27 | #4
  5. Bicycle Made For One
    September 23rd, 2008 at 10:27 | #5

    Hi Chris

    Thanks for this plugin, it’s solved one of Azureus’s few issues; how to seed different types of torrents to different ratios.

    I would echo Ambrose and Hal to some extent. I’ve found the right-click per torrent settings, but I’m mystified by the “Autostop Criteria” tab. What does it do, and how can I interact with it? Clicking on the Plus button does nothing.

    Other than that, I’m pleased and grateful!

  6. Bob
    September 28th, 2008 at 05:00 | #6

    Outstanding plugin concept, well done Chris. Implementation needs some polish but generally I think you have most of what you need in place - keep it simple and focused = good plugin. Here’s my suggestions:

    1) add some simple instructions to readme - ie. *tools->options to enable and change global ratio *right click individual torrent to do per torrent settings

    2) allow more ratios for the global setting - currently only 1.0, 1.5, 2.0, 5.0, whereas for individual you can enter a custom value.

    3) seems that everyone that use this first goes to Tools->Plugins->Autostop which brings up a spreadsheet that absolutely nothing. Remove this from the user experience.

    Otherwise well done.

  7. Bicycle Made For One
    October 2nd, 2008 at 06:01 | #7

    Actually, this isn’t working for me.

    I’ve set the global ratio to 1.0, and individual ratios above that, but they are stopping at 1.0, with a Seeding Rank of “Share Ratio OK”.

    If I raise the ratio under Queue>Seeding>Ignore Rules, torrents which do have a ratio limit of 1.0 start seeding, despite Autostop’s global ratio telling them they’ve reached their target.

    I’m on Vuze 3.1.1.0 on Java 1.6 on Unbuntu 8.04; any help gratefully received.

  8. October 5th, 2008 at 15:12 | #8

    I don’t really understand how to use this plugin…I have the fields filled out like so: Host: talk.google.com Port: 5222 Name: myemailaddress@gmail.com (is this right?)

    User Name: myemailaddress@gmail.com (?) Resource Name: windows_computer Password: mypassword

    In the log it says: Unable to perform initial connection to the Jabber service. XMPPError connecting to talk.google.com:5222.

    So I’m guessing one of those fields is wrong.

  9. October 6th, 2008 at 15:06 | #9

    Google is not full-featured jabber server. Read many jabber clients FAQs - there are special description how o connect to GTalk, which features to suspend and which to enforce.

    For example, jabber server should support multiple connections with different programs/computers into the single login/password. Can you connect to the same GTalk from two computers at the same time ? If you cannot, AzJabber would seems fail too.

  10. October 6th, 2008 at 19:49 | #10

    azjabber does, however, work with gmail; it’s gmail that I use it for. Colin, here’s the settings that work:

    Host: talk.google.com Port: 5222 Name: gmail.com

    User name: Resource name: anything at all Password: I can’t tell you that

  11. stan
    October 15th, 2008 at 21:09 | #11

    I admit to being an idiot, but could someone please give me an example of a rule for Auto Categorizer? All I want to do is have torrents categorized based on tracker URL. I’m entering the domain as the tracker URL, and it’s not working.

  12. Kelsey
    October 16th, 2008 at 14:03 | #12

    I’m having the same issues - for some reason it’s managed to save just one category, but after a restart, all others are gone.

  13. stan
    October 16th, 2008 at 19:45 | #13

    never fucking mind, i figured it out. what a pain in the ass.

  14. Daryle
    October 22nd, 2008 at 21:40 | #14

    So the download link is still broken and unfortunately the link:

    http://azureus.sourceforge.net/plugin_details.php?plugin=autostop

    is two version old and missing the plugin.properties file again. Is there any way to get the most recent beta with the proper properties file?

  15. Daryle
    October 22nd, 2008 at 22:11 | #15

    Heh. A little poking around helps. I noticed the plugin link was broken but based on the other links I guessed:

    http://www.offby1.net/plugins/

    and lo and behold a directory listing of all plugin versions. However I’m still confused about two things.

    First I would have thought that the beta3 was most recent. However it (and the other betas) are all 28 kB. However the one at sourceforge is 36 kB; although the betas are more recently dated.

    Also there is still no sign of the plugin.properties file. I am no Azureus plugin expert but something seems missing since the only option on the plugin page are enable and detailed logging which seem sort of like defaults. Where is this file and don’t I need it?

  16. fuzzymok
    October 29th, 2008 at 10:07 | #16

    @Colin

    In Host configuration Name must be gmail.com and in client configuration “username” is your gmail username, but you don’t have to put “@gmail.com”, for me these settings work well.

  17. Simon
    November 16th, 2008 at 20:15 | #17

    Well, it’s still on my livejournal feed. So I, at least, still see it. Ha!

    That, and as said prior, good luck.

  18. sweet mama
    November 19th, 2008 at 23:56 | #18

    I’m here, too. congratulations! Hi to your future offspring from me, too :)

  19. sweet mama
    November 20th, 2008 at 17:34 | #19

    hind sight makes some things sound much different.

  20. mel
    November 20th, 2008 at 23:36 | #20

    Well lad-di-da. I wasn’t expecting this. Paint me tickled! (just go with it. I think by now we all know I like it random and nonsequiturial and that’s just how it’s gonna be)

    I love that you are starting this up and in the form of a journal and outlet.

  21. jenny
    November 22nd, 2008 at 11:34 | #21

    I read this!

  22. November 23rd, 2008 at 10:54 | #22

    Frequent reader who infrequently comments. Feed me weird things, and who knows what’ll come out of me once I’ve digested it.

    That was unintentionally scatological, I swear.

    Long story short: post. Whether I reply or no, I have read your writings.

  23. moi
    December 4th, 2008 at 07:43 | #23

    I’m still here way in the distance, I may have not looked for a bit but will now.

  24. sweet mama
    December 4th, 2008 at 09:20 | #24

    that was fast!

  25. mel
    December 8th, 2008 at 00:42 | #25

    I think how this is all falling into place is quite impressive. It is impressing upon me how I have watched you relationship evolve in a lovely fashion (and I have only seen near 3 yrs of it) and how gracefully you both have been handling the swiftness of it all. What does it say about me that it freaks me out at the same time? Oh right, that I am holding onto my selfish lifestyle quite happily and afraid my grip may be tenuous.

  26. December 8th, 2008 at 04:16 | #26

    Yeah, there’s definitely an interesting sense of momentum to this whole thing. It’s sort of been there for me since I found out about the baby; I’ve been swept along in events, with what feels like only marginal control over the direction of travel.

    This isn’t to say it’s a bad feeling, but it’s certainly interesting.

    I know that from your perspective there’s a lot changing too. Char and I both plan to ensure that the life we have — the friends, the interests, &c — don’t fall victim to the massive change in our family life that is coming. Moving doesn’t make this easier, of course; it takes us out of our current comfortable place, makes it a bit more of an effort to visit us, and more of an effort for us to see some of our friends. We hope that this won’t be too much of a change to work around, but obviously there’s going to be some.

  27. moi
    December 8th, 2008 at 07:48 | #27

    So, not much socializing yet outside of work? You’d have picked up more words I think. What are the women like? We, and Edmonchuk, got some snow since you left, actually a fair bit here.

  28. sweet mama
    December 8th, 2008 at 10:19 | #28

    /_\

    change is good

  29. sweet mama
    December 8th, 2008 at 10:22 | #29

    /\ is what I meant

  30. sweet mama
    December 8th, 2008 at 10:22 | #30

    gir. it’s not working. you can erase these if you want.

  31. Mir
    December 8th, 2008 at 17:15 | #31

    /\ /__\

    If it makes it sound any better, all our friends with kids really stressed how much our lives would change when the wee one arrived but I wouldn’t say it’s to our detriment. I think we get to see our firends more often now that we have Sophie. This is especially true for me, as I am no longer working 70-80 hours per week.

  32. Jo-Anne
    December 9th, 2008 at 17:46 | #32

    Congratulations Chris and Char and I am very happy that you have restarted this site. I like to keep up with what everyone is up to

  33. December 10th, 2008 at 05:10 | #33

    Yeah, that would certainly make it easier to have a life, wouldn’t it?

    It’ll be a bit different for us, obviously. It’s different for everyone. Char will get to be diurnal if she wants to, which will be a very pleasant state of affairs. Additionally, our good friends have all made clear that they won’t stand for us vanishing from the radar, which means that the biggest concern is pretty much taken care of. I’m still not aggressively looking forward to it, but the things that worried me are, one by one, becoming less of an issue.

  34. moi
    December 14th, 2008 at 08:32 | #34

    It breaks my heart to know this part of you is over, I wish it wouldn’t have worked out like this. Hey, You’re way in front of me on the numbers. I think it is great that you did get to fly over Brazil.

  35. December 15th, 2008 at 04:08 | #35

    Yeah, it’s pretty hard to take, no mistake. I mean, it is what it is, but I don’t have to like it. Safety über alles, but oh, what a price to pay.

  36. mel
    December 22nd, 2008 at 13:30 | #36

    I am with Moi on this one. I know what it meant to you and the fact that you couldn’t get into it with me when it was fresh I understood meant it was pretty emotional. More reason to have coffee Tuesday evening. Let’s dish about you.

  37. Mir
    January 26th, 2009 at 15:19 | #37

    It’s a nice place. I can’t wait to see you guys settled in.

  38. moi
    January 27th, 2009 at 15:52 | #38

    How’s the unpacking going?

  39. mel
    January 28th, 2009 at 13:36 | #39

    A shout out echo. You two made it easier on us being uber organized and ready for us. Forking over $150 in beer was a nice touch as well. I can’t wait to warm you house!!!

  40. Mir
    February 13th, 2009 at 09:26 | #40

    Oh my god, you’re right. Anyone who’s watched that movie would have quite a different response to that ad than Telus expects, I’m sure. It’s powerful music, just not in the way they want it to be.

  41. Simon
    February 13th, 2009 at 10:30 | #41

    Good lord. Even knowing what that was going to be didn’t prepare me for that.

  42. February 13th, 2009 at 10:35 | #42

    I know!

    Make no mistake, I can (and do!) listen to Lux Aeterna without irony purely for the joy of the sound; it’s a beautiful song. But this… this is just… Words fail. They really do.

  43. sweet mama
    February 17th, 2009 at 17:11 | #43

    I don’t know. I like it, and I have a pretty visceral reaction to the song. Sadly, I think Telus did a good job reinventing that. Makes me want a smart phone.

  44. Charling
    February 20th, 2009 at 01:13 | #44

    I’ll use the obvious analogy, here:

    You want some dope who graduated PhD in Agriculture to irrigate your crops with Gatorade, cause it quenches all thirst? He tried really, really hard in school ….. :/

    You want some guy to have to try really really hard to remember a differential diagnosis on your dying mom when he only has 10 minutes to administer treatment or her stroke will permanently ruin her brain?

    I didn’t think so.

  45. Mir
    February 20th, 2009 at 09:14 | #45

    Yeah, there is more to it than effort, for sure. I really don’t like that this “self-esteem” grading is going on in elementary schools, but to hear that some people think it should be happening in post-secondary education too, gross. In hindsight I guess I shouldn’t be that surprised, those kids who got graded “W” for Wonderful will eventually get to college age.

  46. mel
    February 23rd, 2009 at 12:39 | #46

    Damn right you’ll teach your kids a mere sense of entitlement is not what will get you by alone. If your kid’s generation could be the first one in a while that starts to purge this sense of entitlement that has been going on that would be great.

  47. mel
    February 23rd, 2009 at 12:48 | #47

    also… this wraps is up http://mwkworks.com/lifesnotfair.html

  48. moi
    April 1st, 2009 at 07:47 | #48

    I always liked Maligne then frozen although I never saw it that often. Char told me a bit about you going up there. Talk to you when you get home, moi

  49. charling
    April 6th, 2009 at 03:32 | #49

    when are you going to post again? I miss you :(

Comment pages
1 ... 29 30 31
Comments are closed.