├── version.sbt ├── activator-launch-1.2.10.jar ├── project ├── plugins.sbt └── build.properties ├── src ├── main │ └── java │ │ └── com │ │ └── jasongoodwin │ │ └── monads │ │ ├── TryMapFunction.java │ │ ├── TrySupplier.java │ │ ├── TryConsumer.java │ │ ├── Futures.java │ │ └── Try.java └── test │ └── java │ └── com │ └── jasongoodwin │ └── monads │ ├── FuturesTest.java │ └── TryTest.java ├── .gitignore ├── LICENSE ├── README.md └── activator /version.sbt: -------------------------------------------------------------------------------- 1 | version := "0.4.1" 2 | -------------------------------------------------------------------------------- /activator-launch-1.2.10.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasongoodwin/better-java-monads/HEAD/activator-launch-1.2.10.jar -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "0.2.1") 2 | 3 | addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8.3") -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Sat Oct 18 09:43:24 EDT 2014 3 | template.uuid=1f2d4384-7409-45c1-b261-7821efc6900b 4 | sbt.version=0.13.5 5 | -------------------------------------------------------------------------------- /src/main/java/com/jasongoodwin/monads/TryMapFunction.java: -------------------------------------------------------------------------------- 1 | package com.jasongoodwin.monads; 2 | 3 | 4 | public interface TryMapFunction { 5 | R apply(T t) throws Throwable; 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | .cache/ 6 | .history/ 7 | .lib/ 8 | dist/* 9 | target/ 10 | lib_managed/ 11 | src_managed/ 12 | project/boot/ 13 | project/plugins/project/ 14 | 15 | # IDE specific 16 | .scala_dependencies 17 | .worksheet 18 | .idea 19 | -------------------------------------------------------------------------------- /src/main/java/com/jasongoodwin/monads/TrySupplier.java: -------------------------------------------------------------------------------- 1 | package com.jasongoodwin.monads; 2 | 3 | /** 4 | * This is similar to the Java Supplier function type. 5 | * It has a checked exception on it to allow it to be used in lambda expressions on the Try monad. 6 | * @param 7 | */ 8 | 9 | public interface TrySupplier{ 10 | T get() throws Throwable; 11 | } 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); 2 | you may not use this file except in compliance with the License. 3 | You may obtain a copy of the License at 4 | 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | -------------------------------------------------------------------------------- /src/main/java/com/jasongoodwin/monads/TryConsumer.java: -------------------------------------------------------------------------------- 1 | package com.jasongoodwin.monads; 2 | 3 | /** 4 | * This is similar to the Java {@link java.util.function.Consumer Consumer} function type. 5 | * It has a checked exception on it to allow it to be used in lambda expressions on the Try monad. 6 | * 7 | * @param 8 | * @param the type of throwable thrown by {@link #accept(Object)} 9 | */ 10 | public interface TryConsumer { 11 | 12 | /** 13 | * Performs this operation on the given argument. 14 | * 15 | * @param t the input argument 16 | */ 17 | void accept(T t) throws E; 18 | 19 | } -------------------------------------------------------------------------------- /src/main/java/com/jasongoodwin/monads/Futures.java: -------------------------------------------------------------------------------- 1 | package com.jasongoodwin.monads; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.CompletableFuture; 5 | import java.util.stream.Collectors; 6 | 7 | public class Futures { 8 | /** 9 | * Convert List of CompletableFutures to CompletableFuture with a List. 10 | * @param futures List of Futures 11 | * @param type 12 | * @return CompletableFuture with a List 13 | */ 14 | 15 | public static CompletableFuture> sequence(List> futures) { 16 | return CompletableFuture. 17 | allOf(futures.toArray(new CompletableFuture[futures.size()])). 18 | thenApply(v -> 19 | futures.stream(). 20 | map(CompletableFuture::join). 21 | collect(Collectors.toList()) 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/jasongoodwin/monads/FuturesTest.java: -------------------------------------------------------------------------------- 1 | package com.jasongoodwin.monads; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.List; 6 | import java.util.concurrent.CompletableFuture; 7 | import java.util.stream.Collectors; 8 | import java.util.stream.IntStream; 9 | 10 | public class FuturesTest { 11 | @Test 12 | public void itShouldConvertAListOfFuturesToAFutureWithAList() throws Exception { 13 | //given a list of futures, 14 | List list = IntStream.range(0, 100).boxed().collect(Collectors.toList()); 15 | int size = list.size(); 16 | List> futures = list 17 | .stream() 18 | .map(x -> CompletableFuture.supplyAsync(() -> x)) 19 | .collect(Collectors.toList()); 20 | 21 | //when we call sequence, 22 | CompletableFuture> futureList = Futures.sequence(futures); 23 | 24 | //then we should get a future with a list 25 | List collectedIntegers = Futures.sequence(futures).get(); 26 | assert(collectedIntegers.size() == size); 27 | assert(list.get(5) == collectedIntegers.get(5)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | better-java8-monads 2 | ================== 3 | 4 | This library was built immediately after Java8 became GA to help fill in some blanks (Try, Futures.sequence) 5 | There are some other libraries mentioned w/ this project on Stack Overflow: https://stackoverflow.com/questions/27787772/try-monad-in-java-8 6 | 7 | Feature Overview 8 | ---------------- 9 | 10 | *Try* - Optional exists to express nulls in types, but there is no way to express success/failure in types. Try to the rescue! The Try type is very similar to the Try in Scala's standard lib. 11 | 12 | *CompletableFuture.sequence* - If you have a list of futures, there is no obvious way to get a future of a list. This will come in handy! 13 | 14 | Usage 15 | ===== 16 | 17 | Import into your project: 18 | 19 | SBT 20 | --- 21 | 22 | "com.jason-goodwin" % "better-monads" % "0.4.0" 23 | 24 | Maven 25 | ----- 26 | 27 | 28 | com.jason-goodwin 29 | better-monads 30 | 0.4.0 31 | 32 | 33 | Try 34 | === 35 | 36 | The try monad was attributed to Twitter and placed into the Scala standard library. 37 | While both Scala and Haskell have a monad Either which has a left and a right type, 38 | a Try is specifically of a type T on success or an exception on failure. 39 | 40 | Usage 41 | ----- 42 | 43 | The Try api is meant to be similar to the Optional type so has the same functions. 44 | - get() returns the held value or throws the thrown exception 45 | - getUnchecked() returns the held value or throws the thrown exception wrapped in a RuntimeException instance 46 | - map(x) maps the success value x to a new value and type or otherwise passes the Failure forward. 47 | - flatMap((x) -> f(x)) maps the success value x to a new Try of f(x). 48 | - recover((t) -> x) will return the success value of the Try in the success case or the value x in the failure case. Exposes the exception. 49 | - recoverWith((t) -> f(x)) will return the success value of the Try in the success case or a new try of f(x) in the failure case. Exposes the exception. 50 | - filter((x) -> isTrue(x)) - If Success, returns the same Success if the predicate succeeds, otherwise, returns a Failure with type NoSuchElementException. 51 | - onSuccess((x) -> f(x)) execute some code on success - takes Consumer (eg requires no return value). 52 | - onFailure((x) -> f(x)) execute some code on failure - takes Consumer (eg requires no return value). 53 | - orElse(x) will return the success value of the Try in success case or the value x in failure case. 54 | - orElseTry(f) will return the success value of the Try in success case or a new Try(f) in the failure case. 55 | - orElseThrow(() -> throw new T) gets result or on failure will throw checked exception of type T 56 | - toOptional() will return Optional of success value of Try (if not null), otherwise it will return an empty Optional 57 | 58 | Futures 59 | ======= 60 | There is no sequence method in the Java8 library so I've provided one. You'll find the function Futures.sequence which will convert a `List>` into a `CompletableFuture>`. This is useful in the common use case of processing all of the elements of a list concurrently. 61 | 62 | Usage 63 | ----- 64 | Simply call Futures.sequence on a `List>` to get back a single future with the list of your items. 65 | 66 | List list = IntStream.range(0, 100).boxed().collect(Collectors.toList()); 67 | int size = list.size(); 68 | List> futures = list 69 | .stream() 70 | .map(x -> CompletableFuture.supplyAsync(() -> x)) 71 | .collect(Collectors.toList()); 72 | 73 | CompletableFuture> futureList = Futures.sequence(futures); 74 | 75 | 76 | Tests 77 | ===== 78 | 79 | See the tests for examples of all functionality. 80 | -------------------------------------------------------------------------------- /src/test/java/com/jasongoodwin/monads/TryTest.java: -------------------------------------------------------------------------------- 1 | package com.jasongoodwin.monads; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.Optional; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | public class TryTest { 10 | @Test 11 | public void itShouldBeSuccessOnSuccess() throws Throwable{ 12 | Try t = Try.ofFailable(() -> "hey"); 13 | assertTrue(t.isSuccess()); 14 | } 15 | 16 | @Test 17 | public void itShouldHoldValueOnSuccess() throws Throwable{ 18 | Try t = Try.ofFailable(() -> "hey"); 19 | assertEquals("hey", t.get()); 20 | } 21 | 22 | @Test 23 | public void itShouldMapOnSuccess() throws Throwable{ 24 | Try t = Try.ofFailable(() -> "hey"); 25 | Try intT = t.map((x) -> 5); 26 | intT.get(); 27 | assertEquals(5, intT.get().intValue()); 28 | } 29 | 30 | @Test 31 | public void itShouldFlatMapOnSuccess() throws Throwable { 32 | Try t = Try.ofFailable(() -> "hey"); 33 | Try intT = t.flatMap((x) -> Try.ofFailable(() -> 5)); 34 | intT.get(); 35 | assertEquals(5, intT.get().intValue()); 36 | } 37 | 38 | @Test 39 | public void itShouldOrElseOnSuccess() { 40 | String t = Try.ofFailable(() -> "hey").orElse("jude"); 41 | assertEquals("hey", t); 42 | } 43 | 44 | @Test 45 | public void itShouldReturnValueWhenRecoveringOnSuccess() { 46 | String t = Try.ofFailable(() -> "hey").recover((e) -> "jude"); 47 | assertEquals("hey", t); 48 | } 49 | 50 | 51 | @Test 52 | public void itShouldReturnValueWhenRecoveringWithOnSuccess() throws Throwable { 53 | String t = Try.ofFailable(() -> "hey") 54 | .recoverWith((x) -> 55 | Try.ofFailable(() -> "Jude") 56 | ).get(); 57 | assertEquals("hey", t); 58 | } 59 | 60 | @Test 61 | public void itShouldOrElseTryOnSuccess() throws Throwable { 62 | Try t = Try.ofFailable(() -> "hey").orElseTry(() -> "jude"); 63 | 64 | assertEquals("hey", t.get()); 65 | } 66 | 67 | @Test 68 | public void itShouldBeFailureOnFailure(){ 69 | Try t = Try.ofFailable(() -> { 70 | throw new Exception("e"); 71 | }); 72 | assertFalse(t.isSuccess()); 73 | } 74 | 75 | @Test(expected = IllegalArgumentException.class) 76 | public void itShouldThrowExceptionOnGetOfFailure() throws Throwable{ 77 | Try t = Try.ofFailable(() -> { 78 | throw new IllegalArgumentException("e"); 79 | }); 80 | t.get(); 81 | } 82 | 83 | @Test 84 | public void itShouldMapOnFailure(){ 85 | Try t = Try.ofFailable(() -> { 86 | throw new Exception("e"); 87 | }).map((x) -> "hey" + x); 88 | 89 | assertFalse(t.isSuccess()); 90 | } 91 | 92 | @Test 93 | public void itShouldFlatMapOnFailure(){ 94 | Try t = Try.ofFailable(() -> { 95 | throw new Exception("e"); 96 | }).flatMap((x) -> Try.ofFailable(() -> "hey")); 97 | 98 | assertFalse(t.isSuccess()); 99 | } 100 | 101 | @Test 102 | public void itShouldOrElseOnFailure() { 103 | String t = Try.ofFailable(() -> { 104 | throw new IllegalArgumentException("e"); 105 | }).orElse("jude"); 106 | 107 | assertEquals("jude", t); 108 | } 109 | 110 | @Test 111 | public void itShouldOrElseTryOnFailure() throws Throwable { 112 | Try t = Try.ofFailable(() -> { 113 | throw new IllegalArgumentException("e"); 114 | }).orElseTry(() -> "jude"); 115 | 116 | assertEquals("jude", t.get()); 117 | } 118 | 119 | @Test(expected = RuntimeException.class) 120 | public void itShouldGetAndThrowUncheckedException() throws Throwable { 121 | Try.ofFailable(() -> { 122 | throw new Exception(); 123 | }).getUnchecked(); 124 | 125 | } 126 | 127 | @Test 128 | public void itShouldGetValue() throws Throwable { 129 | final String result = Try.ofFailable(() -> "test").getUnchecked(); 130 | 131 | assertEquals("test", result); 132 | } 133 | 134 | @Test 135 | public void itShouldReturnRecoverValueWhenRecoveringOnFailure() { 136 | String t = Try.ofFailable(() -> "hey") 137 | .map((x) -> { 138 | throw new Exception("fail"); 139 | }) 140 | .recover((e) -> "jude"); 141 | assertEquals("jude", t); 142 | } 143 | 144 | 145 | @Test 146 | public void itShouldReturnValueWhenRecoveringWithOnFailure() throws Throwable { 147 | String t = Try.ofFailable(() -> { 148 | throw new Exception("oops"); 149 | }) 150 | .recoverWith((x) -> 151 | Try.ofFailable(() -> "Jude") 152 | ).get(); 153 | assertEquals("Jude", t); 154 | } 155 | 156 | @Test 157 | public void itShouldHandleComplexChaining() throws Throwable { 158 | Try.ofFailable(() -> "1").flatMap((x) -> Try.ofFailable(() -> Integer.valueOf(x))).recoverWith((t) -> Try.successful(1)); 159 | } 160 | 161 | @Test 162 | public void itShouldPassFailureIfPredicateIsFalse() throws Throwable { 163 | Try t1 = Try.ofFailable(() -> { 164 | throw new RuntimeException(); 165 | }).filter(o -> false); 166 | 167 | Try t2 = Try.ofFailable(() -> { 168 | throw new RuntimeException(); 169 | }).filter(o -> true); 170 | 171 | assertEquals(t1.isSuccess(), false); 172 | assertEquals(t2.isSuccess(), false); 173 | } 174 | 175 | @Test 176 | public void isShouldPassSuccessOnlyIfPredicateIsTrue() throws Throwable { 177 | Try t1 = Try.ofFailable(() -> "yo mama").filter(s -> s.length() > 0); 178 | Try t2 = Try.ofFailable(() -> "yo mama").filter(s -> s.length() < 0); 179 | 180 | assertEquals(t1.isSuccess(), true); 181 | assertEquals(t2.isSuccess(), false); 182 | } 183 | 184 | @Test 185 | public void itShouldReturnEmptyOptionalIfFailureOrNullSuccess() throws Throwable { 186 | Optional opt1 = Try.ofFailable(() -> { 187 | throw new IllegalArgumentException("Expected exception"); 188 | }).toOptional(); 189 | Optional opt2 = Try.ofFailable(() -> null).toOptional(); 190 | 191 | assertFalse(opt1.isPresent()); 192 | assertFalse(opt2.isPresent()); 193 | } 194 | 195 | @Test 196 | public void isShouldReturnTryValueWrappedInOptionalIfNonNullSuccess() throws Throwable { 197 | Optional opt1 = Try.ofFailable(() -> "yo mama").toOptional(); 198 | 199 | assertTrue(opt1.isPresent()); 200 | } 201 | 202 | @Test(expected = IllegalArgumentException.class) 203 | public void itShouldThrowExceptionFromTryConsumerOnSuccessIfSuccess() throws Throwable { 204 | Try t = Try.ofFailable(() -> "hey"); 205 | 206 | t.onSuccess(s -> { 207 | throw new IllegalArgumentException("Should be thrown."); 208 | }); 209 | } 210 | 211 | @Test 212 | public void itShouldNotThrowExceptionFromTryConsumerOnSuccessIfFailure() throws Throwable { 213 | Try t = Try.ofFailable(() -> { 214 | throw new IllegalArgumentException("Expected exception"); 215 | }); 216 | 217 | t.onSuccess(s -> { 218 | throw new IllegalArgumentException("Should NOT be thrown."); 219 | }); 220 | } 221 | 222 | @Test 223 | public void itShouldNotThrowExceptionFromTryConsumerOnFailureIfSuccess() throws Throwable { 224 | Try t = Try.ofFailable(() -> "hey"); 225 | 226 | t.onFailure(s -> { 227 | throw new IllegalArgumentException("Should NOT be thrown."); 228 | }); 229 | } 230 | 231 | @Test(expected = IllegalArgumentException.class) 232 | public void itShouldThrowExceptionFromTryConsumerOnFailureIfFailure() throws Throwable { 233 | Try t = Try.ofFailable(() -> { 234 | throw new IllegalArgumentException("Expected exception"); 235 | }); 236 | 237 | t.onFailure(s -> { 238 | throw new IllegalArgumentException("Should be thrown."); 239 | }); 240 | } 241 | 242 | @Test(expected = IllegalArgumentException.class) 243 | public void itShouldThrowNewExceptionWhenInvokingOrElseThrowOnFailure() throws Throwable { 244 | Try t = Try.ofFailable(() -> { 245 | throw new Exception("Oops"); 246 | }); 247 | 248 | t.orElseThrow(() -> { 249 | throw new IllegalArgumentException("Should be thrown."); 250 | }); 251 | } 252 | 253 | public void itShouldNotThrowNewExceptionWhenInvokingOrElseThrowOnSuccess() throws Throwable { 254 | Try t = Try.ofFailable(() -> "Ok"); 255 | 256 | String result = t.orElseThrow(() -> { 257 | throw new IllegalArgumentException("Should be thrown."); 258 | }); 259 | 260 | assertEquals(result, "Ok"); 261 | } 262 | 263 | 264 | } 265 | 266 | -------------------------------------------------------------------------------- /activator: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### ------------------------------- ### 4 | ### Helper methods for BASH scripts ### 5 | ### ------------------------------- ### 6 | 7 | realpath () { 8 | ( 9 | TARGET_FILE="$1" 10 | 11 | cd $(dirname "$TARGET_FILE") 12 | TARGET_FILE=$(basename "$TARGET_FILE") 13 | 14 | COUNT=0 15 | while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ] 16 | do 17 | TARGET_FILE=$(readlink "$TARGET_FILE") 18 | cd $(dirname "$TARGET_FILE") 19 | TARGET_FILE=$(basename "$TARGET_FILE") 20 | COUNT=$(($COUNT + 1)) 21 | done 22 | 23 | if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then 24 | cd "$TARGET_FILE" 25 | TARGET_FILEPATH= 26 | else 27 | TARGET_FILEPATH=/$TARGET_FILE 28 | fi 29 | 30 | # make sure we grab the actual windows path, instead of cygwin's path. 31 | if ! is_cygwin; then 32 | echo "$(pwd -P)/$TARGET_FILE" 33 | else 34 | echo $(cygwinpath "$(pwd -P)/$TARGET_FILE") 35 | fi 36 | ) 37 | } 38 | 39 | # TODO - Do we need to detect msys? 40 | 41 | # Uses uname to detect if we're in the odd cygwin environment. 42 | is_cygwin() { 43 | local os=$(uname -s) 44 | case "$os" in 45 | CYGWIN*) return 0 ;; 46 | *) return 1 ;; 47 | esac 48 | } 49 | 50 | # This can fix cygwin style /cygdrive paths so we get the 51 | # windows style paths. 52 | cygwinpath() { 53 | local file="$1" 54 | if is_cygwin; then 55 | echo $(cygpath -w $file) 56 | else 57 | echo $file 58 | fi 59 | } 60 | 61 | # Make something URI friendly 62 | make_url() { 63 | url="$1" 64 | local nospaces=${url// /%20} 65 | if is_cygwin; then 66 | echo "/${nospaces//\\//}" 67 | else 68 | echo "$nospaces" 69 | fi 70 | } 71 | 72 | # Detect if we should use JAVA_HOME or just try PATH. 73 | get_java_cmd() { 74 | if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then 75 | echo "$JAVA_HOME/bin/java" 76 | else 77 | echo "java" 78 | fi 79 | } 80 | 81 | echoerr () { 82 | echo 1>&2 "$@" 83 | } 84 | vlog () { 85 | [[ $verbose || $debug ]] && echoerr "$@" 86 | } 87 | dlog () { 88 | [[ $debug ]] && echoerr "$@" 89 | } 90 | execRunner () { 91 | # print the arguments one to a line, quoting any containing spaces 92 | [[ $verbose || $debug ]] && echo "# Executing command line:" && { 93 | for arg; do 94 | if printf "%s\n" "$arg" | grep -q ' '; then 95 | printf "\"%s\"\n" "$arg" 96 | else 97 | printf "%s\n" "$arg" 98 | fi 99 | done 100 | echo "" 101 | } 102 | 103 | exec "$@" 104 | } 105 | addJava () { 106 | dlog "[addJava] arg = '$1'" 107 | java_args=( "${java_args[@]}" "$1" ) 108 | } 109 | addApp () { 110 | dlog "[addApp] arg = '$1'" 111 | sbt_commands=( "${app_commands[@]}" "$1" ) 112 | } 113 | addResidual () { 114 | dlog "[residual] arg = '$1'" 115 | residual_args=( "${residual_args[@]}" "$1" ) 116 | } 117 | addDebugger () { 118 | addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" 119 | } 120 | addConfigOpts () { 121 | dlog "[addConfigOpts] arg = '$*'" 122 | for item in $* 123 | do 124 | addJava "$item" 125 | done 126 | } 127 | # a ham-fisted attempt to move some memory settings in concert 128 | # so they need not be messed around with individually. 129 | get_mem_opts () { 130 | local mem=${1:-1024} 131 | local meta=$(( $mem / 4 )) 132 | (( $meta > 256 )) || meta=256 133 | (( $meta < 1024 )) || meta=1024 134 | 135 | # default is to set memory options but this can be overridden by code section below 136 | memopts="-Xms${mem}m -Xmx${mem}m" 137 | if [[ "${java_version}" > "1.8" ]]; then 138 | extmemopts="-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=${meta}m" 139 | else 140 | extmemopts="-XX:PermSize=64m -XX:MaxPermSize=${meta}m" 141 | fi 142 | 143 | if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]] || [[ "${java_opts}" == *-XX:MaxMetaspaceSize* ]]; then 144 | # if we detect any of these settings in ${java_opts} we need to NOT output our settings. 145 | # The reason is the Xms/Xmx, if they don't line up, cause errors. 146 | memopts="" 147 | extmemopts="" 148 | fi 149 | 150 | echo "${memopts} ${extmemopts}" 151 | } 152 | require_arg () { 153 | local type="$1" 154 | local opt="$2" 155 | local arg="$3" 156 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then 157 | die "$opt requires <$type> argument" 158 | fi 159 | } 160 | require_arg () { 161 | local type="$1" 162 | local opt="$2" 163 | local arg="$3" 164 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then 165 | die "$opt requires <$type> argument" 166 | fi 167 | } 168 | is_function_defined() { 169 | declare -f "$1" > /dev/null 170 | } 171 | 172 | # If we're *not* running in a terminal, and we don't have any arguments, then we need to add the 'ui' parameter 173 | detect_terminal_for_ui() { 174 | [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && { 175 | addResidual "ui" 176 | } 177 | # SPECIAL TEST FOR MAC 178 | [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && { 179 | echo "Detected MAC OSX launched script...." 180 | echo "Swapping to UI" 181 | addResidual "ui" 182 | } 183 | } 184 | 185 | # Processes incoming arguments and places them in appropriate global variables. called by the run method. 186 | process_args () { 187 | while [[ $# -gt 0 ]]; do 188 | case "$1" in 189 | -h|-help) usage; exit 1 ;; 190 | -v|-verbose) verbose=1 && shift ;; 191 | -d|-debug) debug=1 && shift ;; 192 | -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;; 193 | -jvm-debug) 194 | if echo "$2" | grep -E ^[0-9]+$ > /dev/null; then 195 | addDebugger "$2" && shift 196 | else 197 | addDebugger 9999 198 | fi 199 | shift ;; 200 | -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;; 201 | -D*) addJava "$1" && shift ;; 202 | -J*) addJava "${1:2}" && shift ;; 203 | *) addResidual "$1" && shift ;; 204 | esac 205 | done 206 | 207 | is_function_defined process_my_args && { 208 | myargs=("${residual_args[@]}") 209 | residual_args=() 210 | process_my_args "${myargs[@]}" 211 | } 212 | } 213 | 214 | # Actually runs the script. 215 | run() { 216 | # TODO - check for sane environment 217 | 218 | # process the combined args, then reset "$@" to the residuals 219 | process_args "$@" 220 | detect_terminal_for_ui 221 | set -- "${residual_args[@]}" 222 | argumentCount=$# 223 | 224 | #check for jline terminal fixes on cygwin 225 | if is_cygwin; then 226 | stty -icanon min 1 -echo > /dev/null 2>&1 227 | addJava "-Djline.terminal=jline.UnixTerminal" 228 | addJava "-Dsbt.cygwin=true" 229 | fi 230 | 231 | # run sbt 232 | execRunner "$java_cmd" \ 233 | "-Dactivator.home=$(make_url "$activator_home")" \ 234 | $(get_mem_opts $app_mem) \ 235 | ${java_opts[@]} \ 236 | ${java_args[@]} \ 237 | -jar "$app_launcher" \ 238 | "${app_commands[@]}" \ 239 | "${residual_args[@]}" 240 | 241 | local exit_code=$? 242 | if is_cygwin; then 243 | stty icanon echo > /dev/null 2>&1 244 | fi 245 | exit $exit_code 246 | } 247 | 248 | # Loads a configuration file full of default command line options for this script. 249 | loadConfigFile() { 250 | cat "$1" | sed '/^\#/d' 251 | } 252 | 253 | ### ------------------------------- ### 254 | ### Start of customized settings ### 255 | ### ------------------------------- ### 256 | usage() { 257 | cat < [options] 259 | 260 | Command: 261 | ui Start the Activator UI 262 | new [name] [template-id] Create a new project with [name] using template [template-id] 263 | list-templates Print all available template names 264 | -h | -help Print this message 265 | 266 | Options: 267 | -v | -verbose Make this runner chattier 268 | -d | -debug Set sbt log level to debug 269 | -mem Set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem)) 270 | -jvm-debug Turn on JVM debugging, open at the given port. 271 | 272 | # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) 273 | -java-home Alternate JAVA_HOME 274 | 275 | # jvm options and output control 276 | -Dkey=val Pass -Dkey=val directly to the java runtime 277 | -J-X Pass option -X directly to the java runtime 278 | (-J is stripped) 279 | 280 | # environment variables (read from context) 281 | JAVA_OPTS Environment variable, if unset uses "" 282 | SBT_OPTS Environment variable, if unset uses "" 283 | ACTIVATOR_OPTS Environment variable, if unset uses "" 284 | 285 | In the case of duplicated or conflicting options, the order above 286 | shows precedence: environment variables lowest, command line options highest. 287 | EOM 288 | } 289 | 290 | ### ------------------------------- ### 291 | ### Main script ### 292 | ### ------------------------------- ### 293 | 294 | declare -a residual_args 295 | declare -a java_args 296 | declare -a app_commands 297 | declare -r real_script_path="$(realpath "$0")" 298 | declare -r activator_home="$(realpath "$(dirname "$real_script_path")")" 299 | declare -r app_version="1.2.10" 300 | 301 | declare -r app_launcher="${activator_home}/activator-launch-${app_version}.jar" 302 | declare -r script_name=activator 303 | declare -r java_cmd=$(get_java_cmd) 304 | declare -r java_opts=( "${ACTIVATOR_OPTS[@]}" "${SBT_OPTS[@]}" "${JAVA_OPTS[@]}" "${java_opts[@]}" ) 305 | userhome="$HOME" 306 | if is_cygwin; then 307 | # cygwin sets home to something f-d up, set to real windows homedir 308 | userhome="$USERPROFILE" 309 | fi 310 | declare -r activator_user_home_dir="${userhome}/.activator" 311 | declare -r java_opts_config_home="${activator_user_home_dir}/activatorconfig.txt" 312 | declare -r java_opts_config_version="${activator_user_home_dir}/${app_version}/activatorconfig.txt" 313 | 314 | # Now check to see if it's a good enough version 315 | declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}') 316 | if [[ "$java_version" == "" ]]; then 317 | echo 318 | echo No java installations was detected. 319 | echo Please go to http://www.java.com/getjava/ and download 320 | echo 321 | exit 1 322 | elif [[ ! "$java_version" > "1.6" ]]; then 323 | echo 324 | echo The java installation you have is not up to date 325 | echo Activator requires at least version 1.6+, you have 326 | echo version $java_version 327 | echo 328 | echo Please go to http://www.java.com/getjava/ and download 329 | echo a valid Java Runtime and install before running Activator. 330 | echo 331 | exit 1 332 | fi 333 | 334 | # if configuration files exist, prepend their contents to the java args so it can be processed by this runner 335 | # a "versioned" config trumps one on the top level 336 | if [[ -f "$java_opts_config_version" ]]; then 337 | addConfigOpts $(loadConfigFile "$java_opts_config_version") 338 | elif [[ -f "$java_opts_config_home" ]]; then 339 | addConfigOpts $(loadConfigFile "$java_opts_config_home") 340 | fi 341 | 342 | run "$@" 343 | -------------------------------------------------------------------------------- /src/main/java/com/jasongoodwin/monads/Try.java: -------------------------------------------------------------------------------- 1 | package com.jasongoodwin.monads; 2 | 3 | import java.util.NoSuchElementException; 4 | import java.util.Objects; 5 | import java.util.Optional; 6 | import java.util.function.Function; 7 | import java.util.function.Predicate; 8 | import java.util.function.Supplier; 9 | 10 | /** 11 | * Monadic Try type. 12 | * Represents a result type that could have succeeded with type T or failed with a Throwable. 13 | * Originally was Exception but due to seeing issues with eg play with checked Throwable, 14 | * And also seeing that Scala deals with throwable, 15 | * I made the decision to change it to use Throwable. 16 | * 17 | * @param 18 | */ 19 | 20 | public abstract class Try { 21 | 22 | protected Try() { 23 | } 24 | 25 | public static Try ofFailable(TrySupplier f) { 26 | Objects.requireNonNull(f); 27 | 28 | try { 29 | return Try.successful(f.get()); 30 | } catch (Throwable t) { 31 | return Try.failure(t); 32 | } 33 | } 34 | 35 | /** 36 | * Transform success or pass on failure. 37 | * Takes an optional type parameter of the new type. 38 | * You need to be specific about the new type if changing type 39 | * 40 | * Try.ofFailable(() -> "1").<Integer>map((x) -> Integer.valueOf(x)) 41 | * 42 | * @param f function to apply to successful value. 43 | * @param new type (optional) 44 | * @return Success<U> or Failure<U> 45 | */ 46 | 47 | public abstract Try map(TryMapFunction f); 48 | 49 | /** 50 | * Transform success or pass on failure, taking a Try<U> as the result. 51 | * Takes an optional type parameter of the new type. 52 | * You need to be specific about the new type if changing type. 53 | * 54 | * Try.ofFailable(() -> "1").<Integer>flatMap((x) -> Try.ofFailable(() -> Integer.valueOf(x))) 55 | * returns Integer(1) 56 | * 57 | * @param f function to apply to successful value. 58 | * @param new type (optional) 59 | * @return new composed Try 60 | */ 61 | public abstract Try flatMap(TryMapFunction> f); 62 | 63 | /** 64 | * Specifies a result to use in case of failure. 65 | * Gives access to the exception which can be pattern matched on. 66 | * 67 | * Try.ofFailable(() -> "not a number") 68 | * .<Integer>flatMap((x) -> Try.ofFailable(() ->Integer.valueOf(x))) 69 | * .recover((t) -> 1) 70 | * returns Integer(1) 71 | * 72 | * @param f function to execute on successful result. 73 | * @return new composed Try 74 | */ 75 | 76 | public abstract T recover(Function f); 77 | 78 | /** 79 | * Try applying f(t) on the case of failure. 80 | * @param f function that takes throwable and returns result 81 | * @return a new Try in the case of failure, or the current Success. 82 | */ 83 | public abstract Try recoverWith(TryMapFunction> f); 84 | 85 | /** 86 | * Return a value in the case of a failure. 87 | * This is similar to recover but does not expose the exception type. 88 | * 89 | * @param value return the try's value or else the value specified. 90 | * @return new composed Try 91 | */ 92 | public abstract T orElse(T value); 93 | 94 | /** 95 | * Return another try in the case of failure. 96 | * Like recoverWith but without exposing the exception. 97 | * 98 | * @param f return the value or the value from the new try. 99 | * @return new composed Try 100 | */ 101 | public abstract Try orElseTry(TrySupplier f); 102 | 103 | /** 104 | * Gets the value T on Success or throws the cause of the failure. 105 | * 106 | * @return T 107 | * @throws Throwable produced by the supplier function argument 108 | */ 109 | 110 | public abstract T orElseThrow(Supplier exceptionSupplier) throws X; 111 | 112 | /** 113 | * Gets the value T on Success or throws the cause of the failure. 114 | * 115 | * @return T 116 | * @throws Throwable 117 | */ 118 | public abstract T get() throws Throwable; 119 | 120 | /** 121 | * Gets the value T on Success or throws the cause of the failure wrapped into a RuntimeException 122 | * @return T 123 | * @throws RuntimeException 124 | */ 125 | public abstract T getUnchecked(); 126 | 127 | public abstract boolean isSuccess(); 128 | 129 | /** 130 | * Performs the provided action, when successful 131 | * @param action action to run 132 | * @return new composed Try 133 | * @throws E if the action throws an exception 134 | */ 135 | public abstract Try onSuccess(TryConsumer action) throws E; 136 | 137 | /** 138 | * Performs the provided action, when failed 139 | * @param action action to run 140 | * @return new composed Try 141 | * @throws E if the action throws an exception 142 | */ 143 | public abstract Try onFailure(TryConsumer action) throws E; 144 | 145 | /** 146 | * If a Try is a Success and the predicate holds true, the Success is passed further. 147 | * Otherwise (Failure or predicate doesn't hold), pass Failure. 148 | * @param pred predicate applied to the value held by Try 149 | * @return For Success, the same success if predicate holds true, otherwise Failure 150 | */ 151 | public abstract Try filter(Predicate pred); 152 | 153 | /** 154 | * Try contents wrapped in Optional. 155 | * @return Optional of T, if Success, Empty if Failure or null value 156 | */ 157 | public abstract Optional toOptional(); 158 | 159 | /** 160 | * Factory method for failure. 161 | * 162 | * @param e throwable to create the failed Try with 163 | * @param Type 164 | * @return a new Failure 165 | */ 166 | 167 | public static Try failure(Throwable e) { 168 | return new Failure<>(e); 169 | } 170 | 171 | /** 172 | * Factory method for success. 173 | * 174 | * @param x value to create the successful Try with 175 | * @param Type 176 | * @return a new Success 177 | */ 178 | public static Try successful(U x) { 179 | return new Success<>(x); 180 | } 181 | } 182 | 183 | class Success extends Try { 184 | private final T value; 185 | 186 | public Success(T value) { 187 | this.value = value; 188 | } 189 | 190 | @Override 191 | public Try flatMap(TryMapFunction> f) { 192 | Objects.requireNonNull(f); 193 | try { 194 | return f.apply(value); 195 | } catch (Throwable t) { 196 | return Try.failure(t); 197 | } 198 | } 199 | 200 | @Override 201 | public T recover(Function f) { 202 | Objects.requireNonNull(f); 203 | return value; 204 | } 205 | 206 | @Override 207 | public Try recoverWith(TryMapFunction> f) { 208 | Objects.requireNonNull(f); 209 | return this; 210 | } 211 | 212 | @Override 213 | public T orElse(T value) { 214 | return this.value; 215 | } 216 | 217 | @Override 218 | public Try orElseTry(TrySupplier f) { 219 | Objects.requireNonNull(f); 220 | return this; 221 | } 222 | 223 | @Override 224 | public T orElseThrow(Supplier exceptionSupplier) throws X { 225 | return value; 226 | } 227 | 228 | @Override 229 | public T get() throws Throwable { 230 | return value; 231 | } 232 | 233 | @Override 234 | public T getUnchecked() { 235 | return value; 236 | } 237 | 238 | @Override 239 | public Try map(TryMapFunction f) { 240 | Objects.requireNonNull(f); 241 | try { 242 | return new Success<>(f.apply(value)); 243 | } catch (Throwable t) { 244 | return Try.failure(t); 245 | } 246 | } 247 | 248 | @Override 249 | public boolean isSuccess() { 250 | return true; 251 | } 252 | 253 | @Override 254 | public Try onSuccess(TryConsumer action) throws E { 255 | action.accept(value); 256 | return this; 257 | } 258 | 259 | @Override 260 | public Try filter(Predicate p) { 261 | Objects.requireNonNull(p); 262 | 263 | if (p.test(value)) { 264 | return this; 265 | } else { 266 | return Try.failure(new NoSuchElementException("Predicate does not match for " + value)); 267 | } 268 | } 269 | 270 | @Override 271 | public Optional toOptional() { 272 | return Optional.ofNullable(value); 273 | } 274 | 275 | @Override 276 | public Try onFailure(TryConsumer action) { 277 | return this; 278 | } 279 | } 280 | 281 | 282 | class Failure extends Try { 283 | private final Throwable e; 284 | 285 | Failure(Throwable e) { 286 | this.e = e; 287 | } 288 | 289 | @Override 290 | public Try map(TryMapFunction f) { 291 | Objects.requireNonNull(f); 292 | return Try.failure(e); 293 | } 294 | 295 | @Override 296 | public Try flatMap(TryMapFunction> f) { 297 | Objects.requireNonNull(f); 298 | return Try.failure(e); 299 | } 300 | 301 | @Override 302 | public T recover(Function f) { 303 | Objects.requireNonNull(f); 304 | return f.apply(e); 305 | } 306 | 307 | @Override 308 | public Try recoverWith(TryMapFunction> f) { 309 | Objects.requireNonNull(f); 310 | try{ 311 | return f.apply(e); 312 | }catch(Throwable t){ 313 | return Try.failure(t); 314 | } 315 | } 316 | 317 | @Override 318 | public T orElse(T value) { 319 | return value; 320 | } 321 | 322 | @Override 323 | public Try orElseTry(TrySupplier f) { 324 | Objects.requireNonNull(f); 325 | return Try.ofFailable(f); 326 | } 327 | 328 | @Override 329 | public T orElseThrow(Supplier exceptionSupplier) throws X { 330 | throw exceptionSupplier.get(); 331 | } 332 | 333 | @Override 334 | public T get() throws Throwable { 335 | throw e; 336 | } 337 | 338 | @Override 339 | public T getUnchecked() { 340 | throw new RuntimeException(e); 341 | } 342 | 343 | @Override 344 | public boolean isSuccess() { 345 | return false; 346 | } 347 | 348 | @Override 349 | public Try onSuccess(TryConsumer action) { 350 | return this; 351 | } 352 | 353 | @Override 354 | public Try filter(Predicate pred) { 355 | return this; 356 | } 357 | 358 | @Override 359 | public Optional toOptional() { 360 | return Optional.empty(); 361 | } 362 | 363 | @Override 364 | public Try onFailure(TryConsumer action) throws E { 365 | action.accept(e); 366 | return this; 367 | } 368 | } 369 | --------------------------------------------------------------------------------