blob: 3e5158a8dea14abcc4a7a4abf437e79f7ef70cac [file] [log] [blame]
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN2">
<html>
<head>
<title>Strict Classloading</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<link href="http://dev.eclipse.org/default_style.css" rel="stylesheet" type="text/css">
</head>
<body bgcolor="#ffffff">
<table width="100%">
<tr><td style="background:#0080C0"><b><span style="color:white">Proposal</span></b></td></tr>
</table>
<h1>Strict Classloading</h1>
<blockquote>
<p><b>Summary</b> <br>
Eclipse embodies a component/modularity model. It allows plug-ins to express
what they need of others (requiring plug-ins and importing packages) as well
as what they contribute to others (providing/exporting packages). Ideally
plug-ins would export/provide only that code which they consider to be API.
Historically however the Eclipse plug-ins (and likely much of the community)
have exported all of the code they contain. This is helpful to consumers
living on the edge or pushing the envelope but it hinders both security as
well as static API analysis done for security or performance optimization.
Here we propose a path forward which allows plug-in producers to express their
API and thus the runtime to enforce API-only consumption but still enables
wild-men developers to access non-API on a selective basis.
<p>Last
Modified: December 16, 2004</p>
</blockquote>
<h2>Problem Definition</h2>
<p>Currently the Eclipse plug-ins expose all their code. The typical plugin.xml looks
something like</p>
<pre> &lt;plugin ...&gt;
&lt;runtime&gt;<br> &lt;library name=&quot;resources.jar&quot;&gt;<br><strong><font color="#FF0000"> &lt;export name=&quot;*&quot;/&gt;<br></font></strong> &lt;/library&gt;<br> &lt;/runtime&gt;
&lt;/plugin&gt;</pre>
<p>The use of * in the export filter means that all classes in the related
JAR are exposed to consumers. Typically these JARs contain both API and implementation
classes.</p>
<p>Beyond the appalling aesthetics of exposing the non-API classes, there are a
number of pragmatic reasons why this is bad. In particular, this practice inhibits
runtime security and static analysis.</p>
<h3>Issue: Runtime Security</h3>
<p>Key to the Java security model is understanding and limiting the entry points
to a piece of function. The security mechanisms are costly both at execution
and development time. It is simply infeasible to secure every part of a large
chunk of code like Eclipse if we have to consider all visible methods on all
visible classes as entry points. Instead, we look to the API to provide a choke
point
through which all external access must go. If we secure the API then the implementation
code need not be secured (or at least requires less work to secure). Of course,
if you take this approach, the runtime must gurarantee that only the API types
are visible to outside parties. To do this, there must be a clear, machine-readable
description of these types in a plug-in.</p>
<h3>Issue: Static Analysis</h3>
<p>Static code analysis is increasingly interesting. It enables both security analysis
and verification as well as performance optimizations such as code reduction
and inlining. These analysis techniques begin by identifying the entry points
in a body of code and then exhaustively traversing all execution paths. In
the case of security analysis, the algorithm is looking for unsafe operations
and places where permissions are required. For performance analysis the tools
look for dead code (code which is not traversed), inlining opportunities and
classes, methods and fields which can be obfuscated. Without an explicit and
complete specification of the API, these analyses are not possible. </p>
<h3>Issue: Internal Type Access</h3>
<p>Historically Eclipse has not limited access to the internal classes of a plug-in.
We define the API in Javadoc and warn programmers not to stray. This approach
is weak and leads to component interaction via non-API routes. Enforcing API-only
visibility eliminates this weakness. </p>
<p>Despite the pitfalls,
some developers find it convenient or necessary to use internal classes.
Some Eclipse plug-ins have been known to do this! In addition, various test
suites
rely on access to internal types to implement white-box tests. Simply cutting
off this access is not friendly.</p>
<h3>Issue: Accurate Development-time Classpaths</h3>
<p>PDE builds development time classpaths from the exported packages it finds in
the plug-ins it manages. Because Eclipse plug-ins typically export all their
code, dependent plug-ins have all internal classes on their classpath at development
time. This can lead to unintended use of internals through the convenience
of code completion. Developers know there is a type called FooBar so type Foo&lt;ctrl&gt;&lt;space&gt;
and choose from the results (or use Organize Imports). Because they find a
type and things seem to work they are happy. Unfortunately, it is entirely
possible that the FooBar used is not API since PDE has no way of telling the
difference.</p>
<h2>Proposal</h2>
<p>Plug-in producers are in the best position to define their API. They clearly know
the bounds of what they want to expose/support. Actually defining the
API is not particularly challenging. The plugin.xml &lt;export name=&quot;&quot;/&gt; syntax
and manifest.mf Export-Package: header both allow export specification. The
specifications are given using an obvious and simple format. For example, the
plugin.xml and manifest.mf markup (only one is needed) to expose the API for
the Resource plug-in is shown below.</p>
<pre> &lt;plugin ...&gt;
&lt;runtime&gt;<br> &lt;library name=&quot;resources.jar&quot;&gt;<br><strong><font color="#FF0000"> &lt;export name=&quot;org.eclipse.core.resources, org.eclipse.core.resources.refresh,
org.eclipse.core.resources.team&quot;/&gt;<br></font></strong> &lt;/library&gt;<br> &lt;/runtime&gt;
&lt;/plugin&gt;</pre>
<pre> <strong><font color="#FF0000">Export-Package:
org.eclipse.core.resources,
org.eclipse.core.resources.refresh,
org.eclipse.core.resources.team</font></strong>
</pre>
<h3>Defining the Exports</h3>
<p>The task of defining the exports is relatively straightforward. For most plug-ins
it amounts to creating entries similar to those above. Notice that packages
which are not to be exposed are simply not listed. Since Eclipse already uses
package naming conventions for API packages, this process should take about
5 minutes per Eclipse plug-in. Simply open the jar for a plug-in and note all
of the API packages.
List these packages in your choice of manifest (plugin.xml or manifest.mf).
The effort required for non-Eclipse plug-ins which may not follow any package
naming conventions may be slightly greater but is still quite minimal.</p>
<p>Note that classes used in executable extensions and Bundle.loadClass() do not
need to be listed in the exports as they are loaded by asking the plug-in itself
to
load
the class rather than going through the normal classloader delegation mechanism.</p>
<p>Of course, if you are using non-API in someone else's plug-in you will have to
either clean up your code or negotiate to have the required types exposed by
the prerequisite plug-in.</p>
<p>Notice also that there is a subtle difference between exposing a type and it
being API. A type is not API simply because it is exported by a plug-in's manifest.
plug-ins may be willing to expose types which are provisional API or specific
types which are made available for targetted users (e.g, so-called SPI). The
definition of API remains under this model. That is, the Javadoc is still the
final source.</p>
<h3>Exposing Internals (Strict mode)</h3>
<p><em>[Note: We need a better name here. &quot;strict mode&quot; is hard to negate (what is the
opposite?). &quot;test mode&quot; is ok but has a wider connotation. Strict mode may
in fact be one aspect of a supposed test mode but it likely does not cover
all aspects.]</em></p>
<p>As mentioned in the problem definition, various people expect access to a wider
range of classes. To support this we introduce a new (non-standard) OSGi manifest.mf
header <em>Export-InternalPackage</em> and a <em>osgi.strictClassLoading</em> property for the framework. The Export-InternalPackage header has exactly the
same syntax as Export-Package.</p>
<p>When the framework is in <em>strict mode</em> (i.e.,
osgi.strictClassLoading = true), only types listed by the Export-Package headers
are considered. When
the framework is in <em>non-strict</em> mode (i.e., osgi.strictClassLoading
= false), the elements on the Export-Package and Export-InternalPackage headers
are merged (in that order) and considered by the runtime.</p>
<p>We do not propose any changes to the plugin.xml syntax. Developers using plugin.xml
can specify their exports as outlined above and the manifest generator will
automatically generate Export-InternalPackage header entries for all packages
found in the plug-in but not included in the generated Export-Package header.
Note however that the manifest.mf format offers additional power in filtering
types from a package. Developers wishing to be more precise about their exports
(sub-package control) can acheive this using the manifest.mf file.</p>
<p>By default Eclipse will default to non-strict mode (osgi.strictClassLoading =
false) so as to increase compatibility. </p>
<p>As a bare minimum the Eclipse RCP plug-ins must operate correctly in strict mode.
Optimally the entire Eclipse SDK will operate correctly in strict mode. </p>
<h3>Runtime/OSGi impact</h3>
<p>The OSGi code needs to support the strict mode flag. This impacts the following
areas:</p>
<ul>
<li><strong>Resolver:</strong> The code which gathers the list of exported packages
now has to optionally gather elements from the Export-InternalPackage header.</li>
<li><strong>State cache:</strong> The state cache change detection test needs to include a check that the strictness
flag is the same as this affects the package wirings. If the strictness changes,
the state cache needs to be flushed.</li>
</ul>
<h3>PDE UI impact</h3>
<p>PDE UI will need to expose the new export header in the appropriate way (e.g.,
in the package related editor pages). There are several things to note here</p>
<ul>
<li>The Export-Package header allows includes/exclude filters to be
specified. These should be exposed</li>
<li>The ability to put a given package found in a bundle in either export list or
explicitly not export it at all should be supported.</li>
<li>PDE will likely have to retain a list of the packages and types
which have
been expressly NOT put on either the Export-Package or Export-InternalPackage
headers. These are elements which the developer has decided to keep private
in all cases. Maintaining this list is needed to differentiate between
elements are not mentioned in either header because they are new
or because they are to be private. The negative list can
be kept in a separate manifest header (favoured) or though separate metadata
(e.g., build.properties).</li>
</ul>
<p>PDE should offer the developer the option to use strict mode development-time
classpaths. The semantics are the same here as putting the runtime in strict
mode. That is, in strict mode only the Export-Package header is used where
as in non-strict mode all exports are used. There may be an additional/related
ability to add the Export-InternalPackage entries to the secondary classpath
for a plug-in project.</p>
<p>In any event, PDE should use the new JDT classpath includes/excludes capabilities
to match the compiler classpath to the actual runtime classpath as close as
possible.</p>
<h3>PDE Build impact</h3>
<p>PDE build likely needs some switches similar to those described above to manage
the classpath computations.</p>
<h3>Manifest generator impact</h3>
<p>As mentioned above, the manifest generator will be updated to generate Export-InternalPackage:
entries for every package /type that is not listed in the Export-Package header. </p>
<h3>Additional impacts</h3>
<p>All RCP plug-ins are expected to run correctly in strict mode. This means that
their Export-Package list must include all API as well as any packages/classes
which are known to be required by friendly plugins. Again, the Export-Package
list is NOT a declaration of API. It is a visibility statement much like the
public keyword in Java.</p>
<h3>Other notes</h3>
<p>The exact syntax of the package lists is open to change. The use of OSGi directives
on the standard Export-Package header (for example) has been suggested. The
various approaches have benefits and drawbacks and will be considered as an
implementation detail. Here we note that the final syntax may not be as
described above.</p>
<h2>Summary</h2>
<p>This proposal details the set of changes required to correctly and completely
specify the public face (including API) of a plug-in. These changes are needed
to allow Eclipse to run in a secure mode as well as facilitating various analysis
techniques for security and performance optimization. The changes are quite
modest and can be completed by related teams with limited effort.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
</body>
</html>