changeset 1140:eb4fbb8e17d9

- Implemented jvm respawn, in case the jvm exits for whatever reason - Fix http://www.goes.noaa.gov/HURRLOOPS/huirloop.html - Fix panel size on initialization -- it is now correct
author Deepak Bhole <dbhole@redhat.com>
date Tue, 21 Oct 2008 16:17:19 -0400
parents 5fabd5969958
children 560a859a217b
files ChangeLog IcedTeaPlugin.cc plugin/icedtea/sun/applet/PluginAppletSecurityContext.java plugin/icedtea/sun/applet/PluginAppletViewer.java plugin/icedtea/sun/applet/PluginMain.java
diffstat 5 files changed, 303 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Oct 21 16:45:19 2008 +0200
+++ b/ChangeLog	Tue Oct 21 16:17:19 2008 -0400
@@ -1,3 +1,13 @@
+2008-10-21  Deepak Bhole  <dbhole@redhat.com>
+
+	* IcedTeaPlugin.cc: Implemented JVM respawning.
+	* plugin/icedtea/sun/applet/PluginAppletSecurityContext.java: Prepopulate
+	classes requested by LiveConnect at initialization.
+	* plugin/icedtea/sun/applet/PluginAppletViewer.java: Fix panel applet
+	resizing when window height and width is in %.
+	* plugin/icedtea/sun/applet/PluginMain.java: Handle error during NetX
+	initialization gracefully.
+
 2008-10-21  Matthias Klose  <doko@ubuntu.com>
 
 	* configure.ac: Add new option --with-pkgversion,
--- a/IcedTeaPlugin.cc	Tue Oct 21 16:45:19 2008 +0200
+++ b/IcedTeaPlugin.cc	Tue Oct 21 16:17:19 2008 -0400
@@ -319,6 +319,8 @@
 static PRBool factory_created = PR_FALSE;
 static IcedTeaPluginFactory* factory = NULL;
 
+static PRBool jvm_attached = PR_FALSE;
+
 // Applet viewer input channel (needs to be static because it is used in plugin_in_pipe_callback)
 GIOChannel* in_from_appletviewer = NULL;
 
@@ -492,30 +494,24 @@
 // shutdown (so that the permanent loop does not block 
 // proper exit). We need better error handling
 
