March 11, 2015

JAX-RS Client API Groovy DSL, prototype

Groovy language is DSL friendly language.  See http://groovy-lang.org/dsls.html or http://groovy.codehaus.org/Writing+Domain-Specific+Languages.

Let me share with you my JAX-RS Client API Groovy DSL prototype, code is available in GitHub https://github.com/shamoh/jersey-groovy repo. It just supports GET and POST methods. This is really just concept prototype. But it shows power of Groovy and way how simplified JAX-RS Client API could look like.

The GitHub repository contains couple of unit tests to show the DSL. And also uses Spock Framework what is another Groovy DSL example, in this case as testing framework/API.

Examples

GET plain text, no parameters

JAX-RS Client API:
WebTarget target = ClientBuilder.newClient().target(RESOURCE_URL)
def response = target.request(TEXT_PLAIN_TYPE).get(String)
response == "Hello World!"
Jersey Groovy DSL:
def response = GET[RESOURCE_URL].text
response == "Hello World!"


GET plain text, path parameter

JAX-RS Client API:
WebTarget target = ClientBuilder.newClient().target(RESOURCE_URL)
def response = target.path("Jersey").request(TEXT_PLAIN_TYPE).get(String)
response == "Hello Jersey!"
Jersey Groovy DSL:
def response = GET[RESOURCE_URL/"Jersey"].text
response == "Hello Jersey!"


GET XML, path parameter

JAX-RS Client API:
WebTarget target = ClientBuilder.newClient().target(RESOURCE_URL)
def response = target.path("Jersey").request(APPLICATION_XML_TYPE).get(Message)
response instanceof Message
response.greeting == "Hello Jersey!"
Jersey Groovy DSL:
def response = (GET[RESOURCE_URL/"Jersey"] & APPLICATION_XML_TYPE) as Message
response instanceof Message
response.greeting == "Hello Jersey!"


POST plain text, response text

JAX-RS Client API:
WebTarget target = ClientBuilder.newClient().target(RESOURCE_URL)
def Entity entity = Entity.entity("Jersey", TEXT_PLAIN_TYPE);
def response = target.request().post(entity, String)
response == "Hello Jersey!"
Jersey Groovy DSL:
def response = POST[RESOURCE_URL] << "Jersey" as String
response == "Hello Jersey!"

POST object as JSON, response text

JAX-RS Client API:
WebTarget target = ClientBuilder.newClient().target(RESOURCE_URL)
def Entity entity = Entity.entity(new Message("Jersey", new Date()), APPLICATION_JSON_TYPE)
def response = target.request().post(entity, String)
response == "Hello Jersey!"
Jersey Groovy DSL:
def response = (POST[RESOURCE_URL] | APPLICATION_JSON_TYPE) \
                   << new Message("Jersey", new Date()) as String
response == "Hello Jersey!"

(And finally there is simple slide deck to introduce the DSL API, same examples.)

Enjoy.

JAX-RS Message Body Writer&Reader using Kryo serialization framework


In this post I'm gonna show you how simple it is to enhance JAX-RS application by a new body format. In this example I use Kryo serialization framework.

Kryo serialization framework


Kryo is a fast and efficient object graph serialization framework for Java. The goals of the project are speed, efficiency, and an easy to use API. The project is useful any time objects need to be persisted, whether to a file, database, or over the network.
Kryo can also perform automatic deep and shallow copying/cloning. This is direct copying from object to object, not object->bytes->object.

JAX-RS Message Body providers

JAX-RS allows you to enhance JAX-RS engine to support another body type. There is MessageBodyReader and MessageBodyWriter SPI to be implemented. I have chosen to use application/x-kryo media type for Kryo (de)serialized message bodies.


package cz.kramolis.jersey.kryo;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

