Archive

Author Archive

Programmatically set environment for Symfony project

October 20th, 2010 Nick No comments

I have recently been working on a web project built on Symfony 1.4. There are a team of developers, each has their own installation and we had the problem whereby each needed their own particular configuration settings. Symfony allows for multiple configurations to be specified using “environments” but the problem is how to get each installation to choose the correct environment without changing index.php – which of course would affect everyone else each time it was committed to Subversion.

The solution was to create a simple class that looks for the existence of a file, environment.cfg, in the configuration directory. The contents of the file specify which environment to use. This file is not committed to Subversion each developer can specify their own environment without affecting anything in Subversion.

We added a few lines to our index.php file like this:

// Load the class which chooses the environment
require_once(dirname(__FILE__).'/../lib/helper/ApplicationEnvironment.class.php');

// Instantiate the class - specify the default environment to "live" (if there is no environment.cfg file)
$environment = new ApplicationEnvironment("live");

// Try and read environment.cfg
$environment->overrideFromFile(dirname(__FILE__).'/../config/environment.cfg');

// Create a configuration object using the chosen environment
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', $environment->getEnvironment(), $environment->getDebugMode());

Note that the debug mode option is also set by the ApplicationEnvironment object. This is determined by the URL:

// in ApplicationEnvironment.class.php
    public function getDebugMode()
    {
        $debugAddresses = array("debug.example.com");

        return in_array($_SERVER['SERVER_NAME'], $debugAddresses);
    }

We could have used the environment.cfg file to specify this, for example on another line, but we also needed to specify debug mode on our live server so that if the site is accessed at www.oursite.com it is not in debug mode, but if it is accessed via debug.oursite.com then it is in debug mode (of course debug.oursite.com is http password protected).

CLI

Next we had the problem of setting the environment. We could of course rely on each developer to put --env=my-environment but it would be much nicer to have this done automatically. The solution was to create a simple script that resides in the application root directory next to symfony.php that looks something like this:

require_once(dirname(__FILE__).'/lib/helper/ApplicationEnvironment.class.php');

$environment = new ApplicationEnvironment("live");
$environment->overrideFromFile(dirname(__FILE__).'/config/environment.cfg');

$envSet = false;

foreach ($argv as $i => $arg)
{
    if (substr($arg, 0, 6) == "--env=")
    {
        $envSet = true;
        break;
    }
}

if (!$envSet)
{
    $argv[] = sprintf("--env=%s", $environment->getEnvironment());
    $argc++;

    $_SERVER['argv'] = $argv;
    $_SERVER['argc'] = $argc;
}

chdir(dirname(__FILE__));
require_once(dirname(__FILE__).'/config/ProjectConfiguration.class.php');
include(sfCoreAutoload::getInstance()->getBaseDir().'/command/cli.php');

It’s a bit of a nasty mess, but it works at least.

Download

ApplicationEnvironment.class.php

svn: ‘http://subversion.tigris.org/xmlns/dav/md5-checksum’ was not present on the resource

October 14th, 2010 Nick No comments

I recently got this somewhat cryptic error message when trying to merge something in Subversion. In the root directory of my working copy, I ran svn merge, something along the lines of:

svn merge -c 1234 http://svn.example.com/myproject/trunk ./

Only to get:
svn: 'http://subversion.tigris.org/xmlns/dav/md5-checksum' was not present on the resource

I had absolutely no idea what it was on about, and a fair bit of Googling turned up nothing. Then, just for fun, I tried going one level up from my working copy and trying again:


cd ../
svn merge -c 1234 http://svn.example.com/myproject/trunk ./myworkingcopy/

And lo and behold – it worked. I haven’t the foggiest idea why, but it does. Any ideas anyone?

Using SVN v1.6.6

Categories: Linux Tags: , , , ,

cast from pointer to integer of different size – cast to pointer from integer of different size

October 8th, 2010 Nick 2 comments

On a 32-bit system my code compiled without any problem. On a 64-bit system I get the warnings:

warning: cast from pointer to integer of different size
warning: cast to pointer from integer of different size

It turned out the problem was how I was passing an integer to a new thread using pthread_create(). The final argument is a void pointer which can be used to pass a pointer to some data. In my case I was casting an integer to a void pointer in the parent thread, and then re-casting it back to an integer inside the thread.

