aboutsummaryrefslogtreecommitdiffstats
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java374
1 files changed, 111 insertions, 263 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
index e915544..e8fbadb 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
@@ -21,8 +21,6 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AndroidPrintStream;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
-import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
-import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
@@ -33,11 +31,9 @@ import com.android.sdklib.build.ApkBuilder.JarStatus;
import com.android.sdklib.build.ApkBuilder.SigningInfo;
import com.android.sdklib.build.ApkCreationException;
import com.android.sdklib.build.DuplicateFileException;
-import com.android.sdklib.build.IArchiveBuilder;
import com.android.sdklib.build.SealedApkException;
import com.android.sdklib.internal.build.DebugKeyProvider;
import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException;
-import com.android.sdklib.internal.build.SignedJarBuilder;
import com.android.sdklib.util.GrabProcessOutput;
import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
import com.android.sdklib.util.GrabProcessOutput.Wait;
@@ -46,9 +42,6 @@ import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
-import org.eclipse.core.resources.IResourceProxy;
-import org.eclipse.core.resources.IResourceProxyVisitor;
-import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
@@ -69,8 +62,12 @@ import java.io.PrintStream;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TreeMap;
/**
@@ -108,11 +105,14 @@ public class BuildHelper {
private final boolean mVerbose;
private final boolean mDebugMode;
+ private final Set<String> mCompiledCodePaths = new HashSet<String>();
+
public static final boolean BENCHMARK_FLAG = false;
public static long sStartOverallTime = 0;
public static long sStartJavaCTime = 0;
private final static int MILLION = 1000000;
+ private String mProguardFile;
/**
* An object able to put a marker on a resource.
@@ -128,14 +128,18 @@ public class BuildHelper {
* @param errStream
* @param debugMode whether this is a debug build
* @param verbose
+ * @throws CoreException
*/
public BuildHelper(IProject project, AndroidPrintStream outStream,
- AndroidPrintStream errStream, boolean debugMode, boolean verbose) {
+ AndroidPrintStream errStream, boolean debugMode, boolean verbose,
+ ResourceMarker resMarker) throws CoreException {
mProject = project;
mOutStream = outStream;
mErrStream = errStream;
mDebugMode = debugMode;
mVerbose = verbose;
+
+ gatherPaths(resMarker);
}
public void updateCrunchCache() throws AaptExecException, AaptResultException {
@@ -169,11 +173,6 @@ public class BuildHelper {
}
}
- public static void writeResources(IArchiveBuilder builder, IJavaProject javaProject)
- throws DuplicateFileException, ApkCreationException, SealedApkException, CoreException {
- writeStandardResources(builder, javaProject, null);
- }
-
/**
* Packages the resources of the projet into a .ap_ file.
* @param manifestFile the manifest of the project.
@@ -287,9 +286,7 @@ public class BuildHelper {
* @param intermediateApk The path to the temporary resource file.
* @param dex The path to the dex file.
* @param output The path to the final package file to create.
- * @param javaProject the java project being compiled
* @param libProjects an optional list of library projects (can be null)
- * @param referencedJavaProjects referenced projects.
* @return true if success, false otherwise.
* @throws ApkCreationException
* @throws AndroidLocationException
@@ -299,8 +296,7 @@ public class BuildHelper {
* @throws DuplicateFileException
*/
public void finalDebugPackage(String intermediateApk, String dex, String output,
- final IJavaProject javaProject, List<IProject> libProjects,
- List<IJavaProject> referencedJavaProjects, ResourceMarker resMarker)
+ List<IProject> libProjects, ResourceMarker resMarker)
throws ApkCreationException, KeytoolException, AndroidLocationException,
NativeLibInJarException, DuplicateFileException, CoreException {
@@ -324,8 +320,7 @@ public class BuildHelper {
// from the keystore, get the signing info
SigningInfo info = ApkBuilder.getDebugKey(keystoreOsPath, mVerbose ? mOutStream : null);
- finalPackage(intermediateApk, dex, output, javaProject, libProjects,
- referencedJavaProjects,
+ finalPackage(intermediateApk, dex, output, libProjects,
info != null ? info.key : null, info != null ? info.certificate : null, resMarker);
}
@@ -341,9 +336,7 @@ public class BuildHelper {
* @param dex The path to the dex file.
* @param output The path to the final package file to create.
* @param debugSign whether the apk must be signed with the debug key.
- * @param javaProject the java project being compiled
* @param libProjects an optional list of library projects (can be null)
- * @param referencedJavaProjects referenced projects.
* @param abiFilter an optional filter. If not null, then only the matching ABI is included in
* the final archive
* @return true if success, false otherwise.
@@ -353,9 +346,8 @@ public class BuildHelper {
* @throws DuplicateFileException
*/
public void finalPackage(String intermediateApk, String dex, String output,
- final IJavaProject javaProject, List<IProject> libProjects,
- List<IJavaProject> referencedJavaProjects, PrivateKey key,
- X509Certificate certificate, ResourceMarker resMarker)
+ List<IProject> libProjects,
+ PrivateKey key, X509Certificate certificate, ResourceMarker resMarker)
throws NativeLibInJarException, ApkCreationException, DuplicateFileException,
CoreException {
@@ -365,19 +357,24 @@ public class BuildHelper {
mVerbose ? mOutStream: null);
apkBuilder.setDebugMode(mDebugMode);
- // Now we write the standard resources from the project and the referenced projects.
- writeStandardResources(apkBuilder, javaProject, referencedJavaProjects);
+ // either use the full compiled code paths or just the proguard file
+ // if present
+ Collection<String> pathsCollection = mCompiledCodePaths;
+ if (mProguardFile != null) {
+ pathsCollection = Collections.singletonList(mProguardFile);
+ mProguardFile = null;
+ }
- // Now we write the standard resources from the external jars
- for (String libraryOsPath : getExternalDependencies(resMarker)) {
- File libFile = new File(libraryOsPath);
- if (libFile.isFile()) {
- JarStatus jarStatus = apkBuilder.addResourcesFromJar(new File(libraryOsPath));
+ // Now we write the standard resources from all the output paths.
+ for (String path : pathsCollection) {
+ File file = new File(path);
+ if (file.isFile()) {
+ JarStatus jarStatus = apkBuilder.addResourcesFromJar(file);
// check if we found native libraries in the external library. This
// constitutes an error or warning depending on if they are in lib/
if (jarStatus.getNativeLibs().size() > 0) {
- String libName = new File(libraryOsPath).getName();
+ String libName = file.getName();
String msg = String.format(
"Native libraries detected in '%1$s'. See console for more information.",
@@ -418,11 +415,11 @@ public class BuildHelper {
}
}
}
- } else if (libFile.isDirectory()) {
+ } else if (file.isDirectory()) {
// this is technically not a source folder (class folder instead) but since we
// only care about Java resources (ie non class/java files) this will do the
// same
- apkBuilder.addSourceFolder(libFile);
+ apkBuilder.addSourceFolder(file);
}
}
@@ -453,66 +450,15 @@ public class BuildHelper {
}
}
- /**
- * Return a list of the project output for compiled Java code.
- * @return
- * @throws CoreException
- */
- public String[] getProjectJavaOutputs() throws CoreException {
- IFolder outputFolder = BaseProjectHelper.getJavaOutputFolder(mProject);
-
- // get the list of referenced projects output to add
- List<IProject> javaProjects = ProjectHelper.getReferencedProjects(mProject);
- List<IJavaProject> referencedJavaProjects = BuildHelper.getJavaProjects(javaProjects);
-
- // get the project output, and since it's a new list object, just add the outputFolder
- // of the project directly to it.
- List<String> projectOutputs = getProjectJavaOutputs(referencedJavaProjects);
-
- projectOutputs.add(0, outputFolder.getLocation().toOSString());
-
- return projectOutputs.toArray(new String[projectOutputs.size()]);
+ public void setProguardOutput(String proguardFile) {
+ mProguardFile = proguardFile;
}
- /**
- * Returns an array for all the compiled code for the project. This can include the
- * code compiled by Eclipse for the main project and dependencies (Java only projects), as well
- * as external jars used by the project or its library.
- *
- * This array of paths is compatible with the input for dx and can be passed as is to
- * {@link #executeDx(IJavaProject, String[], String)}.
- *
- * @param resMarker
- * @return a array (never empty) containing paths to compiled code.
- * @throws CoreException
- */
- public String[] getCompiledCodePaths(boolean includeProjectOutputs, ResourceMarker resMarker)
- throws CoreException {
-
- // get the list of libraries to include with the source code
- String[] libraries = getExternalDependencies(resMarker);
-
- int startIndex = 0;
-
- String[] compiledPaths;
-
- if (includeProjectOutputs) {
- String[] projectOutputs = getProjectJavaOutputs();
-
- compiledPaths = new String[libraries.length + projectOutputs.length];
-
- System.arraycopy(projectOutputs, 0, compiledPaths, 0, projectOutputs.length);
- startIndex = projectOutputs.length;
- } else {
- compiledPaths = new String[libraries.length];
- }
-
- System.arraycopy(libraries, 0, compiledPaths, startIndex, libraries.length);
-
- return compiledPaths;
+ public Collection<String> getCompiledCodePaths() {
+ return mCompiledCodePaths;
}
- public void runProguard(List<File> proguardConfigs, File inputJar, String[] jarFiles,
+ public void runProguard(List<File> proguardConfigs, File inputJar, Collection<String> jarFiles,
File obfuscatedJar, File logOutput)
throws ProguardResultException, ProguardExecException, IOException {
IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
@@ -723,13 +669,14 @@ public class BuildHelper {
/**
* Execute the Dx tool for dalvik code conversion.
* @param javaProject The java project
- * @param inputPath the path to the main input of dex
+ * @param inputPaths the input paths for DX
* @param osOutFilePath the path of the dex file to create.
*
* @throws CoreException
* @throws DexException
*/
- public void executeDx(IJavaProject javaProject, String[] inputPaths, String osOutFilePath)
+ public void executeDx(IJavaProject javaProject, Collection<String> inputPaths,
+ String osOutFilePath)
throws CoreException, DexException {
// get the dex wrapper
@@ -917,153 +864,94 @@ public class BuildHelper {
}
/**
- * Writes the standard resources of a project and its referenced projects
- * into a {@link SignedJarBuilder}.
- * Standard resources are non java/aidl files placed in the java package folders.
- * @param builder the archive builder.
- * @param javaProject the javaProject object.
- * @param referencedJavaProjects the java projects that this project references.
- * @throws ApkCreationException if an error occurred
- * @throws SealedApkException if the APK is already sealed.
- * @throws DuplicateFileException if a file conflicts with another already added to the APK
- * at the same location inside the APK archive.
- * @throws CoreException
- */
- private static void writeStandardResources(IArchiveBuilder builder, IJavaProject javaProject,
- List<IJavaProject> referencedJavaProjects)
- throws DuplicateFileException, ApkCreationException, SealedApkException,
- CoreException {
- IWorkspace ws = ResourcesPlugin.getWorkspace();
- IWorkspaceRoot wsRoot = ws.getRoot();
-
- writeStandardProjectResources(builder, javaProject, wsRoot);
-
- if (referencedJavaProjects != null) {
- for (IJavaProject referencedJavaProject : referencedJavaProjects) {
- // only include output from non android referenced project
- // (This is to handle the case of reference Android projects in the context of
- // instrumentation projects that need to reference the projects to be tested).
- if (referencedJavaProject.getProject().hasNature(
- AdtConstants.NATURE_DEFAULT) == false) {
- writeStandardProjectResources(builder, referencedJavaProject, wsRoot);
- }
- }
- }
- }
-
- /**
- * Writes the standard resources of a {@link IJavaProject} into a {@link SignedJarBuilder}.
- * Standard resources are non java/aidl files placed in the java package folders.
- * @param jarBuilder the {@link ApkBuilder}.
- * @param javaProject the javaProject object.
- * @param wsRoot the {@link IWorkspaceRoot}.
- * @throws ApkCreationException if an error occurred
- * @throws SealedApkException if the APK is already sealed.
- * @throws DuplicateFileException if a file conflicts with another already added to the APK
- * at the same location inside the APK archive.
+ * Computes all the project output and dependencies that must go into building the apk.
+ *
+ * @param resMarker
* @throws CoreException
*/
- private static void writeStandardProjectResources(IArchiveBuilder builder,
- IJavaProject javaProject, IWorkspaceRoot wsRoot)
- throws DuplicateFileException, ApkCreationException, SealedApkException, CoreException {
- // get the source pathes
- List<IPath> sourceFolders = BaseProjectHelper.getSourceClasspaths(javaProject);
-
- // loop on them and then recursively go through the content looking for matching files.
- for (IPath sourcePath : sourceFolders) {
- IResource sourceResource = wsRoot.findMember(sourcePath);
- if (sourceResource != null && sourceResource.getType() == IResource.FOLDER) {
- writeFolderResources(builder, javaProject, (IFolder) sourceResource);
- }
- }
- }
-
- private static void writeFolderResources(IArchiveBuilder builder,
- final IJavaProject javaProject, IFolder root) throws CoreException,
- ApkCreationException, SealedApkException, DuplicateFileException {
- final List<IPath> pathsToPackage = new ArrayList<IPath>();
- root.accept(new IResourceProxyVisitor() {
- @Override
- public boolean visit(IResourceProxy proxy) throws CoreException {
- if (proxy.getType() == IResource.FOLDER) {
- // If this folder isn't wanted, don't traverse into it.
- return ApkBuilder.checkFolderForPackaging(proxy.getName());
- }
- // If it's not a folder, it must be a file. We won't see any other resource type.
- if (!ApkBuilder.checkFileForPackaging(proxy.getName())) {
- return true;
- }
- IResource res = proxy.requestResource();
- if (!javaProject.isOnClasspath(res)) {
- return true;
- }
- // Just record that we need to package this. Packaging here throws
- // inappropriate checked exceptions.
- IPath location = res.getLocation();
- pathsToPackage.add(location);
- return true;
- }
- }, 0);
- IPath rootLocation = root.getLocation();
- for (IPath path : pathsToPackage) {
- IPath archivePath = path.makeRelativeTo(rootLocation);
- builder.addFile(path.toFile(), archivePath.toString());
- }
- }
+ private void gatherPaths(ResourceMarker resMarker)
+ throws CoreException {
+ IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
- /**
- * Returns an array of external dependencies used the project. This can be paths to jar files
- * or to source folders.
- *
- * @param resMarker if non null, used to put Resource marker on problem files.
- * @return an array of OS-specific absolute file paths
- */
- private final String[] getExternalDependencies(ResourceMarker resMarker) {
- // get a java project from it
+ // get a java project for the project.
IJavaProject javaProject = JavaCore.create(mProject);
- IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
- ArrayList<String> oslibraryList = new ArrayList<String>();
+ // get the output of the main project
+ IPath path = javaProject.getOutputLocation();
+ IResource outputResource = wsRoot.findMember(path);
+ if (outputResource != null && outputResource.getType() == IResource.FOLDER) {
+ mCompiledCodePaths.add(outputResource.getLocation().toOSString());
+ }
// we could use IJavaProject.getResolvedClasspath directly, but we actually
// want to see the containers themselves.
IClasspathEntry[] classpaths = javaProject.readRawClasspath();
if (classpaths != null) {
for (IClasspathEntry e : classpaths) {
- // if this is a classpath variable reference, we resolve it.
- if (e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
- e = JavaCore.getResolvedClasspathEntry(e);
+ // ignore non exported entries, unless it's the LIBRARIES container,
+ // in which case we always want it (there may be some older projects that
+ // have it as non exported).
+ if (e.isExported() ||
+ (e.getEntryKind() == IClasspathEntry.CPE_CONTAINER &&
+ e.getPath().toString().equals(AdtConstants.CONTAINER_LIBRARIES))) {
+ handleCPE(e, javaProject, wsRoot, resMarker);
}
+ }
+ }
+ }
- if (e.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
- handleClasspathEntry(e, wsRoot, oslibraryList, resMarker);
- } else if (e.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
- // get the container
- try {
- IClasspathContainer container = JavaCore.getClasspathContainer(
- e.getPath(), javaProject);
- // ignore the system and default_system types as they represent
- // libraries that are part of the runtime.
- if (container.getKind() == IClasspathContainer.K_APPLICATION) {
- IClasspathEntry[] entries = container.getClasspathEntries();
- for (IClasspathEntry entry : entries) {
- handleClasspathEntry(entry, wsRoot, oslibraryList, resMarker);
- }
- }
- } catch (JavaModelException jme) {
- // can't resolve the container? ignore it.
- AdtPlugin.log(jme, "Failed to resolve ClasspathContainer: %s", e.getPath());
+ private void handleCPE(IClasspathEntry entry, IJavaProject javaProject,
+ IWorkspaceRoot wsRoot, ResourceMarker resMarker) {
+
+ // if this is a classpath variable reference, we resolve it.
+ if (entry.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
+ entry = JavaCore.getResolvedClasspathEntry(entry);
+ }
+
+ if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
+ IProject refProject = wsRoot.getProject(entry.getPath().lastSegment());
+ try {
+ // ignore if it's an Android project, or if it's not a Java Project
+ if (refProject.hasNature(JavaCore.NATURE_ID) &&
+ refProject.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
+ IJavaProject refJavaProject = JavaCore.create(refProject);
+
+ // get the output folder
+ IPath path = refJavaProject.getOutputLocation();
+ IResource outputResource = wsRoot.findMember(path);
+ if (outputResource != null && outputResource.getType() == IResource.FOLDER) {
+ mCompiledCodePaths.add(outputResource.getLocation().toOSString());
}
}
+ } catch (CoreException exception) {
+ // can't query the project nature? ignore
}
- }
- return oslibraryList.toArray(new String[oslibraryList.size()]);
+ } else if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
+ handleClasspathLibrary(entry, wsRoot, resMarker);
+ } else if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
+ // get the container
+ try {
+ IClasspathContainer container = JavaCore.getClasspathContainer(
+ entry.getPath(), javaProject);
+ // ignore the system and default_system types as they represent
+ // libraries that are part of the runtime.
+ if (container.getKind() == IClasspathContainer.K_APPLICATION) {
+ IClasspathEntry[] entries = container.getClasspathEntries();
+ for (IClasspathEntry cpe : entries) {
+ handleCPE(cpe, javaProject, wsRoot, resMarker);
+ }
+ }
+ } catch (JavaModelException jme) {
+ // can't resolve the container? ignore it.
+ AdtPlugin.log(jme, "Failed to resolve ClasspathContainer: %s", entry.getPath());
+ }
+ }
}
- private void handleClasspathEntry(IClasspathEntry e, IWorkspaceRoot wsRoot,
- ArrayList<String> oslibraryList, ResourceMarker resMarker) {
+ private void handleClasspathLibrary(IClasspathEntry e, IWorkspaceRoot wsRoot,
+ ResourceMarker resMarker) {
// get the IPath
IPath path = e.getPath();
@@ -1077,7 +965,7 @@ public class BuildHelper {
// case of a jar file (which could be relative to the workspace or a full path)
if (resource != null && resource.exists() &&
resource.getType() == IResource.FILE) {
- oslibraryList.add(resource.getLocation().toOSString());
+ mCompiledCodePaths.add(resource.getLocation().toOSString());
} else {
// if the jar path doesn't match a workspace resource,
// then we get an OSString and check if this links to a valid file.
@@ -1085,7 +973,7 @@ public class BuildHelper {
File f = new File(osFullPath);
if (f.isFile()) {
- oslibraryList.add(osFullPath);
+ mCompiledCodePaths.add(osFullPath);
} else {
String message = String.format( Messages.Couldnt_Locate_s_Error,
path);
@@ -1102,7 +990,7 @@ public class BuildHelper {
// this can be the case for a class folder.
if (resource != null && resource.exists() &&
resource.getType() == IResource.FOLDER) {
- oslibraryList.add(resource.getLocation().toOSString());
+ mCompiledCodePaths.add(resource.getLocation().toOSString());
} else {
// if the path doesn't match a workspace resource,
// then we get an OSString and check if this links to a valid folder.
@@ -1110,53 +998,13 @@ public class BuildHelper {
File f = new File(osFullPath);
if (f.isDirectory()) {
- oslibraryList.add(osFullPath);
+ mCompiledCodePaths.add(osFullPath);
}
}
}
}
/**
- * Returns the list of the output folders for the specified {@link IJavaProject} objects, if
- * they are Android projects.
- *
- * @param referencedJavaProjects the java projects.
- * @return a new list object containing the output folder paths.
- * @throws CoreException
- */
- private List<String> getProjectJavaOutputs(List<IJavaProject> referencedJavaProjects)
- throws CoreException {
- ArrayList<String> list = new ArrayList<String>();
-
- IWorkspace ws = ResourcesPlugin.getWorkspace();
- IWorkspaceRoot wsRoot = ws.getRoot();
-
- for (IJavaProject javaProject : referencedJavaProjects) {
- // only include output from non android referenced project
- // (This is to handle the case of reference Android projects in the context of
- // instrumentation projects that need to reference the projects to be tested).
- if (javaProject.getProject().hasNature(AdtConstants.NATURE_DEFAULT) == false) {
- // get the output folder
- IPath path = null;
- try {
- path = javaProject.getOutputLocation();
- } catch (JavaModelException e) {
- continue;
- }
-
- IResource outputResource = wsRoot.findMember(path);
- if (outputResource != null && outputResource.getType() == IResource.FOLDER) {
- String outputOsPath = outputResource.getLocation().toOSString();
-
- list.add(outputOsPath);
- }
- }
- }
-
- return list;
- }
-
- /**
* Checks a {@link IFile} to make sure it should be packaged as standard resources.
* @param file the IFile representing the file.
* @return true if the file should be packaged as standard java resources.