Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Wednesday, 31 October 2012

OS X jruby 1.7.0 "require: command not found"

Had tons of trouble with this.
I have rvm and "raw" installs of ruby and jruby.
I do this so I can simulate a live environment.
Our servers don't allow rvm installs, but I like to use it to test stuff.
So I have to have an install of jruby like this:

bandit:~ kim$ ls -l /opt
...elided...
lrwxr-xr-x   1 kim     admin    11 30 Oct 23:00 jruby@ -> jruby-1.7.0
drwxr-xr-x   9 kim     admin   306 30 Oct 22:59 jruby-1.7.0/
drwxr-xr-x   9 kim     admin   306 11 Oct 15:43 jruby-1.7.0.RC2/
...elided...

This allows me to switch back and forth.
The problem arises when you have your path pointing at /opt/jruby/bin and install rails.
When you verify all paths:

bandit:~ kim$ which jruby
/opt/jruby-1.7.0/bin/jruby
bandit:~ kim$ which rails
/opt/jruby-1.7.0/bin/rails
bandit:~ kim$ rails -v
/opt/jruby-1.7.0/bin/rails: line 10: require: command not found
/opt/jruby-1.7.0/bin/rails: line 12: version: command not found
/opt/jruby-1.7.0/bin/rails: line 16: syntax error near unexpected token `('
/opt/jruby-1.7.0/bin/rails: line 16: `  str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding'

Which is a pain in the ass.

The issue is sort of to do with the environment.
I was first was led down several garden paths regarding interference from rvm.
That's not the case.
At the top of the /opt/jruby-1.7.0/bin/rails script is this:

#!/opt/jruby-1.7.0/bin/jruby

What happens is that the jruby script has this at the top:

#!/usr/bin/env bash

This starts a new bash with virtually no included stuff.
You can try it yourself by doing "env bash" and when the shell starts, poke around in the path.

And that's the problem.
OS-X has an primitive install of ruby, rails etc.
You can check it yourself.
Just do a "ls -l /usr/bin/r*".
You'll see rails, rake and so on.

So how do you fix this?
Simple. Change the line at the top of the /opt/jruby-1.7.0/bin/rails script to this:

#!/usr/bin/env jruby
#!/opt/jruby-1.7.0/bin/jruby

Just leave the old line in for reference.

bandit:~ kim$ rails -v
Rails 3.2.8

Patched.

Caveat: You'll have other issues with rake et al.

Thursday, 19 January 2012

Creating a dummy groovy/maven project and make it executable


Problem:
I like groovy.
The company uses maven and java.
The live servers don't have groovy installed.
So I downloaded the Intellij Idea community edition and got to work.

So here is a contrived solution that yields an executable jar file.
First go to some project folder (I was testing with Idea CE 11 so I used IdeaProjects):

bandit:IdeaProjects user$ pwd
/Users/user/IdeaProjects

Now use maven to create an example project:

bandit:IdeaProjects user$ mvn archetype:generate -DarchetypeGroupId=org.codehaus.gmaven.archetypes -DarchetypeArtifactId=gmaven-archetype-basic
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO]    task-segment: [archetype:generate] (aggregator-style)
[INFO] ------------------------------------------------------------------------
[INFO] Preparing archetype:generate
[INFO] No goals needed for project - skipping
[INFO] [archetype:generate {execution: default-cli}]
[INFO] Generating project in Interactive mode
[INFO] Archetype [org.codehaus.gmaven.archetypes:gmaven-archetype-basic:1.4] found in catalog remote
Define value for property 'groupId': : com.company
Define value for property 'artifactId': : example
Define value for property 'version': 1.0-SNAPSHOT: 
Define value for property 'package': com.company: 
[INFO] Using property: name = Example Project
Confirm properties configuration:
groupId: com.company
artifactId: example
version: 1.0-SNAPSHOT
package: com.company
name: Example Project
Y: y
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 20 seconds
[INFO] Finished at: Thu Jan 19 16:31:24 EST 2012
[INFO] Final Memory: 12M/81M
[INFO] ------------------------------------------------------------------------

This will create a project called example:

bandit:IdeaProjects user$ cd example
bandit:example user$ find . -print
.
./pom.xml
./src
./src/main
./src/main/groovy
./src/main/groovy/com
./src/main/groovy/com/company
./src/main/groovy/com/company/Example.groovy
./src/main/groovy/com/company/Helper.java
./src/test
./src/test/groovy
./src/test/groovy/com
./src/test/groovy/com/company
./src/test/groovy/com/company/ExampleTest.groovy
./src/test/groovy/com/company/HelperTest.groovy