On a 32-bit system both void pointers and integers occupy 32 bits so everything was ok. On my 64-bit system, integers are still 32-bits but void pointers are 64-bits.

One solution use long instead of int, which are, according to the C99 standard, the same size as void pointers on 32-bit and 64-bit systems.

The proper solution is not to cast integers to pointers in the first place but use pointers throughout:

    void hello(void *i)
    {
        int *pointer;

        /* Cast the void pointer to a integer pointer */
        pointer = (int *) i;

        /* Display the value pointed to by the pointer */
        printf("Value is: %d\n", *pointer);
    }

    int main()
    {
        int value, *pointer;

        /* Assign a value */
        value = 11;

        /* Display the original value */
        printf("Value is: %d\n", value);

        /* Set the pointer to the location of the value */
        pointer = &value;

        /* Call a function, cast the pointer to a void pointer */
        hello((void *)pointer);

        return 1;
    }

Of course it should be taken into account that when using threads there is the problem that the value referenced by the pointer could be changed by any of the threads.

Categories: C Tags: , , , , , ,

Redirect and append STDOUT and STDERR to a log file

June 8th, 2010 Nick No comments

Redirecting STDOUT to a file is easy:

command > foo.log

Redirecting STDERR to a file is also easy:

command 2> foo.log

Redirecting both STDOUT and STDERR to a file is also fairly straightforward:

command &> foo.log

But what if we want to append to the log rather than truncating it each time we write? This is done for STDOUT by using the >> operator, and for STDERR by using 2>>. Unfortunately &>> does not exist, so how do we append both STDOUT and STDERR?

The solution is to redirect STDERR to STDOUT, and then redirect STDOUT to a file using the append operator:

command 1>> foo.txt 2>&1

Multitasking: PHP in parallel

March 2nd, 2010 Nick 1 comment

Update: I have recently solved the issue of multitasking in PHP with a standalone application, The Fat Controller, which handles multitasking for you. Read more here.

Like most websites, glogster.com periodically sends out an e-newsletter to its user-base.    This is done by a simple PHP script that opens a socket to a mail server and goes through the user database, sending an email to each one.    This approach has worked fine until recently, the number of users has gone up dramatically and now it takes an infeasibly long time to send emails to all our users.

