- They are the same thing. Plug-ins are bundles, and bundles are plug-ins: they mean the same thing.
- They are jars, and more than jars: OSGi bundles are jar files with yummy metadata inside. Much of this metadata is in the jar's manifest, found at META-INF/MANIFEST.MF. This metadata, when read by an OSGi runtime container, is what gives the bundle its power.
- They limit visibility: With OSGi, just because a class is public doesn't mean you can get to it. All bundles include an export list of package names, and if a package isn't in the export list, it doesn't exist to the outside world. This allows developers to build an extensive internal class hierarchy and minimize the surface area of the bundle's API without abusing the notion of package-private visibility. A common pattern, for instance, is to put interfaces in one package and implementations in another, and only export the interface package.
- They have versions: All OSGi bundles are given a version number, so it's possible for an application to simultaneously access different versions of the same bundle (e.g. junit 3.8.1 and junit 4.0.) Since each bundle has its own classloader, both bundles' classes can coexist in the same JVM.
- They express their dependencies on other bundles: OSGi bundles declare which other bundles they depend upon. This allows them to ensure that any dependencies are met before the bundle is resolved. Only resolved bundles can be activated. Because bundles have versions, versioning can be included in the dependency specification, so one bundle can depend on version junit version 3.8.1 and another bundle can depend on junit version 4.0.
- They have lifecycle events: Bundles can contain code that is run when the bundle is activated, and again when the bundle is deactivated. You can read more information in the documentation for the BundleActivator interface.
- They collaborate with each other through an extension mechanism: Bundles may define a contract for extension, and in kind they can extend other bundles' extensions. This is how bundles configure each other. If bundles are black boxes, this would be the wires and junctures connecting them.
Most of these configurations can expressed through code, but by using XML the configurations can be read and processed without instantiating the associated bundles or classes. Sometimes I find the use of a plugin.xml file to be more of a pain than just writing code, but it delays helps delay unnecessary performance penalties for as long as possible.
- They are meant to be run in an OSGi runtime container: Sure, these are jar files, and you can put them on the classpath just like any other plain-ol' jar file. If you do that, though, you lose all the benefits of the bundle architecture like limited package visibility, verioning, et cetera. It's the OSGi runtime container that provides the power behind bundles. Well-known implementations include Eclipse's Equinox, Knopflerfish, Apache Felix and Concierge.
Some bundles are often used as plain-ol' jars due to their structure and power: SWT and JFace (combined they are Eclipse's answer to Swing) and JDT (a powerful Java parsing library) are well-known examples.
If you want to get started with OSGi, try this introductory tutorial, read the OSGi specifications, or look at the soon-to-be released book Equinox and OSGi: The Power Behind Eclipse (currently available on Safari Rough Cuts.)
Parenthetically: I realize there is a battle between Sun camp and OSGi camp over the future of the Java module specification. This post isn't about that, but if you want some details, you can start by looking at JSR 294.
Thanks to Jeff McCaffer and Chris Lopez for their valuable feedback on the draft.