-#define PROCESS_PENDING_EVENTS_REF2(reference)  \
-    ResultContainer *resultC;                    \
-	factory->result_map.Get(reference, &resultC); \
-    if (factory->shutting_down == PR_TRUE && \
-		resultC->errorOccurred == PR_TRUE) \
-	{                                                           \
-		PLUGIN_DEBUG_0ARG("Error occured. Exiting function\n");            \
+#define PROCESS_PENDING_EVENTS_REF(reference) \
+    if (jvm_attached == PR_FALSE) \
+	{ \
+	    fprintf(stderr, "Error on Java side detected. Abandoning wait and returning.\n"); \
 		return NS_ERROR_FAILURE; \
 	} \
-	PRBool hasPending;  \
-	factory->current->HasPendingEvents(&hasPending); \
-	if (hasPending == PR_TRUE) { \
-		PRBool processed = PR_FALSE; \
-		factory->current->ProcessNextEvent(PR_TRUE, &processed); \
-	} else if (g_main_context_pending (NULL)) { \
-	   g_main_context_iteration(NULL, false); \
-	} else { \
-		PR_Sleep(PR_INTERVAL_NO_WAIT); \
-	}
-
-
-#define PROCESS_PENDING_EVENTS_REF(reference) \
 	if (g_main_context_pending (NULL)) { \
 	   g_main_context_iteration(NULL, false); \
+	} \
+    PRBool hasPending;  \
+    factory->current->HasPendingEvents(&hasPending); \
+	if (hasPending == PR_TRUE) \
+	{ \
+	  PRBool processed = PR_FALSE; \
+	  factory->current->ProcessNextEvent(PR_TRUE, &processed); \
+	} else \
+	{\
+	    PR_Sleep(PR_INTERVAL_NO_WAIT); \
 	}
 
 #define PROCESS_PENDING_EVENTS \
@@ -524,7 +520,11 @@
 	if (hasPending == PR_TRUE) { \
 		PRBool processed = PR_FALSE; \
 		factory->current->ProcessNextEvent(PR_TRUE, &processed); \
-	} else { \
+	} \
+	if (g_main_context_pending (NULL)) { \
+       g_main_context_iteration(NULL, false); \
+    } else \
+    { \
 		PR_Sleep(PR_INTERVAL_NO_WAIT); \
 	}
 
@@ -945,6 +945,7 @@
   nsISecureEnv* secureEnv;
   nsDataHashtable<nsUint32HashKey,ResultContainer*> result_map;
 
+  void InitializeJava();
   void GetMember ();
   void SetMember ();
   void GetSlot ();
@@ -954,6 +955,7 @@
   void Call ();
   void Finalize ();
   void ToString ();
+  void MarkInstancesVoid ();
   nsCOMPtr<nsILiveconnect> liveconnect;
 
   // normally, we shouldn't have to track unref'd handles, but in some cases, 
@@ -970,21 +972,21 @@
   nsresult StartAppletviewer ();
   void ProcessMessage();
   void ConsumeMsgFromJVM();
-  void InitializeJava();
   nsCOMPtr<IcedTeaEventSink> sink;
   nsCOMPtr<nsISocketTransport> transport;
   nsCOMPtr<nsIProcess> applet_viewer_process;
   PRBool connected;
   PRUint32 next_instance_identifier;
-  // Does not do construction/deconstruction or reference counting.
-  nsDataHashtable<nsUint32HashKey, IcedTeaPluginInstance*> instances;
   PRUint32 object_identifier_return;
+  PRUint32 instance_count;
   int javascript_identifier;
   int name_identifier;
   int args_identifier;
   int string_identifier;
   int slot_index;
   int value_identifier;
+  // Does not do construction/deconstruction or reference counting.
+  nsDataHashtable<nsUint32HashKey, IcedTeaPluginInstance*> instances;
 
   // Applet viewer input pipe name.
   gchar* in_pipe_name;
@@ -1015,6 +1017,7 @@
 
   nsIPluginInstancePeer* peer;
   PRBool initialized;
+  PRBool fatalErrorOccurred;
 
 private:
 
@@ -1449,6 +1452,7 @@
   value_identifier (0),
   connected (PR_FALSE),
   liveconnect (0),
+  instance_count(0),
   shutting_down(PR_FALSE),
   in_pipe_name(NULL),
   in_watch_source(NULL),
@@ -1533,6 +1537,7 @@
   if (!instance)
     return NS_ERROR_OUT_OF_MEMORY;
 
+  instance_count++;
   return instance->QueryInterface (iid, result);
 }
 
@@ -1576,7 +1581,16 @@
   result = threadManager->GetCurrentThread (getter_AddRefs (current));
   PLUGIN_CHECK_RETURN ("current thread", result);
 
-  InitializeJava();
+  if (jvm_attached == PR_FALSE)
+  {
+    // using printf on purpose.. this should happen rarely
+    printf("Initializing JVM...\n");
+
+    // mark attached right away, in case another initialize() call 
+    //is made (happens if multiple applets are present on the same page)
+    jvm_attached = PR_TRUE;
+    InitializeJava();
+  }
 
   return NS_OK;
 }
@@ -1610,7 +1624,26 @@
 
   result = StartAppletviewer ();
   PLUGIN_CHECK ("started appletviewer", result);
-
+}
+
+void
+IcedTeaPluginFactory::MarkInstancesVoid ()
+{
+      PLUGIN_TRACE_FACTORY ();
+	
+      IcedTeaPluginInstance* instance = NULL;
+
+      int instance_id = 1;
+
+      while (instance_id <= instance_count)
+	  {
+		if (instances.Get(instance_id, &instance))
+		{
+            PLUGIN_DEBUG_2ARG("Marking %d of %d void\n", instance_id, instance_count);
+            instance->fatalErrorOccurred = PR_TRUE;
+	    }
+		instance_id++;
+	  }
 }
 
 NS_IMETHODIMP
