Java tutorial
/* * Copyright (C) 2016 Giancarlo Frison <giancarlo@gfrison.com> * * Licensed under the UbiCrypt License, Version 1.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://github.com/gfrison/ubicrypt/LICENSE.md * 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 ubicrypt.core; import com.google.common.base.Throwables; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.dataformat.smile.SmileFactory; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.module.afterburner.AfterburnerModule; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPKeyPair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.ConfigurableApplicationContext; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.RandomAccessFile; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousFileChannel; import java.nio.channels.CompletionHandler; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.Enumeration; import java.util.Iterator; import java.util.Set; import java.util.Spliterator; import java.util.Spliterators; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Predicate; import java.util.stream.Stream; import java.util.stream.StreamSupport; import rx.Observable; import rx.Subscriber; import rx.functions.Action1; import rx.functions.Func1; import ubicrypt.core.crypto.PGPEC; import ubicrypt.core.dto.UbiFile; import ubicrypt.core.exp.NotFoundException; import ubicrypt.core.util.PGPKValue; import ubicrypt.core.util.PGPKValueDeserializer; import ubicrypt.core.util.PGPKValueSerializer; import ubicrypt.core.util.PathDeserializer; import ubicrypt.core.util.PathSerializer; import static org.apache.commons.lang3.StringUtils.startsWith; import static org.apache.commons.lang3.StringUtils.trim; public class Utils { public static final Predicate<? super UbiFile> trackedFile = file -> !(file.isDeleted() || file.isRemoved()); private static final Logger log = LoggerFactory.getLogger(Utils.class); public static final Action1<Throwable> logError = err -> log.error(err.getMessage(), err); private static final SmileFactory smile = new SmileFactory(); private static final ObjectMapper mapper = new ObjectMapper(smile); public static Func1<InputStream, String> is2string = is -> { try { return IOUtils.toString(is, "UTF-8"); } catch (IOException e) { Throwables.propagate(e); return null; } }; static { configureMapper(mapper); } public static void configureMapper(final ObjectMapper mapper) { mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.registerModule(new Jdk8Module()); mapper.registerModule(new JavaTimeModule()); mapper.registerModule(new SimpleModule("ubicrypt module") { { addSerializer(new PGPKValueSerializer(PGPKValue.class)); addDeserializer(PGPKValue.class, new PGPKValueDeserializer(PGPKValue.class)); addSerializer(new PathSerializer(Path.class)); addDeserializer(Path.class, new PathDeserializer(Path.class)); } }); mapper.registerModule(new AfterburnerModule()); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); } public static int deviceId() { try { final Enumeration<NetworkInterface> it = NetworkInterface.getNetworkInterfaces(); int ret = 0; while (it.hasMoreElements() && ret == 0) { ret = Arrays.hashCode(it.nextElement().getHardwareAddress()); } return ret; } catch (final SocketException e) { Throwables.propagate(e); } return -1; } public static Observable<Long> write(final Path target, final byte[] bytes) { return write(target, new ByteArrayInputStream(bytes)); } private static void unsubscribe(final Subscriber<? super Long> subscriber, final InputStream is, final FileLock lock) { close(is, lock); if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } } private static void close(final InputStream is, final FileLock lock) { try { lock.release(); } catch (final IOException e) { } try { is.close(); } catch (final IOException e1) { } } public static Observable<Long> write(final Path fullPath, final InputStream inputStream) { return Observable.create(subscriber -> { try { final AtomicLong offset = new AtomicLong(0); final AsynchronousFileChannel afc = AsynchronousFileChannel.open(fullPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE); afc.lock(new Object(), new CompletionHandler<FileLock, Object>() { @Override public void completed(final FileLock lock, final Object attachment) { //acquired lock final byte[] buf = new byte[1 << 16]; try { final int len = inputStream.read(buf); if (len == -1) { unsubscribe(subscriber, inputStream, lock); return; } afc.write(ByteBuffer.wrap(Arrays.copyOfRange(buf, 0, len)), offset.get(), null, new CompletionHandler<Integer, Object>() { @Override public void completed(final Integer result, final Object attachment) { //written chunk of bytes subscriber.onNext(offset.addAndGet(result)); final byte[] buf = new byte[1 << 16]; int len; try { len = inputStream.read(buf); if (len == -1) { unsubscribe(subscriber, inputStream, lock); log.debug("written:{}", fullPath); return; } } catch (final IOException e) { subscriber.onError(e); return; } if (len == -1) { unsubscribe(subscriber, inputStream, lock); log.debug("written:{}", fullPath); return; } afc.write(ByteBuffer.wrap(Arrays.copyOfRange(buf, 0, len)), offset.get(), null, this); } @Override public void failed(final Throwable exc, final Object attachment) { subscriber.onError(exc); } }); } catch (final Exception e) { close(inputStream, lock); subscriber.onError(e); } } @Override public void failed(final Throwable exc, final Object attachment) { log.error("error on getting lock for:{}, error:{}", fullPath, exc.getMessage()); try { inputStream.close(); } catch (final IOException e) { } subscriber.onError(exc); } }); } catch (final Exception e) { log.error("error on file:{}", fullPath); subscriber.onError(e); } }); } public static InputStream readIs(final Path path) { final PipedInputStream pis = new PipedInputStream(); final AtomicLong pos = new AtomicLong(0); try { final PipedOutputStream ostream = new PipedOutputStream(pis); final AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.READ); final ByteBuffer buffer = ByteBuffer.allocate(1 << 16); channel.read(buffer, pos.get(), buffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(final Integer result, final ByteBuffer buf) { try { if (result == -1) { ostream.close(); return; } final byte[] bytes = new byte[result]; System.arraycopy(buf.array(), 0, bytes, 0, result); ostream.write(bytes); ostream.flush(); if (result < 1 << 16) { ostream.close(); return; } pos.addAndGet(result); final ByteBuffer buffer = ByteBuffer.allocate(1 << 16); channel.read(buffer, pos.get(), buffer, this); } catch (final IOException e) { Throwables.propagate(e); } } @Override public void failed(final Throwable exc, final ByteBuffer attachment) { log.error(exc.getMessage(), exc); } }); } catch (final IOException e) { if (e instanceof NoSuchFileException) { throw new NotFoundException(path); } Throwables.propagate(e); } return pis; } public static InputStream convert(final Observable<byte[]> source) { final PipedOutputStream pos = new PipedOutputStream(); try { final PipedInputStream pis = new PipedInputStream(pos); source.subscribe(bytes -> { try { pos.write(bytes); pos.flush(); } catch (final IOException e) { Throwables.propagate(e); } }, err -> { log.error(err.getMessage(), err); try { pis.close(); } catch (final IOException e) { } }); return pis; } catch (final IOException e) { Throwables.propagate(e); } return null; } public static Observable<byte[]> read(final Path path) { return Observable.create(subscriber -> { final AtomicLong pos = new AtomicLong(0); try { final AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.READ); read(pos, ByteBuffer.allocate(1 << 16), channel, subscriber); } catch (final Throwable e) { subscriber.onError(e); } }); } private static void read(final AtomicLong pos, final ByteBuffer buffer, final AsynchronousFileChannel channel, final Subscriber<? super byte[]> subscriber) { channel.read(buffer, pos.get(), pos, new CompletionHandler<Integer, AtomicLong>() { @Override public void completed(final Integer result, final AtomicLong attachment) { if (result == -1) { subscriber.onCompleted(); return; } subscriber.onNext(buffer.array()); if (result < 1 << 16) { subscriber.onCompleted(); return; } pos.addAndGet(result); read(pos, ByteBuffer.allocate(1 << 16), channel, subscriber); } @Override public void failed(final Throwable exc, final AtomicLong attachment) { subscriber.onError(exc); } }); } public static Path ubiqFolder() { Path def = null; if ((System.getProperty("os.name")).toUpperCase().contains("WIN")) { def = Paths.get(System.getenv("AppData"), "ubicrypt"); } else { def = Paths.get(System.getProperty("user.home"), ".ubicrypt"); } return Paths.get(System.getProperty("conf", def.toString())); } public static boolean isAppInUse(final Path ubiqFolder) throws IOException { Files.createDirectories(ubiqFolder); final File file = Paths.get(ubiqFolder.toString(), "lock").toFile(); final FileChannel channel = new RandomAccessFile(file, "rw").getChannel(); final FileLock lock; try { lock = channel.tryLock(); } catch (final OverlappingFileLockException e) { return true; } if (lock == null) { return true; } Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { try { lock.release(); channel.close(); } catch (final Exception e) { e.printStackTrace(); } } }); return false; } public static <T> T unmarshall(final InputStream is, final Class<T> remoteConfigClass) { try { return mapper.readValue(is, remoteConfigClass); } catch (final IOException e) { Throwables.propagate(e); } return null; } public static byte[] marshall(final Object obj) { try { return mapper.writeValueAsBytes(obj); } catch (final IOException e) { Throwables.propagate(e); return null; } } public static InputStream marshallIs(final Object obj) { try { return new ByteArrayInputStream(mapper.writeValueAsBytes(obj)); } catch (final IOException e) { Throwables.propagate(e); return null; } } public static <T> T unmarshall(final byte[] content, final Class<T> clz) { try { return mapper.readValue(content, clz); } catch (final IOException e) { log.debug("error unmarshall:{}", new String(content)); Throwables.propagate(e); } return null; } private static Path getLocalConfigFile(final String file) { return ubiqFolder().resolve(file); } public static Path securityFile() { return getLocalConfigFile("sec"); } public static Path ownPKRingFile() { return getLocalConfigFile("mypks"); } public static Path configFile() { return getLocalConfigFile("config"); } public static PGPKeyPair readPrivateKey(final char[] password) throws PGPException { try { return PGPEC.extractEncryptKeyPair(PGPEC.readSK(Files.newInputStream(Utils.securityFile())), password); } catch (final PGPException e) { throw e; } catch (final IOException e) { Throwables.propagate(e); } return null; } public static void close(final Closeable... closeables) { for (final Closeable closeable : closeables) { try { if (closeable != null) { closeable.close(); } } catch (final Exception e) { } } } public static <T> Stream<T> toStream(final Iterator<T> iterator) { return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false); } public static String machineName() { try { return System.getProperty("user.name") + "/" + InetAddress.getLocalHost().getHostName(); } catch (final UnknownHostException e) { log.error(e.getMessage(), e); } return System.getProperty("user.name"); } public static <T> Set<T> copySynchronized(final Set<T> set) { final Set<T> ret = ConcurrentHashMap.newKeySet(); ret.addAll(set); return ret; } public static <T> T springIt(ConfigurableApplicationContext ctx, T object) { return springIt(ctx, object, object.getClass().getName()); } public static <T> T springIt(ConfigurableApplicationContext ctx, T object, String name) { final AutowireCapableBeanFactory factory = ctx.getAutowireCapableBeanFactory(); factory.autowireBean(object); factory.applyBeanPostProcessorsBeforeInitialization(object, name); factory.applyBeanPostProcessorsAfterInitialization(object, name); return object; } public static Observable.OnSubscribe<Boolean> emptySubject() { return subscriber -> subscriber.onCompleted(); } public static void setProperties(String[] args) { if (args == null || args.length == 0) { return; } int i = 0; do { String arg = trim(args[i]); String pured = arg; if (startsWith(arg, "-") || startsWith(arg, "--")) { pured = StringUtils.substringAfterLast(arg, "-"); } if (StringUtils.contains(pured, "=")) { String[] split = StringUtils.split(pured, '='); System.setProperty(split[0], split[1]); i++; continue; } i++; if (i >= args.length) { System.setProperty(pured, "true"); return; } String val = trim(args[i]); if (StringUtils.equals(val, "=")) { i++; if (i >= args.length) { return; } val = trim(args[i]); } if (startsWith(val, "-")) { System.setProperty(pured, "true"); } else { System.setProperty(pured, val); i++; } } while (i < args.length); } }