Florian
Florian

Reputation: 53

Serving static files with jetty in karaf (outside of bundle)

we are struggling with the simple issue of serving static files from somewhere in the filesystem but outside of the web application, but we can't get it running.

There are sever examples on how to do this, but none of them seem to work and so far we couldn't find a confirmation from someone that it actually does work.

The jetty.xml found in the etc directory has been edited as it is stated here https://ops4j1.jira.com/wiki/display/paxweb/Advanced+Jetty+Configuration or here ops4j GitHub sample

So added to jetty.xml this:

<Get name="handler">
        <Call name="addHandler">
            <Arg>
                <New class="org.eclipse.jetty.servlet.ServletContextHandler">
                    <Set name="contextPath">/fileserver</Set>
                    <Set name="resourceBase">/Users/Shared/testenv</Set>
                    <Call name="addServlet">
                        <Arg>org.eclipse.jetty.servlet.DefaultServlet</Arg>
                        <Arg>/</Arg>
                    </Call>
                </New>
            </Arg>
        </Call>
    </Get>

or this:

<Get name="handler">
        <Call name="addHandler">
            <Arg>
                <New class="org.eclipse.jetty.server.handler.ContextHandler">
                    <Set name="contextPath">/fileserver</Set>
                    <Set name="handler">
                        <New class="org.eclipse.jetty.server.handler.ResourceHandler">
                            <Set name="resourceBase">/Users/Shared/testenv</Set>
                            <Set name="directoriesListed">true</Set>
                        </New>
                    </Set>
                </New>
            </Arg>
        </Call>
    </Get>

With both versions jetty / karaf starts up fine and when karaf is shut down I can see

2015-06-02 12:02:57,838 | INFO | pool-7-thread-2 | ContextHandler
| 113 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.15.v20140411 | stopped o.e.j.s.ServletContextHandler{/fileserver,file:/Users/Shared/testenv/}

But the files are not served under localhost:8181/fileserver

The only way it is working (in a freshly installed karaf container) is to use

<Set name="handler">
        <New class="org.eclipse.jetty.server.handler.HandlerList">
            <Set name="handlers">
                <Array type="org.eclipse.jetty.server.Handler">
                    <Item>
                        <New class="org.eclipse.jetty.servlet.ServletContextHandler">
                            <Set name="contextPath">/fileserver</Set>
                            <Set name="resourceBase">/Users/Shared/testenv</Set>
                            <Call name="addServlet">
                                <Arg>org.eclipse.jetty.servlet.DefaultServlet</Arg>
                                <Arg>/</Arg>
                            </Call>
                        </New>
                    </Item>
                </Array>
            </Set>
        </New>
    </Set>

But doing this, will break other web applications running in karaf. For example we are using the Camel Servlet component.

So does someone has a working configuration of serving static files through jetty instance in karaf or now how to do this?

Any help appreciated. Thank in advance!

By the way: Using Karaf 3.0.3

EDIT:

I rerun the test with the snippet given by Achim and enabled DEBUG loggin.

2015-06-03 15:33:25,492 | DEBUG | pool-6-thread-1 | XmlConfiguration | 71 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.15.v20140411 | XML o.e.j.s.h.ContextHandler{/,null}.setContextPath(/static-content) 2015-06-03 15:33:25,527 | DEBUG | pool-6-thread-1 | XmlConfiguration | 71 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.15.v20140411 | XML o.e.j.s.h.ContextHandler{/static-content,null}.setHandler(org.eclipse.jetty.server.handler.ResourceHandler@3855ace4) 2015-06-03 15:33:25,529 | DEBUG | pool-6-thread-1 | Container
| 71 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.15.v20140411 | Container o.e.j.s.h.ContextHandler{/static-content,null} + org.eclipse.jetty.server.handler.ResourceHandler@3855ace4 as handler 2015-06-03 15:33:25,529 | DEBUG | pool-6-thread-1 | Container
| 71 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.15.v20140411 | Container org.ops4j.pax.web.service.jetty.internal.JettyServerHandlerCollection@6665534e + o.e.j.s.h.ContextHandler{/static-content,null} as handler 2015-06-03 15:33:25,542 | DEBUG | pool-6-thread-1 | AbstractLifeCycle
| 71 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.15.v20140411 | starting o.e.j.s.h.ContextHandler{/static-content,null} 2015-06-03 15:33:25,542 | DEBUG | pool-6-thread-1 | AbstractHandler
| 71 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.15.v20140411 | starting o.e.j.s.h.ContextHandler{/static-content,null} 2015-06-03 15:33:25,543 | DEBUG | pool-6-thread-1 | AbstractLifeCycle
| 71 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.15.v20140411 | STARTED o.e.j.s.h.ContextHandler{/static-content,null} 2015-06-03 15:34:27,974 | DEBUG | /static-content | Server
| 71 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.15.v20140411 | REQUEST /static-content on AsyncHttpConnection@638f2d20,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=-5,l=10,c=0},r=1 2015-06-03 15:34:27,974 | DEBUG | /static-content | ServerModel
| 78 - org.ops4j.pax.web.pax-web-spi - 3.1.4 | Matching [/static-content]... 2015-06-03 15:34:27,975 | DEBUG | /static-content | ServerModel | 78 - org.ops4j.pax.web.pax-web-spi - 3.1.4 | Path [/static-content] does not match any context 2015-06-03 15:34:27,975 | DEBUG | /static-content | Server | 71 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.15.v20140411 | RESPONSE /static-content 200 handled=false