@@ -2218,11 +2251,25 @@
 NS_IMPL_ISUPPORTS2 (IcedTeaPluginInstance, nsIPluginInstance,
                     nsIJVMPluginInstance)
 
+
 NS_IMETHODIMP
 IcedTeaPluginInstance::Initialize (nsIPluginInstancePeer* aPeer)
 {
   PLUGIN_TRACE_INSTANCE ();
 
+  // Ensure that there is a jvm running...
+ 
+  if (jvm_attached == PR_FALSE)
+  {
+    // using printf on purpose.. this should happen rarely
+    fprintf(stderr, "WARNING: Looks like the JVM is not up. Attempting to re-initialize...\n");
+
+    // mark attached right away, in case another initialize() call 
+    //is made (happens if multiple applets are present on the same page)
+    jvm_attached = PR_TRUE;
+    factory->InitializeJava();
+  }
+
   // Send applet tag message to appletviewer.
   // FIXME: nsCOMPtr
   char const* documentbase;
@@ -2254,8 +2301,8 @@
   tagMessage += appletTag;
   tagMessage += "</embed>";
 
-  // remove \n characters from the message
-  tagMessage.StripChars("\n");
+  // remove newline characters from the message
+  tagMessage.StripChars("\r\n");
 
   factory->SendMessageToAppletViewer (tagMessage);
 
@@ -2307,6 +2354,11 @@
 {
   PLUGIN_TRACE_INSTANCE ();
 
+  if (fatalErrorOccurred == PR_TRUE)
+  {
+      return NS_OK;
+  }
+
   nsCString destroyMessage (instanceIdentifierPrefix);
   destroyMessage += "destroy";
   factory->SendMessageToAppletViewer (destroyMessage);
@@ -2335,10 +2387,17 @@
 
             PLUGIN_DEBUG_1ARG ("IcedTeaPluginInstance::SetWindow: Instance %p waiting for initialization...\n", this);
 
-           while (initialized == PR_FALSE) {
+           while (initialized == PR_FALSE && this->fatalErrorOccurred == PR_FALSE) {
               PROCESS_PENDING_EVENTS;
             }
 
+            // did we bail because there is no jvm?
+            if (this->fatalErrorOccurred == PR_TRUE)
+			{
+				PLUGIN_DEBUG_0ARG("Initialization failed. SetWindow returning\n");
+				return NS_ERROR_FAILURE;
+			}
+
             PLUGIN_DEBUG_1ARG ("Instance %p initialization complete...\n", this);
        }
 
@@ -2584,6 +2643,9 @@
   {
       PLUGIN_DEBUG ("appletviewer has stopped.");
       keep_installed = FALSE;
+	  jvm_attached = PR_FALSE;
+
+	  factory->MarkInstancesVoid();
   } else
   {
   
@@ -2667,10 +2729,7 @@
   return NS_OK;
 }
 
-#include <nsIWebNavigation.h>
 #include <nsServiceManagerUtils.h>
-#include <nsIExternalProtocolService.h>
-#include <nsNetUtil.h>
 
 void
 IcedTeaPluginFactory::HandleMessage (nsCString const& message)
@@ -2725,7 +2784,6 @@
           if (instance != 0)
 		  {
             instance->peer->ShowStatus (nsCString (rest).get ());
-
           }
         }
       else if (command == "initialized")
@@ -2738,6 +2796,16 @@
 			PLUGIN_DEBUG_1ARG ("to %d...\n", instance->initialized);
 		  }
 		}