Ok. Now create a new project in Idea CE and do this:
1. use import project from external model and click next
2. choose maven and click next
3. choose the correct root directory and check import Maven projects automatically and click next
4. choose the com.company:example:1.0-SNAPSHOT maven project and click next
5. ignore the next panel as it's fine and click finish

Now you have the project open, edit the pom.xml.
It will look something like this:

<?xml version="1.0" encoding="UTF-8"?>
<!--
    Generated from archetype; please customize.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.company</groupId>
    <artifactId>example</artifactId>
    <name>example project</name>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.gmaven</groupId>
                <artifactId>gmaven-plugin</artifactId>
                <version>1.4</version>
                <configuration>
                    <providerSelection>1.8</providerSelection>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>generateStubs</goal>
                            <goal>compile</goal>
                            <goal>generateTestStubs</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

What we need to do is add some extras.

In the dependencies add this:

        <dependency>
            <groupId>org.codehaus.gmaven.runtime</groupId>
            <artifactId>gmaven-runtime-1.8</artifactId>
            <version>1.4</version>
        </dependency>

And in the plugins add this:

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>

Now open the Example.groovy in src/main/groovy/com.company.
It should look like this:

//
// Generated from archetype; please customize.
//

package com.company

/**
 * Example Groovy class.
 */
class Example {
    def show() {
        println 'Hello World'
    }
}

Add a main method:

//
// Generated from archetype; please customize.
//

package com.company

/**
 * Example Groovy class.
 */
class Example {
    public static void main(String[] args) {
        System.out.println("Working!");
    }
    def show() {
        println 'Hello World'
    }
}

Now go back to the command line and run a package:

bandit:example user$ mvn clean package
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building example project
[INFO]    task-segment: [clean, package]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] Deleting directory /Users/user/IdeaProjects/example/target
[INFO] [groovy:generateStubs {execution: default}]
[INFO] Generated 1 Java stub
[INFO] [resources:resources {execution: default-resources}]
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/user/IdeaProjects/example/src/main/resources
[INFO] [compiler:compile {execution: default-compile}]
[WARNING] File encoding has not been set, using platform encoding MacRoman, i.e. build is platform dependent!
[INFO] Compiling 2 source files to /Users/user/IdeaProjects/example/target/classes
[INFO] [groovy:compile {execution: default}]
[INFO] Compiled 1 Groovy class
[INFO] [groovy:generateTestStubs {execution: default}]
[INFO] Generated 2 Java stubs
[INFO] [resources:testResources {execution: default-testResources}]
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/user/IdeaProjects/example/src/test/resources
[INFO] [compiler:testCompile {execution: default-testCompile}]
[WARNING] File encoding has not been set, using platform encoding MacRoman, i.e. build is platform dependent!
[INFO] Compiling 2 source files to /Users/user/IdeaProjects/example/target/test-classes
[INFO] [groovy:testCompile {execution: default}]
[INFO] Compiled 2 Groovy classes
[INFO] [surefire:test {execution: default-test}]
[INFO] Surefire report directory: /Users/user/IdeaProjects/example/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.company.ExampleTest
Hello World
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.411 sec
Running com.company.HelperTest
Hello World
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.013 sec

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

[INFO] [jar:jar {execution: default-jar}]
[INFO] Building jar: /Users/user/IdeaProjects/example/target/example-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6 seconds
[INFO] Finished at: Thu Jan 19 16:47:12 EST 2012
[INFO] Final Memory: 26M/81M
[INFO] ------------------------------------------------------------------------

And now we can run it:

bandit:example user$ java -cp target/example-1.0-SNAPSHOT.jar:/opt/local/share/java/groovy/lib/* com.company.Example
Working!

I should have set UTF-8 instead of MacRoman and also obviously I could have used maven to put the Main-Class in the manifest, but this is a contrived example.
Also obviously I have to ensure the groovy 1.8 jars are on the server...
Or...
I could jars-in-a-jar solution...

That darned "No suitable ClassLoader found for grab" with groovyc and java -cp


Update Apr 7, 2012: Hmm. Seems this is a very popular post. I intend to expand it and cover how to approach the details more clearly.

I like groovy.
Problem is, we don't have it installed on our live servers.
So I have to compile the groovy code into java class files and deploy them.
But when you're using the @Grab annotation, things go bad.

I kinda like using @Grab to snarf the specific dependencies, but you always run into the dreaded:

Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab

Now all that @Grab does is download the jars you needed and pops them into ~/.groovy/grapes.
Once it's done it's job you don't need it for either running groovy scripts or for the java class.
Here's a rather artificial contrived explanation of how to get around the ClassLoader issue.

(Of course you could just use grape directly, or specify your own .m2 jars or whatever, but I noted this question open in a few places, so I contrived this example)

First create your script, say, x.groovy...

@Grab(group='com.gmongo', module='gmongo', version='0.8')
import com.gmongo.GMongo
// Instantiate a com.gmongo.GMongo object instead of com.mongodb.Mongo
def mongo = new GMongo("the.mongo.ip.address", 27017)
def db = mongo.getDB("some_database")
println db.my_collection.find([SomeKey:[$ne:"0"],SomeOtherKey:"0"]).count()

Now run it:

groovy x.groovy
149845

Cool.
Now we use groovyc to compile it...

groovyc -d classes x.groovy

But when we try to run it using java...

# Building CLASSPATH here purely for clarity...
# We compiled the groovy class into classes:
CLASSPATH="classes"
# We need the gmongo jar:
CLASSPATH="$CLASSPATH:~/.groovy/grapes/com.gmongo/gmongo/jars/gmongo-0.8.jar"
# And because tis is just an example, I knew I needed this one as well:
CLASSPATH="$CLASSPATH:~/.groovy/grapes/org.mongodb/mongo-java-driver/jars/mongo-java-driver-2.5.2.jar"
# And I dev on a Mac and use MacPorts, so groovy is installed in /opt/local/share:
CLASSPATH="$CLASSPATH:/opt/local/share/java/groovy/lib/*"
# Now run it:
java -cp $CLASSPATH x
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 ...elided for brevity...
 at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:165)
 at x.<clinit>(x.groovy)
</clinit>

Rats. So we need to remove the @Grab now that it's done it's work and downloaded the jars...

//@Grab(group='com.gmongo', module='gmongo', version='0.8')
import com.gmongo.GMongo
// Instantiate a com.gmongo.GMongo object instead of com.mongodb.Mongo
def mongo = new GMongo("192.168.4.112", 27017)
def db = mongo.getDB("some_database")
println db.my_collection.find([SomeKey:[$ne:"0"],SomeOtherKey:"0"]).count()

And re-compile using the specific gmongo jar:

groovyc -cp ~/.groovy/grapes/com.gmongo/gmongo/jars/gmongo-0.8.jar -d classes x.groovy

And now we can run it as before

# Building CLASSPATH here purely for clarity...
...elided...
java -cp $CLASSPATH x
149845

Hope that explains it for those stuck with that issue.

Oh, and if you need to see the classpath from within a script go here: http://blog.blindgaenger.net/print_groovys_classpath_for_debugging.html
Waaaay cool.

Wednesday, 11 January 2012

Stopping and/or Restarting an embedded Jetty instance via web call


I had a need to allow a web call to stop and restart an embedded Jetty instance.
So basically, I need to have something like this:

curl -v http://localhost:9103/some/path  # Calls a handler and does something RESTful 
curl -v http://localhost:9103/stop       # Stops the Jetty instance
curl -v http://localhost:9103/restart    # Restarts the Jetty instance

First up create a main class:

package com.company;

import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.NCSARequestLog;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EmbeddedJetty {

  private static Logger log = LoggerFactory.getLogger(EmbeddedJetty.class);

  public static void main(String[] args) throws Exception {
    while (true) {
      // Realistically all parameters should be read from some external file...
      log.info("Starting Jetty on port 9103");
      Server server = new Server(9103);

      HandlerCollection handlers = new HandlerCollection();
      ContextHandlerCollection contexts = new ContextHandlerCollection();

      // I added this to show how to add access logs to an embedded server.
      RequestLogHandler requestLogHandler = new RequestLogHandler();
      NCSARequestLog requestLog = new NCSARequestLog("/tmp/jetty-yyyy_mm_dd.request.log");
      requestLog.setAppend(true);
      requestLog.setExtended(true);
      requestLog.setLogTimeZone("UTC");
      requestLogHandler.setRequestLog(requestLog);

      // We want the server to gracefully allow current requests to stop
      server.setGracefulShutdown(1000);
      server.setStopAtShutdown(true);

      // Now add the handlers
      YourHandler yourHandler = new YourHandler(server);
      handlers.setHandlers(new Handler[]{contexts, yourHandler, requestLogHandler});
      server.setHandler(handlers);
      
      // Start the server
      server.start();
      server.join();
      
      // It's stopped.
      log.info("Jetty stopped");
      if (!yourHandler.restartPlease) {
        break;
      }
      log.warn("Restarting Jetty");
    }
  }
}

Now the handler:

package com.company;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class YourHandler extends AbstractHandler {

  private static Logger log = LoggerFactory.getLogger(YourHandler.class);
  private Server server = null;
  public Boolean restartPlease = false;

  public YourHandler(Server server) {
    this.server = server;
  }

  private boolean stopServer(HttpServletResponse response) throws IOException {
    log.warn("Stopping Jetty");
    response.setStatus(202);
    response.setContentType("text/plain");
    ServletOutputStream os = response.getOutputStream();
    os.println("Shutting down.");
    os.close();
    response.flushBuffer();
    try {
      // Stop the server.
      new Thread() {

        @Override
        public void run() {
          try {
            log.info("Shutting down Jetty...");
            server.stop();
            log.info("Jetty has stopped.");
          } catch (Exception ex) {
            log.error("Error when stopping Jetty: " + ex.getMessage(), ex);
          }
        }
      }.start();
    } catch (Exception ex) {
      log.error("Unable to stop Jetty: " + ex);
      return false;
    }
    return true;
  }

  @Override
  public void handle(String string, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {

    String pathInfo = request.getPathInfo();
    // THIS SHOULD OBVIOUSLY BE SECURED!!!
    if ("/stop".equals(pathInfo)) {
      stopServer(response);
      return;
    }
    if ("/restart".equals(pathInfo)) {
      restartPlease = true;
      stopServer(response);
      return;
    }

  // Go off and do the rest of your RESTful calls...
  // And close off how you please
    response.sendRedirect("http://nowhere.com");
  }
}

And you can now run your main class from your IDE of choice.

Just for reference I added a log4j.properties:

# Site
log4j.logger.com.company=DEBUG, SITE_CONSOLE
log4j.additivity.com.company=false

# Set root logger level
log4j.rootLogger=DEBUG, CONSOLE
log4j.logger.org.eclipse=INFO, CONSOLE
log4j.additivity.org.eclipse=false

# ---------------------------------------------------------------------------------
# Appenders - Notice I have added a discriminator to the conversion pattern
# --------------------------------------------------------------------------------- 
# For console logging - not in production obviously as you'd use a rolling file appender
log4j.appender.SITE_CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.SITE_CONSOLE.layout=org.apache.log4j.EnhancedPatternLayout
log4j.appender.SITE_CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS}{UTC} SITE  %-5p [%t] %c{1.} %x - %m%n

# The baseline CONSOLE logger
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.EnhancedPatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS}{UTC} JETTY %-5p [%t] %c{1.} %x - %m%n

I created a plain Java SE app and added a 'lib' folder with these jars in it:

jetty-continuation-8.0.4.v20111024.jar
jetty-http-8.0.4.v20111024.jar
jetty-io-8.0.4.v20111024.jar
jetty-server-8.0.4.v20111024.jar
jetty-servlet-8.0.4.v20111024.jar
jetty-servlets-8.0.4.v20111024.jar
jetty-util-8.0.4.v20111024.jar
jetty-webapp-8.0.4.v20111024.jar
log4j-1.2.16.jar
servlet-api-3.0.jar
slf4j-api-1.6.2.jar
slf4j-log4j12-1.6.2.jar

From the Jetty8 release.
I then ensured that the MANIFEST included these jars.
Just for reference the META-INF/MANIFEST.MF looks like this:

cat META-INF/MANIFEST.MF 
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.6.0_29-b11-402-10M3527 (Apple Inc.)
Class-Path: lib/jetty-all-8.0.4.v20111024-javadoc.jar lib/jetty-contin
 uation-8.0.4.v20111024.jar lib/jetty-http-8.0.4.v20111024.jar lib/jet
 ty-io-8.0.4.v20111024.jar lib/jetty-server-8.0.4.v20111024.jar lib/je
 tty-servlet-8.0.4.v20111024.jar lib/jetty-servlets-8.0.4.v20111024.ja
 r lib/jetty-util-8.0.4.v20111024.jar lib/jetty-webapp-8.0.4.v20111024
 .jar lib/log4j-1.2.16.jar lib/servlet-api-3.0.jar lib/slf4j-api-1.6.2
 .jar lib/slf4j-log4j12-1.6.2.jar
X-COMMENT: Main-Class will be added automatically by build
Main-Class: com.company.EmbeddedJetty

I know I included the javadoc un-intentionally...
This scheme requires a lib folder alongside the jar with those files in it.
(Need to figure out how to bundle the Jetty8 jars so that the app is self-contained...)
So the jar can be started like this:

java -jar /some/folder/EmbeddedJetty/dist/EmbeddedJetty.jar

K

Thursday, 1 December 2011

Hosting multiple Jetty instances each with multiple ports!


I had a requirement to fulfill.
On the web tier I needed multiple instances of Jetty.
Each of these must have each webapp running on a different port.
And on the app tier the same.

So for example:
web tier:
  /opt/jetty91xx
    web-app-1 running on port 9101
    web-app-2 running on port 9102
    web-app-3 running on port 9103
  /opt/jetty92xx
    web-app-4 running on port 9201
    web-app-5 running on port 9202
    web-app-6 running on port 9203
app tier:
  /opt/jetty94xx
    web-app-7 running on port 9401
    web-app-8 running on port 9402
    web-app-9 running on port 9403
  /opt/jetty95xx
    web-app-10 running on port 9501
    web-app-11 running on port 9502
    web-app-12 running on port 9503
It's not as fiddly as that in reality, but the reason was to have sets of apps running in the same VM that are 'associated'.

So... How to do it?

Easy.
I'll use the jetty91xx example for the explanation below.

First go get the latest release of jetty and unpack it into /opt/jetty-latest or whatever.
Then create /opt/jetty91xx and copy the entire contents of /opt/jetty-latest into it effectively creating a clone.
So now we switch the /opt/jetty91xx.
(If you're on Mac OS-X you need to do a "mkdir work" in the folder to avoid OS-X /tmp/nasties)
Edit the etc/jetty.xml.
Part the way down is an <call name="addConnector">...</call>.
Comment this out.
Above it add this:
<set name="connectors">
  <array type="org.eclipse.jetty.server.Connector">
    <item>
      <new class="org.eclipse.jetty.server.nio.SelectChannelConnector" id="conn9101">
        <set name="port">9101</set>
        <set name="maxIdleTime">30000</set>
        <set name="Acceptors">1</set>
        <set name="name">conn9101</set>
      </new>
    </item>
    <item>
      <new class="org.eclipse.jetty.server.nio.SelectChannelConnector" id="conn9102">
        <set name="port">9102</set>
        <set name="maxIdleTime">30000</set>
        <set name="Acceptors">1</set>
        <set name="name">conn9102</set>
      </new>
    </item>
    <item>
      <new class="org.eclipse.jetty.server.nio.SelectChannelConnector" id="conn9103">
        <set name="port">9103</set>
        <set name="maxIdleTime">30000</set>
        <set name="Acceptors">1</set>
        <set name="name">conn9103</set>
      </new>
    </item>
  </array>
</set>
Ok. Your connectors are in and named.
Now go off and prepare your war files.
For simple example purposes I will assume you have port.9100.war, port.9101.war and port.9102.war.
Create contexts/port.9101.xml and put this in it:
<configure class="org.eclipse.jetty.webapp.WebAppContext">
  <set name="contextPath">/port.9101</set>
  <set name="war"><systemproperty default="." name="jetty.home">/webapps/port.9101.war</systemproperty></set>
  <set name="extractWAR">true</set>
  <set name="copyWebDir">false</set>
  <set name="defaultsDescriptor"><systemproperty default="." name="jetty.home">/etc/webdefault.xml</systemproperty></set>
  <set name="connectorNames"><array type="String"><item>conn9101</item></array></set>
</configure>
Create contexts/port.9102.xml and contexts/port.9103.xml in the same manner changing the contextPath, war name and connectorNames.

Now start the instance:
/usr/bin/java -Djetty.home=/opt/jetty91xx -Djava.io.tmpdir=/tmp \
  -Xms1024M -Xmx1024M -Xmn640M -Xss128k \
  -XX:ParallelGCThreads=4 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC \
  -XX:+CMSIncrementalMode -XX:SurvivorRatio=8 \
  -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31 \
  -Dcom.sun.management.jmxremote.port=9876 \
  -Dcom.sun.management.jmxremote.ssl=false \
  -Dcom.sun.management.jmxremote.authenticate=false \
  -DOPTIONS=server,security,servlet,xml,webapp,deploy,jmx,requestlog \
  -jar /opt/jetty91xx/start.jar \
  --pre=etc/jetty-logging.xml \
  --daemon
You will need to change -Djetty.home and the '-jar' option for each instance.
Also I've given a full path here with a bunch of options.
Normally you would use /etc/init.d/jetty91xx.sh start of course.
Tip: While trying this out, leave of the --daemon and you can stop and start jetty by using ^c.

Ok. You should be able to drop the 3 war files into the webapps folder now.
And then...
http://[whatever]:9101/port.9101
  http://[whatever]:9102/port.9102
  http://[whatever]:9103/port.9103
And they'll all be running in the same VM.

You can then create a new /opt/jetty92xx and start agin.