Here I noticed a difference between the Get Version (not working) and the Set version (working).

Set sets the class org.eclipse.jetty.server.handler.HandlerList Get gets and add to the class org.ops4j.pax.web.service.jetty.internal.JettyServerHandlerCollection, which is described as

Jetty Handler collection that calls only the handler (=context) that matches the request path after performing the substring based matching of requests path to registered aliases

Could there be a problem regarding the aliases?

EDIT 2:

I tried digging into this, but I really can't get this to work. I do not know about the differences between the integration test and the regular karaf, but there must be an issue. To reproduce the issue, just take a fresh karaf (3.0.3) container, do feature:install war and add the snippet to the etc/jetty.xml so it looks like this and edit the path of the resourceBase so it matches a local path.

<?xml version="1.0"?>

<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//
DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">

<Configure class="org.eclipse.jetty.server.Server">

    <!-- =========================================================== -->
    <!-- Set connectors -->
    <!-- =========================================================== -->
    <!-- One of each type! -->
    <!-- =========================================================== -->

    <!-- Use this connector for many frequently idle connections and for 
        threadless continuations. -->
    <Call name="addConnector">
        <Arg>
            <New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
                <Set name="host">
                    <Property name="jetty.host" />
                </Set>
                <Set name="port">
                    <Property name="jetty.port" default="8181" />
                </Set>
                <Set name="maxIdleTime">300000</Set>
                <Set name="Acceptors">2</Set>
                <Set name="statsOn">false</Set>
                <Set name="confidentialPort">8443</Set>
                <Set name="lowResourcesConnections">20000</Set>
                <Set name="lowResourcesMaxIdleTime">5000</Set>
            </New>
        </Arg>
    </Call>

    <!-- =========================================================== -->
    <!-- Configure Authentication Realms -->
    <!-- Realms may be configured for the entire server here, or -->
    <!-- they can be configured for a specific web app in a context -->
    <!-- configuration (see $(jetty.home)/contexts/test.xml for an -->
    <!-- example). -->
    <!-- =========================================================== -->
    <Call name="addBean">
        <Arg>
            <New class="org.eclipse.jetty.plus.jaas.JAASLoginService">
                <Set name="name">karaf</Set>
                <Set name="loginModuleName">karaf</Set>
                <Set name="roleClassNames">
                    <Array type="java.lang.String">
                        <Item>org.apache.karaf.jaas.boot.principal.RolePrincipal
                        </Item>
                    </Array>
                </Set>
            </New>
        </Arg>
    </Call>
    <Call name="addBean">
        <Arg>
            <New class="org.eclipse.jetty.plus.jaas.JAASLoginService">
                <Set name="name">default</Set>
                <Set name="loginModuleName">karaf</Set>
                <Set name="roleClassNames">
                    <Array type="java.lang.String">
                        <Item>org.apache.karaf.jaas.boot.principal.RolePrincipal
                        </Item>
                    </Array>
                </Set>
            </New>
        </Arg>
    </Call>
    <Get name="handler">
    <Call name="addHandler">
        <Arg>
            <New class="org.eclipse.jetty.server.handler.ContextHandler">
                <Set name="contextPath">/static-content</Set>
                <Set name="handler">
                    <New class="org.eclipse.jetty.server.handler.ResourceHandler">
                        <Set name="resourceBase">/Users/Shared/testenv/in</Set>
                        <Set name="directoriesListed">true</Set>
                    </New>
                </Set>
            </New>
        </Arg>
    </Call>