+      else if (command == "fatalError")
+        {
+          IcedTeaPluginInstance* instance = NULL;
+          instances.Get (identifier, &instance);
+          if (instance != 0) {
+			PLUGIN_DEBUG_2ARG ("Setting instance.fatalErrorOccurred for %p from %d ", instance, instance->fatalErrorOccurred);
+            instance->fatalErrorOccurred = PR_TRUE;
+			PLUGIN_DEBUG_1ARG ("to %d...\n", instance->fatalErrorOccurred);
+		  }
+		}
       else if (command == "url")
         {
           IcedTeaPluginInstance* instance = NULL;
@@ -3727,6 +3795,7 @@
   peer(0),
   liveconnect_window (0),
   initialized(PR_FALSE),
+  fatalErrorOccurred(PR_FALSE),
   instanceIdentifierPrefix ("")
 {
   PLUGIN_TRACE_INSTANCE ();
@@ -4272,7 +4341,6 @@
 #include <nsNetCID.h>
 #include <nsServiceManagerUtils.h>
 #include <iostream>
-#include <jvmmgr.h>
 #include <nsIPrincipal.h>
 #include <nsIScriptSecurityManager.h>
 #include <nsIURI.h>
@@ -5444,6 +5512,8 @@
               char const* aClassName, char const* aContractID,
               nsIFactory** aFactory)
 {
+  PLUGIN_DEBUG_0ARG("NSGetFactory called\n");
+
   static NS_DEFINE_CID (PluginCID, NS_PLUGIN_CID);
   if (!aClass.Equals (PluginCID))
     return NS_ERROR_FACTORY_NOT_LOADED;
--- a/plugin/icedtea/sun/applet/PluginAppletSecurityContext.java	Tue Oct 21 16:45:19 2008 +0200
+++ b/plugin/icedtea/sun/applet/PluginAppletSecurityContext.java	Tue Oct 21 16:17:19 2008 -0400
@@ -1010,6 +1010,135 @@
 				+ " " + message);
 	}
 	
+	public void prePopulateLCClasses() {
+		
+		int classID;
+		
+		prepopulateClass("netscape/javascript/JSObject");
+		classID = prepopulateClass("netscape/javascript/JSException");
+		prepopulateMethod(classID, "<init>", "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;I)V");
+		prepopulateMethod(classID, "<init>", "(ILjava/lang/Object;)V");
+		prepopulateField(classID, "lineno");
+		prepopulateField(classID, "tokenIndex");
+		prepopulateField(classID, "source");
+		prepopulateField(classID, "filename");
+		prepopulateField(classID, "wrappedExceptionType");
+		prepopulateField(classID, "wrappedException");
+		
+		classID = prepopulateClass("netscape/javascript/JSUtil");
+		prepopulateMethod(classID, "getStackTrace", "(Ljava/lang/Throwable;)Ljava/lang/String;");
+
+		prepopulateClass("java/lang/Object");
+		classID = prepopulateClass("java/lang/Class");
+		prepopulateMethod(classID, "getMethods", "()[Ljava/lang/reflect/Method;");
+		prepopulateMethod(classID, "getConstructors", "()[Ljava/lang/reflect/Constructor;");
+		prepopulateMethod(classID, "getFields", "()[Ljava/lang/reflect/Field;");
+		prepopulateMethod(classID, "getName", "()Ljava/lang/String;");
+		prepopulateMethod(classID, "isArray", "()Z");
+		prepopulateMethod(classID, "getComponentType", "()Ljava/lang/Class;");
+		prepopulateMethod(classID, "getModifiers", "()I");
+		
+
+		classID = prepopulateClass("java/lang/reflect/Method");
+		prepopulateMethod(classID, "getName", "()Ljava/lang/String;");
+		prepopulateMethod(classID, "getParameterTypes", "()[Ljava/lang/Class;");
+		prepopulateMethod(classID, "getReturnType", "()Ljava/lang/Class;");
+		prepopulateMethod(classID, "getModifiers", "()I");
+
+		classID = prepopulateClass("java/lang/reflect/Constructor");
+		prepopulateMethod(classID, "getParameterTypes", "()[Ljava/lang/Class;");
+		prepopulateMethod(classID, "getModifiers", "()I");
+		
+		classID = prepopulateClass("java/lang/reflect/Field");
+		prepopulateMethod(classID, "getName", "()Ljava/lang/String;");
+		prepopulateMethod(classID, "getType", "()Ljava/lang/Class;");
+		prepopulateMethod(classID, "getModifiers", "()I");
+		
+		classID = prepopulateClass("java/lang/reflect/Array");
+		prepopulateMethod(classID, "newInstance", "(Ljava/lang/Class;I)Ljava/lang/Object;");
+		
+		classID = prepopulateClass("java/lang/Throwable");
+		prepopulateMethod(classID, "toString", "()Ljava/lang/String;");
+		prepopulateMethod(classID, "getMessage", "()Ljava/lang/String;");
+		
+		classID = prepopulateClass("java/lang/System");
+		prepopulateMethod(classID, "identityHashCode", "(Ljava/lang/Object;)I");
+		
+		classID = prepopulateClass("java/lang/Boolean");
+		prepopulateMethod(classID, "booleanValue", "()D");
+		prepopulateMethod(classID, "<init>", "(Z)V");
+
+		classID = prepopulateClass("java/lang/Double");
+		prepopulateMethod(classID, "doubleValue", "()D");
+		prepopulateMethod(classID, "<init>", "(D)V");
+
+		classID = prepopulateClass("java/lang/Void");
+		prepopulateField(classID, "TYPE");
+
+		prepopulateClass("java/lang/String");		
+		prepopulateClass("java/applet/Applet");
+	}
+
+	private int prepopulateClass(String name) {
+		name = name.replace('/', '.');
+		ClassLoader cl = liveconnectLoader;
+		Class c = null;
+
+		try {
+			c = cl.loadClass(name);
+			store.reference(c);
+		} catch (ClassNotFoundException cnfe) {
+			// do nothing ... this should never happen
+			cnfe.printStackTrace();
+		}
+
+		return store.getIdentifier(c);
+	}
+	
+	private int prepopulateMethod(int classID, String methodName, String signatureStr) {
+		Signature signature = parseCall(signatureStr, ((Class) store.getObject(classID)).getClassLoader(), Signature.class);
+		Object[] a = signature.getClassArray();
+
+		Class c = (Class) store.getObject(classID);
+		Method m = null;
+		Constructor cs = null;
+		Object o = null;
+		
+		try {
+			if (methodName.equals("<init>")
+					|| methodName.equals("<clinit>")) {
+				o = cs = c.getConstructor(signature.getClassArray());
+				store.reference(cs);
+			} else {
+				o = m = c.getMethod(methodName, signature.getClassArray());
+				store.reference(m);
+			}
+		} catch (NoSuchMethodException e) {
+			// should never happen
+			e.printStackTrace();
+		}
+		
+		return store.getIdentifier(m);
+	}
+	
+	private int prepopulateField(int classID, String fieldName) {
+
+		Class c = (Class) store.getObject(classID);
+		Field f = null;
+		try {
+			f = c.getField(fieldName);
+		} catch (SecurityException e) {
+			// should never happen
+			e.printStackTrace();
+		} catch (NoSuchFieldException e) {
+			// should never happen			
+			e.printStackTrace();
+		}
+
+		store.reference(f);
+		return store.getIdentifier(f);
+	}
+
 	public void dumpStore() {
 		store.dump();
 	}
