Skip to content

Commit 99de19d

Browse files
committed
Use custom API for attributes
- replace service map with attributes - replace locals with attributes - introduce registry as bridge interface for DI provider
1 parent 82ad75c commit 99de19d

File tree

18 files changed

+270
-110
lines changed

18 files changed

+270
-110
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*
14+
* Copyright 2014 Edgar Espina
15+
*/
16+
package io.jooby;
17+
18+
import javax.annotation.Nonnull;
19+
import javax.annotation.Nullable;
20+
import java.util.Objects;
21+
22+
public class AttributeKey<T> {
23+
24+
private final Class<T> type;
25+
private final String name;
26+
private final int hashCode;
27+
28+
public AttributeKey(@Nonnull Class<T> type, @Nonnull String name) {
29+
this.type = type;
30+
this.name = name;
31+
this.hashCode = type.hashCode() * 31 + name.hashCode();
32+
}
33+
34+
public AttributeKey(@Nonnull Class<T> type) {
35+
this.type = type;
36+
this.name = null;
37+
this.hashCode = type.hashCode();
38+
}
39+
40+
public @Nullable String getName() {
41+
return name;
42+
}
43+
44+
public @Nonnull Class<T> getType() {
45+
return type;
46+
}
47+
48+
@Override public int hashCode() {
49+
return hashCode;
50+
}
51+
52+
@Override public boolean equals(Object obj) {
53+
if (this == obj) {
54+
return true;
55+
}
56+
if (obj instanceof AttributeKey) {
57+
AttributeKey that = (AttributeKey) obj;
58+
return Objects.equals(name, that.name) && type.equals(that.type);
59+
}
60+
return false;
61+
}
62+
63+
@Override public String toString() {
64+
StringBuilder string = new StringBuilder();
65+
string.append(type.getTypeName());
66+
if (name != null) {
67+
string.append(".").append(name);
68+
}
69+
return string.toString();
70+
}
71+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*
14+
* Copyright 2014 Edgar Espina
15+
*/
16+
package io.jooby;
17+
18+
import javax.annotation.Nonnull;
19+
import javax.annotation.Nullable;
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
import java.util.NoSuchElementException;
23+
import java.util.Set;
24+
import java.util.function.Function;
25+
26+
public class AttributeMap {
27+
28+
private Map<AttributeKey, Object> map;
29+
30+
public AttributeMap(Map<AttributeKey, Object> map) {
31+
this.map = map;
32+
}
33+
34+
public AttributeMap() {
35+
this(new HashMap<>());
36+
}
37+
38+
public boolean contains(@Nonnull AttributeKey<?> key) {
39+
return map.containsKey(key);
40+
}
41+
42+
public @Nullable <V> V computeIfAbsent(@Nonnull AttributeKey<V> key,
43+
@Nonnull Throwing.Function<AttributeKey<V>, V> mappingFunction) {
44+
Function fn = mappingFunction;
45+
return (V) map.computeIfAbsent(key, fn);
46+
}
47+
48+
public @Nonnull <V> V get(@Nonnull AttributeKey<V> key) {
49+
V value = (V) map.get(key);
50+
if (value == null) {
51+
throw new NoSuchElementException(key.toString());
52+
}
53+
return value;
54+
}
55+
56+
public @Nullable <V> V getOrNull(@Nonnull AttributeKey<V> key) {
57+
return (V) map.get(key);
58+
}
59+
60+
public @Nonnull <V> V getOrDefault(@Nonnull AttributeKey<V> key, @Nonnull V value) {
61+
return (V) map.getOrDefault(key, value);
62+
}
63+
64+
public @Nullable <V> V put(@Nonnull AttributeKey<V> key, @Nonnull V value) {
65+
return (V) map.put(key, value);
66+
}
67+
68+
public @Nullable <V> V putIfAbsent(@Nonnull AttributeKey<V> key, @Nonnull V value) {
69+
return (V) map.putIfAbsent(key, value);
70+
}
71+
72+
public @Nullable <V> V remove(@Nonnull AttributeKey<V> key) {
73+
return (V) map.remove(key);
74+
}
75+
76+
public @Nonnull Set<AttributeKey> keySet() {
77+
return map.keySet();
78+
}
79+
80+
public Map<String, Object> toMap() {
81+
Map<String, Object> result = new HashMap<>();
82+
for (Map.Entry<AttributeKey, Object> entry : map.entrySet()) {
83+
AttributeKey key = entry.getKey();
84+
if (key.getName() != null) {
85+
result.put(key.getName(), entry.getValue());
86+
}
87+
}
88+
return result;
89+
}
90+
}

jooby/src/main/java/io/jooby/Context.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ public interface Context {
6060
* **********************************************************************************************
6161
*/
6262

63+
@Nonnull AttributeMap attributes();
64+
6365
@Nonnull Router router();
6466

6567
/**
@@ -310,12 +312,6 @@ default long contentLength() {
310312

311313
@Nonnull Context detach(@Nonnull Runnable action);
312314

313-
@Nullable <T> T attribute(@Nonnull String name);
314-
315-
@Nonnull Context attribute(@Nonnull String name, @Nonnull Object value);
316-
317-
@Nonnull Map<String, Object> attributes();
318-
319315
/**
320316
* **********************************************************************************************
321317
* **** Response methods *************************************************************************

jooby/src/main/java/io/jooby/Jooby.java

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.util.LinkedList;
3232
import java.util.List;
3333
import java.util.Map;
34+
import java.util.NoSuchElementException;
3435
import java.util.ServiceLoader;
3536
import java.util.Spliterator;
3637
import java.util.concurrent.Executor;
@@ -44,7 +45,7 @@
4445
import static java.util.Spliterators.spliteratorUnknownSize;
4546
import static java.util.stream.StreamSupport.stream;
4647

47-
public class Jooby implements Router {
48+
public class Jooby implements Router, Registry {
4849

4950
private RouterImpl router;
5051

@@ -60,8 +61,6 @@ public class Jooby implements Router {
6061

6162
private LinkedList<Throwing.Runnable> stopCallbacks;
6263

63-
private Map<Object, Object> services = new HashMap<>();
64-
6564
/**
6665
* Not ideal but useful. We want to have access to environment properties from instance
6766
* initializer. So external method before creating a new Jooby instance does a call to
@@ -72,6 +71,8 @@ public class Jooby implements Router {
7271

7372
protected Env environment;
7473

74+
private Registry registry;
75+
7576
public Jooby() {
7677
router = new RouterImpl(new RouteAnalyzer(getClass().getClassLoader(), false));
7778
environment = ENV.get();
@@ -286,50 +287,36 @@ public Jooby errorCode(@Nonnull Class<? extends Throwable> type,
286287
return this;
287288
}
288289

289-
public @Nonnull <T> T require(@Nonnull Class<T> type) {
290-
return findService(type, type.getName());
290+
@Nonnull @Override public AttributeMap attributes() {
291+
return router.attributes();
291292
}
292293

293294
public @Nonnull <T> T require(@Nonnull Class<T> type, @Nonnull String name) {
294-
return findService(type, type.getName() + "." + name);
295-
}
296-
297-
private @Nonnull <T> T findService(@Nonnull Class<T> type, @Nonnull String key) {
298-
Object service = services.get(key);
299-
if (service == null) {
300-
throw new IllegalStateException("Service not found: " + type);
301-
}
302-
return type.cast(service);
303-
}
304-
305-
public @Nonnull <T> Jooby addService(@Nonnull Class<T> type, @Nonnull T service) {
306-
putService(type, null, service);
307-
return this;
308-
}
309-
310-
public @Nonnull <T> Jooby addService(@Nonnull T service) {
311-
putService(service.getClass(), null, service);
312-
return this;
295+
return require(new AttributeKey<>(type, name));
313296
}
314297

315-
public @Nonnull <T> Jooby addService(@Nonnull String name, @Nonnull T service) {
316-
putService(service.getClass(), name, service);
317-
return this;
298+
public @Nonnull <T> T require(@Nonnull Class<T> type) {
299+
return require(new AttributeKey<>(type));
318300
}
319301

320-
public @Nonnull <T> Jooby addService(@Nonnull Class<T> type, @Nonnull String name, @Nonnull T service) {
321-
putService(type, name, service);
302+
public @Nonnull Jooby registry(@Nonnull Registry registry) {
303+
this.registry = registry;
322304
return this;
323305
}
324306

325-
private void putService(@Nonnull Class type, String name, @Nonnull Object service) {
326-
String defkey = type.getName();
327-
String key = type.getName();
328-
if (name != null) {
329-
key += "." + name;
307+
private <T> T require(AttributeKey<T> key) {
308+
AttributeMap attributes = attributes();
309+
if (attributes.contains(key)) {
310+
return attributes.get(key);
311+
}
312+
if (registry != null) {
313+
String name = key.getName();
314+
if (name == null) {
315+
return registry.require(key.getType());
316+
}
317+
return registry.require(key.getType(), name);
330318
}
331-
services.put(key, service);
332-
services.putIfAbsent(defkey, service);
319+
throw new NoSuchElementException(key.toString());
333320
}
334321

335322
/** Boot: */
@@ -420,7 +407,8 @@ public static void run(@Nonnull Supplier<Jooby> provider, String... args) {
420407
run(provider, ExecutionMode.DEFAULT, args);
421408
}
422409

423-
public static void run(@Nonnull Supplier<Jooby> provider, @Nonnull ExecutionMode mode, String... args) {
410+
public static void run(@Nonnull Supplier<Jooby> provider, @Nonnull ExecutionMode mode,
411+
String... args) {
424412
Server server;
425413
try {
426414
Env environment = Env.defaultEnvironment(args);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*
14+
* Copyright 2014 Edgar Espina
15+
*/
16+
package io.jooby;
17+
18+
import javax.annotation.Nonnull;
19+
20+
public interface Registry {
21+
@Nonnull <T> T require(@Nonnull Class<T> type);
22+
23+
@Nonnull <T> T require(@Nonnull Class<T> type, @Nonnull String name);
24+
}

jooby/src/main/java/io/jooby/Router.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ interface Match {
5757
List<String> METHODS = unmodifiableList(
5858
asList(GET, POST, PUT, DELETE, PATCH, HEAD, CONNECT, OPTIONS, TRACE));
5959

60+
@Nonnull AttributeMap attributes();
61+
6062
@Nonnull Router caseSensitive(boolean caseSensitive);
6163

6264
@Nonnull Router ignoreTrailingSlash(boolean ignoreTrailingSlash);

jooby/src/main/java/io/jooby/Throwing.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public interface Consumer<V> extends java.util.function.Consumer<V> {
149149
* @param <V2> Input type.
150150
*/
151151
@FunctionalInterface
152-
public interface Consumer2<V1, V2> {
152+
public interface Consumer2<V1, V2> extends java.util.function.BiConsumer<V1, V2> {
153153
/**
154154
* Performs this operation on the given argument.
155155
*

jooby/src/main/java/io/jooby/internal/RouterImpl.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package io.jooby.internal;
1717

18+
import io.jooby.AttributeMap;
1819
import io.jooby.ErrorHandler;
1920
import io.jooby.Jooby;
2021
import io.jooby.Context;
@@ -39,6 +40,7 @@
3940
import java.util.List;
4041
import java.util.Map;
4142
import java.util.NoSuchElementException;
43+
import java.util.concurrent.ConcurrentHashMap;
4244
import java.util.concurrent.Executor;
4345
import java.util.function.Predicate;
4446
import java.util.stream.Collectors;
@@ -125,11 +127,17 @@ public Stack executor(Executor executor) {
125127

126128
private Map<String, Parser> parsers = new HashMap<>();
127129

130+
private AttributeMap attributes = new AttributeMap(new ConcurrentHashMap<>());
131+
128132
public RouterImpl(RouteAnalyzer analyzer) {
129133
this.analyzer = analyzer;
130134
stack.addLast(new Stack(""));
131135
}
132136

137+
@Nonnull @Override public AttributeMap attributes() {
138+
return attributes;
139+
}
140+
133141
@Nonnull @Override public Router caseSensitive(boolean caseSensitive) {
134142
this.caseSensitive = caseSensitive;
135143
chi.setCaseSensitive(caseSensitive);

0 commit comments

Comments
 (0)