diff --git a/.gitignore b/.gitignore
index cd99249298..9bf866d3bc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
.DS_Store
.AppleDouble
+*.iml
._*
*~
/build/shared/reference.zip
diff --git a/app/build.xml b/app/build.xml
index b6d97218ab..bef3f06b70 100644
--- a/app/build.xml
+++ b/app/build.xml
@@ -13,14 +13,14 @@
-
+
-
+
@@ -30,22 +30,19 @@
+ nowarn="true">
-
diff --git a/app/lib/jna-platform.jar b/app/lib/jna-platform.jar
index d0fd2e4f52..4722681821 100644
Binary files a/app/lib/jna-platform.jar and b/app/lib/jna-platform.jar differ
diff --git a/app/lib/jna.jar b/app/lib/jna.jar
index 25243176ea..0b5fabdd8b 100644
Binary files a/app/lib/jna.jar and b/app/lib/jna.jar differ
diff --git a/app/src/processing/app/Platform.java b/app/src/processing/app/Platform.java
index 16a31bf80e..dc3c8c0da1 100644
--- a/app/src/processing/app/Platform.java
+++ b/app/src/processing/app/Platform.java
@@ -339,10 +339,10 @@ static public File getJavaHome() {
File[] plugins = getContentFile("../PlugIns").listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return dir.isDirectory() &&
- name.endsWith(".jdk") && !name.startsWith(".");
+ name.contains("jdk") && !name.startsWith(".");
}
});
- return new File(plugins[0], "Contents/Home/jre");
+ return new File(plugins[0], "Contents/Home");
}
// On all other platforms, it's the 'java' folder adjacent to Processing
return getContentFile("java");
@@ -412,4 +412,4 @@ static public int unsetenv(String variable) {
static public int getSystemDPI() {
return inst.getSystemDPI();
}
-}
\ No newline at end of file
+}
diff --git a/app/src/processing/app/platform/LinuxPlatform.java b/app/src/processing/app/platform/LinuxPlatform.java
index fe3d7ce859..56c7859ded 100644
--- a/app/src/processing/app/platform/LinuxPlatform.java
+++ b/app/src/processing/app/platform/LinuxPlatform.java
@@ -40,20 +40,6 @@ public class LinuxPlatform extends DefaultPlatform {
public void initBase(Base base) {
super.initBase(base);
- String javaVendor = System.getProperty("java.vendor");
- String javaVM = System.getProperty("java.vm.name");
- if (javaVendor == null ||
- (!javaVendor.contains("Sun") && !javaVendor.contains("Oracle")) ||
- javaVM == null || !javaVM.contains("Java")) {
- Messages.showWarning("Not fond of this Java VM",
- "Processing requires Java 8 from Oracle.\n" +
- "Other versions such as OpenJDK, IcedTea,\n" +
- "and GCJ are strongly discouraged. Among other things, you're\n" +
- "likely to run into problems with sketch window size and\n" +
- "placement. For more background, please read the wiki:\n" +
- "https://github.com/processing/processing/wiki/Supported-Platforms#linux", null);
- }
-
// Set x11 WM_CLASS property which is used as the application
// name by Gnome3 and other window managers.
// https://github.com/processing/processing/issues/2534
diff --git a/app/src/processing/app/platform/ThinkDifferent.java b/app/src/processing/app/platform/ThinkDifferent.java
index dc905c62a3..01c9823bc3 100644
--- a/app/src/processing/app/platform/ThinkDifferent.java
+++ b/app/src/processing/app/platform/ThinkDifferent.java
@@ -24,11 +24,15 @@
import java.awt.event.*;
import java.io.File;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.List;
import javax.swing.*;
-import com.apple.eawt.*;
-import com.apple.eawt.AppEvent.*;
+import com.apple.eawt.Application;
import processing.app.*;
import processing.app.ui.About;
@@ -65,44 +69,40 @@ static protected void init(final Base base) {
if (adapter == null) {
adapter = new ThinkDifferent(); //base);
}
-
- application.setAboutHandler(new AboutHandler() {
- public void handleAbout(AboutEvent ae) {
- new About(null);
- }
+
+ setHandler(application, "setAboutHandler", (proxy, method, args) -> {
+ new About(null);
+ return null;
});
-
- application.setPreferencesHandler(new PreferencesHandler() {
- public void handlePreferences(PreferencesEvent arg0) {
- base.handlePrefs();
- }
+
+ setHandler(application, "setPreferencesHandler", (proxy, method, args) -> {
+ base.handlePrefs();
+ return null;
});
- application.setOpenFileHandler(new OpenFilesHandler() {
- public void openFiles(OpenFilesEvent event) {
- for (File file : event.getFiles()) {
- base.handleOpen(file.getAbsolutePath());
- }
+ setHandler(application, "setOpenFileHandler", (proxy, method, args) -> {
+ Method m = args[0].getClass().getMethod("getFiles");
+ for (File file : (List) m.invoke(args[0])) {
+ base.handleOpen(file.getAbsolutePath());
}
+ return null;
});
-
- application.setPrintFileHandler(new PrintFilesHandler() {
- public void printFiles(PrintFilesEvent event) {
- // TODO not yet implemented
- }
+
+ setHandler(application, "setPrintFileHandler", (proxy, method, args) -> {
+ // TODO not yet implemented
+ return null;
});
-
- application.setQuitHandler(new QuitHandler() {
- public void handleQuitRequestWith(QuitEvent event, QuitResponse response) {
- if (base.handleQuit()) {
- response.performQuit();
- } else {
- response.cancelQuit();
- }
+
+ setHandler(application, "setQuitHandler", (proxy, method, args) -> {
+ if (base.handleQuit()) {
+ args[1].getClass().getMethod("performQuit").invoke(args[1]);
+ } else {
+ args[1].getClass().getMethod("cancelQuit").invoke(args[1]);
}
+ return null;
});
- // Set the menubar to be used when nothing else is open.
+ // Set the menubar to be used when nothing else is open.
JMenuBar defaultMenuBar = new JMenuBar();
JMenu fileMenu = buildFileMenu(base);
defaultMenuBar.add(fileMenu);
@@ -117,12 +117,12 @@ public void handleQuitRequestWith(QuitEvent event, QuitResponse response) {
e.printStackTrace(); // oh well, never mind
}
// } else {
-// // The douchebags at Oracle didn't feel that a working f*king menubar
-// // on OS X was important enough to make it into the 7u40 release.
+// // The douchebags at Oracle didn't feel that a working f*king menubar
+// // on OS X was important enough to make it into the 7u40 release.
// //http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8007267
// // It languished in the JDK 8 source and has been backported for 7u60:
// //http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8022667
-//
+//
// JFrame offscreen = new JFrame();
// offscreen.setUndecorated(true);
// offscreen.setJMenuBar(defaultMenuBar);
@@ -131,12 +131,51 @@ public void handleQuitRequestWith(QuitEvent event, QuitResponse response) {
// offscreen.setVisible(true);
// }
}
-
+
// public ThinkDifferent(Base base) {
// this.base = base;
// }
+ /**
+ * Sets a handler on an instance of {@link Application}, taking into account JVM version
+ * differences.
+ *
+ * @param app an instance of {@link Application}
+ * @param name the "set handler" method name
+ * @param handler the handler
+ */
+ private static void setHandler(Application app, String name, InvocationHandler handler) {
+ // Determine which version of com.apple.eawt.Application to use and pass it a handler of the
+ // appropriate type
+ Method[] methods = app.getClass().getMethods();
+ for (Method m : methods) {
+ if (!name.equals(m.getName())) {
+ continue;
+ }
+ if (m.getParameterCount() != 1) {
+ continue;
+ }
+ Class paramType = m.getParameterTypes()[0];
+ try {
+ // Allow a null handler
+ Object proxy = null;
+ if (handler != null) {
+ proxy = Proxy.newProxyInstance(
+ paramType.getClassLoader(), new Class>[] { paramType }, handler);
+ }
+ m.invoke(app, proxy);
+ } catch (IllegalArgumentException ex) {
+ // TODO: Print error?: method doesn't take an interface, etc.
+ } catch (IllegalAccessException ex) {
+ // TODO: Print error?: Other method invocation problem
+ } catch (InvocationTargetException ex) {
+ ex.getCause().printStackTrace();
+ // TODO: Print ex.getCause() a different way?
+ }
+ break;
+ }
+ }
/**
* Gimpy file menu to be used on OS X when no sketches are open.
@@ -162,7 +201,7 @@ public void actionPerformed(ActionEvent e) {
fileMenu.add(item);
item = Toolkit.newJMenuItemShift(Language.text("menu.file.sketchbook"), 'K');
- item.addActionListener(new ActionListener() {
+ item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
base.getNextMode().showSketchbookFrame();
diff --git a/app/src/processing/app/platform/WindowsPlatform.java b/app/src/processing/app/platform/WindowsPlatform.java
index 3c4759a375..fd29eed58b 100644
--- a/app/src/processing/app/platform/WindowsPlatform.java
+++ b/app/src/processing/app/platform/WindowsPlatform.java
@@ -64,10 +64,16 @@ public class WindowsPlatform extends DefaultPlatform {
"\\" + APP_NAME.toLowerCase() + ".exe \"%1\"";
static final String REG_DOC = APP_NAME + ".Document";
+ // Starting with Java 9, the scaling is done automatically. If DPI is
+ // used to scaling within the application, one ends up with 2x the
+ // expected scale. See JEP 263.
+ private static final int WINDOWS_NATIVE_DPI = 96;
public void initBase(Base base) {
super.initBase(base);
+
checkAssociations();
+
//checkQuickTime();
checkPath();
@@ -627,56 +633,9 @@ public int unsetenv(String variable) {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
- // Need to extend com.sun.jna.platform.win32.User32 to access
- // Win32 function GetDpiForSystem()
- interface ExtUser32 extends StdCallLibrary, com.sun.jna.platform.win32.User32 {
- ExtUser32 INSTANCE = (ExtUser32) Native.loadLibrary("user32", ExtUser32.class, W32APIOptions.DEFAULT_OPTIONS);
-
- public int GetDpiForSystem();
-
- public int SetProcessDpiAwareness(int value);
-
- public final int DPI_AWARENESS_INVALID = -1;
- public final int DPI_AWARENESS_UNAWARE = 0;
- public final int DPI_AWARENESS_SYSTEM_AWARE = 1;
- public final int DPI_AWARENESS_PER_MONITOR_AWARE = 2;
-
- public Pointer SetThreadDpiAwarenessContext(Pointer dpiContext);
-
- public final Pointer DPI_AWARENESS_CONTEXT_UNAWARE = new Pointer(-1);
- public final Pointer DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = new Pointer(-2);
- public final Pointer DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = new Pointer(-3);
- }
-
-
- static private int detected = detectSystemDPI();
-
-
public int getSystemDPI() {
- if (detected == -1) {
- return super.getSystemDPI();
- }
- return detected;
+ // Note that this is supported "natively" within Java - See JEP 263.
+ return WINDOWS_NATIVE_DPI;
}
-
- public static int detectSystemDPI() {
- try {
- ExtUser32.INSTANCE.SetProcessDpiAwareness(ExtUser32.DPI_AWARENESS_SYSTEM_AWARE);
- } catch (Throwable e) {
- // Ignore error
- }
- try {
- ExtUser32.INSTANCE.SetThreadDpiAwarenessContext(ExtUser32.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
- } catch (Throwable e) {
- // Ignore error (call valid only on Windows 10)
- }
- try {
- return ExtUser32.INSTANCE.GetDpiForSystem();
- } catch (Throwable e) {
- // DPI detection failed, fall back with default
- System.out.println("DPI detection failed, fallback to 96 dpi");
- return -1;
- }
- }
}
diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java
index b63084c63a..c3e6c617cb 100644
--- a/app/src/processing/app/syntax/JEditTextArea.java
+++ b/app/src/processing/app/syntax/JEditTextArea.java
@@ -76,12 +76,17 @@ public class JEditTextArea extends JComponent
/** The size of the offset between the leftmost padding and the code */
public static final int leftHandGutter = 6;
+ private final Segment TEST_SEGMENT;
+
private InputMethodSupport inputMethodSupport;
private TextAreaDefaults defaults;
private Brackets bracketHelper = new Brackets();
+ private FontMetrics cachedPartialPixelWidthFont;
+ private float partialPixelWidth;
+
/**
* Creates a new JEditTextArea with the specified settings.
@@ -90,6 +95,9 @@ public class JEditTextArea extends JComponent
public JEditTextArea(TextAreaDefaults defaults, InputHandler inputHandler) {
this.defaults = defaults;
+ char[] testSegmentContents = {'w'};
+ TEST_SEGMENT = new Segment(testSegmentContents, 0, 1);
+
// Enable the necessary events
enableEvents(AWTEvent.KEY_EVENT_MASK);
@@ -113,6 +121,8 @@ public void actionPerformed(ActionEvent e) {
lineSegment = new Segment();
bracketLine = bracketPosition = -1;
blink = true;
+ cachedPartialPixelWidthFont = null;
+ partialPixelWidth = 0;
// Initialize the GUI
setLayout(new ScrollLayout());
@@ -632,7 +642,7 @@ public int _offsetToX(int line, int offset) {
// If syntax coloring is disabled, do simple translation
if (tokenMarker == null) {
lineSegment.count = offset;
- return x + Utilities.getTabbedTextWidth(lineSegment, fm, x, painter, 0);
+ return x + getTabbedTextWidth(lineSegment, fm, x, painter, 0);
} else {
// If syntax coloring is enabled, we have to do this
@@ -664,10 +674,10 @@ public int _offsetToX(int line, int offset) {
int length = tokens.length;
if (offset + segmentOffset < lineSegment.offset + length) {
lineSegment.count = offset - (lineSegment.offset - segmentOffset);
- return x + Utilities.getTabbedTextWidth(lineSegment, fm, x, painter, 0);
+ return x + getTabbedTextWidth(lineSegment, fm, x, painter, 0);
} else {
lineSegment.count = length;
- x += Utilities.getTabbedTextWidth(lineSegment, fm, x, painter, 0);
+ x += getTabbedTextWidth(lineSegment, fm, x, painter, 0);
lineSegment.offset += length;
}
tokens = tokens.next;
@@ -1310,6 +1320,72 @@ else if ( Character.isWhitespace(ch) )
return CharacterKinds.Other;
}
+ /**
+ * Get the width in pixels of a segment of text within the IDE.
+ *
+ *
+ * Fractional-font aware implementation of Utilities.getTabbedTextWidth that determines if there
+ * are fractional character widths present in a font in order to return a more accurate pixel
+ * width for an input segment.
+ *
+ *
+ * @param s The segment of text for which a pixel width should be returned.
+ * @param metrics The metrics for the font in which the given segment will be drawn.
+ * @param x The x origin.
+ * @param expander The strategy for converting tabs into characters.
+ * @param startOffset The offset to apply before the text will be drawn.
+ * @return The width of the input segment in pixels with fractional character widths considered.
+ */
+ private int getTabbedTextWidth(Segment s, FontMetrics metrics, float x, TabExpander expander,
+ int startOffset) {
+
+ float additionalOffset = getPartialPixelWidth(metrics, x, expander, startOffset) * s.length();
+
+ return (int) Math.round(
+ Utilities.getTabbedTextWidth(s, metrics, x, expander, startOffset) + additionalOffset
+ );
+ }
+
+ /**
+ * Get any partial widths applied within a font.
+ *
+ *
+ * Get any partial widths applied within a font, caching results for the latest requested font
+ * (as identified via a FontMetrics object). Note that this is calculated for a sample character
+ * and is only valid for extrapolation in a monospaced font (that one might want to use in an
+ * IDE).
+ *
Corresponds to the CFBundleIconFile key in the Info.plist file.
No
+
+
hideDockIcon
+
Set to true to prevent display of the application icon on the Mac OS X dock.
+ Corresponds to the LSUIElement key in the Info.plist file.
+ (Details)
+
No
+
shortversion
The release version number string for the application.
@@ -100,10 +107,86 @@
Parameters
file.
No
+
+
privileged
+
Bool value if the bundled application is to be launched as privileged.
+
No
+
mainclassname
The name of the bundled application's main class.
+
Yes, alternative to jnlplaunchername
Yes
+
+
+
plistClassPaths
+
A comma-separated list of classpath-elements which are used for the java application.
+ $APP_ROOT is replaced by the path of the .app-bundle. Required only if not
+ the libraries specified by the classpath and librarypath should be used.
+
No
+
+
+
jvmRequired
+
Specifies the required Java virtual machine version.
+
No (defaults to 1.7)
+
+
+
jrePreferred
+
If set to true, a Java Runtime Edition is required for execution. If both jrePreferred and jdkPreferred are set true, then a JRE is preferred but a JDK ia also accepted.
+
No
+
+
+
jdkPreferred
+
If set to true, a Java Development Kit is required for execution. If both jrePreferred and jdkPreferred are set true, then a JRE is preferred but a JDK ia also accepted.
+
No
+
+
+
minimumSystemVersion
+
Indicates the minimum version of OS X required for this app to run.
+ Corresponds to the LSMinimumSystemVersion key in the Info.plist
+ file.
The name of the bundled applications's jnlp file. The file has to be be copied to the Contents/Java folder. It has to be present during execution of the application. This serves as option to deliver signed applications based upon JNLP files. The application can then be signed and the signature won't break when modifying the JNLP file.
+
No, alternative to mainclassname
+
+
+
workingdirectory
+
Specifies the working directory of the application.
+ $APP_ROOT is replaced by the path of the .app-bundle.
+
No
+
+
+
privileged
+
If set, the application is started with administrator privileges.
+
No
+
+
+
highResolutionCapable
+
Sets the High Resolution Capable flag.
+ Corresponds to the NSHighResolutionCapable key in the Info.plist
+ file.
+
No
+
+
+
supportsAutomaticGraphicsSwitching
+
Allow OpenGL applications to utilize the integrated GPU.
+ Corresponds to the NSSupportsAutomaticGraphicsSwitching key in the Info.plist
+
No
+
+
+
ignorePSN
+
If set to true, the -psn... arguments passed by the OS
+ to the application is filtered out.
+
No
+
+
+
isDebug
+
If set to true, additiona console output will be produced during startup.
+
No
@@ -137,6 +220,23 @@
librarypath
option
Specifies a command-line option to be passed to the JVM at startup.
+
Options may be named, which allows the bundled Java program
+itself to override the option (e.g. to adjust heap settings). Changes will take effect upon restart of the
+application. Assuming your CFBundleIdentifier (settable via the attribute identifier)
+is com.oracle.appbundler. Then you can override a named option by calling
+
import java.util.prefs.Preferences;
+ [...]
+ Preferences jvmOptions = Preferences.userRoot().node("/com/oracle/appbundler/JVMOptions");
+ jvmOptions.put("name", "value"); // use option name and desired value here
+ jvmOptions.flush();
+The corresponding entries will be stored in a file called
+~/Library/Preferences/com.oracle.appbundler.plist. To manipulate the file
+without Java's Preferences from the command line, you should use the tool
+defaults.
+For example, to add an entry via the command line, use:
+ defaults write com.oracle.appbundler /com/oracle/appbundler/ -dict-add JVMOptions/ '{"name"="value";}'
+Of named options, only the value is passed to the JVM, not the name. The name merely serves as identifier.
+
Attribute
@@ -148,6 +248,11 @@
option
The option value.
Yes
+
+
name
+
The option name.
+
No
+
argument
@@ -165,6 +270,218 @@
argument
+
environment
+
Specifies an environment variable set via LSEnvironment entry in the Info.plist file. See the
+ Apple Documentation for details.
+
The string $APP_ROOT will be replaced by the application bundle path in all environment variables on runtime.
+
+
+
Attribute
+
Description
+
Required
+
+
+
name
+
The variable name.
+
Yes
+
+
+
value
+
The variable value.
+
Yes
+
+
+
+
architecture
+
Specifies the elements of the LSArchitecturePriority array in Info.plist.
+(Details)
+
+
+
Attribute
+
Description
+
Required
+
+
+
name
+
The architecture name.
+
Yes
+
+
+
+
plistentry
+
Adds an arbitrary entry to the generated Info.plist.
+
+
+
Attribute
+
Description
+
Required
+
+
+
key
+
The entry's key.
+
Yes
+
+
+
value
+
The entry's value.
+
Yes
+
+
+
type
+
The entry's type.
+
No (defaults to String)
+
+
+
+
+
scheme
+
Specifies a protocol for which the bundled application should be registered as a handler.
+
+
+
Attribute
+
Description
+
Required
+
+
+
value
+
The protocol scheme.
+
Yes
+
+
+
+
bundledocument
+
Describes a document associated with your application.
+
+
+
Attribute
+
Description
+
Corresponding CFBundleDocumentTypes key
+
Required
+
+
+
contentTypes
+
A comma-separated list of Uniform Type Identifiers defining each a supported file type.
+ If you reference custom filetypes, declare them using a typedeclaration.
+ If set, extensions and isPackage are ignored. See the UTI reference for a list of default UTIs.
+
LSItemContentTypes
+
Yes (or extensions)
+
+
+
extensions
+
A comma-separated list of filename extensions (minus the leading period) to map to this document type.
+
CFBundleTypeExtensions
+
Yes (or contentTypes)
+
+
+
icon
+
The name of the icon file (.icns) to associate with this OS X document type.
+
CFBundleTypeIconFile
+
No (But recommended)
+
+
+
name
+
The abstract name for the document type.
+
CFBundleTypeName
+
No (But recommended)
+
+
+
role
+
This key specifies the app's role with respect to the type. The value can be Editor, Viewer, Shell, or None.
+
CFBundleTypeRole
+
No (default: Editor)
+
+
+
handlerRank
+
Determines how Launch Services ranks this app among the apps that declare themselves editors or viewers of files of this type. The possible values are: Owner (this app is the creator of files of this type), Alternate (this app is a secondary viewer of files of this type), None (this app must never be used to open files of this type, but it accepts drops of files of this type), Default (default; this app doesn't accept drops of files of this type).
+
LSHandlerRank
+
No
+
+
+
isPackage
+
Specifies whether the document is distributed as a bundle. If set to true, the bundle directory is treated as a file.
The unique UTI for the declared type. Following the reverse-DNS format beginning with com.companyName is a simple way to ensure uniqueness.
+
UTTypeIdentifier
+
Yes
+
+
+
description
+
A user-visible description of this type.
+
UTTypeDescription
+
No
+
+
+
icon
+
The name of the icon file (.icns) to associate with this UTI.
+
UTTypeIconFile
+
No
+
+
+
referenceUrl
+
The URL of a reference document describing this type.
+
UTTypeReferenceURL
+
No
+
+
+
conformsTo
+
A comma-separated list of UTIs to which this identifier conforms.
+ Although a custom UTI can conform to any UTI, public.data or com.apple.package must be at the root of the conformance hierarchy.
+
UTTypeConformsTo
+
No (defaults to public.data)
+
+
+
extensions
+
A comma-separated list of filename extensions (minus the leading period) to map to this UTI.
A comma-separated list of mime types which identify this UTI.
+
UTTypeTagSpecification -> public.mime-type
+
No
+
+
+
osTypes
+
Comma-separated list of four-character codes formerly used to identify types.
+ Only specify OSTypes if you really know what you're doing.
+
UTTypeTagSpecification -> com.apple.ostype
+
No
+
+
+
imported
+
If set to true, the declaration is listed under the UTImportedTypeDeclarations key.
+ Otherwise, it is listed under UTExportedTypeDeclarations.
+ If your code relies on third-party UTI types that may not be present on the system, you should declare those UTIs as imported types.
+
-
+
No
+
+
+
Examples
Generate a launcher for the "Swing Set" demo, bundling the JRE defined by the JAVA_HOME
environment variable with the resulting executable.
Generate a launcher for a JNLP File, copy it into the package and sign the package. You need to have a Developer ID Profile to sign the application.
+
You can now dynamically modify the zip content (only the JNLP file), deliver it with your web service and the application should directly unpack and can be run