--- a/plugin/icedtea/sun/applet/PluginAppletViewer.java	Tue Oct 21 16:45:19 2008 +0200
+++ b/plugin/icedtea/sun/applet/PluginAppletViewer.java	Tue Oct 21 16:17:19 2008 -0400
@@ -47,11 +47,11 @@
 import java.io.PrintStream;
 import java.io.Reader;
 import java.io.StringReader;
+import java.lang.reflect.InvocationTargetException;
 import java.net.MalformedURLException;
 import java.net.SocketPermission;
 import java.net.URL;
 import java.security.AccessController;
-import java.security.Policy;
 import java.security.PrivilegedAction;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -60,6 +60,8 @@
 import java.util.Map;
 import java.util.Vector;
 
+import javax.swing.SwingUtilities;
+
 import net.sourceforge.jnlp.NetxPanel;
 import sun.awt.AppContext;
 import sun.awt.SunToolkit;
@@ -261,12 +263,6 @@
     showStatus(amh.getMessage("status.start"));
  	initEventQueue();
  	
- 	try {
- 	    write("initialized");
- 	} catch (IOException ioe) {
- 		ioe.printStackTrace();
- 	}
- 	
     // Wait for a maximum of 10 seconds for the panel to initialize
     // (happens in a separate thread)
  	Applet a;
@@ -282,6 +278,12 @@
    	 }
     }
 