@Consumes("application/x-kryo")
@Produces("application/x-kryo")
public class KryoMessageBodyProvider implements
                        MessageBodyWriter<Object>,
                        MessageBodyReader<Object> {

    //
    // MessageBodyWriter
    //

    @Override
    public long getSize(Object object, Class<?> type, Type genericType,
                        Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    @Override
    public boolean isWriteable(Class<?> type, Type genericType,
                        Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public void writeTo(Object object, Class<?> type, Type genericType,
                        Annotation[] annotations, MediaType mediaType,
                        MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
            throws IOException, WebApplicationException {
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();

        final Kryo kryo = new Kryo();
        final Output output = new Output(baos);

        kryo.writeObject(output, object);
        output.close();

        entityStream.write(baos.toByteArray());
    }

    //
    // MessageBodyReader
    //

    @Override
    public boolean isReadable(Class<?> type, Type genericType,
                        Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public Object readFrom(Class<Object> type, Type genericType,
                           Annotation[] annotations, MediaType mediaType,
                           MultivaluedMap<String, String> httpHeaders,
                           InputStream entityStream) throws IOException, WebApplicationException {
        try {
            Kryo kryo = new Kryo();
            Input input = new Input(entityStream);
            Object object = kryo.readObject(input, type);
            input.close();

            return object;
        } catch (Exception e) {
            throw new WebApplicationException(e);
        }
    }

}

You can see it is really simple Message Body provider. But I hope it shows you the way...

Than you need to register provider class into JAX-RS Application, e.g.


package cz.kramolis.jersey.kryo;

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.core.Application;

public class JaxRsApplication extends Application {

    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>() {{
        add(KryoMessageBodyProvider.class);
        add(PersonResource.class);
    }};

    @Override
    public Set<Class<?>> getClasses() {
        return APP_CLASSES;
    }

}


And finally usage of the new body type is very easy:


package cz.kramolis.jersey.kryo;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/")
@Consumes("application/x-kryo")
@Produces("application/x-kryo")
public class PersonResource {

    @POST
    public Person echo(final Person person) {
        return person;
    }

    @PUT
    public void put(final Person person) {
    }

    @GET
    public Person get() {
        return new Person("Wolfgang", 21, "Salzburg");
    }

}


Conclusion

Whole sources are available in dedicated GitHub repository. Use it as you need. JAX-RS provides really simple way how to extend it's support. And Kryo is really powerful framework. Give it a try. Look at deeper JVM serializers comparision.

In one of the next blog posts I will provider some performance data comparing Kryo with JSON, XML and others message body providers.

Links


February 14, 2015

Jersey 2.16 has been released

Jersey team is happy to announce availability of Jersey 2.16.

For an overview of changes, bug fixes, new features and 7 merged GitHub pull requests, please consult Jersey 2.16 Release Notes.

Highlights

JAX-B providers separated from the core

From version 2.16 onwards, all JAX-B providers are being bundled in a separate module.

The main motivation behind that is t allow using Jersey Client on Android. Current status of Android support has been described by Jakub Podlešák in his Jersey 2.x Client on Android blog post.

Performance gain when using Sub-Resource Locators

We improved the performance for using sub-resource locators in an Jersey application. The performance gains are available for cases when the sub-resource locator method returns either a resource class (return value is e.g. Class<?> or Class<MySubResource>) or a (non-proxied) resource instance (when return value is an instance of MySubResource class).

This is described by Michal Gajdoš in more detail in his Performance Improvements of Sub-Resource Locators in Jersey blog post.

Another blog post about is again by Jakub about general Jersey 2 performance improvements.

More unified connector configuration options

Jetty connector and Apache connector have been previously using their own custom properties to set SSL context on a connector. These properties have been deprecated and the code has been updated to read the SSL context information from the JAX-RS client configuration. This means that all Jersey connectors now properly accept SSL configuration as configured via standard JAX-RS ClientBuilder methods.

Previously, all Jersey connectors have been using their own default chunk size when HTTP chunked coding was used. Since Jersey 2.16, there is a new default chunk size value used by all connectors, if a custom chunk size is not set. The new default value is stored under ClientProperties.DEFAULT_CHUNK_SIZE client property.

Links

To download Jersey 2.16, please check our download page.

You can also check out the refreshed Jersey 2.16 documentation:

Other blog posts

Feedback

For feedback send an email to: users@jersey.java.net (archived here)
or log bugs/features here.


January 17, 2015

Reactive Jersey Client using Java 8, RxJava and Netflix Hystrix

In this post I will show you how to use Jersey Client Reactive API, it's RxJava Observable support together with Netflix Hystrix, latency and fault tolerance library. All sources are placed in GitHub jersey--examples--rx-client-java8-webapp project.

Jersey framework contains Reactive Client API. Reactive Jersey Client API is a generic API allowing end users to utilize the popular reactive programming model when using Jersey Client. Reactive programming is very popular these days. It helps you to implement asynchronous code with solved callback hell issue.


Jersey

Jersey supports RxJava ObservableJava 8 CompletionStage and CompletableFutureGuava ListenableFuture and FuturesJSR-166e CompletableFuture. It also provides SPI to be extended by custom reactive libraries.

There is excellent 3 part blog post series written by Michal Gajdoš about Reactive Jersey Client:
  1. Reactive Jersey Client – Motivation
  2. Reactive Jersey Client – Usage and Supported Reactive Libraries
  3. Reactive Jersey Client – Customization (SPI)

Jersey source code contains also examples for supported reactive libraries, see modules:


Hystrix

Hystrix is a latency and fault tolerance library designed to isolate points of access to remote systems, services and 3rd party libraries, stop cascading failure and enable resilience in complex distributed systems where failure is inevitable.

You usually use Hystrix to isolate dependent system call into HystrixCommand class. It allows to be executed synchronously, asynchronously (returns java.util.concurrent.Future) and also asynchronously via RxJava Observable.

Code

In case dependent system already supports RxJava Observable API you can use specific HystrixObservableCommand class. And thanks to Jersey Reactive Client API and its RxJava support it allows us to write resilient JAX-RS client code.

private class ForecastCommand extends HystrixObservableCommand<Forecast> {
    private final Destination destination;
    ...
    @Override
    protected Observable<Forecast> run() {
        return RxObservable.from(forecastWebTarget)
                .resolveTemplate("destination", destination.getDestination())
                .request().rx()
                .get(Forecast.class);
    }
    @Override
    protected Observable<Forecast> getFallback() {
        return Observable.just(new Forecast(destination.getDestination(), "N/A"));
    }
}

The code above creates new Hystrix RxJava Observable ready Command that invokes remote REST API using Jersey Reactive Client API. Whenever API call fails or times out a getFallback() method is invoked. And in this case we return Forecast instance with N/A forecast value.

Whole example web application enhanced by code using Hystrix is placed into my GitHub jersey--examples--rx-client-java8-webapp repository. Shown and little but simplified code snipped is from HystrixObservableAgentResource class. The example module is clone of rx-client-java8-webapp.

To run the example:
    mvn clean package jetty:run

At beginning of HystrixObservableAgentResource class there are TIMEOUT_* constants. You can play with values and you will see that sometimes you don't get all domain objects' values and sometimes you see default values (N/A for Forecast and -1 for Calculation). You can also play with Thread.sleep intervals but because of Hystrix timeout support the overall JAX-RS resource method processing time is never (too much) longer than TIMEOUT_OVERALL (currently 900 ms).

You can also check unit test RxClientsTest. Processing time is expected between 850 and 950 ms!


Links





November 18, 2013

How to configure JDK logging for Jersey 2 tests

Jersey uses JDK Logging API to log messages.

One way how to configure JDK Logging is a properties file. In such case you have to set JVM system property java.util.logging.config.file to reference the properties file.

Initially the logging configuration file logging.properties can look:

#All attributes details
handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level=FINEST
java.util.logging.SimpleFormatter.format=%4$-7s [%3$s] %5$s%6$s%n

#All log level details
.level=INFO
org.glassfish.jersey.level=CONFIG
org.glassfish.jersey.tracing.level=FINEST

The configuration explained:

  • handlers=java.util.logging.ConsoleHandler - log messages are written to System.err
  • java.util.logging.ConsoleHandler.level=FINEST - allow to log FINEST message;
    default is INFO so more detailed messages are suppressed by default
  • java.util.logging.SimpleFormatter.format - example of simple and single line format - just level, logger name and message;
    Note: It just works with Java 7.
  • org.glassfish.jersey.level=CONFIG - example of per-package level configuration;
    log all SEVERE, WARNING, INFO and CONFIG level Jersey messages
  • org.glassfish.jersey.tracing.level=FINEST - example of detailed level configuration of specified package
And as mentioned it is necessary to set system property java.util.logging.config.file. Do you want to see log messages during unit testing (e.g. using maven)? There are two options:

  • run tests in new JVM instance (fork option) and configure the system property in such maven plugin
  • enhance maven command line parameter and add the
    -Djava.util.logging.config.file=... property

For example, run test application using Jetty server for test module tracing-support:

~/jersey2/tests/integration/tracing-support$ mvn compile jetty:run -Djava.util.logging.config.file=src/test/resources/logging.properties

-that's all folks

November 16, 2013

Jersey 2 - Tracing support

What is Jersey?

This is my first Jersey related post so let me introduce Jersey framework.
Jersey RESTful Web Services framework is open source, production quality, framework for developing RESTful Web Services in Java that provides support for JAX-RS APIs and serves as a JAX-RS (JSR 311 & JSR 339) Reference Implementation.

Jersey framework is more than the JAX-RS Reference Implementation. Jersey provides it’s own API that extend the JAX-RS toolkit with additional features and utilities to further simplify RESTful service and client development. Jersey also exposes numerous extension SPIs so that developers may extend Jersey to best suit their needs.
Today I would like to introduce Tracing support feature that is part of Jersey 2 since 2.3 release. Current version of writing this blog post is 2.4.1.

Tracing Support

Jersey can provide tracing or diagnostic information about server-side processing of individual requests. This facility may provide vital information when troubleshooting your misbehaving Jersey or JAX-RS application. When enabled, Jersey tracing facility collects useful information from all parts of JAX-RS server-side request processing pipeline: PreMatchRequestFilter, ResourceMatching, RequestFilter, ReadIntercept, MBR, Invoke, ResponseFilter, WriteIntercept, MBW, as well as ExceptionHandling.

The collected tracing information related to a single request is returned to the requesting client in the HTTP headers of a response for the request. The information is also logged on the server-side using a dedicated Java Logger instance.

Configuration options


Tracing support is disabled by default. You can enable it either "globally" for all application requests or selectively per request. The tracing support activation is controlled by setting the jersey.config.server.tracing application configuration property (ServerProperties.TRACING). The property value is expected to be one of the following:
  • OFF - tracing support is disabled (default value).
  • ON_DEMAND - tracing support is in a stand-by mode; it is enabled selectively per request, via a special X-Jersey-Tracing-Accept HTTP request header.
  • ALL - tracing support is enabled for all request.

The level of detail of the information provided by Jersey tracing facility - the tracing threshold - can be customized. The tracing threshold can be set at the application level via jersey.config.server.tracing.threshold application configuration property (ServerProperties.TRACING_THRESHOLD), or at a request level, via X-Jersey-Tracing-Threshold HTTP request header. The request level configuration overrides any application level setting. There are 3 supported levels of detail for Jersey tracing:
  • SUMMARY - very basic summary information about the main request processing stages.
  • TRACE - detailed information about activities in all the main request processing stages (default threshold value).
  • VERBOSE - most verbose mode that provides extended information similar to TRACE level, however with details on entity providers (MBR/MBW) that were skipped during the provider selection phase for any reason (lower priority, pattern matching, etc). Additionally, in this mode all received request headers are echoed as part of the tracing information.
Both properties accept String value only.

Tracing Log


As mentioned earlier, all tracing information is also logged using a dedicated Java Logger at server-side. The individual tracing messages are logged immediately as the tracing events occur. The default name of the tracing logger is prefixed org.glassfish.jersey.tracing. with a default suffix general. This logger name can be customized per request by including a X-Jersey-Tracing-Logger HTTP request header as will be shown later.

Configuring tracing support via HTTP request headers


Whenever the tracing support is active (ON_DEMAND or ALL) you can customize the tracing behaviour by including one or more of the following request HTTP headers in your individual requests:
  • X-Jersey-Tracing-Accept - used to enable the tracing support for the particular request. It is applied only when the application-level tracing support is configured to ON_DEMAND mode. The value of the header is not used by the Jersey tracing facility and as such it can be any arbitrary (even empty) string.
  • X-Jersey-Tracing-Threshold - used to override the tracing level of detail. Allowed values are: SUMMARY, TRACE, VERBOSE.
  • X-Jersey-Tracing-Logger - used to override the tracing Java logger name suffix.

Format of the HTTP response headers


At the end of request processing all tracing messages are appended to the HTTP response as individual headers named X-Jersey-Tracing-nnn where nnn is index number of message starting at 0.

Each tracing message is in the following format: CATEGORY [TIME] TEXT, e.g.

X-Jersey-Tracing-007: WI          [85.95 / 183.69 ms | 46.77 %] WriteTo summary: 4 interceptors

The CATEGORY is used to categorize tracing events according to the following event types:
  • START - start of request processing information
  • PRE-MATCH - pre-matching request filter processing
  • MATCH - matching request URI to a resource method
  • REQ-FILTER - request filter processing
  • RI - entity reader interceptor processing
  • MBR - message body reader selection and invocation
  • INVOKE - resource method invocation
  • RESP-FILTER - response filter processing
  • WI - write interceptor processing
  • MBW - message body writer selection and invocation
  • MVC - template engine integration
  • EXCEPTION - exception mapping
  • FINISHED - processing finish summary

The TIME, if present, is a composite value that consists of 3 parts [ duration / time_from_start | total_req_ratio ]:
  1. duration - the duration of the current trace event [milliseconds]; e.g. duration of filter processing
  2. time_from_start - the end time of the current event with respect to the request processing start time [milliseconds]
  3. total_req_ratio - the duration of the current event with respect to the total request processing time [percentage]; this value tells you how significant part of the whole request processing time has been spent in the processing phase described by the current event

There are certain tracing events that do not have any duration. In such case, duration values are not set (---- literal).

The tracing event TEXT is a free-form detailed text information about the current diagnostic event.
For better identification, instances of JAX-RS components are represented by class name, identity hash code and @Priority value if set, example of filter instance with priority 5001: [org.glassfish.jersey.tests.integration.tracing.ContainerResponseFilter5001 @494a8227 #5001].

Tracing Examples


Threshold header, SUMMARY level example


Example of SUMMARY level messages from tests/integration/tracing-support module. Test application returns tracing headers for every request (AllTracingSupport.javaTracingSupport.java).

Run test application using Jetty server:

~/jersey2/tests/integration/tracing-support$ mvn compile jetty:run

Let's trace POST request with SUMMARY level using "Threshold" header (replacing default TRACE level):

$ curl -i http://localhost:9998/ALL/root/sub-resource-locator/sub-resource-method -H content-type:application/x-jersey-test --data '-=#[LKR]#=-' -H X-Jersey-Tracing-Threshold:SUMMARY -H accept:application/x-jersey-test -X POST

X-Jersey-Tracing-000: START       [ ---- /  ---- ms |  ---- %] baseUri=[http://localhost:9998/ALL/] requestUri=[http://localhost:9998/ALL/root/sub-resource-locator/sub-resource-method] method=[POST] authScheme=[n/a] accept=[application/x-jersey-test] accept-encoding=n/a accept-charset=n/a accept-language=n/a content-type=[application/x-jersey-test] content-length=[11]
X-Jersey-Tracing-001: PRE-MATCH   [ 0.01 /  0.68 ms |  0.01 %] PreMatchRequest summary: 2 filters
X-Jersey-Tracing-002: MATCH       [ 8.44 /  9.15 ms |  4.59 %] RequestMatching summary
X-Jersey-Tracing-003: REQ-FILTER  [ 0.01 /  9.20 ms |  0.00 %] Request summary: 2 filters
X-Jersey-Tracing-004: RI          [86.14 / 95.49 ms | 46.87 %] ReadFrom summary: 3 interceptors
X-Jersey-Tracing-005: INVOKE      [ 0.04 / 95.70 ms |  0.02 %] Resource [org.glassfish.jersey.tests.integration.tracing.SubResource @901a4f3] method=[public org.glassfish.jersey.tests.integration.tracing.Message org.glassfish.jersey.tests.integration.tracing.SubResource.postSub(org.glassfish.jersey.tests.integration.tracing.Message)]
X-Jersey-Tracing-006: RESP-FILTER [ 0.01 / 96.55 ms |  0.00 %] Response summary: 2 filters
X-Jersey-Tracing-007: WI          [85.95 / 183.69 ms | 46.77 %] WriteTo summary: 4 interceptors
X-Jersey-Tracing-008: FINISHED    [ ---- / 183.79 ms |  ---- %] Response status: 200/SUCCESSFUL|OK

Accept header, MVC integration, TRACE level example


Second example shows MVC message (from jersey-mvc-jsp integration module) using examples/bookstore-webapp module.

Run test application using Jetty server:

~/jersey2/examples/bookstore-webapp$ mvn compile jetty:run

Let's trace GET request using "Accept" header to activate on-demand tracing support messages (MyApplication.java). This is just snippet of default TRACE level messages to demonstrate MVC JSP forwarding message:

$ curl -i http://localhost:9998/items/3/tracks/0 -H X-Jersey-Tracing-Accept:whatever

...
X-Jersey-Tracing-033: WI          [ 0.00 / 23.39 ms |  0.02 %] [org.glassfish.jersey.server.mvc.internal.TemplateMethodInterceptor @141bcd49 #4000] BEFORE context.proceed()
X-Jersey-Tracing-034: WI          [ 0.01 / 23.42 ms |  0.02 %] [org.glassfish.jersey.filter.LoggingFilter @2d427def #-2147483648] BEFORE context.proceed()
X-Jersey-Tracing-035: MBW         [ ---- / 23.45 ms |  ---- %] Find MBW for type=[org.glassfish.jersey.server.mvc.internal.ImplicitViewable] genericType=[org.glassfish.jersey.server.mvc.internal.ImplicitViewable] mediaType=[[javax.ws.rs.core.MediaType @7bfbfeae]] annotations=[]
X-Jersey-Tracing-036: MBW         [ ---- / 23.52 ms |  ---- %] [org.glassfish.jersey.server.mvc.internal.ViewableMessageBodyWriter @78b353d4] IS writeable
X-Jersey-Tracing-037: MVC         [ ---- / 24.05 ms |  ---- %] Forwarding view to JSP page [/org/glassfish/jersey/examples/bookstore/webapp/resource/Track/index.jsp], model [org.glassfish.jersey.examples.bookstore.webapp.resource.Track @3937f594]
X-Jersey-Tracing-038: MBW         [ 1.09 / 24.63 ms |  4.39 %] WriteTo by [org.glassfish.jersey.server.mvc.internal.ViewableMessageBodyWriter @78b353d4]
X-Jersey-Tracing-039: WI          [ 0.00 / 24.67 ms |  0.01 %] [org.glassfish.jersey.filter.LoggingFilter @2d427def #-2147483648] AFTER context.proceed()
X-Jersey-Tracing-040: WI          [ 0.00 / 24.70 ms |  0.01 %] [org.glassfish.jersey.server.mvc.internal.TemplateMethodInterceptor @141bcd49 #4000] AFTER context.proceed()
...

Logger header, VERBOSE level example


The last example shows JDK logger name configuration and also uses  tests/integration/tracing-support module.

Run test application using Jetty server with JDK logger config file set (JDK logging configuration from file works just on Java 7):

~/jersey2/tests/integration/tracing-supportmvn compile jetty:run -Djava.util.logging.config.file=src/test/resources/logging.properties

Let's trace POST request with VERBOSE level using "Threshold" header and customize server-side logger name (suffix "lkr") using "Logger" header:

$ curl -i http://localhost:9998/ALL/root/sub-resource-locator/sub-resource-method -H content-type:application/x-jersey-test --data '-=#[LKR]#=-' -H X-Jersey-Tracing-Threshold:VERBOSE -H X-Jersey-Tracing-Logger:lkr -H accept:application/x-jersey-test -X POST

Snippet of response Tracing headers, see "skipped" messages available just with VERBOSE level:

X-Jersey-Tracing-055: WI          [ 0.01 / 110.41 ms |  0.00 %] [org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor @25f3027c #10] BEFORE context.proceed()
X-Jersey-Tracing-056: WI          [43.14 / 153.76 ms | 21.66 %] [org.glassfish.jersey.tests.integration.tracing.WriterInterceptor39 @19378d8d #39] BEFORE context.proceed()
X-Jersey-Tracing-057: WI          [ 0.02 / 154.15 ms |  0.01 %] [org.glassfish.jersey.tests.integration.tracing.WriterInterceptor45 @e2e30ea #45] BEFORE context.proceed()
X-Jersey-Tracing-058: WI          [ 0.01 / 154.49 ms |  0.01 %] [org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor @7a8d59eb #3000] BEFORE context.proceed()
X-Jersey-Tracing-059: MBW         [ ---- / 154.72 ms |  ---- %] Find MBW for type=[org.glassfish.jersey.tests.integration.tracing.Message] genericType=[org.glassfish.jersey.tests.integration.tracing.Message] mediaType=[[org.glassfish.jersey.message.internal.AcceptableMediaType @77d470a6]] annotations=[@javax.ws.rs.Path(value=sub-resource-method), @javax.ws.rs.POST()]
X-Jersey-Tracing-060: MBW         [ ---- / 154.95 ms |  ---- %] [org.glassfish.jersey.tests.integration.tracing.MessageBodyWriterTestFormat @2b40fbd9] IS writeable
X-Jersey-Tracing-061: MBW         [ ---- / 155.14 ms |  ---- %] [org.glassfish.jersey.tests.integration.tracing.MessageBodyWriterGeneric @375679be] is skipped
X-Jersey-Tracing-062: MBW         [ ---- / 155.28 ms |  ---- %] [org.glassfish.jersey.message.internal.XmlCollectionJaxbProvider$General @4e0919d8] is skipped
X-Jersey-Tracing-063: MBW         [ ---- / 155.42 ms |  ---- %] [org.glassfish.jersey.message.internal.XmlRootElementJaxbProvider$General @683ca3a4] is skipped
X-Jersey-Tracing-064: MBW         [ 0.03 / 155.58 ms |  0.01 %] WriteTo by [org.glassfish.jersey.tests.integration.tracing.MessageBodyWriterTestFormat @2b40fbd9]
X-Jersey-Tracing-065: WI          [ 0.00 / 155.77 ms |  0.00 %] [org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor @7a8d59eb #3000] AFTER context.proceed()
X-Jersey-Tracing-066: WI          [42.11 / 198.10 ms | 21.15 %] [org.glassfish.jersey.tests.integration.tracing.WriterInterceptor45 @e2e30ea #45] AFTER context.proceed()
X-Jersey-Tracing-067: WI          [ 0.01 / 198.48 ms |  0.01 %] [org.glassfish.jersey.tests.integration.tracing.WriterInterceptor39 @19378d8d #39] AFTER context.proceed()
X-Jersey-Tracing-068: WI          [ 0.00 / 198.72 ms |  0.00 %] [org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor @25f3027c #10] AFTER context.proceed()
X-Jersey-Tracing-069: WI          [88.53 / 198.93 ms | 44.46 %] WriteTo summary: 4 interceptors

And same messages written to server-side log (customized logger name):

FINER   [org.glassfish.jersey.tracing.lkr] WI_BEFORE [org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor @25f3027c #10] BEFORE context.proceed() [ 0.01 ms]
FINER   [org.glassfish.jersey.tracing.lkr] WI_BEFORE [org.glassfish.jersey.tests.integration.tracing.WriterInterceptor39 @19378d8d #39] BEFORE context.proceed() [43.14 ms]
FINER   [org.glassfish.jersey.tracing.lkr] WI_BEFORE [org.glassfish.jersey.tests.integration.tracing.WriterInterceptor45 @e2e30ea #45] BEFORE context.proceed() [ 0.02 ms]
FINER   [org.glassfish.jersey.tracing.lkr] WI_BEFORE [org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor @7a8d59eb #3000] BEFORE context.proceed() [ 0.01 ms]
FINER   [org.glassfish.jersey.tracing.lkr] MBW_FIND Find MBW for type=[org.glassfish.jersey.tests.integration.tracing.Message] genericType=[org.glassfish.jersey.tests.integration.tracing.Message] mediaType=[[org.glassfish.jersey.message.internal.AcceptableMediaType @77d470a6]] annotations=[@javax.ws.rs.Path(value=sub-resource-method), @javax.ws.rs.POST()] [ ---- ms]
FINER   [org.glassfish.jersey.tracing.lkr] MBW_SELECTED [org.glassfish.jersey.tests.integration.tracing.MessageBodyWriterTestFormat @2b40fbd9] IS writeable [ ---- ms]
FINEST  [org.glassfish.jersey.tracing.lkr] MBW_SKIPPED [org.glassfish.jersey.tests.integration.tracing.MessageBodyWriterGeneric @375679be] is skipped [ ---- ms]
FINEST  [org.glassfish.jersey.tracing.lkr] MBW_SKIPPED [org.glassfish.jersey.message.internal.XmlCollectionJaxbProvider$General @4e0919d8] is skipped [ ---- ms]
FINEST  [org.glassfish.jersey.tracing.lkr] MBW_SKIPPED [org.glassfish.jersey.message.internal.XmlRootElementJaxbProvider$General @683ca3a4] is skipped [ ---- ms]
FINER   [org.glassfish.jersey.tracing.lkr] MBW_WRITE_TO WriteTo by [org.glassfish.jersey.tests.integration.tracing.MessageBodyWriterTestFormat @2b40fbd9] [ 0.03 ms]
FINER   [org.glassfish.jersey.tracing.lkr] WI_AFTER [org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor @7a8d59eb #3000] AFTER context.proceed() [ 0.00 ms]
FINER   [org.glassfish.jersey.tracing.lkr] WI_AFTER [org.glassfish.jersey.tests.integration.tracing.WriterInterceptor45 @e2e30ea #45] AFTER context.proceed() [42.11 ms]
FINER   [org.glassfish.jersey.tracing.lkr] WI_AFTER [org.glassfish.jersey.tests.integration.tracing.WriterInterceptor39 @19378d8d #39] AFTER context.proceed() [ 0.01 ms]
FINER   [org.glassfish.jersey.tracing.lkr] WI_AFTER [org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor @25f3027c #10] AFTER context.proceed() [ 0.00 ms]
FINE    [org.glassfish.jersey.tracing.lkr] WI_SUMMARY WriteTo summary: 4 interceptors [88.53 ms]

You can see some differences. There is just duration value (if applicable), no time_from_start or total_req_ratio.

Message tracing level is transformed to JDK logging level:

  • SUMMARY -> FINE
  • TRACE   -> FINER
  • VERBOSE -> FINEST

Each message type has unique ID transformed to message category while formatting HTTP response headers:
  • START                   -> START
  • START_HEADERS           -> START
  • PRE_MATCH               -> PRE-MATCH
  • PRE_MATCH_SUMMARY       -> PRE-MATCH
  • MATCH_PATH_FIND         -> MATCH
  • MATCH_PATH_NOT_MATCHED  -> MATCH
  • MATCH_PATH_SELECTED     -> MATCH
  • MATCH_PATH_SKIPPED      -> MATCH
  • MATCH_LOCATOR           -> MATCH
  • MATCH_RESOURCE_METHOD   -> MATCH
  • MATCH_RUNTIME_RESOURCE  -> MATCH
  • MATCH_RESOURCE          -> MATCH
  • MATCH_SUMMARY           -> MATCH
  • REQUEST_FILTER          -> REQ-FILTER
  • REQUEST_FILTER_SUMMARY  -> REQ-FILTER
  • METHOD_INVOKE           -> INVOKE
  • DISPATCH_RESPONSE       -> INVOKE
  • RESPONSE_FILTER         -> RESP-FILTER
  • RESPONSE_FILTER_SUMMARY -> RESP-FILTER
  • FINISHED                -> FINISHED
  • EXCEPTION_MAPPING       -> EXCEPTION
  • RI_BEFORE               -> RI
  • RI_AFTER                -> RI
  • RI_SUMMARY              -> RI
  • MBR_FIND                -> MBR
  • MBR_NOT_READABLE        -> MBR
  • MBR_SELECTED            -> MBR
  • MBR_SKIPPED             -> MBR
  • MBR_READ_FROM           -> MBR
  • MBW_FIND                -> MBW
  • MBW_NOT_WRITEABLE       -> MBW
  • MBW_SELECTED            -> MBW
  • MBW_SKIPPED             -> MBW
  • MBW_WRITE_TO            -> MBW
  • WI_BEFORE               -> WI
  • WI_AFTER                -> WI
  • WI_SUMMARY              -> WI
  • JSP_FORWARD             -> MVC

Feedback

This is extended version of user guide capture Tracing Support. I am looking forward to your comments bellow the post. You can also use Jersey mailing lists. Don't hesitate to report any bugs, feature, and/or improvement requests in Jersey JIRA issue tracker.

Thanks,
-lk

March 31, 2012

Methods in a Java interface with or without public access modifier

Zlobím se na tvůrce jazyka Java, že v případě interface je modifikátor public u metod nepovinný. :o 

Moc děkuji tvůrcům jazyka Java, že vytvořili velmi přehledný jazyk, jehož součástí je i jmenná konvence, která podporuje dlouhé názvy a "potlačuje" zkratkovitost v zápisu kódu.

Jsem přesvědčen, že Sun udělal chybu a public u metod interfacu měl být povinný. Jak je někde volba, tak zákonitě vznikají 2 nesmiřitelné tábory. Jeden tvrdí, že redundantní informace má být potlačena. Druzí jsou přesvědčeni o poctivosti plného zápisu a explicitním potvrzení, že i metoda interfacu je veřejná.

Jistě jste pochopili, že já patřím k té druhé skupině. Mám rád čitelný kód. Nejsem líný napsat více znaků pokud jsem přesvědčen, že to přispěje k přehlednosti zdrojáku. Do této kategorie spadá i deklarace public metod interfacu. Na variantě první mi vadí, že deklarace viditelnosti metody je kontextově závislá - default je u třídy jiný než u rozhraní a to mi nevyhovuje. Podpůrný argument, proč public uvádět i u interfaců je migrace interface na abstraktní třídu a už musíte modifikátory doplňovat.

Mnohem víc je ale pro mě důležité, aby celý tým dodržoval stejné konvence. Kód by měl vypadat podobně a všichni by s konvencemi měli souhlasit. A v tomto případě asi budu muset ustoupit ze své pravdy, protože se zdá, že většina javovské populace preferuje klíčové slovo public nepsat. A co se teda zlobím na kluky ze Sunu, že přímo v The Java™ Language Specification (JSL) doporučují redundantní public nepoužívat: It is permitted, but strongly discouraged as a matter of style, to redundantly specify the public modifier for interface methods.

Je to teda ústupek na úkor čitelnosti kódu, což je jen škoda pro začínající a poctivé programátory. :P (Omlouvám se za malou demagogii.)

Najde se vůbec někdo, kdo stejně jako já preferuje tu ukecanější variantu?

Odkazy: