From e61a5679f079eef7e453c40d395009a56444c650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kohlschu=CC=88tter?= Date: Thu, 18 Apr 2024 14:31:27 +0200 Subject: [PATCH] common: Add helpers to retain Java 7 compatibility for certain features In new code, we may use CompletableFuture and Map.computeIfAbsent, which are both missing in Java 7. These cannot be translated by retrolambda alone, and animal-sniffer-plugin will rightfully complain about that during compilation. Provide shim methods that do the right thing. --- .../java/org/newsclub/net/unix/AFFuture.java | 48 +++++++++ .../java/org/newsclub/net/unix/Java7Util.java | 42 ++++++++ .../org/newsclub/net/unix/Java7Util.java | 99 +++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 junixsocket-common/src/main/java/org/newsclub/net/unix/AFFuture.java create mode 100644 junixsocket-common/src/main/java/org/newsclub/net/unix/Java7Util.java create mode 100644 junixsocket-common/src/main/java8/org/newsclub/net/unix/Java7Util.java diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFFuture.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFFuture.java new file mode 100644 index 000000000..d527900f7 --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFFuture.java @@ -0,0 +1,48 @@ +/* + * junixsocket + * + * Copyright 2009-2024 Christian Kohlschütter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.newsclub.net.unix; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.eclipse.jdt.annotation.Nullable; + +/** + * junixsocket-internal variant of a bare-bones {@link Future}, to allow compiling + * junixsocket-common with retrolambda for Java 1.7. + * + * @param the type of results supplied by this supplier + */ +@FunctionalInterface +interface AFFuture { + + /** + * Waits if necessary for the computation to complete, and then retrieves its result. + * + * @return the computed result + * @throws CancellationException if the computation was cancelled + * @throws ExecutionException if the computation threw an exception + * @throws InterruptedException if the current thread was interrupted while waiting + */ + T get() throws InterruptedException, ExecutionException; + + static <@Nullable U> AFFuture supplyAsync(AFSupplier supplier) { + return Java7Util.supplyAsync(supplier); + } +} diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/Java7Util.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/Java7Util.java new file mode 100644 index 000000000..852fc2667 --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/Java7Util.java @@ -0,0 +1,42 @@ +/* + * junixsocket + * + * Copyright 2009-2024 Christian Kohlschütter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.newsclub.net.unix; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Helper class to allow codebase to remain compatible with Java 7 (via retrolambda, + * animal-sniffer). + * + * @author Christian Kohlschütter + */ +@IgnoreJRERequirement // see src/main/java8 +final class Java7Util { + static <@Nullable U> AFFuture supplyAsync(AFSupplier supplier) { + return CompletableFuture.supplyAsync(supplier::get)::get; + } + + static V computeIfAbsent(Map map, K key, + AFFunction mappingFunction) { + return map.computeIfAbsent(key, mappingFunction::apply); + } +} diff --git a/junixsocket-common/src/main/java8/org/newsclub/net/unix/Java7Util.java b/junixsocket-common/src/main/java8/org/newsclub/net/unix/Java7Util.java new file mode 100644 index 000000000..f449fe169 --- /dev/null +++ b/junixsocket-common/src/main/java8/org/newsclub/net/unix/Java7Util.java @@ -0,0 +1,99 @@ +/* + * junixsocket + * + * Copyright 2009-2024 Christian Kohlschütter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.newsclub.net.unix; + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Semaphore; + +import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Helper class to allow codebase to remain compatible with Java 7 (via retrolambda, + * animal-sniffer). + * + * @author Christian Kohlschütter + */ +@IgnoreJRERequirement // see src/main/java8 +final class Java7Util { + private static final boolean JAVA8_OR_LATER; + + static { + boolean ok; + try { + Class.forName("java.util.concurrent.CompletableFuture"); + ok = true; + } catch (ClassNotFoundException e) { + ok = false; + } + JAVA8_OR_LATER = ok; + } + + static <@Nullable U> AFFuture supplyAsync(AFSupplier supplier) { + if (JAVA8_OR_LATER) { + return CompletableFuture.supplyAsync(supplier::get)::get; + } else { + return new Java7CompletableFuture<>(supplier); + } + } + + static V computeIfAbsent(Map map, K key, + AFFunction mappingFunction) { + if (JAVA8_OR_LATER) { + return map.computeIfAbsent(key, mappingFunction::apply); + } + + Objects.requireNonNull(mappingFunction); + V v; + if ((v = map.get(key)) == null) { + V newValue; + if ((newValue = mappingFunction.apply(key)) != null) { + map.put(key, newValue); + return newValue; + } + } + + return v; + } + + static final class Java7CompletableFuture<@Nullable T> implements AFFuture { + private final Semaphore sema = new Semaphore(0); + private T object = null; + + Java7CompletableFuture(AFSupplier supplier) { + Thread t = new Thread(new Runnable() { + + @Override + public void run() { + object = supplier.get(); + sema.release(); + } + }); + t.start(); + } + + @Override + public T get() throws InterruptedException, ExecutionException { + sema.acquire(); + return object; + } + } +}