1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package com.jcabi.aspects.aj;
31
32 import com.jcabi.aspects.Immutable;
33 import com.jcabi.aspects.Parallel;
34 import com.jcabi.log.VerboseThreads;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.LinkedList;
38 import java.util.concurrent.Callable;
39 import java.util.concurrent.CountDownLatch;
40 import java.util.concurrent.ExecutionException;
41 import java.util.concurrent.ExecutorService;
42 import java.util.concurrent.Executors;
43 import java.util.concurrent.Future;
44 import org.aspectj.lang.ProceedingJoinPoint;
45 import org.aspectj.lang.annotation.Around;
46 import org.aspectj.lang.annotation.Aspect;
47 import org.aspectj.lang.reflect.MethodSignature;
48
49
50
51
52
53
54
55
56 @Aspect
57 @Immutable
58 public final class Parallelizer {
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73 @Around("execution(@com.jcabi.aspects.Parallel * * (..))")
74 public Object wrap(final ProceedingJoinPoint point)
75 throws Parallelizer.ParallelException {
76 final int total = ((MethodSignature) point.getSignature())
77 .getMethod().getAnnotation(Parallel.class).threads();
78 final Collection<Callable<Throwable>> callables =
79 new ArrayList<>(total);
80 final CountDownLatch start = new CountDownLatch(1);
81 for (int thread = 0; thread < total; ++thread) {
82 callables.add(Parallelizer.callable(point, start));
83 }
84 final ExecutorService executor = Executors
85 .newFixedThreadPool(total, new VerboseThreads());
86 final Collection<Future<Throwable>> futures =
87 new ArrayList<>(total);
88 for (final Callable<Throwable> callable : callables) {
89 futures.add(executor.submit(callable));
90 }
91 start.countDown();
92 final Collection<Throwable> failures = new LinkedList<>();
93 for (final Future<Throwable> future : futures) {
94 Parallelizer.process(failures, future);
95 }
96 executor.shutdown();
97 if (!failures.isEmpty()) {
98 throw Parallelizer.exceptions(failures);
99 }
100 return null;
101 }
102
103
104
105
106
107
108 private static void process(final Collection<Throwable> failures,
109 final Future<Throwable> future) {
110 final Throwable exception;
111 try {
112 exception = future.get();
113 if (exception != null) {
114 failures.add(exception);
115 }
116 } catch (final InterruptedException ex) {
117 Thread.currentThread().interrupt();
118 failures.add(ex);
119 } catch (final ExecutionException ex) {
120 failures.add(ex);
121 }
122 }
123
124
125
126
127
128
129 @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
130 private static Parallelizer.ParallelException exceptions(
131 final Iterable<Throwable> failures) {
132 Parallelizer.ParallelException current = null;
133 for (final Throwable failure : failures) {
134 current = new Parallelizer.ParallelException(failure, current);
135 }
136 return current;
137 }
138
139
140
141
142
143
144
145 @SuppressWarnings("PMD.AvoidCatchingThrowable")
146 private static Callable<Throwable> callable(final ProceedingJoinPoint point,
147 final CountDownLatch start) {
148 return () -> {
149 Throwable result = null;
150 try {
151 start.await();
152 point.proceed();
153
154 } catch (final Throwable ex) {
155 result = ex;
156 }
157 return result;
158 };
159 }
160
161
162
163
164
165 private static final class ParallelException extends Exception {
166
167
168
169
170 private static final long serialVersionUID = 0x8743ef363febc422L;
171
172
173
174
175 private final transient Parallelizer.ParallelException next;
176
177
178
179
180
181
182 protected ParallelException(final Throwable cause,
183 final Parallelizer.ParallelException nxt) {
184 super(cause);
185 this.next = nxt;
186 }
187
188
189
190
191
192 public Parallelizer.ParallelException getNext() {
193 return this.next;
194 }
195 }
196
197 }