00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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 <qvaluelist.h>
00041 #include <qdir.h>
00042 #include <qeventloop.h>
00043
00044 #include <stdlib.h>
00045 #include <assert.h>
00046
00047 #define KJAS_CREATE_CONTEXT (char)1
00048 #define KJAS_DESTROY_CONTEXT (char)2
00049 #define KJAS_CREATE_APPLET (char)3
00050 #define KJAS_DESTROY_APPLET (char)4
00051 #define KJAS_START_APPLET (char)5
00052 #define KJAS_STOP_APPLET (char)6
00053 #define KJAS_INIT_APPLET (char)7
00054 #define KJAS_SHOW_DOCUMENT (char)8
00055 #define KJAS_SHOW_URLINFRAME (char)9
00056 #define KJAS_SHOW_STATUS (char)10
00057 #define KJAS_RESIZE_APPLET (char)11
00058 #define KJAS_GET_URLDATA (char)12
00059 #define KJAS_URLDATA (char)13
00060 #define KJAS_SHUTDOWN_SERVER (char)14
00061 #define KJAS_JAVASCRIPT_EVENT (char)15
00062 #define KJAS_GET_MEMBER (char)16
00063 #define KJAS_CALL_MEMBER (char)17
00064 #define KJAS_PUT_MEMBER (char)18
00065 #define KJAS_DEREF_OBJECT (char)19
00066 #define KJAS_AUDIOCLIP_PLAY (char)20
00067 #define KJAS_AUDIOCLIP_LOOP (char)21
00068 #define KJAS_AUDIOCLIP_STOP (char)22
00069 #define KJAS_APPLET_STATE (char)23
00070 #define KJAS_APPLET_FAILED (char)24
00071 #define KJAS_DATA_COMMAND (char)25
00072 #define KJAS_PUT_URLDATA (char)26
00073 #define KJAS_PUT_DATA (char)27
00074
00075
00076 class JSStackFrame;
00077
00078 typedef QMap< int, KJavaKIOJob* > KIOJobMap;
00079 typedef QMap< int, JSStackFrame* > JSStack;
00080
00081 class JSStackFrame {
00082 public:
00083 JSStackFrame(JSStack & stack, QStringList & a)
00084 : jsstack(stack), args(a), ticket(counter++), ready(false), exit (false) {
00085 jsstack.insert( ticket, this );
00086 }
00087 ~JSStackFrame() {
00088 jsstack.erase( ticket );
00089 }
00090 JSStack & jsstack;
00091 QStringList & args;
00092 int ticket;
00093 bool ready : 1;
00094 bool exit : 1;
00095 static int counter;
00096 };
00097
00098 int JSStackFrame::counter = 0;
00099
00100 class KJavaAppletServerPrivate
00101 {
00102 friend class KJavaAppletServer;
00103 private:
00104
00105 int counter;
00106 QMap< int, QGuardedPtr<KJavaAppletContext> > contexts;
00107 QString appletLabel;
00108 JSStack jsstack;
00109 KIOJobMap kiojobs;
00110 bool javaProcessFailed;
00111 bool useKIO;
00112
00113
00114 };
00115
00116 static KJavaAppletServer* self = 0;
00117
00118 KJavaAppletServer::KJavaAppletServer()
00119 {
00120 d = new KJavaAppletServerPrivate;
00121 process = new KJavaProcess();
00122
00123 connect( process, SIGNAL(received(const QByteArray&)),
00124 this, SLOT(slotJavaRequest(const QByteArray&)) );
00125
00126 setupJava( process );
00127
00128 if( process->startJava() ) {
00129 d->appletLabel = i18n( "Loading Applet" );
00130 d->javaProcessFailed = false;
00131 }
00132 else {
00133 d->appletLabel = i18n( "Error: java executable not found" );
00134 d->javaProcessFailed = true;
00135 }
00136 }
00137
00138 KJavaAppletServer::~KJavaAppletServer()
00139 {
00140 quit();
00141
00142 delete process;
00143 delete d;
00144 }
00145
00146 QString KJavaAppletServer::getAppletLabel()
00147 {
00148 if( self )
00149 return self->appletLabel();
00150 else
00151 return QString::null;
00152 }
00153
00154 QString KJavaAppletServer::appletLabel()
00155 {
00156 return d->appletLabel;
00157 }
00158
00159 KJavaAppletServer* KJavaAppletServer::allocateJavaServer()
00160 {
00161 if( self == 0 )
00162 {
00163 self = new KJavaAppletServer();
00164 self->d->counter = 0;
00165 }
00166
00167 self->d->counter++;
00168 return self;
00169 }
00170
00171 void KJavaAppletServer::freeJavaServer()
00172 {
00173 self->d->counter--;
00174
00175 if( self->d->counter == 0 )
00176 {
00177
00178
00179
00180 KConfig config( "konquerorrc", true );
00181 config.setGroup( "Java/JavaScript Settings" );
00182 if( config.readBoolEntry( "ShutdownAppletServer", true ) )
00183 {
00184 int value = config.readNumEntry( "AppletServerTimeout", 60 );
00185 QTimer::singleShot( value*1000, self, SLOT( checkShutdown() ) );
00186 }
00187 }
00188 }
00189
00190 void KJavaAppletServer::checkShutdown()
00191 {
00192 if( self->d->counter == 0 )
00193 {
00194 delete self;
00195 self = 0;
00196 }
00197 }
00198
00199 void KJavaAppletServer::setupJava( KJavaProcess *p )
00200 {
00201 KConfig config ( "konquerorrc", true );
00202 config.setGroup( "Java/JavaScript Settings" );
00203
00204 QString jvm_path = "java";
00205
00206 QString jPath = config.readPathEntry( "JavaPath" );
00207 if ( !jPath.isEmpty() && jPath != "java" )
00208 {
00209
00210 if( jPath[jPath.length()-1] == '/' )
00211 jPath.remove(jPath.length()-1, 1);
00212
00213 QDir dir( jPath );
00214 if( dir.exists( "bin/java" ) )
00215 {
00216 jvm_path = jPath + "/bin/java";
00217 }
00218 else if (dir.exists( "/jre/bin/java" ) )
00219 {
00220 jvm_path = jPath + "/jre/bin/java";
00221 }
00222 else if( QFile::exists(jPath) )
00223 {
00224
00225 jvm_path = jPath;
00226 }
00227 }
00228
00229
00230 p->setJVMPath( jvm_path );
00231
00232
00233 QString kjava_class = locate("data", "kjava/kjava.jar");
00234 kdDebug(6100) << "kjava_class = " << kjava_class << endl;
00235 if( kjava_class.isNull() )
00236 return;
00237
00238 QDir dir( kjava_class );
00239 dir.cdUp();
00240 kdDebug(6100) << "dir = " << dir.absPath() << endl;
00241
00242 QStringList entries = dir.entryList( "*.jar" );
00243 kdDebug(6100) << "entries = " << entries.join( ":" ) << endl;
00244
00245 QString classes;
00246 for( QStringList::Iterator it = entries.begin();
00247 it != entries.end(); it++ )
00248 {
00249 if( !classes.isEmpty() )
00250 classes += ":";
00251 classes += dir.absFilePath( *it );
00252 }
00253 p->setClasspath( classes );
00254
00255
00256 QString extraArgs = config.readEntry( "JavaArgs" );
00257 p->setExtraArgs( extraArgs );
00258
00259 if( config.readBoolEntry( "ShowJavaConsole", false) )
00260 {
00261 p->setSystemProperty( "kjas.showConsole", QString::null );
00262 }
00263
00264 if( config.readBoolEntry( "UseSecurityManager", true ) )
00265 {
00266 QString class_file = locate( "data", "kjava/kjava.policy" );
00267 p->setSystemProperty( "java.security.policy", class_file );
00268
00269 p->setSystemProperty( "java.security.manager",
00270 "org.kde.kjas.server.KJASSecurityManager" );
00271 }
00272
00273 d->useKIO = config.readBoolEntry( "UseKio", false);
00274 if( d->useKIO )
00275 {
00276 p->setSystemProperty( "kjas.useKio", QString::null );
00277 }
00278
00279
00280 if( KProtocolManager::useProxy() )
00281 {
00282
00283
00284
00285
00286 KURL dummyURL( "http://www.kde.org/" );
00287 QString httpProxy = KProtocolManager::proxyForURL(dummyURL);
00288 kdDebug(6100) << "httpProxy is " << httpProxy << endl;
00289
00290 KURL url( httpProxy );
00291 p->setSystemProperty( "http.proxyHost", url.host() );
00292 p->setSystemProperty( "http.proxyPort", QString::number( url.port() ) );
00293 }
00294
00295
00296 p->setMainClass( "org.kde.kjas.server.Main" );
00297 }
00298
00299 void KJavaAppletServer::createContext( int contextId, KJavaAppletContext* context )
00300 {
00301
00302 if ( d->javaProcessFailed ) return;
00303
00304 d->contexts.insert( contextId, context );
00305
00306 QStringList args;
00307 args.append( QString::number( contextId ) );
00308 process->send( KJAS_CREATE_CONTEXT, args );
00309 }
00310
00311 void KJavaAppletServer::destroyContext( int contextId )
00312 {
00313
00314 if ( d->javaProcessFailed ) return;
00315 d->contexts.remove( contextId );
00316
00317 QStringList args;
00318 args.append( QString::number( contextId ) );
00319 process->send( KJAS_DESTROY_CONTEXT, args );
00320 }
00321
00322 bool KJavaAppletServer::createApplet( int contextId, int appletId,
00323 const QString & name, const QString & clazzName,
00324 const QString & baseURL, const QString & user,
00325 const QString & password, const QString & authname,
00326 const QString & codeBase, const QString & jarFile,
00327 QSize size, const QMap<QString,QString>& params,
00328 const QString & windowTitle )
00329 {
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340 if ( d->javaProcessFailed ) return false;
00341
00342 QStringList args;
00343 args.append( QString::number( contextId ) );
00344 args.append( QString::number( appletId ) );
00345
00346
00347 args.append( name );
00348 args.append( clazzName );
00349 args.append( baseURL );
00350 args.append( user );
00351 args.append( password );
00352 args.append( authname );
00353 args.append( codeBase );
00354 args.append( jarFile );
00355
00356 args.append( QString::number( size.width() ) );
00357 args.append( QString::number( size.height() ) );
00358
00359 args.append( windowTitle );
00360
00361
00362 int num = params.count();
00363 QString num_params = QString("%1").arg( num, 8 );
00364 args.append( num_params );
00365
00366 QMap< QString, QString >::ConstIterator it;
00367
00368 for( it = params.begin(); it != params.end(); ++it )
00369 {
00370 args.append( it.key() );
00371 args.append( it.data() );
00372 }
00373
00374 process->send( KJAS_CREATE_APPLET, args );
00375
00376 return true;
00377 }
00378
00379 void KJavaAppletServer::initApplet( int contextId, int appletId )
00380 {
00381 if ( d->javaProcessFailed ) return;
00382 QStringList args;
00383 args.append( QString::number( contextId ) );
00384 args.append( QString::number( appletId ) );
00385
00386 process->send( KJAS_INIT_APPLET, args );
00387 }
00388
00389 void KJavaAppletServer::destroyApplet( int contextId, int appletId )
00390 {
00391 if ( d->javaProcessFailed ) return;
00392 QStringList args;
00393 args.append( QString::number(contextId) );
00394 args.append( QString::number(appletId) );
00395
00396 process->send( KJAS_DESTROY_APPLET, args );
00397 }
00398
00399 void KJavaAppletServer::startApplet( int contextId, int appletId )
00400 {
00401 if ( d->javaProcessFailed ) return;
00402 QStringList args;
00403 args.append( QString::number(contextId) );
00404 args.append( QString::number(appletId) );
00405
00406 process->send( KJAS_START_APPLET, args );
00407 }
00408
00409 void KJavaAppletServer::stopApplet( int contextId, int appletId )
00410 {
00411 if ( d->javaProcessFailed ) return;
00412 QStringList args;
00413 args.append( QString::number(contextId) );
00414 args.append( QString::number(appletId) );
00415
00416 process->send( KJAS_STOP_APPLET, args );
00417 }
00418
00419 void KJavaAppletServer::sendURLData( int loaderID, int code, const QByteArray& data )
00420 {
00421 QStringList args;
00422 args.append( QString::number(loaderID) );
00423 args.append( QString::number(code) );
00424
00425 process->send( KJAS_URLDATA, args, data );
00426 }
00427
00428 void KJavaAppletServer::removeDataJob( int loaderID )
00429 {
00430 KIOJobMap::iterator it = d->kiojobs.find( loaderID );
00431 if (it != d->kiojobs.end()) {
00432 it.data()->deleteLater();
00433 d->kiojobs.erase( it );
00434 }
00435 }
00436
00437 void KJavaAppletServer::quit()
00438 {
00439 QStringList args;
00440
00441 process->send( KJAS_SHUTDOWN_SERVER, args );
00442 }
00443
00444 void KJavaAppletServer::slotJavaRequest( const QByteArray& qb )
00445 {
00446
00447
00448 QString cmd;
00449 QStringList args;
00450 int index = 0;
00451 int qb_size = qb.size();
00452
00453
00454 char cmd_code = qb[ index++ ];
00455 ++index;
00456
00457
00458 QString contextID;
00459 while( qb[index] != 0 && index < qb_size )
00460 {
00461 contextID += qb[ index++ ];
00462 }
00463 bool ok;
00464 int ID_num = contextID.toInt( &ok );
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477 ++index;
00478
00479 if (cmd_code == KJAS_PUT_DATA) {
00480
00481 if (ok) {
00482 KIOJobMap::iterator it = d->kiojobs.find( ID_num );
00483 if (ok && it != d->kiojobs.end()) {
00484 QByteArray qba;
00485 qba.setRawData(qb.data() + index, qb.size() - index - 1);
00486 it.data()->data(qba);
00487 qba.resetRawData(qb.data() + index, qb.size() - index - 1);
00488 }
00489 kdDebug(6100) << "PutData(" << ID_num << ") size=" << qb.size() - index << endl;
00490 } else
00491 kdError(6100) << "PutData error " << ok << endl;
00492 return;
00493 }
00494
00495 while( index < qb_size )
00496 {
00497 int sep_pos = qb.find( 0, index );
00498 if (sep_pos < 0) {
00499 kdError(6100) << "Missing separation byte" << endl;
00500 sep_pos = qb_size;
00501 }
00502
00503 args.append( QString::fromLocal8Bit( qb.data() + index, sep_pos - index ) );
00504 index = sep_pos + 1;
00505 }
00506
00507
00508 switch( cmd_code )
00509 {
00510 case KJAS_SHOW_DOCUMENT:
00511 cmd = QString::fromLatin1( "showdocument" );
00512 break;
00513
00514 case KJAS_SHOW_URLINFRAME:
00515 cmd = QString::fromLatin1( "showurlinframe" );
00516 break;
00517
00518 case KJAS_SHOW_STATUS:
00519 cmd = QString::fromLatin1( "showstatus" );
00520 break;
00521
00522 case KJAS_RESIZE_APPLET:
00523 cmd = QString::fromLatin1( "resizeapplet" );
00524 break;
00525
00526 case KJAS_GET_URLDATA:
00527 if (ok && args.size () > 0) {
00528 d->kiojobs.insert(ID_num, new KJavaDownloader(ID_num, args[0]));
00529 kdDebug(6100) << "GetURLData(" << ID_num << ") url=" << args[0] << endl;
00530 } else
00531 kdError(6100) << "GetURLData error " << ok << " args:" << args.size() << endl;
00532 return;
00533 case KJAS_PUT_URLDATA:
00534 if (ok && args.size () > 0) {
00535 KJavaUploader *job = new KJavaUploader(ID_num, args[0]);
00536 d->kiojobs.insert(ID_num, job);
00537 job->start();
00538 kdDebug(6100) << "PutURLData(" << ID_num << ") url=" << args[0] << endl;
00539 } else
00540 kdError(6100) << "PutURLData error " << ok << " args:" << args.size() << endl;
00541 return;
00542 case KJAS_DATA_COMMAND:
00543 if (ok && args.size () > 0) {
00544 int cmd = args[0].toInt( &ok );
00545 KIOJobMap::iterator it = d->kiojobs.find( ID_num );
00546 if (ok && it != d->kiojobs.end())
00547 it.data()->jobCommand( cmd );
00548 kdDebug(6100) << "KIO Data command: " << ID_num << " " << args[0] << endl;
00549 } else
00550 kdError(6100) << "KIO Data command error " << ok << " args:" << args.size() << endl;
00551 return;
00552 case KJAS_JAVASCRIPT_EVENT:
00553 cmd = QString::fromLatin1( "JS_Event" );
00554 kdDebug(6100) << "Javascript request: "<< contextID
00555 << " code: " << args[0] << endl;
00556 break;
00557 case KJAS_GET_MEMBER:
00558 case KJAS_PUT_MEMBER:
00559 case KJAS_CALL_MEMBER: {
00560 int ticket = args[0].toInt();
00561 JSStack::iterator it = d->jsstack.find(ticket);
00562 if (it != d->jsstack.end()) {
00563 kdDebug(6100) << "slotJavaRequest: " << ticket << endl;
00564 args.pop_front();
00565 it.data()->args.operator=(args);
00566 it.data()->ready = true;
00567 it.data()->exit = true;
00568 } else
00569 kdDebug(6100) << "Error: Missed return member data" << endl;
00570 return;
00571 }
00572 case KJAS_AUDIOCLIP_PLAY:
00573 cmd = QString::fromLatin1( "audioclip_play" );
00574 kdDebug(6100) << "Audio Play: url=" << args[0] << endl;
00575 break;
00576 case KJAS_AUDIOCLIP_LOOP:
00577 cmd = QString::fromLatin1( "audioclip_loop" );
00578 kdDebug(6100) << "Audio Loop: url=" << args[0] << endl;
00579 break;
00580 case KJAS_AUDIOCLIP_STOP:
00581 cmd = QString::fromLatin1( "audioclip_stop" );
00582 kdDebug(6100) << "Audio Stop: url=" << args[0] << endl;
00583 break;
00584 case KJAS_APPLET_STATE:
00585 kdDebug(6100) << "Applet State Notification for Applet " << args[0] << ". New state=" << args[1] << endl;
00586 cmd = QString::fromLatin1( "AppletStateNotification" );
00587 break;
00588 case KJAS_APPLET_FAILED:
00589 kdDebug(6100) << "Applet " << args[0] << " Failed: " << args[1] << endl;
00590 cmd = QString::fromLatin1( "AppletFailed" );
00591 break;
00592 default:
00593 return;
00594 break;
00595 }
00596
00597
00598 if( !ok )
00599 {
00600 kdError(6100) << "could not parse out contextID to call command on" << endl;
00601 return;
00602 }
00603
00604 KJavaAppletContext* context = d->contexts[ ID_num ];
00605 if( context )
00606 context->processCmd( cmd, args );
00607 else if (cmd != "AppletStateNotification")
00608 kdError(6100) << "no context object for this id" << endl;
00609 }
00610
00611 void KJavaAppletServer::endWaitForReturnData() {
00612 kdDebug(6100) << "KJavaAppletServer::endWaitForReturnData" << endl;
00613 killTimers();
00614 JSStack::iterator it = d->jsstack.begin();
00615 for (; it != d->jsstack.end(); ++it)
00616 it.data()->exit = true;
00617 }
00618
00619 void KJavaAppletServer::timerEvent(QTimerEvent *) {
00620 endWaitForReturnData();
00621 kdDebug(6100) << "KJavaAppletServer::timerEvent timeout" << endl;
00622 }
00623
00624 void KJavaAppletServer::waitForReturnData(JSStackFrame * frame) {
00625 kdDebug(6100) << ">KJavaAppletServer::waitForReturnData" << endl;
00626 killTimers();
00627 startTimer(15000);
00628 while (!frame->exit)
00629 kapp->eventLoop()->processEvents (QEventLoop::AllEvents | QEventLoop::WaitForMore);
00630 if (d->jsstack.size() <= 1)
00631 killTimers();
00632 kdDebug(6100) << "<KJavaAppletServer::waitForReturnData stacksize:" << d->jsstack.size() << endl;
00633 }
00634
00635 bool KJavaAppletServer::getMember(QStringList & args, QStringList & ret_args) {
00636 JSStackFrame frame( d->jsstack, ret_args );
00637 args.push_front( QString::number(frame.ticket) );
00638
00639 process->send( KJAS_GET_MEMBER, args );
00640 waitForReturnData( &frame );
00641
00642 return frame.ready;
00643 }
00644
00645 bool KJavaAppletServer::putMember( QStringList & args ) {
00646 QStringList ret_args;
00647 JSStackFrame frame( d->jsstack, ret_args );
00648 args.push_front( QString::number(frame.ticket) );
00649
00650 process->send( KJAS_PUT_MEMBER, args );
00651 waitForReturnData( &frame );
00652
00653 return frame.ready && ret_args.count() > 0 && ret_args[0].toInt();
00654 }
00655
00656 bool KJavaAppletServer::callMember(QStringList & args, QStringList & ret_args) {
00657 JSStackFrame frame( d->jsstack, ret_args );
00658 args.push_front( QString::number(frame.ticket) );
00659
00660 process->send( KJAS_CALL_MEMBER, args );
00661 waitForReturnData( &frame );
00662
00663 return frame.ready;
00664 }
00665
00666 void KJavaAppletServer::derefObject( QStringList & args ) {
00667 process->send( KJAS_DEREF_OBJECT, args );
00668 }
00669
00670 bool KJavaAppletServer::usingKIO() {
00671 return d->useKIO;
00672 }
00673
00674 #include "kjavaappletserver.moc"