Thomas Vachuska
Committed by Ray Milkey

Adding ability to easily create name thread factories with threads belonging to …

…hierarchical thread groups.

Change-Id: Iaab3251c13e14b73c54a8edc945f5aa476a7ca54
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.util;
17 +
18 +import org.apache.commons.lang3.concurrent.ConcurrentUtils;
19 +
20 +import java.util.concurrent.ConcurrentHashMap;
21 +import java.util.concurrent.ThreadFactory;
22 +
23 +import static com.google.common.base.MoreObjects.toStringHelper;
24 +
25 +/**
26 + * Thread factory for creating threads that belong to the specified thread group.
27 + */
28 +public final class GroupedThreadFactory implements ThreadFactory {
29 +
30 + public static final String DELIMITER = "/";
31 +
32 + private final ThreadGroup group;
33 +
34 + // Cache of created thread factories.
35 + private static final ConcurrentHashMap<String, GroupedThreadFactory> FACTORIES =
36 + new ConcurrentHashMap<>();
37 +
38 + /**
39 + * Returns thread factory for producing threads associated with the specified
40 + * group name. The group name-space is hierarchical, based on slash-delimited
41 + * name segments, e.g. {@code onos/intent}.
42 + *
43 + * @param groupName group name
44 + * @return thread factory
45 + */
46 + public static GroupedThreadFactory groupedThreadFactory(String groupName) {
47 + GroupedThreadFactory factory = FACTORIES.get(groupName);
48 + if (factory != null) {
49 + return factory;
50 + }
51 +
52 + // Find the parent group or root the group hierarchy under default group.
53 + int i = groupName.lastIndexOf(DELIMITER);
54 + if (i > 0) {
55 + String name = groupName.substring(0, i);
56 + ThreadGroup parentGroup = groupedThreadFactory(name).threadGroup();
57 + factory = new GroupedThreadFactory(new ThreadGroup(parentGroup, groupName));
58 + } else {
59 + factory = new GroupedThreadFactory(new ThreadGroup(groupName));
60 + }
61 +
62 + return ConcurrentUtils.putIfAbsent(FACTORIES, groupName, factory);
63 + }
64 +
65 + // Creates a new thread group
66 + private GroupedThreadFactory(ThreadGroup group) {
67 + this.group = group;
68 + }
69 +
70 + /**
71 + * Returns the thread group associated with the factory.
72 + *
73 + * @return thread group
74 + */
75 + public ThreadGroup threadGroup() {
76 + return group;
77 + }
78 +
79 + @Override
80 + public Thread newThread(Runnable r) {
81 + return new Thread(group, r);
82 + }
83 +
84 + @Override
85 + public String toString() {
86 + return toStringHelper(this).add("group", group).toString();
87 + }
88 +}
...@@ -15,16 +15,16 @@ ...@@ -15,16 +15,16 @@
15 */ 15 */
16 package org.onlab.util; 16 package org.onlab.util;
17 17
18 -import static java.nio.file.Files.delete; 18 +import com.google.common.base.Strings;
19 -import static java.nio.file.Files.walkFileTree; 19 +import com.google.common.primitives.UnsignedLongs;
20 -import static org.slf4j.LoggerFactory.getLogger; 20 +import com.google.common.util.concurrent.ThreadFactoryBuilder;
21 +import org.slf4j.Logger;
21 22
22 import java.io.BufferedReader; 23 import java.io.BufferedReader;
23 import java.io.File; 24 import java.io.File;
24 import java.io.FileInputStream; 25 import java.io.FileInputStream;
25 import java.io.IOException; 26 import java.io.IOException;
26 import java.io.InputStreamReader; 27 import java.io.InputStreamReader;
27 -import java.lang.Thread.UncaughtExceptionHandler;
28 import java.nio.charset.StandardCharsets; 28 import java.nio.charset.StandardCharsets;
29 import java.nio.file.FileVisitResult; 29 import java.nio.file.FileVisitResult;
30 import java.nio.file.Files; 30 import java.nio.file.Files;
...@@ -38,11 +38,10 @@ import java.util.Collection; ...@@ -38,11 +38,10 @@ import java.util.Collection;
38 import java.util.List; 38 import java.util.List;
39 import java.util.concurrent.ThreadFactory; 39 import java.util.concurrent.ThreadFactory;
40 40
41 -import org.slf4j.Logger; 41 +import static java.nio.file.Files.delete;
42 - 42 +import static java.nio.file.Files.walkFileTree;
43 -import com.google.common.base.Strings; 43 +import static org.onlab.util.GroupedThreadFactory.groupedThreadFactory;
44 -import com.google.common.primitives.UnsignedLongs; 44 +import static org.slf4j.LoggerFactory.getLogger;
45 -import com.google.common.util.concurrent.ThreadFactoryBuilder;
46 45
47 public abstract class Tools { 46 public abstract class Tools {
48 47
...@@ -62,13 +61,25 @@ public abstract class Tools { ...@@ -62,13 +61,25 @@ public abstract class Tools {
62 return new ThreadFactoryBuilder() 61 return new ThreadFactoryBuilder()
63 .setNameFormat(pattern) 62 .setNameFormat(pattern)
64 // FIXME remove UncaughtExceptionHandler before release 63 // FIXME remove UncaughtExceptionHandler before release
65 - .setUncaughtExceptionHandler(new UncaughtExceptionHandler() { 64 + .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on {}", t.getName(), e)).build();
65 + }
66 66
67 - @Override 67 + /**
68 - public void uncaughtException(Thread t, Throwable e) { 68 + * Returns a thread factory that produces threads named according to the
69 - log.error("Uncaught exception on {}", t.getName(), e); 69 + * supplied name pattern and from the specified thread-group. The thread
70 - } 70 + * group name is expected to be specified in slash-delimited format, e.g.
71 - }).build(); 71 + * {@code onos/intent}.
72 + *
73 + * @param groupName group name in slash-delimited format to indicate hierarchy
74 + * @param pattern name pattern
75 + * @return thread factory
76 + */
77 + public static ThreadFactory groupedThreads(String groupName, String pattern) {
78 + return new ThreadFactoryBuilder()
79 + .setThreadFactory(groupedThreadFactory(groupName))
80 + .setNameFormat(pattern)
81 + // FIXME remove UncaughtExceptionHandler before release
82 + .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on {}", t.getName(), e)).build();
72 } 83 }
73 84
74 /** 85 /**
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.util;
17 +
18 +import org.junit.Test;
19 +import org.onlab.junit.TestTools;
20 +
21 +import static org.junit.Assert.*;
22 +
23 +/**
24 + * Tests of the group thread factory.
25 + */
26 +public class GroupedThreadFactoryTest {
27 +
28 + @Test
29 + public void basics() {
30 + GroupedThreadFactory a = GroupedThreadFactory.groupedThreadFactory("foo");
31 + GroupedThreadFactory b = GroupedThreadFactory.groupedThreadFactory("foo");
32 + assertSame("factories should be same", a, b);
33 +
34 + assertTrue("wrong toString", a.toString().contains("foo"));
35 + Thread t = a.newThread(() -> TestTools.print("yo"));
36 + assertSame("wrong group", a.threadGroup(), t.getThreadGroup());
37 + }
38 +
39 + @Test
40 + public void hierarchical() {
41 + GroupedThreadFactory a = GroupedThreadFactory.groupedThreadFactory("foo/bar");
42 + GroupedThreadFactory b = GroupedThreadFactory.groupedThreadFactory("foo/goo");
43 + GroupedThreadFactory p = GroupedThreadFactory.groupedThreadFactory("foo");
44 +
45 + assertSame("groups should be same", p.threadGroup(), a.threadGroup().getParent());
46 + assertSame("groups should be same", p.threadGroup(), b.threadGroup().getParent());
47 +
48 + assertEquals("wrong name", "foo/bar", a.threadGroup().getName());
49 + assertEquals("wrong name", "foo/goo", b.threadGroup().getName());
50 + assertEquals("wrong name", "foo", p.threadGroup().getName());
51 + }
52 +
53 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
16 package org.onlab.util; 16 package org.onlab.util;
17 17
18 import org.junit.Test; 18 import org.junit.Test;
19 +import org.onlab.junit.TestTools;
20 +
21 +import java.util.concurrent.ThreadFactory;
19 22
20 import static org.junit.Assert.*; 23 import static org.junit.Assert.*;
21 24
...@@ -42,4 +45,20 @@ public class ToolsTest { ...@@ -42,4 +45,20 @@ public class ToolsTest {
42 assertEquals("ffffffffffffffff", Tools.toHex(0xffffffffffffffffL)); 45 assertEquals("ffffffffffffffff", Tools.toHex(0xffffffffffffffffL));
43 46
44 } 47 }
48 +
49 + @Test
50 + public void namedThreads() {
51 + ThreadFactory f = Tools.namedThreads("foo-%d");
52 + Thread t = f.newThread(() -> TestTools.print("yo"));
53 + assertTrue("wrong pattern", t.getName().startsWith("foo-"));
54 + }
55 +
56 + @Test
57 + public void groupedThreads() {
58 + ThreadFactory f = Tools.groupedThreads("foo/bar", "foo-%d");
59 + Thread t = f.newThread(() -> TestTools.print("yo"));
60 + assertTrue("wrong pattern", t.getName().startsWith("foo-"));
61 + assertTrue("wrong group", t.getThreadGroup().getName().equals("foo/bar"));
62 + }
63 +
45 } 64 }
......