khtml Library API Documentation

kjavaappletserver.cpp

00001 /* This file is part of the KDE project
00002  *
00003  * Copyright (C) 2000 Richard Moore <rich@kde.org>
00004  *               2000 Wynn Wilkes <wynnw@caldera.com>
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Library General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Library General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Library General Public License
00017  * along with this library; see the file COPYING.LIB.  If not, write to
00018  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00019  * Boston, MA 02111-1307, USA.
00020  */
00021 
00022 #include <config.h>
00023 #include "kjavaappletserver.h"
00024 #include "kjavaappletcontext.h"
00025 #include "kjavaprocess.h"
00026 #include "kjavadownloader.h"
00027 
00028 #include <kdebug.h>
00029 #include <kconfig.h>
00030 #include <klocale.h>
00031 #include <kparts/browserextension.h>
00032 #include <kapplication.h>
00033 #include <kstandarddirs.h>
00034 
00035 #include <kio/job.h>
00036 #include <kio/kprotocolmanager.h>
00037 
00038 #include <qtimer.h>
00039 #include <qguardedptr.h>
00040 #include <qdir.h>
00041 
00042 #include <stdlib.h>
00043 #include <assert.h>
00044 
00045 #define KJAS_CREATE_CONTEXT    (char)1
00046 #define KJAS_DESTROY_CONTEXT   (char)2
00047 #define KJAS_CREATE_APPLET     (char)3
00048 #define KJAS_DESTROY_APPLET    (char)4
00049 #define KJAS_START_APPLET      (char)5
00050 #define KJAS_STOP_APPLET       (char)6
00051 #define KJAS_INIT_APPLET       (char)7
00052 #define KJAS_SHOW_DOCUMENT     (char)8
00053 #define KJAS_SHOW_URLINFRAME   (char)9
00054 #define KJAS_SHOW_STATUS       (char)10
00055 #define KJAS_RESIZE_APPLET     (char)11
00056 #define KJAS_GET_URLDATA       (char)12
00057 #define KJAS_URLDATA           (char)13
00058 #define KJAS_SHUTDOWN_SERVER   (char)14
00059 #define KJAS_JAVASCRIPT_EVENT   (char)15
00060 #define KJAS_GET_MEMBER        (char)16
00061 #define KJAS_CALL_MEMBER       (char)17
00062 #define KJAS_PUT_MEMBER        (char)18
00063 #define KJAS_DEREF_OBJECT      (char)19
00064 #define KJAS_AUDIOCLIP_PLAY    (char)20
00065 #define KJAS_AUDIOCLIP_LOOP    (char)21
00066 #define KJAS_AUDIOCLIP_STOP    (char)22
00067 #define KJAS_APPLET_STATE      (char)23
00068 #define KJAS_APPLET_FAILED     (char)24
00069 
00070 
00071 class JSStackNode {
00072 public:
00073     JSStackNode() : ready(false) {}
00074     bool ready;
00075     QStringList args;
00076     int size;
00077 };
00078 
00079 // For future expansion
00080 class KJavaAppletServerPrivate
00081 {
00082 friend class KJavaAppletServer;
00083 private:
00084    int counter;
00085    int ticketcounter;
00086    QMap< int, QGuardedPtr<KJavaAppletContext> > contexts;
00087    QString appletLabel;
00088    QMap< int, JSStackNode* > jsstack;
00089    bool javaProcessFailed;
00090 };
00091 
00092 static KJavaAppletServer* self = 0;
00093 
00094 KJavaAppletServer::KJavaAppletServer()
00095 {
00096     d = new KJavaAppletServerPrivate;
00097     d->ticketcounter = 0;
00098     process = new KJavaProcess();
00099 
00100     connect( process, SIGNAL(received(const QByteArray&)),
00101              this,    SLOT(slotJavaRequest(const QByteArray&)) );
00102 
00103     setupJava( process );
00104 
00105     if( process->startJava() ) {
00106         d->appletLabel = i18n( "Loading Applet" );
00107         d->javaProcessFailed = false;
00108     }
00109     else {
00110         d->appletLabel = i18n( "Error: java executable not found" );
00111         d->javaProcessFailed = true;
00112     }
00113 }
00114 
00115 KJavaAppletServer::~KJavaAppletServer()
00116 {
00117     quit();
00118 
00119     delete process;
00120     delete d;
00121 }
00122 
00123 QString KJavaAppletServer::getAppletLabel()
00124 {
00125     if( self )
00126         return self->appletLabel();
00127     else
00128         return QString::null;
00129 }
00130 
00131 QString KJavaAppletServer::appletLabel()
00132 {
00133     return d->appletLabel;
00134 }
00135 
00136 KJavaAppletServer* KJavaAppletServer::allocateJavaServer()
00137 {
00138    if( self == 0 )
00139    {
00140       self = new KJavaAppletServer();
00141       self->d->counter = 0;
00142    }
00143 
00144    self->d->counter++;
00145    return self;
00146 }
00147 
00148 void KJavaAppletServer::freeJavaServer()
00149 {
00150     self->d->counter--;
00151 
00152     if( self->d->counter == 0 )
00153     {
00154         //instead of immediately quitting here, set a timer to kill us
00155         //if there are still no servers- give us one minute
00156         //this is to prevent repeated loading and unloading of the jvm
00157         KConfig config( "konquerorrc", true );
00158         config.setGroup( "Java/JavaScript Settings" );
00159         if( config.readBoolEntry( "ShutdownAppletServer", true )  )
00160         {
00161             int value = config.readNumEntry( "AppletServerTimeout", 60 );
00162             QTimer::singleShot( value*1000, self, SLOT( checkShutdown() ) );
00163         }
00164     }
00165 }
00166 
00167 void KJavaAppletServer::checkShutdown()
00168 {
00169     if( self->d->counter == 0 )
00170     {
00171         delete self;
00172         self = 0;
00173     }
00174 }
00175 
00176 void KJavaAppletServer::setupJava( KJavaProcess *p )
00177 {
00178     KConfig config ( "konquerorrc", true );
00179     config.setGroup( "Java/JavaScript Settings" );
00180 
00181     QString jvm_path = "java";
00182 
00183     QString jPath = config.readPathEntry( "JavaPath" );
00184     if ( !jPath.isEmpty() && jPath != "java" )
00185     {
00186         // Cut off trailing slash if any
00187         if( jPath[jPath.length()-1] == '/' )
00188             jPath.remove(jPath.length()-1, 1);
00189             
00190         QDir dir( jPath );
00191         if( dir.exists( "bin/java" ) )
00192         {
00193             jvm_path = jPath + "/bin/java";
00194         } 
00195         else if (dir.exists( "/jre/bin/java" ) )
00196         { 
00197             jvm_path = jPath + "/jre/bin/java";
00198         } 
00199         else if( QFile::exists(jPath) )
00200         {
00201             //check here to see if they entered the whole path the java exe
00202             jvm_path = jPath;
00203         }
00204     }
00205 
00206     //check to see if jvm_path is valid and set d->appletLabel accordingly
00207     p->setJVMPath( jvm_path );
00208 
00209     // Prepare classpath variable
00210     QString kjava_class = locate("data", "kjava/kjava.jar");
00211     kdDebug(6100) << "kjava_class = " << kjava_class << endl;
00212     if( kjava_class.isNull() ) // Should not happen
00213         return;
00214 
00215     QDir dir( kjava_class );
00216     dir.cdUp();
00217     kdDebug(6100) << "dir = " << dir.absPath() << endl;
00218 
00219     QStringList entries = dir.entryList( "*.jar" );
00220     kdDebug(6100) << "entries = " << entries.join( ":" ) << endl;
00221 
00222     QString classes;
00223     for( QStringList::Iterator it = entries.begin();
00224          it != entries.end(); it++ )
00225     {
00226         if( !classes.isEmpty() )
00227             classes += ":";
00228         classes += dir.absFilePath( *it );
00229     }
00230     p->setClasspath( classes );
00231 
00232     // Fix all the extra arguments
00233     QString extraArgs = config.readEntry( "JavaArgs" );
00234     p->setExtraArgs( extraArgs );
00235 
00236     if( config.readBoolEntry( "ShowJavaConsole", false) )
00237     {
00238         p->setSystemProperty( "kjas.showConsole", QString::null );
00239     }
00240 
00241     if( config.readBoolEntry( "UseSecurityManager", true ) )
00242     {
00243         QString class_file = locate( "data", "kjava/kjava.policy" );
00244         p->setSystemProperty( "java.security.policy", class_file );
00245 
00246         p->setSystemProperty( "java.security.manager",
00247                               "org.kde.kjas.server.KJASSecurityManager" );
00248     }
00249 
00250     //check for http proxies...
00251     if( KProtocolManager::useProxy() )
00252     {
00253         // only proxyForURL honours automatic proxy scripts
00254         // we do not know the applet url here so we just use a dummy url
00255         // this is a workaround for now
00256         // FIXME
00257         KURL dummyURL( "http://www.kde.org/" );
00258         QString httpProxy = KProtocolManager::proxyForURL(dummyURL);
00259         kdDebug(6100) << "httpProxy is " << httpProxy << endl;
00260 
00261         KURL url( httpProxy );
00262         p->setSystemProperty( "http.proxyHost", url.host() );
00263         p->setSystemProperty( "http.proxyPort", QString::number( url.port() ) );
00264     }
00265 
00266     //set the main class to run
00267     p->setMainClass( "org.kde.kjas.server.Main" );
00268 }
00269 
00270 void KJavaAppletServer::createContext( int contextId, KJavaAppletContext* context )
00271 {
00272 //    kdDebug(6100) << "createContext: " << contextId << endl;
00273     if ( d->javaProcessFailed ) return;
00274 
00275     d->contexts.insert( contextId, context );
00276 
00277     QStringList args;
00278     args.append( QString::number( contextId ) );
00279     process->send( KJAS_CREATE_CONTEXT, args );
00280 }
00281 
00282 void KJavaAppletServer::destroyContext( int contextId )
00283 {
00284 //    kdDebug(6100) << "destroyContext: " << contextId << endl;
00285     if ( d->javaProcessFailed ) return;
00286     d->contexts.remove( contextId );
00287 
00288     QStringList args;
00289     args.append( QString::number( contextId ) );
00290     process->send( KJAS_DESTROY_CONTEXT, args );
00291 }
00292 
00293 bool KJavaAppletServer::createApplet( int contextId, int appletId,
00294                              const QString & name, const QString & clazzName,
00295                              const QString & baseURL, const QString & user,
00296                              const QString & password, const QString & authname,
00297                              const QString & codeBase, const QString & jarFile,
00298                              QSize size, const QMap<QString,QString>& params,
00299                              const QString & windowTitle )
00300 {
00301 //    kdDebug(6100) << "createApplet: contextId = " << contextId     << endl
00302 //              << "              appletId  = " << appletId      << endl
00303 //              << "              name      = " << name          << endl
00304 //              << "              clazzName = " << clazzName     << endl
00305 //              << "              baseURL   = " << baseURL       << endl
00306 //              << "              codeBase  = " << codeBase      << endl
00307 //              << "              jarFile   = " << jarFile       << endl
00308 //              << "              width     = " << size.width()  << endl
00309 //              << "              height    = " << size.height() << endl;
00310 
00311     if ( d->javaProcessFailed ) return false;
00312 
00313     QStringList args;
00314     args.append( QString::number( contextId ) );
00315     args.append( QString::number( appletId ) );
00316 
00317     //it's ok if these are empty strings, I take care of it later...
00318     args.append( name );
00319     args.append( clazzName );
00320     args.append( baseURL );
00321     args.append( user );
00322     args.append( password );
00323     args.append( authname );
00324     args.append( codeBase );
00325     args.append( jarFile );
00326 
00327     args.append( QString::number( size.width() ) );
00328     args.append( QString::number( size.height() ) );
00329 
00330     args.append( windowTitle );
00331 
00332     //add on the number of parameter pairs...
00333     int num = params.count();
00334     QString num_params = QString("%1").arg( num, 8 );
00335     args.append( num_params );
00336 
00337     QMap< QString, QString >::ConstIterator it;
00338 
00339     for( it = params.begin(); it != params.end(); ++it )
00340     {
00341         args.append( it.key() );
00342         args.append( it.data() );
00343     }
00344 
00345     process->send( KJAS_CREATE_APPLET, args );
00346 
00347     return true;
00348 }
00349 
00350 void KJavaAppletServer::initApplet( int contextId, int appletId )
00351 {
00352     if ( d->javaProcessFailed ) return;
00353     QStringList args;
00354     args.append( QString::number( contextId ) );
00355     args.append( QString::number( appletId ) );
00356 
00357     process->send( KJAS_INIT_APPLET, args );
00358 }
00359 
00360 void KJavaAppletServer::destroyApplet( int contextId, int appletId )
00361 {
00362     if ( d->javaProcessFailed ) return;
00363     QStringList args;
00364     args.append( QString::number(contextId) );
00365     args.append( QString::number(appletId) );
00366 
00367     process->send( KJAS_DESTROY_APPLET, args );
00368 }
00369 
00370 void KJavaAppletServer::startApplet( int contextId, int appletId )
00371 {
00372     if ( d->javaProcessFailed ) return;
00373     QStringList args;
00374     args.append( QString::number(contextId) );
00375     args.append( QString::number(appletId) );
00376 
00377     process->send( KJAS_START_APPLET, args );
00378 }
00379 
00380 void KJavaAppletServer::stopApplet( int contextId, int appletId )
00381 {
00382     if ( d->javaProcessFailed ) return;
00383     QStringList args;
00384     args.append( QString::number(contextId) );
00385     args.append( QString::number(appletId) );
00386 
00387     process->send( KJAS_STOP_APPLET, args );
00388 }
00389 
00390 void KJavaAppletServer::sendURLData( const QString& loaderID,
00391                                      const QString& url,
00392                                      const QByteArray& data )
00393 {
00394     QStringList args;
00395     args.append( loaderID );
00396     args.append( url );
00397 
00398     process->send( KJAS_URLDATA, args, data );
00399 
00400 }
00401 
00402 void KJavaAppletServer::quit()
00403 {
00404     QStringList args;
00405 
00406     process->send( KJAS_SHUTDOWN_SERVER, args );
00407 }
00408 
00409 void KJavaAppletServer::slotJavaRequest( const QByteArray& qb )
00410 {
00411     // qb should be one command only without the length string,
00412     // we parse out the command and it's meaning here...
00413     QString cmd;
00414     QStringList args;
00415     int index = 0;
00416     int qb_size = qb.size();
00417 
00418     //get the command code
00419     char cmd_code = qb[ index++ ];
00420     ++index; //skip the next sep
00421 
00422     //get contextID
00423     QString contextID;
00424     while( qb[index] != 0 && index < qb_size )
00425     {
00426         contextID += qb[ index++ ];
00427     }
00428     ++index; //skip the sep
00429 
00430     //now parse out the arguments
00431     while( index < qb_size )
00432     {
00433         QString tmp;
00434         while( qb[index] != 0 )
00435             tmp += qb[ index++ ];
00436 
00437         kdDebug(6100) << "KJavaAppletServer::slotJavaRequest: "<< tmp << endl;
00438         args.append( tmp );
00439 
00440         ++index; //skip the sep
00441     }
00442     //here I should find the context and call the method directly
00443     //instead of emitting signals
00444     switch( cmd_code )
00445     {
00446         case KJAS_SHOW_DOCUMENT:
00447             cmd = QString::fromLatin1( "showdocument" );
00448             break;
00449 
00450         case KJAS_SHOW_URLINFRAME:
00451             cmd = QString::fromLatin1( "showurlinframe" );
00452             break;
00453 
00454         case KJAS_SHOW_STATUS:
00455             cmd = QString::fromLatin1( "showstatus" );
00456             break;
00457 
00458         case KJAS_RESIZE_APPLET:
00459             cmd = QString::fromLatin1( "resizeapplet" );
00460             break;
00461 
00462         case KJAS_GET_URLDATA:
00463             //here we need to get some data for a class loader and send it back...
00464             kdDebug(6100) << "GetURLData from classloader: "<< contextID
00465                           << " for url: " << args[0] << endl;
00466             break;
00467         case KJAS_JAVASCRIPT_EVENT:
00468             cmd = QString::fromLatin1( "JS_Event" );
00469             kdDebug(6100) << "Javascript request: "<< contextID
00470                           << " code: " << args[0] << endl;
00471             break;
00472         case KJAS_GET_MEMBER:
00473         case KJAS_PUT_MEMBER:
00474         case KJAS_CALL_MEMBER: {
00475             int ticket = args[0].toInt();
00476             QMap<int, JSStackNode*>::iterator it = d->jsstack.find(ticket);
00477             if (it != d->jsstack.end()) {
00478                 kdDebug(6100) << "slotJavaRequest: " << ticket << endl; 
00479                 args.pop_front();
00480                 it.data()->args = args;
00481                 it.data()->ready = true;
00482                 process->syncCommandReceived(ticket);
00483             } else
00484                 kdDebug(6100) << "Error: Missed return member data" << endl;
00485             return;
00486         }
00487         case KJAS_AUDIOCLIP_PLAY:
00488             cmd = QString::fromLatin1( "audioclip_play" );
00489             kdDebug(6100) << "Audio Play: url=" << args[0] << endl;
00490             break;
00491         case KJAS_AUDIOCLIP_LOOP:
00492             cmd = QString::fromLatin1( "audioclip_loop" );
00493             kdDebug(6100) << "Audio Loop: url=" << args[0] << endl;
00494             break;
00495         case KJAS_AUDIOCLIP_STOP:
00496             cmd = QString::fromLatin1( "audioclip_stop" );
00497             kdDebug(6100) << "Audio Stop: url=" << args[0] << endl;
00498             break;
00499         case KJAS_APPLET_STATE:
00500             kdDebug(6100) << "Applet State Notification for Applet " << args[0] << ". New state=" << args[1] << endl;
00501             cmd = QString::fromLatin1( "AppletStateNotification" );
00502             break;
00503         case KJAS_APPLET_FAILED:
00504             kdDebug(6100) << "Applet " << args[0] << " Failed: " << args[1] << endl;
00505             cmd = QString::fromLatin1( "AppletFailed" );
00506             break;
00507         default:
00508             return;
00509             break;
00510     }
00511 
00512     if( cmd_code == KJAS_GET_URLDATA )
00513     {
00514         new KJavaDownloader( contextID, args[0] );
00515     }
00516     else
00517     {
00518         bool ok;
00519         int contextID_num = contextID.toInt( &ok );
00520 
00521         if( !ok )
00522         {
00523             kdError(6100) << "could not parse out contextID to call command on" << endl;
00524             return;
00525         }
00526 
00527         KJavaAppletContext* context = d->contexts[ contextID_num ];
00528         if( context )
00529             context->processCmd( cmd, args );
00530         else if (cmd != "AppletStateNotification") 
00531             kdError(6100) << "no context object for this id" << endl;
00532     }
00533 }
00534 
00535 bool KJavaAppletServer::getMember(int contextId, int appletId, const unsigned long objid, const QString & name, int & type, unsigned long & rid, QString & value) {
00536     QStringList args;
00537     int ticket = d->ticketcounter++;
00538     args.append( QString::number(contextId) );
00539     args.append( QString::number(appletId) );
00540     args.append( QString::number(ticket) );
00541     args.append( QString::number(objid) );
00542     args.append( name );
00543 
00544     JSStackNode * frame = new JSStackNode;
00545     d->jsstack.insert(ticket, frame); 
00546     kdDebug(6100) << "KJavaAppletServer::getMember " << name << " " << ticket << endl;
00547     process->sendSync( ticket, KJAS_GET_MEMBER, args );
00548 
00549     bool retval = frame->ready;
00550     if (retval && frame->args.count() == 3) {
00551         type = frame->args[0].toInt(&retval);
00552         if (retval && type >= 0) {
00553             rid = frame->args[1].toInt(&retval);
00554             value = frame->args[2];
00555         } else
00556             retval = false;
00557     } else {
00558         kdError(6100) << "getMember: " << (retval ? "args ":"timeout ") << ticket << endl;
00559     }
00560 
00561     d->jsstack.erase(ticket);
00562     delete frame;
00563 
00564     return retval;
00565 }
00566 
00567 bool KJavaAppletServer::putMember(int contextId, int appletId, const unsigned long objid, const QString & name, const QString & value) {
00568     QStringList args;
00569     int ticket = d->ticketcounter++;
00570     args.append( QString::number(contextId) );
00571     args.append( QString::number(appletId) );
00572     args.append( QString::number(ticket) );
00573     args.append( QString::number(objid) );
00574     args.append( name );
00575     args.append( value );
00576 
00577     JSStackNode * frame = new JSStackNode;
00578     d->jsstack.insert(ticket, frame); 
00579     kdDebug(6100) << "KJavaAppletServer::putMember " << name << " " << ticket << endl;
00580 
00581     process->sendSync( ticket, KJAS_PUT_MEMBER, args );
00582 
00583     bool retval = frame->ready;
00584     if (retval) {
00585         retval = frame->args[0].toInt(&retval);
00586     } else {
00587         kdError(6100) << "putMember: timeout" << endl;
00588     }
00589 
00590     d->jsstack.erase(ticket);
00591     delete frame;
00592 
00593     return retval;
00594 }
00595 
00596 bool KJavaAppletServer::callMember(int contextId, int appletId, const unsigned long objid, const QString & name, const QStringList & fargs, int & type, unsigned long & rid, QString & value) {
00597     QStringList args;
00598     int ticket = d->ticketcounter++;
00599     args.append( QString::number(contextId) );
00600     args.append( QString::number(appletId) );
00601     args.append( QString::number(ticket) );
00602     args.append( QString::number(objid) );
00603     args.append( name );
00604     for (QStringList::const_iterator it = fargs.begin(); it != fargs.end(); it++)
00605         args.append(*it);
00606 
00607     JSStackNode * frame = new JSStackNode;
00608     d->jsstack.insert(ticket, frame); 
00609 
00610     kdDebug(6100) << "KJavaAppletServer::callMember " << name << " " << ticket << endl;
00611     process->sendSync( ticket, KJAS_CALL_MEMBER, args );
00612 
00613     bool retval = frame->ready;
00614     if (retval) {
00615         type = frame->args[0].toInt(&retval);
00616         if (retval && type > -1) {
00617             rid = frame->args[1].toInt(&retval);
00618             if (retval)
00619                 value = frame->args[2];
00620         } else
00621             retval = false;
00622     } else {
00623         kdError(6100) << "callMember: timeout return data" << endl;
00624     }
00625 
00626     d->jsstack.erase(ticket);
00627     delete frame;
00628 
00629     return retval;
00630 }
00631 
00632 void KJavaAppletServer::derefObject(int contextId, int appletId, const unsigned long objid) {
00633     QStringList args;
00634     args.append( QString::number(contextId) );
00635     args.append( QString::number(appletId) );
00636     args.append( QString::number(objid) );
00637 
00638     process->send( KJAS_DEREF_OBJECT, args );
00639 }
00640 #include "kjavaappletserver.moc"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:34:11 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001