Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

11 February 2019

How to customise Jetty embedded in Spark Java framework

EDIT: I uploaded a full working example, ready to be customized, on github. If you just want a working solution, go there. Otherwise, keep reading for the explanation.

When it comes to Java microframeworks for API development, I've been using Spark for some time in a couple of different projects. For most casual needs, it works out of the box; however, there are circumstances where the "easy" options for configuring the embedded Jetty instance, are simply not enough. In that case, you have to take control and basically replace the instance with one that you completely control.

Due to how Jetty works, there are multiple moving parts: the server takes care of things like thread pools, the socket is responsible for SSL and other low-level protocol stuff, and the handler is where you do high-level middleware (changing headers etc). This means that you might need several factories to take care of all these. The good news is that Spark is granular enough that you can (more or less) limit yourself to what you need. The main pattern is: there will be a factory for each element, with a create() method returning an interface; you implement the interface and swap out the default factory with your own. However, these factories at the moment (Spark 2.8) are somewhat nested, so depending on where you need to work, you might have to replace a bunch of classes before you hit the point you're interested in.

The main entry point is EmbeddedServers.add(identifier, serverFactory). This is what you'll call from the main() method you use for all the post() and get() configuration directives. It is important that EmbeddedServers should appear right at the top, before any other configuration directive, otherwise Spark will use its default factory. It should look more or less like this:

EmbeddedServers.add(
     EmbeddedServers.Identifiers.JETTY, 
     new MyWonderfulServerFactory());

For the identifier, we re-use the default one because otherwise Spark will take over again. Nothing to do there.

The server factory is where real work begins. Your class (or lambda) needs to implement the spark.embeddedserver.EmbeddedServerFactory interface, which has one method:

public EmbeddedServer create(
            Routes routeMatcher, 
            StaticFilesConfiguration staticFilesConfiguration, 
            ExceptionMapper exceptionMapper, 
            boolean hasMultipleHandler)

As a starter, you can copy the content of spark.embeddedserver.jetty.EmbeddedJettyFactory as-is. You don't need constructors (unless you want them). Strictly speaking you don't need withThreadPool() and withHttpOnly() methods either, but I suggest you keep them anyway; just change the return type to match MyWonderfulServerFactory.

Create() does three things:

  1. Initializing the route matcher, which you probably don't want to touch;
  2. Initializing the Jetty handler for that matcher, which you may want to configure for things like header manipulation and other middleware;
  3. Creating the embedded server, which is likely what you are after.

I will assume we want to tweak n.3. The main place where to pay attention is this line at the end of create():

return (new EmbeddedJettyServer(this.serverFactory, handler)
        ).withThreadPool(this.threadPool);

This will return the default configuration, and we don't want that. So we change it to something like:

return (new MyWonderfulEmbeddedServer(handler)
        ).withThreadPool(this.threadPool);

Now we need a MyWonderfulEmbeddedServer class, which should implement spark.embeddedserver.EmbeddedServer. Again, as a starting point, you can copy spark.embeddedserver.jetty.EmbeddedJettyServer, and change the return types to match. I also suggest you get rid of the factory parameter in constructor and the related field, which adds a bit of unnecessary complexity. That factory is actually used only in one place:

if (this.threadPool == null) {
    this.server = this.serverFactory.create(
       maxThreads, minThreads, threadIdleTimeoutMillis);
} else {
    this.server = this.serverFactory.create(this.threadPool);
}

Which you can replace with the following (straight from spark.embeddedserver.jetty.JettyServer):

if (this.threadPool == null) {
    if (maxThreads > 0) {
       int min = minThreads > 0 ? minThreads : 8;
       int idleTimeout = threadIdleTimeoutMillis > 0 ? threadIdleTimeoutMillis : '\uea60';
       server = new Server(new QueuedThreadPool(maxThreads, min, idleTimeout));
    } else {
       server = new Server();
    }
} else {
    this.server = threadPool != null ? new Server(threadPool) : new Server();
}

It's a bunch of stuff related to the amount of threads and timeouts. If that's what you were trying to configure, btw, this is where you can do it. To be honest, I believe Spark developers intended that the "proper" way, to do that particular customisation, would be to keep the factory parameter as it is, implement your own alternative to the badly-named JettyServer class -- it should be something like JettyThreadConfigFactory, really -- which implements the similarly badly-named JettyServerFactory, and pass it to the constructor in MyWonderfulServerFactory.create().

In my case, though, I was after an SSL customisation, and for that I had to swap out yet another factory, spark.embeddedserver.jetty.SocketConnectorFactory, mentioned in the second half of this block:

ServerConnector connector;
if (sslStores == null) {
    connector = SocketConnectorFactory.createSocketConnector(
         this.server, host, port);
} else {
    connector = SocketConnectorFactory.createSecureSocketConnector(
         this.server, host, port, sslStores);
}

In this case there is no interface, you can just extend the existing class with what you need. I wanted to enforce a particular set of SSL ciphers and protocols, so I overrode createSecureSocketConnector() adding the following bits:

sslContextFactory.setExcludeProtocols("SSLv3", "SSLv2", "TLSv1.2");
// first we clear existing exclusions
sslContextFactory.setExcludeCipherSuites(new String[]{});
// then we re-add what we need
sslContextFactory.setIncludeCipherSuites(bigArrayOfCipherNames);

And that's it. Now you know how to instantiate your own Jetty instance for Spark. It's a bit convoluted, and hopefully a "spark 3" will give us a better architecture to work with, one day. In the meantime, this is how you can do it.

09 December 2015

how to fix jEdit 5.3.0 on OSX with Retina screen

jEdit is a great little editor: very flexible, much plugins, such macros, so java.
However, for some reason jEdit developers strenuously refuse to fix their OSX package to support Retina screens. Three years since these screens started getting popular, you still have to repeat the following procedure after each jEdit installation or update, in order to avoid getting blurry fonts everywhere:
  1. quit jEdit if open
  2. in Finder, right-click on jEdit in Applications, select Show Package Contents
  3. go to the Contents folder, then edit Info.plist by adding these two lines at the end of the file, just before </dict>:
    <key>NSHighResolutionCapable</key>
    <true/>
  4. force OSX to re-cache the plist by executing the following command in a terminal:
    defaults read /Applications/jEdit.app/Contents/Info.plist
  5. Restart jEdit. Icons will still look crappy (the "classic" theme slightly better than "tango"), but the rest will be ok.
On a more positive note, jEdit 5.3.0 (running on Java 1.8.0_66) seems to have fixed the crashes I've had for a year. Welcome back, "little editor that could".

08 January 2008

for once in my lifetime...

... I'm actually (fairly) happy with the design of one of my Java programs. I'm crap at design, I always try to decouple too much and end up with writing loads of bridging code that doesn't really do anything. This time it seems like I've found a good balance... well, we'll see if/when this little thing will actually be released.

Meanwhile, KDelicious is picking up steam, thanks to my relentless advertising :) -- it's now on Freshmeat too!

jEdit plugin for Thinlet dev

Too bad I won't use it soon, but here's a nice jEdit plugin to display Thinlets in realtime as you modify the XML.

21 November 2007

On the Samurai Principle

Some blogs recently pondered on the Samurai Principle. It made me think, because I'm one of the many "guilty" of returning None/null rather than raising an exception. To me, it happens more often when working with Python rather than Java, because it's very natural to write "if something_not_found: return None". Also, abusing exceptions can end up polluting your code with dozens of nested try-catch/except, which kill readability and tend to be much worse than a simple "if (obj is None):".

This said, I agree that NullPointerExceptions are a royal pain, and "AttributeError: 'NoneType' object has no attribute 'something' " is equally bad to deal with. API programmers in particular should try hard to return this sort of stuff as little as possible.

I guess the bottom line is that in medium stat virtus, as it's often the case with these "absolute principles of programming"; at the end of the day, when we program we are applying science, not demonstrating it. And as much as we love them, samurai lost all their wars ;)

29 October 2007

Pain can be useful

So, I've spent a few hours improving my little J2ME project. It's pretty much done, what I need now is a little bit of polishing and, more importantly, the server-side app, which I just started to "conjure up" in django.

I didn't remember how painful Java development can be. Fixed-size arrays, .put() and .get() every five seconds, loads of redundant declarations (if a method can only return a String, why should I also declare that a reference to the result is of type String? what else could it be?), not having an interactive environment to quickly test out ideas. Eclipse goes a long way to reduce the pain, but it's still so much more painful than doing the same thing in Python. And when everything is done... NullPointerException.

The good side is that fear of pain forces you to really think things through before you write them down. In Python, I end up re-writing things over and over again, because there's no (perceived) penalty in doing it, and I more or less "code my thoughts"; of course, I eventually end up spending lots of time on this sort of messing.
With Java, I'm forced to think about the proper structure up-front, because I dread having to write it down more than once; but this means that it's then a matter of monkey-coding a clear structure, and once it's done, it's done, and I can get on with other things.

Anonymization by Anonymouse.org ~ Adverts
Anonymouse better ad-free, faster and with encryption?
X