| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.tomee.server.composer; |
| |
| import org.tomitribe.swizzle.stream.StreamBuilder; |
| import org.tomitribe.util.Duration; |
| |
| import java.io.File; |
| import java.io.FileFilter; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.concurrent.TimeUnit; |
| import java.util.function.Consumer; |
| import java.util.function.Supplier; |
| |
| /** |
| * It is an intentional design of this class that the build() method be implemented by |
| * the actual Builder subclass. |
| * |
| * It is further a design goal of this class that the build() method of the subclasses |
| * be something that can be executed multiple times, allowing for clustered setups |
| * to be created without the need for more than one builder instance. |
| * |
| * This class effectively allows "building logic" to be registered in the form of a |
| * handful of java.util.function.* classes. This allows the building logic to be |
| * collected and then executed in the `build()` method, facilitating the idea that |
| * the build method could be safely executed N times. |
| * |
| * It is an essential design choice for builders to not modify the state in this class |
| * in their `build()` methods. The result of respecting this design choice is that |
| * they could do fun things like hand their `build()` method over as a `Supplier` to |
| * the `java.util.Stream.generate()` method of the Java 8 Streams API. |
| * |
| * @param <T> Should be set to the subclasses' type |
| */ |
| public abstract class ServerBuilder<T extends ServerBuilder<T>> { |
| |
| protected final File archive; |
| protected FileFilter filter = pathname -> true; |
| protected Duration await = new Duration("1 minute"); |
| protected boolean list = false; |
| protected final ArrayList<Consumer<File>> homeConsumers = new ArrayList<>(); |
| protected final ArrayList<Consumer<T>> builderConsumers = new ArrayList<>(); |
| protected final ArrayList<Consumer<StreamBuilder>> watches = new ArrayList<>(); |
| protected final Map<String, String> env = new HashMap<>(); |
| protected final Archive modifications = Archive.archive(); |
| |
| public ServerBuilder(final String coordinates) throws IOException { |
| this(Mvn.mvn(coordinates)); |
| } |
| public ServerBuilder(final File archive) throws IOException { |
| this.archive = archive; |
| } |
| |
| public Duration await() { |
| return this.await; |
| } |
| |
| public File archive() { |
| return archive; |
| } |
| |
| public T env(final String name, final String value) { |
| env.put(name, value); |
| return (T) this; |
| } |
| |
| public T add(final String name, final byte[] bytes) { |
| modifications.add(name, bytes); |
| return (T) this; |
| } |
| |
| public T add(final String name, final Supplier<byte[]> content) { |
| modifications.add(name, content); |
| return (T) this; |
| } |
| |
| public T add(final String name, final String content) { |
| modifications.add(name, content); |
| return (T) this; |
| } |
| |
| public T add(final String name, final File content) { |
| modifications.add(name, content); |
| return (T) this; |
| } |
| |
| public T home(final Consumer<File> customization) { |
| homeConsumers.add(customization); |
| return (T) this; |
| } |
| |
| public T watch(final String token, final Consumer<String> consumer) { |
| this.watches.add(stream -> stream.watch(token, consumer)); |
| return (T) this; |
| } |
| |
| public T watch(final String begin, final String end, final Consumer<String> consumer) { |
| this.watches.add(stream -> stream.watch(begin, end, consumer)); |
| return (T) this; |
| } |
| |
| public T watch(final String token, final Runnable runnable) { |
| this.watches.add(stream -> stream.watch(token, s -> runnable.run())); |
| return (T) this; |
| } |
| |
| public T watch(final String begin, final String end, final Runnable runnable) { |
| this.watches.add(stream -> stream.watch(begin, end, s -> runnable.run())); |
| return (T) this; |
| } |
| |
| public T and(final Consumer<T> consumer) { |
| this.builderConsumers.add(consumer); |
| return (T) this; |
| } |
| |
| /** |
| * Filters files from the Tomcat/TomEE archive that we may not want |
| * By default webapps are excluded |
| */ |
| public T filter(final FileFilter filter) { |
| this.filter = filter; |
| return (T) this; |
| } |
| |
| public T list(final boolean list) { |
| this.list = list; |
| return (T) this; |
| } |
| |
| /** |
| * How long should we wait for the server to start? |
| * Default is 30 seconds |
| */ |
| public T await(final Duration duration) { |
| this.await = duration; |
| return (T) this; |
| } |
| |
| /** |
| * How long should we wait for the server to start? |
| * Default is 30 seconds |
| */ |
| public T await(final long time, final TimeUnit unit) { |
| this.await = new Duration(time, unit); |
| return (T) this; |
| } |
| |
| protected void applyHomeConsumers(final File home) { |
| // run any customization logic that's been added |
| for (final Consumer<File> customization : homeConsumers) { |
| customization.accept(home); |
| } |
| } |
| |
| protected void applyModifications(final File home) throws IOException { |
| // copy user files |
| modifications.toDir(home); |
| } |
| |
| protected void applyBuilderConsumers() { |
| for (final Consumer<T> consumer : builderConsumers) { |
| consumer.accept((T) this); |
| } |
| } |
| |
| } |