+    // Still null?
+    if (panel.getApplet() == null) {
+    	this.streamhandler.write("instance " + identifier + " reference " + -1 + " fatalError " + "Initialization failed");
+    	return;
+    }
+
     PluginDebug.debug("Applet initialized");
 
     // Applet initialized. Find out it's classloader and add it to the list
@@ -297,6 +299,12 @@
     }
 
     AppletSecurityContextManager.getSecurityContext(0).associateSrc(a.getClass().getClassLoader(), codeBase);
+    
+ 	try {
+ 	    write("initialized");
+ 	} catch (IOException ioe) {
+ 		ioe.printStackTrace();
+ 	}
 
      }
 
@@ -386,20 +394,49 @@
      public void handleMessage(int reference, String message)
      {
          if (message.startsWith("width")) {
-        	 
-        	 String[] dimMsg = message.split(" ");
+
         	 // 0 => width, 1=> width_value, 2 => height, 3=> height_value
+        	 String[] dimMsg = message.split(" ");
         	 
-        	 int height = (int) (proposedHeightFactor*Integer.parseInt(dimMsg[3]));
-        	 int width = (int) (proposedWidthFactor*Integer.parseInt(dimMsg[1]));
+        	 final int height = (int) (proposedHeightFactor*Integer.parseInt(dimMsg[3]));
+        	 final int width = (int) (proposedWidthFactor*Integer.parseInt(dimMsg[1]));
 
         	 if (panel instanceof NetxPanel)
         		 ((NetxPanel) panel).updateSizeInAtts(height, width);
 
-        	 panel.setSize(width, height);
-        	 setSize(width, height);
+        	 try {
+				SwingUtilities.invokeAndWait(new Runnable() {
+					 public void run() {
+
+			        	 setSize(width, height);
+						 
+						 // There is a rather odd drawing bug whereby resizing 
+						 // the panel makes no difference on initial call 
+						 // because the panel thinks that it is already the 
+						 // right size. Validation has no effect there either. 
+						 // So we work around by setting size to 1, validating, 
+						 // and then setting to the right size and validating 
+						 // again. This is not very efficient, and there is 
+						 // probably a better way -- but resizing happens 
+						 // quite infrequently, so for now this is how we do it
 
-        	 panel.validate();
+			        	 panel.setSize(1,1);
+			        	 panel.validate();
+
+			        	 panel.setSize(width, height);
+			        	 panel.validate();
+					 }
+				 });
+			} catch (InterruptedException e) {
+				// do nothing
+				e.printStackTrace();
+			} catch (InvocationTargetException e) {
+				// do nothing
+				e.printStackTrace();
+			}
+
+//        	 this.validate();
+//        	 panel.validate();
          } else if (message.startsWith("destroy")) {
              dispose();
          } else if (message.startsWith("GetJavaObject")) {
--- a/plugin/icedtea/sun/applet/PluginMain.java	Tue Oct 21 16:45:19 2008 +0200
+++ b/plugin/icedtea/sun/applet/PluginMain.java	Tue Oct 21 16:17:19 2008 -0400
@@ -54,7 +54,14 @@
     public static void main(String args[])
 	throws IOException
     {
-    	PluginMain pm = new PluginMain(System.getProperty("user.home") + "/.icedteaplugin/icedtea-plugin-to-appletviewer", System.getProperty("user.home") + "/.icedteaplugin/icedtea-appletviewer-to-plugin");
+
+    	try {
+    		PluginMain pm = new PluginMain(System.getProperty("user.home") + "/.icedteaplugin/icedtea-plugin-to-appletviewer", System.getProperty("user.home") + "/.icedteaplugin/icedtea-appletviewer-to-plugin");
+    	} catch (Exception e) {
+    		e.printStackTrace();
+    		System.err.println("Something very bad happened. I don't know what to do, so I am going to exit :(");
+    		System.exit(1);
+    	}
     }
 
     public PluginMain(String inPipe, String outPipe) {
@@ -76,6 +83,7 @@
     	connect(inPipe, outPipe);
 
     	securityContext = new PluginAppletSecurityContext(0);
+    	securityContext.prePopulateLCClasses();
     	securityContext.setStreamhandler(streamHandler);
     	AppletSecurityContextManager.addContext(0, securityContext);