After optimising the script as much as possible, the only alternative option was to run many instances of the script simultaneously.   PHP doesn’t support multithreading, and although PHP does support POSIX style process control ( http://www.php.net/manual/en/book.pcntl.php ) I decided to create a wrapper in C that would handle multiple instances of PHP.   (I haven’t found anything technically wrong with PHP’s process control, I just needed something stable, reliable and potentially able to mulitask other scripts such as Python).

How it works

The wrapper I made (see below for code listing) is fairly simple and works by creating a series of threads, each of which forks and creates a PHP instance (replacing the child process) that runs the emailing script.   The thread from the parent process waits for the PHP process to finish and then ends itself.   Once a thread ends, the main thread creates another one, thereby maintaining a constant number of threads.   Signal handling is handled by a separate thread and can either tell the main thread to stop creating new threads and thereby safely shutdown the program or directly terminate all child processes and thus abruptly end the program.

Almost done

So now I had a way of running any number of PHP processes simultaneously and was almost ready to send emails at lightning speed, however one problem remained.   Once an email has been sent to a user, the database is updated so that I know which user has received which email and thus prevent me from sending the same user the same email more than once.   The problem is; what if one instance of the PHP script reads a user, and before it has time to send them the email and update the database, another PHP instance reads the same user and also sends them the email?   One way to solve this would be through locking the database table before reading and updating each row, however this would slow the database down which is not ideal.

The solution I came up with was to limit the number of simultaneous PHP instances to 8 and have the wrapper send each PHP process a unique instance ID from 0 to 7.   When selecting users from the database, the script looked at the 3 least significant bits (LSBs) of the IDs in the recipients table and selected only those rows whose 3 LSBs equated to the instance ID passed from the wrapper.

SELECT *,
(
	if( (`id` | 1) = `id`, 1, 0) +
	if( (`id` | 2) = `id`, 2, 0) +
	if( (`id` | 4) = `id`, 4, 0)
) AS `IID`
FROM `recipients`
WHERE `sent` = 0
HAVING `IID` = :IID
LIMIT :OFFSET, :BATCH_SIZE;

Just bind the instance ID passed from the wrapper into the query and each instance will now select recipients that are unique to that ID – no two concurrent instances can select the same recipients. Just don’t forget to update each row once each email has been sent.

Note: Using OFFSET in a limit clause is inefficient, for an alternative: http://www.4pmp.com/2010/02/scalable-mysql-avoid-offset-for-large-tables/

Finishing touches

We’re almost there, just one more little feature is required and the system will be perfect (sort of). If a thread process has nothing to do then it will return immediately, so we end up with a situation when thread processes are constantly created and destroyed which is not the most efficient use of resources. The solution was to check the exit status of the PHP process. If zero then all is ok, the thread ends and is available to be restarted immediately as normal. If however, the exit status is non-zero then the thread is marked as “sleeping” and cannot be restarted by the main thread for another 30 seconds.

If the PHP script doesn’t find any rows in the database to process then it returns 250 (a non-zero, non-reserved exit status) and so the calling thread in the wrapper sleeps for 30 seconds before trying again to see if there are any new items in the database to process.

This has the added advantage that if anything goes wrong in the PHP script, such as a fatal error, then the thread will sleep and you won’t end up with perpetuate thread cycling.

Code listing

Here is the code for the wrapper. The code should be fairly straightforward, with the above notes and the inline comments you should be able to figure out what’s going on – just don’t forget to compile with the pthread library. Comments are of course very welcome!

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>

#define NUM_THREADS 8
#define THREAD_STACK_HEADROOM 1048576

pthread_mutex_t mutexSignal;
int slots[NUM_THREADS];
int handledSignal = -1;

void* signalHandler(void* arg);
void waitForThreads();
void killall();
void *task(void *i);

/**
 * Each thread will do this
 *
 */
void *task(void *i)
{
    sigset_t signalSet;
    int iid;
    char *tid;
    int stat_loc;

    iid = (int)i;

    /* Say hello and show the thread number */
    printf("Thread %d: starting\n", iid);

    /* Spawn a child to run the program. */
    pid_t pid=fork();

    switch (pid)
    {
        case 0:
            setsid();
            sigfillset(&signalSet);
            pthread_sigmask(SIG_UNBLOCK, &signalSet, NULL );

            tid = malloc(11*sizeof(char));

            sprintf(tid, "tid=%d", iid);

            char *argv[]={"php", "./sleep.php", tid, NULL};

            execv("/usr/bin/php",argv);
            free(tid);
            exit(EXIT_FAILURE); /* only if execv fails */
        case -1:
            printf("Thread %d: Fork failed\n", iid);
            slots[iid] = 0;
            break;
        default:
            /*  parent process */
            slots[iid] = pid;

            if (pid == waitpid(pid,&stat_loc,WUNTRACED)) /* wait for child to exit */
            {
                printf("Thread %d: Child finished with exit code: %d", iid, WEXITSTATUS(stat_loc));

                if (WEXITSTATUS(stat_loc) != 0)
                {
                    /* Sleep this thread, set available time to now+30s */
                    printf(" Going to sleep\n");
                    slots[iid] = -1 * (time(0) + 30);
                }
                else
                {
                    /* Free this thread slot */
                    printf(" Returning to pool\n");
                    slots[iid] = 0;
                }
            }
            else
            {
                printf("Thread %d: Bad exit\n", iid);
                slots[iid] = 0;
            }
    }

    printf("Thread %d: Finished\n", iid);

    /* Terminate the thread */
    pthread_exit((void*) i);
}

void killall()
{
    int kv,i;

    for (i=0; i<NUM_THREADS;i++)
    {
        kv = kill(slots[i], SIGINT);
        printf("Killed %d: %s\n", slots[i], kv ==0?"ok":"fail");
    }
}

void waitForThreads()
{
    int i, stoppedThreads;

    printf("Waiting for threads to finish\n");

    while(1)
    {
        stoppedThreads = 0;

        for (i=0; i<NUM_THREADS;i++)
        {
            /* Check if thread has finished or is sleeping */
            if (slots[i] == 0 || slots[i] < -1)
            {
                stoppedThreads++;
            }
        }

        if (stoppedThreads == NUM_THREADS)
        {
            break;
        }
    }
}

void* signalHandler( void* arg )
{
    sigset_t signal_set;
    int sig;

    while (1)
    {
        /* Wait for any and all signals */
        sigfillset(&signal_set);

        sigwait(&signal_set, &sig);

        /* When we get this far, we've caught a signal */

        switch(sig)
        {
            /* SIGQUIT */
            case SIGTERM:
            case SIGQUIT:
                pthread_mutex_lock(&mutexSignal);
                handledSignal = SIGQUIT;
                pthread_mutex_unlock(&mutexSignal);
                break;

            /* SIGINT */
            case SIGINT:
                pthread_mutex_lock(&mutexSignal);
                handledSignal = SIGINT;
                pthread_mutex_unlock(&mutexSignal);
                break;

            /* other signals */
            default:
                pthread_mutex_lock(&mutexSignal);
                handledSignal = 0;
                pthread_mutex_unlock(&mutexSignal);
                break;
        }
    }

    return (void*)0;
}

/**
 * Main
 *
 */
int main()
{
    int rc, i;
    int running = 1;
    size_t stacksize;
    sigset_t signalSet;
    pthread_t threadSignalHandler;
    pthread_t thread[NUM_THREADS];
    pthread_attr_t attr;

    /* Initialise the signal mutex lock */
    pthread_mutex_init(&mutexSignal, NULL);

    /* Initialise and set thread attributes */
    pthread_attr_init(&attr);
    //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    /* Ensure each thread has a given stack size - portability */
    pthread_attr_getstacksize(&attr, &stacksize);
    printf("Default stack size = %li\n", (long) stacksize);

        /* Determine and set a new stack size */
        stacksize = sizeof(sigset_t) + THREAD_STACK_HEADROOM;
        printf("Amount of stack calculated per thread = %li\n", (long) stacksize);
        pthread_attr_setstacksize (&attr, stacksize);

    /* block all signals */
    sigfillset(&signalSet);
    pthread_sigmask(SIG_BLOCK, &signalSet, NULL );

    /* create the signal handling thread */
    pthread_create(&threadSignalHandler, NULL, signalHandler, NULL);

    while (running > 0)
    {
        /* Find an empty slot */
        for (i=0; i<NUM_THREADS;i++)
        {
            if (slots[i] == 0)  /* Thread is currently not doing anything, so let's give it something to do */
            {
                /* Set this slot as used, -1 is a temporary value before being replaced by the PID of the forked process (prevents race hazards) */
                slots[i] = -1;

                /* This slot is spare */
                printf("Main: creating thread %d\n", i);

                /* Create a new thread */
                rc = pthread_create(&thread[i], &attr, task, (void *)i);

                if (rc)
                {
                    printf("ERROR: return code from pthread_create() is %d\n", rc);
                    exit(1);
                }

                break;
            }
            else if (slots[i] < -1)     /* Thread is sleeping, see if we should wake it up */
            {
                if (time(0) > (-1 * slots[i]) )
                {
                    /* Thread has slept long enough so let's wake it up */
                    slots[i] = 0;
                }
            }
        }

        pthread_mutex_lock(&mutexSignal);

        switch ( handledSignal )
        {
            case -1:
                break;

            case 0:
                /* The case for signals we're not interested in */
                handledSignal = -1;
                break;

            case SIGTERM:
            case SIGQUIT:
                printf("Main: SIGQUIT\n");
                handledSignal = -1;
                running = -1;
                killall();
                break;

            case SIGINT:
                printf("Main: SIGINT\n" );
                handledSignal = -1;
                running = -1;
                break;
        }

        pthread_mutex_unlock(&mutexSignal);

        /* Sleep for a bit - all this thread creation is hard work! */
        usleep(100000);
    }

    waitForThreads();

    printf("Bye\n");
}

And here’s a sample PHP script (but you can of course have the wrapper call anything).

<?php

list($junk, $tid) = explode("=", $argv[1], 2);

printf("PHP: Hello from thread %d\n", $tid);

sleep(mt_rand(4,14));

// Non-zero exit status if the calling thread should sleep
// Chosen 250 because it's not a reserved status
// NOTE: PHP returns 255 on fatal error
exit(250);

Exercises left to the reader

The above code is not really intended as production-ready, (although I have actually used it in production) and there are still plenty of loose ends that could do with tidying up. One thing you might want to do is detach the child PHP processes from the parent process’ output sockets, thereby effectively sending them to the background. For inspiration take a look at this basic daemonising program: A simple daemon in C.

Another nice addition might be to allow the child process to be specified in command line arguments to the wrapper. It would be useful to specify the sleep time for threads and also the maximum number of concurrent threads – this way you wouldn’t have to recompile each time you changed something.

If you find this useful or interesting then comments would be greatly welcomed!

Categories: C, PHP Tags: , , , ,

Scalable MySQL: Avoid offset for large tables

February 27th, 2010 Nick 3 comments

It’s fairly common to need to iterate through all the rows in a given table and perform an action on each. The usual way to do this is fetch rows from the database in batches using LIMIT, specifying an offset and number of rows to be returned.

SELECT * FROM `myBigTable` LIMIT :OFFSET, :ROW_COUNT;

After each batch of rows is processed, the offset is increased by the size of the batch and the process is repeated until all rows have been processed.

Now that’s not exactly rocket science, but there is a problem – as the offset increases, the time taken for the query to execute progressively increases, which can mean processing very large tables will take an extremely long time. The reason is because offset works on the physical position of rows in the table which is not indexed. So to find a row at offset x, the database engine must iterate through all the rows from 0 to x.

The obvious solution would be to use the primary key instead of offset:

SELECT * FROM `myBigTable` WHERE `id` > :OFFSET LIMIT :BATCH_SIZE;

The problem here is that MySQL doesn’t guarantee that rows appear in the table in the same order as the primary key, so you could end up processing some rows twice, and worse still, some rows not at all. Luckily this can be easily solved by using ORDER BY:

SELECT * FROM `myBigTable` WHERE `id` > :OFFSET ORDER BY `id` ASC;

This might seem a bit counterintuitive using ORDER BY when we want to speed things up, but remember we’re ordering on a uniquely indexed column which will be fast anyway, and compared to the alternative of iterating through each row using an offset, it’s a huge improvement.

Conclusion

The general rule of thumb is “never use offset in a limit clause”. For small tables you probably won’t notice any difference, but with tables with over a million rows you’re going to see huge performance increases.

Remember, this doesn’t just apply to iterating through tables but whenever offset is used. For example implementing pagination of large tables, if the first few pages load quickly but subsequent pages load progressively slower then it’s likely this is the cause (or you are missing some indices!)

SVN: Entry has unexpectedly changed special status

February 18th, 2010 Nick No comments

I just tried to commit my Working Copy when I got the error message:

svn: Entry 'jquery-latest.js' has unexpectedly changed special status

It turns out that someone had created a symlink to whatever is the latest version of jQuery, which apart from being a somewhat dubious solution, should be fine.   However, it seems a Windows user then checked out this file and somehow changed it; either directly or by some strange Windowsy magic.   Anyway, the changed file was then corrupted and the result is the rather cryptic message above.

I tried to remove the offending file:

svn remove jquery-latest.js

But that resulted in:

svn: 'jquery-latest.js' is in the way of the resource actually under version control

Which was simpy resolved by using the --force command:

svn remove --force jquery-latest.js

I recreated the symlink, committed it and it seems now to be ok.

Spinning command line cursor in Java

January 29th, 2010 Nick No comments

As a follow-up to my previous article on displaying a spinning cursor to display program activity, I have created a more useful example that could be adapted for use.

The original problem was this – I was writing a program that performed a particular task and I needed some way of showing the program was alive and functioning correctly whilst doing the task.

In Windows this is done by changing the mouse cursor into an hourglass, unfortunately on the command line we don’t have such GUI luxuries. One classic option is to create a spinning line by displaying | / – \ and so on.

The example below creates two child threads; one to do a particular job (in this case slowly count to 10) and another thread to display a spinning cursor.

/**
 * Spinning cursor test class
 *
 * @author Nick Giles
 */
public class SpinTest
{
    /**
     * Thread which does the stuff we're interested in
     *
     */
    private Thread stuff;

    /**
     * Thread class which does stuff in the background
     *
     */
    private class Stuff implements Runnable
    {
        public void run()
        {
            int result = 0;

            try
            {
                // Slowly count to 10, although you can of course put whatever
                // you want here, presumably something a bit more useful
                while (result < 10)
                {
                    result++;
                    Thread.sleep(1000);
                }
            }
            catch (InterruptedException e)
            {
                // If interrupted then quietly end the thread, you may well
                // want to handle this in some way
            }
        }
    }

    /**
     * Spinner thread
     *
     */
    private class Spinner implements Runnable
    {
        public void run()
        {
            String[] phases = {"|", "/", "-", "\\"};

            try
            {
                while (true)
                {
                    for (String phase : phases)
                    {
                        System.out.printf("\b"+phase);

                        Thread.sleep(100);
                    }
                }
            }
            catch (InterruptedException e)
            {
                // No need to do anything if interrupted
            }
        }
    }

    /**
     * Handles all shutdown functions
     *
     */
    public class ShutdownHandler extends Thread
    {
        public void run()
        {
            // On interrupt, stop doing stuff
            stuff.interrupt();
        }
    }

    public void doStuff()
    {
        try
        {
            // Attach an object to handle shutdown signals (i.e. ctrl+c)
            Runtime.getRuntime().addShutdownHook(new ShutdownHandler()); 

            // Create a new thread for spinning the cursor
            Thread spinner = new Thread(new Spinner());

            // Create a new thread for doing stuff
            this.stuff = new Thread(new Stuff());

            // Nice message to user
            System.out.printf("Doing stuff...  ");

            // Start doing stuff
            this.stuff.start();

            // Start spinning the cursor
            spinner.start();

            // Check the thread doing stuff is still doing stuff
            while (stuff.isAlive())
            {
                stuff.join(1000);
            }

            System.out.printf("\bDone.\n");

            // The thread doing stuff has finished, so stop spinning
            spinner.interrupt();

            // Wait for the spinning thread to terminate
            spinner.join();

            System.out.println("The End.");
        }
        catch (InterruptedException e)
        {
            System.out.println("Interrupted");
        }
    }

    /**
     * Main method
     *
     */
    public static void main(String[] args)
    {
        SpinTest test = new SpinTest();

        test.doStuff();
    }
}

It’s a fairly basic implementation but it should be fairly simple to extend it for a real-world application.

Spinning command line cursor in Java and PHP

January 29th, 2010 Nick 1 comment

Update: I have created an updated article which describes a multithreaded approach in Java Spinning command line cursor in Java

I think I must be really bored this morning, I can’t believe I’m actually blogging this, but it might be useful for someone, who knows.   Anyway, I am currently writing a program that sits and does stuff for a very long time, and I need a way to nicely indicate the program is still running and doing its stuff.   So I have created a little method that prints a spinning cursor on the command line.   The implementation would of course have to be multithreaded; one thread to do stuff, the other to spin the cursor – if there’s time I’ll update it to a more complete solution, but for now here’s the spin implementation:


public class Spin
{
    public static void main(String[] args) throws InterruptedException
    {
        String[] phases = {"|", "/", "-", "\\"};

        System.out.printf("Spinning... |");

        while (true)
        {
            for (String phase : phases)
            {
                System.out.printf("\b"+phase);
                Thread.sleep(100);
            }
        }
    }
}

And here it is in PHP.

<?php

    $phases = array("|", "/", "-", "\\");

    printf("Spinning... |");

    while (1)
    {
        foreach ($phases AS $phase)
        {
            printf('%s%s', chr(8), $phase);
            usleep(100000); // Replace this with one iteration of doing stuff
        }
    }

Of course PHP doesn’t support threading so you’d have to call each iteration of the “doing stuff” loop inside the spin loop which would mean you’d get a bit of a jumpy spinning cursor but I think most people could live with that.

Categories: Java, PHP Tags: , , , , ,

Syntax error, parameterized types are only available if source level is 1.5

December 19th, 2009 Nick 2 comments

I recently installed Eclipse and all of a sudden my Java applications failed to build with the error:

Syntax error, parameterized types are only available if source level is 1.5

After a bit of Googling I figured out that Eclipse installed a different JVM and set it as the default one.   On Ubuntu (at least) the way to change the default JVM is by typing this into the console:

sudo update-alternatives --config java

It will show you which JVM is currently selected and allow you to choose a different one as default.

Categories: Java Tags: , , , , , ,