Showing posts with label jdk8. Show all posts
Showing posts with label jdk8. Show all posts

Sunday, August 31, 2014

Overcoming Java 8 streams

Java 8 streams provide functional and LINQ-like features in a fluent API. But streams are not without drawbacks:

  • Referenced methods and lambdas cannot throw checked exceptions
  • Controlling the threads used, especially for parallel streams, is awkward
  • Streams are not designed for extension

Overcoming these drawbacks requires a "look-a-like" API. For example, implementing java.util.stream.Stream does not help: none of the existing methods throw checked exceptions, and none of the existing stream factory helpers would return your implementation with new methods.

So I wrote my own, copying the existing stream API, updating the methods to throw checked exceptions:

hm.binkley.util.stream.CheckedStream ('develop' branch for now)

From the javadoc:

CheckedStream is a throwing Stream look-a-like with control over thread pool. It cannot be a Stream as it takes throwing versions of suppliers, functions and consumers. Otherwise it is a faithful reproduction.

Write this:

   long beanCount() throws SomeException, OtherException {
       checked(Stream.of(1, 2, 3)).
           map(this::someThrowingFunction).
           peek(That::oldBean).
           count();
   }

not this:

   long beanCount() throws SomeException, OtherException {
       try {
           Stream.of(1, 2, 3).
               map(i -> {
                   try {
                       someThrowingFunction(i);
                   } catch (final SomeException e) {
                       throw new RuntimeException(e);
                   }
               }).
               peek(i -> {
                   try {
                       That.oldBean(i);
                   } catch (final OtherException e) {
                       throw new RuntimeException(e);
                   }
               }).
               count();
       } catch (final RuntimeException e) {
           final Throwable x = e.getCause();
           if (x instanceof SomeException)
               throw (SomeException) x;
           if (x instanceof OtherException)
               throw (OtherException) x;
           throw e;
       }
   }

"Intentional" exceptions (checked exceptions plus CancellationException) have "scrubbed" stacktraces: frames from framework/glue packages are removed before the intentional exception is rethrown to calling code. Scrubbed stacktraces are much easier to understand, the framework and glue code having been removed.

To see the unscrubbed stacktrace, set the system property "hm.binkley.util.stream.CheckedStream.debug" to "true".

Controlling the thread pool used by Stream is a challenge. Deep in the implementation, it checks if being run in a ForkJoinTask, and uses that thread if so, otherwise using the common pool. So with CheckedStream write this:

       checked(stream, new ForkJoinPool()).
           map(currentThread()).
           forEach(System.out::println);

not this:

       try {
           new ForkJoinPool().submit(() -> stream.
                   map(currentThread()).
                   forEach(System.out::println)).
               get();
       } catch (final ExecutionException e) {
           final Throwable x = e.getCause();
           if (x instanceof Error)
               throw (Error) x;
           if (x instanceof RuntimeException)
               // Much tricker when stream functions throw runtime
               throw (RuntimeException) x;
           throw new Error(e); // We have no checked exceptions in this example
       }

Care is taken to respect lazy and terminal operations in using thread pools. Changing thread pool or thread mode mid-stream is supported, and are "immediate" operations: they terminate the existing stream, and start a new one with the changes:

stream.sequential().
    filter(this::someFilter).
    parallel(threads). // Existing lazy operations terminated
    map(this:someMapper).
    forEach(System.out::println);

Immediate operations ensure stream methods are run in the correct threading context.

I hope you'll agree: CheckedStream is nicer to use, especially with existing code using checked exceptions.

Suggestions, bug fixes, improvements welcome!

Tuesday, July 15, 2014

Java 8 magic exception copying

Since I can in Java 8 now parameterize constructors as functions, I can write a generic exception copier:

<E extends Throwable>
E copy(final Throwable from, final Function<String, E> ctor) {
    final E to = ctor.apply(from.getMessage());
    to.setStackTrace(from.getStackTrace());
    for (final Throwable s : from.getSuppressed())
        to.addSuppressed(s);
    return to;
}

Example:

try {
    // Something throws
} catch (final AnException | BeException | KatException e) {
    throw copy(e, IOException::new);
}

