Back to index

lightning-sunbird  0.9+nobinonly
MRJSession.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is mozilla.org code.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1998
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either of the GNU General Public License Version 2 or later (the "GPL"),
00025  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038        MRJSession.cpp
00039 
00040        Encapsulates a session with the MacOS Runtime for Java.
00041        
00042        by Patrick C. Beard.
00043  */
00044 
00045 #include "MRJSession.h"
00046 #include "MRJPlugin.h"
00047 #include "MRJContext.h"
00048 #include "MRJConsole.h"
00049 #include "MRJMonitor.h"
00050 #include "MRJNetworking.h"
00051 
00052 #include <ControlDefinitions.h>
00053 #include <string.h>
00054 #include <Memory.h>
00055 #include <Files.h>
00056 #include <Dialogs.h>
00057 #include <Appearance.h>
00058 #include <Resources.h>
00059 
00060 extern MRJConsole* theConsole;
00061 extern short thePluginRefnum;
00062 
00063 static Str255 consoleOutFilename = "\pMRJPluginAppletOutput";
00064 static bool fileCreated = false;
00065 
00066 static SInt32 java_stdin(JMSessionRef session, void *buffer, SInt32 maxBufferLength)
00067 {
00068        return -1;
00069 }
00070 
00071 static Boolean java_exit(JMSessionRef session, SInt32 status)
00072 {
00073        return false;                             /* not allowed in a plugin. */
00074 }
00075 
00076 static void getItemText(DialogPtr dialog, DialogItemIndex index, ResType textTag, char str[256])
00077 {
00078        ControlHandle control;
00079        if (::GetDialogItemAsControl(dialog, index, &control) == noErr) {
00080               Size textSize;
00081               ::GetControlDataSize(control, kControlNoPart, textTag, &textSize);
00082               if (textSize > 255) textSize = 255;
00083               ::GetControlData(control, kControlNoPart, textTag, textSize, (Ptr)str, &textSize);
00084               str[textSize] = '\0';
00085        }
00086 }
00087 
00088 static void miniEgg () {
00089 
00090 #if 0
00091        
00092        long count = 0;
00093        OSErr myErr;
00094        short myVRef;
00095        long myDirID;
00096        FSSpec mySpec;
00097        short refNum;
00098        Str255 holder;
00099 
00100        myErr = FindFolder(kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder, &myVRef, &myDirID);
00101 
00102        if (myErr == noErr) {
00103        
00104               myErr = FSMakeFSSpec(myVRef, myDirID, "\pFileName", &mySpec);
00105               
00106               if ((myErr != noErr) && (myErr != fnfErr)) {
00107                      return;
00108               }
00109 
00110               myErr = FSpCreate(&mySpec, 'ttxt', 'TEXT', smSystemScript);
00111 
00112               // if it exists just exit.
00113               if (myErr != noErr) {
00114                      return;
00115               }
00116               
00117               //we care if this errs, but not enough to impede mozilla running.
00118               myErr = FSpOpenDF(&mySpec, fsWrPerm, &refNum);
00119 
00120               if (myErr != noErr) {
00121                      return;
00122               }
00123 
00124               sprintf((char *)holder, "egg line1\r");
00125               count = strlen((char *)holder);
00126               myErr = FSWrite(refNum, &count, holder);
00127                              
00128               sprintf((char *)holder, "egg line2\r");
00129               count = strlen((char *)holder);
00130               myErr = FSWrite(refNum, &count, holder);
00131 
00132               sprintf((char *)holder, "egg line3\r");
00133               count = strlen((char *)holder);
00134               myErr = FSWrite(refNum, &count, holder);
00135 
00136            // ...
00137 
00138               FlushVol("\p", refNum);
00139         myErr = FSClose(refNum);
00140        }
00141 
00142 #endif
00143 
00144 }
00145 
00146 static void debug_out(const void *label, const void *message, UInt32 messageLengthInBytes)
00147 {
00148        long count = 0;
00149        OSErr myErr;
00150        short myVRef;
00151        long myDirID;
00152        FSSpec mySpec;
00153        short refNum;
00154        Str255 holder;
00155        
00156        myErr = Gestalt(gestaltFindFolderAttr, &count);
00157        
00158        if ((myErr != noErr) || (! (count & (1 << gestaltFindFolderPresent)))) {
00159               return;
00160        }
00161        
00162        myErr = FindFolder(kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder, &myVRef, &myDirID);
00163 
00164        if (myErr == noErr) {
00165        
00166               myErr = FSMakeFSSpec(myVRef, myDirID, consoleOutFilename, &mySpec);
00167               
00168               if ((myErr != noErr) && (myErr != fnfErr)) {
00169                      return;
00170               }
00171 
00172               //will err if file exists, we don't care.
00173               myErr = FSpCreate(&mySpec, 'ttxt', 'TEXT', smSystemScript);
00174 
00175               //we care if this errs, but not enough to impede mozilla running.
00176               myErr = FSpOpenDF(&mySpec, fsWrPerm, &refNum);
00177 
00178               if (myErr != noErr) {
00179                      return;
00180               }
00181 
00182               if (! fileCreated) {
00183                      //write over
00184                      miniEgg();
00185                      myErr = SetEOF(refNum, 0);
00186                      
00187                      fileCreated = true;
00188                      sprintf((char *)holder, "MRJ Console Output.\rMRJ Plugin Version: %s\r--------------------\r", MRJPlugin::PLUGIN_VERSION);
00189                      
00190                      count = strlen((char *)holder);
00191                      myErr = FSWrite(refNum, &count, holder);
00192                      
00193               } else {
00194                      //append
00195                      myErr = SetFPos(refNum, fsFromLEOF, 0);
00196               }
00197 
00198               count = strlen((char *)label);
00199               myErr = FSWrite(refNum, &count, label);
00200 
00201               count = messageLengthInBytes;
00202         myErr = FSWrite(refNum, &count, message);
00203         
00204               count = 1;
00205         myErr = FSWrite(refNum, &count, "\r");
00206         
00207               FlushVol("\p", refNum);
00208         myErr = FSClose(refNum);
00209        }
00210 
00211 
00212 //     Str255 pmsg;
00213 //if (myErr != noErr) {
00214 //Str255 bla;
00215 //sprintf((char *)bla, "FSpOpenDF error: %d", myErr);
00216 //pmsg[0] = strlen((char *)bla);
00217 //::BlockMoveData(bla, &pmsg[1], pmsg[0]);
00218 //::DebugStr(pmsg);
00219 //}
00220 
00221 }
00222 
00223 static void java_stdout(JMSessionRef session, const void *message, SInt32 messageLengthInBytes)
00224 {
00225        if (theConsole != NULL) {
00226               theConsole->write(message, messageLengthInBytes);
00227        } else {
00228               debug_out("[System.out] ", message, messageLengthInBytes);
00229        }
00230 }
00231 
00232 static void java_stderr(JMSessionRef session, const void *message, SInt32 messageLengthInBytes)
00233 {
00234        if (theConsole != NULL) {
00235               theConsole->write(message, messageLengthInBytes);
00236        } else {
00237               debug_out("[System.err] ", message, messageLengthInBytes);
00238        }
00239 }
00240 
00241 static ControlHandle getItemControl(DialogPtr dialog, DialogItemIndex index)
00242 {
00243        ControlHandle control;
00244        if (::GetDialogItemAsControl(dialog, index, &control) == noErr)
00245               return control;
00246        else
00247               return NULL;
00248 }
00249 
00250 enum {
00251        kUserNameIndex = 3,
00252        kPasswordIndex,
00253        kAuthenticationDialog = 128
00254 };
00255 
00256 static Boolean java_authenticate(JMSessionRef session, const char *url, const char *realm, char userName[255], char password[255])
00257 {
00258        Boolean result = false;
00259        if (thePluginRefnum != -1) {
00260               // ensure resources come from the plugin (yuck!).
00261               short oldRefnum = ::CurResFile();
00262               ::UseResFile(thePluginRefnum);
00263               
00264               DialogRecord storage;
00265               DialogPtr dialog = ::GetNewDialog(kAuthenticationDialog, &storage, WindowPtr(-1));
00266               if (dialog != NULL) {
00267                      // set up default buttons.
00268                      ::SetDialogDefaultItem(dialog, kStdOkItemIndex);
00269                      ::SetDialogCancelItem(dialog, kStdCancelItemIndex);
00270                      ::SetDialogTracksCursor(dialog, true);
00271 
00272                      // set up default keyboard focus.
00273                      ControlHandle userNameControl = getItemControl(dialog, kUserNameIndex);
00274                      if (userNameControl != NULL)
00275                             ::SetKeyboardFocus(dialog, userNameControl, kControlFocusNextPart);
00276                      
00277                      ::ShowWindow(dialog);
00278 
00279                      DialogItemIndex itemHit = 0;
00280                      do {
00281                             ::ModalDialog(ModalFilterUPP(NULL), &itemHit);
00282                      } while (itemHit != 1 && itemHit != 2);
00283                      
00284                      if (itemHit == 1) {
00285                             getItemText(dialog, kUserNameIndex, kControlEditTextTextTag, userName);
00286                             getItemText(dialog, kPasswordIndex, kControlEditTextPasswordTag, password);
00287                             result = true;
00288                      }
00289                      
00290                      ::CloseDialog(dialog);
00291                      ::UseResFile(oldRefnum);
00292               }
00293        }
00294        return result;
00295 }
00296 
00297 static void java_lowmem(JMSessionRef session)
00298 {
00299        /* can ask Netscape to purge some memory. */
00300        // NPN_MemFlush(512 * 1024);
00301 }
00302 
00303 MRJSession::MRJSession()
00304        :      mSession(NULL), mStatus(noErr), mMainEnv(NULL), mFirst(NULL), mLast(NULL), mMessageMonitor(NULL), mLockCount(0)
00305 {
00306        // Make sure JManager exists.
00307        if (&::JMGetVersion != NULL && ::JMGetVersion() >= kJMVersion) {
00308               static JMSessionCallbacks callbacks = {
00309                      kJMVersion,                               /* should be set to kJMVersion */
00310                      &java_stdout,                      /* JM will route "stdout" to this function. */
00311                      &java_stderr,                      /* JM will route "stderr" to this function. */
00312                      &java_stdin,                       /* read from console - can be nil for default behavior (no console IO) */
00313                      &java_exit,                               /* handle System.exit(int) requests */
00314                      &java_authenticate,         /* present basic authentication dialog */
00315                      &java_lowmem                       /* Low Memory notification Proc */
00316               };
00317 
00318               JMTextRef nameRef = NULL;
00319               JMTextRef valueRef = NULL;
00320               OSStatus status = noErr;
00321 
00322               mStatus = ::JMOpenSession(&mSession, eJManager2Defaults, eCheckRemoteCode,
00323                                                                              &callbacks, kTextEncodingMacRoman, NULL);
00324 
00325               // capture the main environment, so it can be distinguished from true Java threads.
00326               if (mStatus == noErr) {
00327                      mMainEnv = ::JMGetCurrentEnv(mSession);
00328               
00329                      // create a monitor for the message queue to unblock Java threads.
00330                      mMessageMonitor = new MRJMonitor(this);
00331 
00332 #ifdef MRJPLUGIN_4X
00333             // hook up MRJ networking layer, to permit SSL, etc.
00334             ::OpenMRJNetworking(this);
00335 #endif
00336               }
00337        } else {
00338               mStatus = kJMVersionError;
00339        }
00340 }
00341 
00342 MRJSession::~MRJSession()
00343 {
00344 #ifdef MRJPLUGIN_4X
00345     // is this perhaps too late?
00346     ::CloseMRJNetworking(this);
00347 #endif
00348 
00349        if (mMessageMonitor != NULL) {
00350               mMessageMonitor->notifyAll();
00351               delete mMessageMonitor;
00352        }
00353 
00354        if (mSession != NULL) {
00355               mStatus = ::JMCloseSession(mSession);
00356               mSession = NULL;
00357        }
00358 }
00359 
00360 JMSessionRef MRJSession::getSessionRef()
00361 {
00362        return mSession;
00363 }
00364 
00365 JNIEnv* MRJSession::getCurrentEnv()
00366 {
00367        return ::JMGetCurrentEnv(mSession);
00368 }
00369 
00370 JNIEnv* MRJSession::getMainEnv()
00371 {
00372        return mMainEnv;
00373 }
00374 
00375 JavaVM* MRJSession::getJavaVM()
00376 {
00377        JNIEnv* env = ::JMGetCurrentEnv(mSession);
00378        JavaVM* vm = NULL;
00379        env->GetJavaVM(&vm);
00380        return vm;
00381 }
00382 
00383 Boolean MRJSession::onMainThread()
00384 {
00385        JNIEnv* env = ::JMGetCurrentEnv(mSession);
00386        return (env == mMainEnv);
00387 }
00388 
00389 inline StringPtr c2p(const char* cstr, StringPtr pstr)
00390 {
00391        pstr[0] = (unsigned char)strlen(cstr);
00392        ::BlockMoveData(cstr, pstr + 1, pstr[0]);
00393        return pstr;
00394 }
00395 
00396 Boolean MRJSession::addToClassPath(const FSSpec& fileSpec)
00397 {
00398        return (::JMAddToClassPath(mSession, &fileSpec) == noErr);
00399 }
00400 
00401 Boolean MRJSession::addToClassPath(const char* dirPath)
00402 {
00403        // Need to convert the path into an FSSpec, and add it MRJ's class path.
00404        Str255 path;
00405        FSSpec pathSpec;
00406        OSStatus status = ::FSMakeFSSpec(0, 0, c2p(dirPath, path), &pathSpec);
00407        if (status == noErr)
00408               status = ::JMAddToClassPath(mSession, &pathSpec);
00409        return (status == noErr);
00410 }
00411 
00412 Boolean MRJSession::addURLToClassPath(const char* fileURL)
00413 {
00414        OSStatus status = noErr;
00415        // Need to convert the URL into an FSSpec, and add it MRJ's class path.
00416        JMTextRef urlRef = NULL;
00417        status = ::JMNewTextRef(mSession, &urlRef, kTextEncodingMacRoman, fileURL, strlen(fileURL));
00418        if (status == noErr) {
00419               FSSpec pathSpec;
00420               status = ::JMURLToFSS(mSession, urlRef, &pathSpec);
00421               if (status == noErr)
00422                      status = ::JMAddToClassPath(mSession, &pathSpec);
00423               ::JMDisposeTextRef(urlRef);
00424        }
00425        
00426        return (status == noErr);
00427 }
00428 
00429 char* MRJSession::getProperty(const char* propertyName)
00430 {
00431        char* result = NULL;
00432        OSStatus status = noErr;
00433        JMTextRef nameRef = NULL, valueRef = NULL;
00434        status = ::JMNewTextRef(mSession, &nameRef, kTextEncodingMacRoman, propertyName, strlen(propertyName));
00435        if (status == noErr) {
00436               status = ::JMGetSessionProperty(mSession, nameRef, &valueRef);
00437               ::JMDisposeTextRef(nameRef);
00438               if (status == noErr && valueRef != NULL) {
00439                      UInt32 valueLength = 0;
00440                      status = ::JMGetTextLengthInBytes(valueRef, kTextEncodingMacRoman, &valueLength);
00441                      if (status == noErr) {
00442                             result = new char[valueLength + 1];
00443                             if (result != NULL) {
00444                                    UInt32 actualLength;
00445                                    status = ::JMGetTextBytes(valueRef, kTextEncodingMacRoman, result, valueLength, &actualLength);
00446                                    result[valueLength] = '\0';
00447                             }
00448                             ::JMDisposeTextRef(valueRef);
00449                      }
00450               }
00451        }
00452        return result;
00453 }
00454 
00455 void MRJSession::setStatus(OSStatus status)
00456 {
00457        mStatus = status;
00458 }
00459 
00460 OSStatus MRJSession::getStatus()
00461 {
00462        return mStatus;
00463 }
00464 
00465 void MRJSession::idle(UInt32 milliseconds)
00466 {
00467        // Each call to idle processes a single message.
00468        dispatchMessage();
00469 
00470        // Guard against entering the VM multiple times.
00471        if (mLockCount == 0) {
00472               lock();
00473               mStatus = ::JMIdle(mSession, milliseconds);
00474               unlock();
00475        }
00476 #if 0
00477         else {
00478               // sleep the current thread.
00479               JNIEnv* env = getCurrentEnv();
00480               jclass threadClass = env->FindClass("java/lang/Thread");
00481               if (threadClass != NULL) {
00482                      jmethodID sleepMethod = env->GetStaticMethodID(threadClass, "sleep", "(J)V");
00483                      env->CallStaticVoidMethod(threadClass, sleepMethod, jlong(milliseconds));
00484                      env->DeleteLocalRef(threadClass);
00485               }
00486        }
00487 #endif
00488 }
00489 
00490 void MRJSession::sendMessage(NativeMessage* message, Boolean async)
00491 {
00492        // can't block the main env, otherwise messages will never be processed!
00493        if (onMainThread()) {
00494               message->execute();
00495        } else {
00496               postMessage(message);
00497               if (!async)
00498                      mMessageMonitor->wait();
00499        }
00500 }
00501 
00505 void MRJSession::postMessage(NativeMessage* message)
00506 {
00507        if (mFirst == NULL) {
00508               mFirst = mLast = message;
00509        } else {
00510               mLast->setNext(message);
00511               mLast = message;
00512        }
00513        message->setNext(NULL);
00514 }
00515 
00516 void MRJSession::dispatchMessage()
00517 {
00518        if (mFirst != NULL) {
00519               NativeMessage* message = mFirst;
00520               mFirst = message->getNext();
00521               if (mFirst == NULL) mLast = NULL;
00522               
00523               message->setNext(NULL);
00524               message->execute();
00525               mMessageMonitor->notify();
00526        }
00527 }
00528 
00529 void MRJSession::lock()
00530 {
00531        ++mLockCount;
00532 }
00533 
00534 void MRJSession::unlock()
00535 {
00536        --mLockCount;
00537 }