</Get>

</Configure>

Try to access the context via browser using localhost:8181/static-content.

Result is always 404 - Not found.

We have tried this on multiple systems running linux and windows.

Upvotes: 3

Views: 1896

Answers (3)

Jorge Martinez
Jorge Martinez

Reputation: 1231

The problem maybe related with pax-web version. In Karaf 3.0.5 it uses version Pax-web 3.2.6 which I have read (sorry I cannot find the link) had a bug related with serving static content.

I have tested @Achim's approach in Karaf 4.0.3 (Pax-web 4.2.3) and it works like charm.

Upvotes: 1

Max Spring
Max Spring

Reputation: 1140

I can't get this to work either. Using a freshly download Karaf 3.0.5:

$ cd /tmp
$ tar xzf ~/Downloads/apache-karaf-3.0.5.tar.gz 
$ ./apache-karaf-3.0.5/bin/karaf 
        __ __                  ____      
       / //_/____ __________ _/ __/      
      / ,<  / __ `/ ___/ __ `/ /_        
     / /| |/ /_/ / /  / /_/ / __/        
    /_/ |_|\__,_/_/   \__,_/_/         

  Apache Karaf (3.0.5)

Hit '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown Karaf.

karaf@root()> 

Checking whether anything is there yet:

$ curl http://localhost:8181/
curl: (7) Failed to connect to localhost port 8181: Connection refused

Good. No Jetty yet.

Installing pax web

karaf@root()> feature:repo-add mvn:org.ops4j.pax.web/pax-web-features/2.1.0/xml/features
Adding feature url mvn:org.ops4j.pax.web/pax-web-features/2.1.0/xml/features
karaf@root()> feature:install pax-jetty
karaf@root()> feature:install http

Checking again with curl

$ curl http://localhost:8181/
...
<h2>HTTP ERROR: 404</h2>
<hr /><i><small>Powered by Jetty://</small></i>
...

Good. Jetty is alive.

Adding the ResourceHandler to apache-karaf-3.0.5/etc/jetty.xml

<Get name="handler">
    <Call name="addHandler">
        <Arg>
            <New class="org.eclipse.jetty.server.handler.ContextHandler">
                <Set name="contextPath">/static-content</Set>
                <Set name="handler">
                    <New class="org.eclipse.jetty.server.handler.ResourceHandler">
                        <Set name="resourceBase">/tmp</Set>
                        <Set name="directoriesListed">true</Set>
                    </New>
                </Set>
            </New>
        </Arg>
    </Call>
</Get>

curl still sees 404:

$ curl http://localhost:8181/
...
<h2>HTTP ERROR: 404</h2>
...

Not sure I need to restart anything. I restart entire Karaf anyway.

Both curls give 404:

$ curl http://localhost:8181/
$ curl http://localhost:8181/static-content

Enabling DEBUG logging in apache-karaf-3.0.5/etc/org.ops4j.pax.logging.cfg

log4j.rootLogger=DEBUG, out, osgi:*

curl still gets 404 for http://localhost:8181/static-content, and the karaf.log says:

2015-11-14 12:57:00,938 | DEBUG | 673-63 Selector0 | nio                              | 70 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.17.v20150415 | created SCEP@5b87b8b7{l(/127.0.0.1:46304)<->r(/127.0.0.1:8181),s=0,open=true,ishut=false,oshut=false,rb=false,wb=false,w=true,i=0}-{AsyncHttpConnection@21d1b60b,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=-14,l=0,c=0},r=0}
2015-11-14 12:57:00,939 | DEBUG | qtp425678673-70  | HttpParser                       | 70 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.17.v20150415 | filled 92/92
2015-11-14 12:57:00,939 | DEBUG |  /static-content | Server                           | 70 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.17.v20150415 | REQUEST /static-content on AsyncHttpConnection@21d1b60b,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=-5,l=3,c=0},r=1
2015-11-14 12:57:00,940 | DEBUG |  /static-content | ServerModel                      | 77 - org.ops4j.pax.web.pax-web-spi - 3.2.6 | Matching [/static-content]...
2015-11-14 12:57:00,940 | DEBUG |  /static-content | ServerModel                      | 77 - org.ops4j.pax.web.pax-web-spi - 3.2.6 | Path [/static-content] does not match any context
2015-11-14 12:57:00,940 | DEBUG |  /static-content | Server                           | 70 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.17.v20150415 | RESPONSE /static-content  200 handled=false
2015-11-14 12:57:00,940 | DEBUG | qtp425678673-70  | AsyncHttpConnection              | 70 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.17.v20150415 | Enabled read interest SCEP@5b87b8b7{l(/127.0.0.1:46304)<->r(/127.0.0.1:8181),s=1,open=true,ishut=false,oshut=false,rb=false,wb=false,w=true,i=0r}-{AsyncHttpConnection@21d1b60b,g=HttpGenerator{s=4,h=0,b=0,c=-1},p=HttpParser{s=0,l=3,c=0},r=1}
2015-11-14 12:57:00,941 | DEBUG | qtp425678673-70  | ChannelEndPoint                  | 70 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.17.v20150415 | ishut SCEP@5b87b8b7{l(/127.0.0.1:46304)<->r(/127.0.0.1:8181),s=1,open=true,ishut=false,oshut=false,rb=false,wb=false,w=true,i=0r}-{AsyncHttpConnection@21d1b60b,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=-14,l=0,c=-3},r=1}
2015-11-14 12:57:00,941 | DEBUG | qtp425678673-70  | HttpParser                       | 70 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.17.v20150415 | filled -1/0
2015-11-14 12:57:00,941 | DEBUG | qtp425678673-70  | AsyncHttpConnection              | 70 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.17.v20150415 | Disabled read interest while writing response SCEP@5b87b8b7{l(/127.0.0.1:46304)<->r(/127.0.0.1:8181),s=1,open=true,ishut=true,oshut=false,rb=false,wb=false,w=true,i=0r}-{AsyncHttpConnection@21d1b60b,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=0,l=0,c=-3},r=1}
2015-11-14 12:57:00,942 | DEBUG | qtp425678673-70  | ChannelEndPoint                  | 70 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.17.v20150415 | close SCEP@5b87b8b7{l(/127.0.0.1:46304)<->r(/127.0.0.1:8181),s=1,open=true,ishut=true,oshut=false,rb=false,wb=false,w=true,i=0r}-{AsyncHttpConnection@21d1b60b,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=0,l=0,c=-3},r=1}
2015-11-14 12:57:00,942 | DEBUG | 673-63 Selector0 | nio                              | 70 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.17.v20150415 | destroyEndPoint SCEP@5b87b8b7{l(null)<->r(0.0.0.0/0.0.0.0:8181),s=0,open=false,ishut=true,oshut=true,rb=false,wb=false,w=true,i=0!}-{AsyncHttpConnection@21d1b60b,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=0,l=0,c=-3},r=1}
2015-11-14 12:57:00,943 | DEBUG | 673-63 Selector0 | AbstractHttpConnection           | 70 - org.eclipse.jetty.aggregate.jetty-all-server - 8.1.17.v20150415 | closed AsyncHttpConnection@21d1b60b,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=0,l=0,c=-3},r=1

Then trying the DefaultServlet as outline in Advanced Jetty Configuration, replacing my previous addition in jetty.xml with

<Get name="handler">
    <Call name="addHandler">
      <Arg>
    <New class="org.eclipse.jetty.servlet.ServletContextHandler">
          <Set name="contextPath">/static-content</Set>
          <Set name="resourceBase">/tmp</Set>
          <Call name="addServlet">
            <Arg>org.eclipse.jetty.servlet.DefaultServlet</Arg>
            <Arg>/</Arg>
          </Call>
        </New>
      </Arg>
    </Call>
</Get>

Restarting Karaf, and curl http://localhost:8181/static-content still gives 404 and Karaf log says essentially the same as with the ResourceHandler.

What's missing?

Upvotes: 0

Achim Nierbeck
Achim Nierbeck

Reputation: 5285

Actually you already got it in your sample, that's the way it is best done. As can be seen with this integration test it is working.

so please make sure you have it according to this:

<Get name="handler">
    <Call name="addHandler">
        <Arg>
            <New class="org.eclipse.jetty.server.handler.ContextHandler">
                <Set name="contextPath">/static-content</Set>
                <Set name="handler">
                    <New class="org.eclipse.jetty.server.handler.ResourceHandler">
                        <Set name="resourceBase">target/logs</Set>
                        <Set name="directoriesListed">true</Set>
                    </New>
                </Set>
            </New>
        </Arg>
    </Call>
</Get>

Upvotes: 0

Related Questions