Skip to content

Commit ff69de7

Browse files
ShriramKumarakshaisarma
authored andcommitted
Helper methods and a RandomPool utility class. (#4)
1 parent cf78d39 commit ff69de7

File tree

17 files changed

+305
-13
lines changed

17 files changed

+305
-13
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2016, Yahoo Inc.
3+
* Licensed under the terms of the Apache License, Version 2.0.
4+
* See the LICENSE file associated with the project for terms.
5+
*/
6+
package com.yahoo.bullet;
7+
8+
import java.util.List;
9+
import java.util.Random;
10+
11+
public class RandomPool<T> {
12+
private List<T> items;
13+
14+
private static final Random RANDOM = new Random();
15+
16+
/**
17+
* Constructor for the RandomPool that takes a list of items.
18+
* @param items A list of items to form the pool with.
19+
*/
20+
public RandomPool(List<T> items) {
21+
this.items = items;
22+
}
23+
24+
/**
25+
* Get a random item from the pool.
26+
*
27+
* @return a randomly chosen item from the pool.
28+
*/
29+
public T get() {
30+
if (items == null || items.isEmpty()) {
31+
return null;
32+
}
33+
return items.get(RANDOM.nextInt(items.size()));
34+
}
35+
36+
/**
37+
* Clear the RandomPool. Gets now return null.
38+
*/
39+
public void clear() {
40+
items = null;
41+
}
42+
43+
@Override
44+
public String toString() {
45+
return items == null ? null : items.toString();
46+
}
47+
48+
@Override
49+
public boolean equals(Object object) {
50+
if (this == object) {
51+
return true;
52+
}
53+
if (object == null) {
54+
return false;
55+
}
56+
if (!(object instanceof RandomPool)) {
57+
return false;
58+
}
59+
RandomPool asPool = (RandomPool) object;
60+
return items == null ? asPool.items == null : items.equals(asPool.items);
61+
}
62+
63+
@Override
64+
public int hashCode() {
65+
// Any number would do since we want RandomPools of null to be equal to each other.
66+
return items == null ? 42 : items.hashCode();
67+
}
68+
}

src/main/java/com/yahoo/bullet/pubsub/Metadata.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/*
2+
* Copyright 2017, Yahoo Inc.
3+
* Licensed under the terms of the Apache License, Version 2.0.
4+
* See the LICENSE file associated with the project for terms.
5+
*/
16
package com.yahoo.bullet.pubsub;
27

38
import lombok.AllArgsConstructor;

src/main/java/com/yahoo/bullet/pubsub/PubSub.java

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
1+
/*
2+
* Copyright 2017, Yahoo Inc.
3+
* Licensed under the terms of the Apache License, Version 2.0.
4+
* See the LICENSE file associated with the project for terms.
5+
*/
16
package com.yahoo.bullet.pubsub;
27

38
import com.yahoo.bullet.BulletConfig;
49

5-
import java.io.Serializable;
610
import java.lang.reflect.Constructor;
711
import java.util.List;
12+
import java.util.Objects;
813

914
/**
1015
* Notation: Partition is a unit of parallelism in the Pub/Sub queue.
1116
*
1217
* Implementations of PubSub should take in a {@link BulletConfig} and use the information to wire up and return
1318
* Publishers and Subscribers.
1419
*/
15-
public abstract class PubSub implements Serializable {
20+
public abstract class PubSub {
1621
/**
1722
* The context determines how the {@link Publisher} and {@link Subscriber} returned by PubSub behave. For example,
1823
* If the Context is {@link Context#QUERY_SUBMISSION}:
@@ -28,49 +33,60 @@ public enum Context {
2833
}
2934

3035
protected Context context;
36+
protected BulletConfig config;
3137

3238
/**
3339
* Instantiate a PubSub using parameters from {@link BulletConfig}.
3440
*
3541
* @param config The {@link BulletConfig} containing all required PubSub parameters.
42+
* @throws PubSubException if the context name is not present or cannot be parsed.
3643
*/
37-
public PubSub(BulletConfig config) {
38-
context = Context.valueOf(config.get(BulletConfig.PUBSUB_CONTEXT_NAME).toString());
44+
public PubSub(BulletConfig config) throws PubSubException {
45+
this.config = config;
46+
try {
47+
this.context = Context.valueOf(getRequiredConfig(String.class, BulletConfig.PUBSUB_CONTEXT_NAME));
48+
} catch (RuntimeException e) {
49+
throw new PubSubException("Cannot create PubSub", e);
50+
}
3951
}
4052

4153
/**
4254
* Get a {@link Publisher} instance wired to write to all allocated partitions in the appropriate queue (See
4355
* {@link PubSub#context}).
4456
*
4557
* @return {@link Publisher} wired as required.
58+
* @throws PubSubException if the Publisher could not be created.
4659
*/
47-
public abstract Publisher getPublisher();
60+
public abstract Publisher getPublisher() throws PubSubException;
4861

4962
/**
5063
* Get a list of n {@link Publisher} instances with the allocated partitions in the appropriate queue
5164
* (See {@link PubSub#context}) split as evenly as possible among them.
5265
*
5366
* @param n The number of Publishers requested.
5467
* @return The {@link List} of n Publishers wired as required.
68+
* @throws PubSubException if Publishers could not be created.
5569
*/
56-
public abstract List<Publisher> getPublishers(int n);
70+
public abstract List<Publisher> getPublishers(int n) throws PubSubException;
5771

5872
/**
5973
* Get a {@link Subscriber} instance wired to read from all allocated partitions in the appropriate queue (See
6074
* {@link PubSub#context}).
6175
*
6276
* @return {@link Subscriber} wired as required.
77+
* @throws PubSubException if the Subscriber could not be created.
6378
*/
64-
public abstract Subscriber getSubscriber();
79+
public abstract Subscriber getSubscriber() throws PubSubException;
6580

6681
/**
6782
* Get a list of n {@link Subscriber} instances with allocated partitions from the appropriate queue
6883
* (See {@link PubSub#context}) split as evenly as possible among them.
6984
*
7085
* @param n The number of Subscribers requested.
7186
* @return The {@link List} of n Subscribers wired as required.
87+
* @throws PubSubException if Subscribers could not be created.
7288
*/
73-
public abstract List<Subscriber> getSubscribers(int n);
89+
public abstract List<Subscriber> getSubscribers(int n) throws PubSubException;
7490

7591
/**
7692
* Create a PubSub instance using the class specified in the config file.
@@ -89,4 +105,21 @@ public static PubSub from(BulletConfig config) throws PubSubException {
89105
throw new PubSubException("Cannot create PubSub instance.", e);
90106
}
91107
}
108+
109+
/**
110+
* A method to get a required configuration of a particular type.
111+
*
112+
* @param name The name of the required configuration.
113+
* @param tClass The class of the required configuration.
114+
* @param <T> The type to cast the configuration to. Inferred from tClass.
115+
* @return The extracted configuration of type T.
116+
* @throws PubSubException if the configuration is missing or cannot be cast to type T.
117+
*/
118+
public <T> T getRequiredConfig(Class<T> tClass, String name) throws PubSubException {
119+
try {
120+
return (T) Objects.requireNonNull(config.get(name));
121+
} catch (Exception e) {
122+
throw PubSubException.forArgument(name, e);
123+
}
124+
}
92125
}

src/main/java/com/yahoo/bullet/pubsub/PubSubException.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/*
2+
* Copyright 2017, Yahoo Inc.
3+
* Licensed under the terms of the Apache License, Version 2.0.
4+
* See the LICENSE file associated with the project for terms.
5+
*/
16
package com.yahoo.bullet.pubsub;
27

38
/**
@@ -22,4 +27,16 @@ public PubSubException(String message) {
2227
public PubSubException(String message, Throwable cause) {
2328
super(message, cause);
2429
}
30+
31+
/**
32+
* Method to create a PubSubException when a required argument could not be read.
33+
*
34+
* @param name The name of the argument that could not be read.
35+
* @param cause The optional {@link Throwable} that caused the exception.
36+
* @return A PubSubException indicating failure to read a required argument.
37+
*/
38+
public static PubSubException forArgument(String name, Throwable cause) {
39+
String message = "Could not read required argument: " + name;
40+
return cause == null ? new PubSubException(message) : new PubSubException(message, cause);
41+
}
2542
}

src/main/java/com/yahoo/bullet/pubsub/PubSubMessage.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/*
2+
* Copyright 2017, Yahoo Inc.
3+
* Licensed under the terms of the Apache License, Version 2.0.
4+
* See the LICENSE file associated with the project for terms.
5+
*/
16
package com.yahoo.bullet.pubsub;
27

38
import com.yahoo.bullet.pubsub.Metadata.Signal;
@@ -13,6 +18,8 @@
1318
*/
1419
@Getter
1520
public class PubSubMessage implements Serializable {
21+
private static final long serialVersionUID = 2407848310969237888L;
22+
1623
private String id;
1724
private int sequence;
1825
private String content;

src/main/java/com/yahoo/bullet/pubsub/Publisher.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/*
2+
* Copyright 2017, Yahoo Inc.
3+
* Licensed under the terms of the Apache License, Version 2.0.
4+
* See the LICENSE file associated with the project for terms.
5+
*/
16
package com.yahoo.bullet.pubsub;
27

38
public interface Publisher {

src/main/java/com/yahoo/bullet/pubsub/Subscriber.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/*
2+
* Copyright 2017, Yahoo Inc.
3+
* Licensed under the terms of the Apache License, Version 2.0.
4+
* See the LICENSE file associated with the project for terms.
5+
*/
16
package com.yahoo.bullet.pubsub;
27

38
public interface Subscriber {

src/main/resources/checkstyle.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
<property name="severity" value="error"/>
1515
</module>
1616

17+
<!--Checks that all files have a copyright header. -->
18+
<module name="Header">
19+
<property name="headerFile" value="src/main/resources/copyright.txt"/>
20+
<property name="ignoreLines" value="2"/>
21+
<property name="fileExtensions" value="java"/>
22+
</module>
23+
1724
<!-- Check each Java file for violations. -->
1825
<module name="TreeWalker">
1926

src/main/resources/copyright.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*
2+
* Copyright 2017, Yahoo Inc.
3+
* Licensed under the terms of the Apache License, Version 2.0.
4+
* See the LICENSE file associated with the project for terms.
5+
*/
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2016, Yahoo Inc.
3+
* Licensed under the terms of the Apache License, Version 2.0.
4+
* See the LICENSE file associated with the project for terms.
5+
*/
6+
package com.yahoo.bullet;
7+
8+
import org.testng.Assert;
9+
import org.testng.annotations.Test;
10+
11+
import java.util.Arrays;
12+
import java.util.Collections;
13+
import java.util.List;
14+
import java.util.Map;
15+
import java.util.function.Function;
16+
import java.util.stream.Collectors;
17+
18+
public class RandomPoolTest {
19+
@Test
20+
public void testDefaultOverrides() {
21+
RandomPool<String> poolA = new RandomPool<>(null);
22+
RandomPool<String> poolB = new RandomPool<>(null);
23+
Assert.assertTrue(poolA.equals(poolA));
24+
Assert.assertEquals(poolA.hashCode(), poolA.hashCode());
25+
26+
Assert.assertFalse(poolA.equals(null));
27+
Assert.assertFalse(poolA.equals("foo"));
28+
29+
Assert.assertTrue(poolA.equals(poolB));
30+
Assert.assertEquals(poolA.hashCode(), poolB.hashCode());
31+
32+
poolA = new RandomPool<>(Collections.singletonList("foo"));
33+
poolB = new RandomPool<>(Collections.singletonList("foo"));
34+
Assert.assertTrue(poolA.equals(poolB));
35+
Assert.assertEquals(poolA.hashCode(), poolB.hashCode());
36+
37+
poolB = new RandomPool<>(Collections.singletonList("bar"));
38+
Assert.assertFalse(poolA.equals(poolB));
39+
40+
List<String> contents = Collections.singletonList("foo");
41+
poolA = new RandomPool<>(contents);
42+
poolB = new RandomPool<>(contents);
43+
Assert.assertTrue(poolA.equals(poolB));
44+
Assert.assertEquals(poolA.hashCode(), poolB.hashCode());
45+
}
46+
47+
@Test
48+
public void testToString() {
49+
RandomPool<String> pool = new RandomPool<>(null);
50+
Assert.assertNull(pool.toString());
51+
pool = new RandomPool<>(Collections.singletonList("foo"));
52+
Assert.assertEquals(pool.toString(), Collections.singletonList("foo").toString());
53+
}
54+
55+
@Test
56+
public void testEmptyCase() {
57+
RandomPool<Integer> pool = new RandomPool<>(null);
58+
Assert.assertNull(pool.get());
59+
pool = new RandomPool<>(Collections.emptyList());
60+
Assert.assertNull(pool.get());
61+
}
62+
63+
@Test
64+
public void testRandomGet() {
65+
List<Integer> list = Arrays.asList(1, 3, 4);
66+
Map<Integer, Integer> map = list.stream().collect(Collectors.toMap(Function.identity(), x -> 0));
67+
RandomPool<Integer> pool = new RandomPool<>(list);
68+
for (int i = 0; i < 1000; ++i) {
69+
int item = pool.get();
70+
map.put(item, map.get(item) + 1);
71+
}
72+
// That this is false is 1 - (2/3)^1000
73+
Assert.assertTrue(map.values().stream().allMatch(v -> v > 0));
74+
}
75+
76+
@Test
77+
public void testGetReturnsNullAfterClear() {
78+
List<Integer> list = Arrays.asList(1, 3, 4);
79+
RandomPool<Integer> pool = new RandomPool<>(list);
80+
pool.clear();
81+
Assert.assertNull(pool.get());
82+
}
83+
}

0 commit comments

Comments
 (0)