khtml Library API Documentation

kjavaprocess.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 "kjavaprocess.h"
00023 
00024 #include <kdebug.h>
00025 #include <kio/kprotocolmanager.h>
00026 
00027 #include <qtextstream.h>
00028 #include <qmap.h>
00029 
00030 #include <config.h>
00031 
00032 #include <unistd.h>
00033 #include <qptrlist.h>
00034 #include <sys/time.h>
00035 #include <sys/types.h>
00036 #include <unistd.h>
00037 #include <errno.h>
00038 
00039 class KJavaProcessPrivate
00040 {
00041 friend class KJavaProcess;
00042 private:
00043     QString jvmPath;
00044     QString classPath;
00045     QString mainClass;
00046     QString extraArgs;
00047     QString classArgs;
00048     QPtrList<QByteArray> BufferList;
00049     QMap<QString, QString> systemProps;
00050     QValueList<int> tickets;
00051     bool processKilled;
00052     int sync_count;
00053 };
00054 
00055 KJavaProcess::KJavaProcess() : KProcess()
00056 {
00057     d = new KJavaProcessPrivate;
00058     d->BufferList.setAutoDelete( true );
00059     d->processKilled = false;
00060     d->sync_count = 0;
00061     
00062     javaProcess = this; //new KProcess();
00063 
00064     connect( javaProcess, SIGNAL( wroteStdin( KProcess * ) ),
00065              this, SLOT( slotWroteData() ) );
00066     connect( javaProcess, SIGNAL( receivedStdout( int, int& ) ),
00067              this, SLOT( slotReceivedData(int, int&) ) );
00068     connect( javaProcess, SIGNAL( processExited (KProcess *) ),
00069              this, SLOT( slotExited (KProcess *) ) );
00070 
00071     d->jvmPath = "java";
00072     d->mainClass = "-help";
00073 }
00074 
00075 KJavaProcess::~KJavaProcess()
00076 {
00077     if ( isRunning() )
00078     {
00079         kdDebug(6100) << "stopping java process" << endl;
00080         stopJava();
00081     }
00082 
00083     //delete javaProcess;
00084     delete d;
00085 }
00086 
00087 bool KJavaProcess::isRunning()
00088 {
00089    return javaProcess->isRunning();
00090 }
00091 
00092 bool KJavaProcess::startJava()
00093 {
00094    return invokeJVM();
00095 }
00096 
00097 void KJavaProcess::stopJava()
00098 {
00099    killJVM();
00100 }
00101 
00102 void KJavaProcess::setJVMPath( const QString& path )
00103 {
00104    d->jvmPath = path;
00105 }
00106 
00107 void KJavaProcess::setClasspath( const QString& classpath )
00108 {
00109     d->classPath = classpath;
00110 }
00111 
00112 void KJavaProcess::setSystemProperty( const QString& name,
00113                                       const QString& value )
00114 {
00115    d->systemProps.insert( name, value );
00116 }
00117 
00118 void KJavaProcess::setMainClass( const QString& className )
00119 {
00120    d->mainClass = className;
00121 }
00122 
00123 void KJavaProcess::setExtraArgs( const QString& args )
00124 {
00125    d->extraArgs = args;
00126 }
00127 
00128 void KJavaProcess::setClassArgs( const QString& args )
00129 {
00130    d->classArgs = args;
00131 }
00132 
00133 //Private Utility Functions used by the two send() methods
00134 QByteArray* KJavaProcess::addArgs( char cmd_code, const QStringList& args )
00135 {
00136     //the buffer to store stuff, etc.
00137     QByteArray* buff = new QByteArray();
00138     QTextOStream output( *buff );
00139     char sep = 0;
00140 
00141     //make space for the command size: 8 characters...
00142     QCString space( "        " );
00143     output << space;
00144 
00145     //write command code
00146     output << cmd_code;
00147 
00148     //store the arguments...
00149     if( args.count() == 0 )
00150     {
00151         output << sep;
00152     }
00153     else
00154     {
00155         for( QStringList::ConstIterator it = args.begin();
00156              it != args.end(); ++it )
00157         {
00158             if( !(*it).isEmpty() )
00159             {
00160                 output << (*it).latin1();
00161             }
00162             output << sep;
00163         }
00164     }
00165 
00166     return buff;
00167 }
00168 
00169 void KJavaProcess::storeSize( QByteArray* buff )
00170 {
00171     int size = buff->size() - 8;  //subtract out the length of the size_str
00172     QString size_str = QString("%1").arg( size, 8 );
00173     kdDebug(6100) << "KJavaProcess::storeSize, size = " << size_str << endl;
00174 
00175     const char* size_ptr = size_str.latin1();
00176     for( int i = 0; i < 8; i++ )
00177         buff->at(i) = size_ptr[i];
00178 }
00179 
00180 void KJavaProcess::sendBuffer( QByteArray* buff )
00181 {
00182     d->BufferList.append( buff );
00183     if( d->BufferList.count() == 1 && d->sync_count == 0)
00184     {
00185         popBuffer();
00186     }
00187 }
00188 
00189 void KJavaProcess::sendSync( int ticket, char cmd_code, const QStringList& args ) {
00190     kdDebug(6100) << ">KJavaProcess::sendSync " << d->sync_count << endl;
00191     if (d->sync_count++ == 0)
00192         javaProcess->suspend();
00193     QByteArray* buff = addArgs( cmd_code, args );
00194     storeSize( buff );
00195     int dummy;
00196     int current_sync_count;
00197     int size = buff->size();
00198     char *data = buff->data();
00199 
00200     d->tickets.append( ticket );
00201 
00202     fd_set fds;
00203     timeval tv;
00204     do {
00205         FD_ZERO(&fds);
00206         FD_SET(in[1], &fds);
00207         tv.tv_sec = 5;
00208         tv.tv_usec = 0;
00209         int retval = select(in[1]+1, 0L, &fds, 0L, &tv);
00210         FD_CLR(in[1], &fds);
00211         if (retval < 0 && errno == EINTR) {
00212             continue;
00213         } else if (retval <= 0) {
00214             kdError(6100) << "KJavaProcess::sendSync " << retval << endl;
00215             goto bail_out;
00216         } else if (KProcess::input_data) {
00217             KProcess::slotSendData(dummy);
00218         } else if( d->BufferList.count() > 0) {
00219             popBuffer();
00220         } else {
00221             int nr = ::write(in[1], data, size);
00222             size -= nr;
00223             data += nr;
00224         }
00225     } while (size > 0);
00226     current_sync_count = d->sync_count;
00227     do {
00228         FD_ZERO(&fds);
00229         FD_SET(out[0], &fds);
00230         tv.tv_sec = 15;
00231         tv.tv_usec = 0;
00232         kdDebug(6100) << "KJavaProcess::sendSync bf read" << endl;
00233         int retval = select(out[0]+1, &fds, 0L, 0L, &tv);
00234         FD_CLR(out[0], &fds);
00235         if (retval < 0 && errno == EINTR) {
00236             continue;
00237         } else if (retval <= 0) {
00238             kdError(6100) << "KJavaProcess::sendSync timeout " << retval<< endl;
00239             break;
00240         } else {
00241             slotReceivedData(out[0], dummy);
00242         }
00243         QValueList<int>::iterator it = d->tickets.find(ticket);
00244         if (it == d->tickets.end())
00245             break;
00246     } while(true);
00247 bail_out:
00248     delete buff;
00249     if (--d->sync_count <= 0) {
00250         javaProcess->resume();
00251         if ( d->BufferList.count() > 0 )
00252             popBuffer();
00253     }
00254     kdDebug(6100) << "<KJavaProcess::sendSync " << d->sync_count << endl;
00255 }
00256 
00257 void KJavaProcess::syncCommandReceived(int ticket) {
00258     d->tickets.remove( ticket );
00259 }
00260 
00261 void KJavaProcess::send( char cmd_code, const QStringList& args )
00262 {
00263     if( isRunning() )
00264     {
00265         QByteArray* buff = addArgs( cmd_code, args );
00266         storeSize( buff );
00267         kdDebug(6100) << "<KJavaProcess::send " << (int)cmd_code << endl;
00268         sendBuffer( buff );
00269     }
00270 }
00271 
00272 void KJavaProcess::send( char cmd_code, const QStringList& args,
00273                          const QByteArray& data )
00274 {
00275     if( isRunning() )
00276     {
00277         kdDebug(6100) << "KJavaProcess::send, qbytearray is size = " << data.size() << endl;
00278 
00279         QByteArray* buff = addArgs( cmd_code, args );
00280         int cur_size = buff->size();
00281         int data_size = data.size();
00282         buff->resize( cur_size + data_size );
00283         memcpy( buff->data() + cur_size, data.data(), data_size );
00284 
00285         storeSize( buff );
00286         sendBuffer( buff );
00287     }
00288 }
00289 
00290 void KJavaProcess::popBuffer()
00291 {
00292     QByteArray* buf = d->BufferList.first();
00293     if( buf )
00294     {
00295 //        DEBUG stuff...
00296 //      kdDebug(6100) << "Sending buffer to java, buffer = >>";
00297 //        for( unsigned int i = 0; i < buf->size(); i++ )
00298 //        {
00299 //            if( buf->at(i) == (char)0 )
00300 //                kdDebug(6100) << "<SEP>";
00301 //            else if( buf->at(i) > 0 && buf->at(i) < 10 )
00302 //                kdDebug(6100) << "<CMD " << (int) buf->at(i) << ">";
00303 //            else
00304 //                kdDebug(6100) << buf->at(i);
00305 //        }
00306 //        kdDebug(6100) << "<<" << endl;
00307 
00308         //write the data
00309         if ( !javaProcess->writeStdin( buf->data(),
00310                                        buf->size() ) )
00311         {
00312             kdError(6100) << "Could not write command" << endl;
00313         }
00314     }
00315 }
00316 
00317 void KJavaProcess::slotWroteData( )
00318 {
00319     //do this here- we can't free the data until we know it went through
00320     d->BufferList.removeFirst();  //this should delete it since we setAutoDelete(true)
00321     kdDebug(6100) << "slotWroteData " << d->BufferList.count() << endl;
00322 
00323     if ( d->BufferList.count() >= 1 && d->sync_count == 0 )
00324     {
00325         popBuffer();
00326     }
00327 }
00328 
00329 
00330 bool KJavaProcess::invokeJVM()
00331 {
00332     
00333     *javaProcess << d->jvmPath;
00334 
00335     if( !d->classPath.isEmpty() )
00336     {
00337         *javaProcess << "-classpath";
00338         *javaProcess << d->classPath;
00339     }
00340 
00341     //set the system properties, iterate through the qmap of system properties
00342     for( QMap<QString,QString>::Iterator it = d->systemProps.begin();
00343          it != d->systemProps.end(); ++it )
00344     {
00345         QString currarg;
00346 
00347         if( !it.key().isEmpty() )
00348         {
00349             currarg = "-D" + it.key();
00350             if( !it.data().isEmpty() )
00351                 currarg += "=" + it.data();
00352         }
00353 
00354         if( !currarg.isEmpty() )
00355             *javaProcess << currarg;
00356     }
00357 
00358     //load the extra user-defined arguments
00359     if( !d->extraArgs.isEmpty() )
00360     {
00361         // BUG HERE: if an argument contains space (-Dname="My name")
00362         // this parsing will fail. Need more sophisticated parsing
00363         QStringList args = QStringList::split( " ", d->extraArgs );
00364         for ( QStringList::Iterator it = args.begin(); it != args.end(); ++it )
00365             *javaProcess << *it;
00366     }
00367 
00368     *javaProcess << d->mainClass;
00369 
00370     if ( !d->classArgs.isNull() )
00371         *javaProcess << d->classArgs;
00372 
00373     kdDebug(6100) << "Invoking JVM now...with arguments = " << endl;
00374     QString argStr;
00375     QTextOStream stream( &argStr );
00376     QValueList<QCString> args = javaProcess->args();
00377     qCopy( args.begin(), args.end(), QTextOStreamIterator<QCString>( stream, " " ) );
00378     kdDebug(6100) << argStr << endl;
00379 
00380     KProcess::Communication flags =  (KProcess::Communication)
00381                                      (KProcess::Stdin | KProcess::Stdout |
00382                                       KProcess::NoRead);
00383 
00384     bool rval = javaProcess->start( KProcess::NotifyOnExit, flags );
00385     if( rval )
00386         javaProcess->resume(); //start processing stdout on the java process
00387     else
00388         killJVM();
00389 
00390     return rval;
00391 }
00392 
00393 void KJavaProcess::killJVM()
00394 {
00395    d->processKilled = true;
00396    javaProcess->kill();
00397 }
00398 
00399 /*  In this method, read one command and send it to the d->appletServer
00400  *  then return, so we don't block the event handling
00401  */
00402 void KJavaProcess::slotReceivedData( int fd, int& )
00403 {
00404     //read out the length of the message,
00405     //read the message and send it to the applet server
00406     char length[9] = { 0 };
00407     int num_bytes = ::read( fd, length, 8 );
00408     if( num_bytes == -1 )
00409     {
00410         kdError(6100) << "could not read 8 characters for the message length!!!!" << endl;
00411         return;
00412     }
00413 
00414     QString lengthstr( length );
00415     bool ok;
00416     int num_len = lengthstr.toInt( &ok );
00417     if( !ok )
00418     {
00419         kdError(6100) << "could not parse length out of: " << lengthstr << endl;
00420         return;
00421     }
00422 
00423     //now parse out the rest of the message.
00424     char* msg = new char[num_len];
00425     num_bytes = ::read( fd, msg, num_len );
00426     if( num_bytes == -1 ||  num_bytes != num_len )
00427     {
00428         kdError(6100) << "could not read the msg, num_bytes = " << num_bytes << endl;
00429         delete[] msg;
00430         return;
00431     }
00432 
00433     QByteArray qb;
00434     emit received( qb.duplicate( msg, num_len ) );
00435     delete[] msg;
00436 }
00437 
00438 void KJavaProcess::slotExited( KProcess *process )
00439 {
00440   if (process && process == javaProcess) {
00441     int status = -1;
00442     if (!d->processKilled) {
00443      status = javaProcess->exitStatus();
00444     }
00445     kdDebug(6100) << "jvm exited with status " << status << endl; 
00446     emit exited(status);
00447   }
00448 }
00449 
00450 #include "kjavaprocess.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