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