This is not a common strategy but one I sometimes use to reduce the incredible size of layered exceptions, especially for logging. It is also handy for shoehorning 3rd-party exceptions into a common framework exception, a nice feature for APIs to simplify calling code. Copy helps reduce boilerplate code.

Thursday, July 10, 2014

Lambda annotations

I am still startled by Java. While playing with Byte Buddy I noodled with adding annotations to a class at runtime. I wrote this:

final Class<? extends X> dynamicType
            = (Class<< extends X>) new ByteBuddy().
        subclass(Object.class).
        implement(X.class).
        annotateType(() -> Inject.class).
        method(named("toString")).intercept(value("Hello World!")).
        method(named("foo")).intercept(value(3)).
        make().
        load(ByteBuddyMain.class.getClassLoader(), WRAPPER).
        getLoaded();

Get a load of "annotatedType"! It wants an annotation instance. Originally I tried:

new Inject() {
    @Override
    public Class<Inject> annotationType() {
        return Inject.class;
    }
}

What the hey, then I thought to try the lambda expression and live dangerously. It works!

Saturday, June 28, 2014

Java 8 predicate for tee

A "tee" method did not make it into Java 8 streams, but you can make your own easily enough:

public final class Tee<T>
        implements Predicate<T> {
    private final Predicate<T> test;
    private final Consumer<T> reject;

    @Nonnull
    public static <T> Predicate<T> tee(
            @Nonnull final Predicate<T> test,
            @Nonnull final Consumer<T> reject) {
        return new Tee<>(test, reject);
    }

    private Tee(final Predicate<T> test,
            final Consumer<T> reject) {
        this.test = test;
        this.reject = reject;
    }

    @Override
    public boolean test(final T t) {
        if (test.test(t))
            return true;
        reject.accept(t);
        return false;
    }

    public static void main(final String... args) {
        asList("a", "bb", "ccc", "dddd").stream().
                filter(tee(Tee::even, err::println)).
                forEach(out::println);
    }

    private static boolean even(final String s) {
        return 0 == s.length() % 2;
    }
}

Wednesday, March 05, 2014

World's smallest DAO

The world's smallest Java DAO (with heavy lifting provided by Spring Framework):

public class SimpleDAO {
    private final DataSourceTransactionManager transactionManager;

    public SimpleDAO(DataSourceTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public <T> T dao(Dao<T> dao) {
        return dao.using(transactionManager);
    }

    public interface Dao<T> {
        default T using(DataSourceTransactionManager transactionManager) {
            return new TransactionTemplate(transactionManager).execute(
                    status -> on(new JdbcTemplate(transactionManager.getDataSource()), status));
        }

        T on(JdbcTemplate jdbcTemplate, TransactionStatus status);
    }
}

Usage:

class InviteesDAO {
    private final SimpleDAO transact;

    InviteesDAO(DatabaseTransactionManager transactionManager) {
        transact = new SimpleDAO(tranactionManager);
    }

    List<String> getInvitees() {
        return transact.dao((jdbcTemplate, status) -> jdbcTemplate.queryForList(
            "SELECT username FROM invitees", String.class));
    }

    void invite(String username) {
        transact.dao((jdbcTemplate, status) -> jdbcTemplate.update(
            "INSERT INTO invitees (username) VALUES (?)", username));
    }
}

UPDATE: Demonstrate with composition rather than inheritance.

Friday, January 31, 2014

JDK8: Improved Iterator

One of my favorite changes in Java 8, default methods on interfaces, adds this gem to venerable Iterator:

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

By default now when you implement an iterable you need not supply "remove()". Beautiful.

There is also a new, defaulted "forEachRemaining(Consumer)".

Tuesday, September 10, 2013

Tuesday, February 05, 2013

Java 8 lambdas and all that

Lovely post from Anton Arhipov at JRebel, Java 8: The First Taste of Lambdas. Kudos for javap decompilation for an "under the hood" explanation. His conclusion:

We can definitely say that lambdas and the accompanied features (defender methods, collections library improvements) will have a great impact on Java very soon. The syntax is quite nice and once developers realize that these features provide value to their productivity, we will see a lot of code that leverages these features.

It was quite interesting for me to see what lambdas are compiled to and I was very happy when I saw the total utilization of the invokedynamic instruction in action without any anonymous inner classes involved at all.