();
13 | // 告警系统客户端API
14 | private final AlarmAgent alarmAgent = new AlarmAgent();
15 | // 告警发送线程
16 | private final AbstractTerminatableThread alarmSendingThread;
17 |
18 | private boolean shutdownRequested = false;
19 |
20 | private static final AlarmMgr INSTANCE = new AlarmMgr();
21 |
22 | private AlarmMgr() {
23 | alarmSendingThread = new AbstractTerminatableThread() {
24 | @Override
25 | protected void doRun() throws Exception {
26 | if (alarmAgent.waitUntilConnected()) {
27 | AlarmInfo alarm;
28 | alarm = alarms.take();
29 | terminationToken.reservations.decrementAndGet();
30 | try {
31 | alarmAgent.sendAlarm(alarm);
32 | } catch (Exception e) {
33 | e.printStackTrace();
34 | }
35 | }
36 | }
37 |
38 | @Override
39 | protected void doCleanup(Exception exp) {
40 | if (null != exp) {
41 | exp.printStackTrace();
42 | }
43 | alarmAgent.disconnect();
44 | }
45 |
46 | };
47 |
48 | alarmAgent.init();
49 | }
50 |
51 | public static AlarmMgr getInstance() {
52 | return INSTANCE;
53 | }
54 |
55 | public void sendAlarm(AlarmType type, String id, String extraInfo) {
56 | final TerminationToken terminationToken = alarmSendingThread.terminationToken;
57 | if (terminationToken.isToShutdown()) {
58 | // log the alarm
59 | System.err.println("rejected alarm:" + id + "," + extraInfo);
60 | return;
61 |
62 | }
63 | try {
64 | AlarmInfo alarm = new AlarmInfo(id, type);
65 | alarm.setExtraInfo(extraInfo);
66 | terminationToken.reservations.incrementAndGet();
67 | alarms.add(alarm);
68 | } catch (Throwable t) {
69 | t.printStackTrace();
70 | }
71 | }
72 |
73 | public void init() {
74 | alarmSendingThread.start();
75 | }
76 |
77 | public synchronized void shutdown() {
78 | if (shutdownRequested) {
79 | throw new IllegalStateException("shutdown already requested!");
80 | }
81 |
82 | alarmSendingThread.terminate();
83 | shutdownRequested = true;
84 | }
85 |
86 | public int pendingAlarms() {
87 | return alarmSendingThread.terminationToken.reservations.get();
88 | }
89 |
90 | }
91 |
92 | class AlarmInfo {
93 | private String id;
94 | private String extraInfo;
95 | private AlarmType type;
96 |
97 | public AlarmInfo(String id, AlarmType type) {
98 | this.id = id;
99 | this.type = type;
100 |
101 | }
102 |
103 | public String getId() {
104 | return id;
105 | }
106 |
107 | public void setId(String id) {
108 | this.id = id;
109 | }
110 |
111 | public String getExtraInfo() {
112 | return extraInfo;
113 | }
114 |
115 | public void setExtraInfo(String extraInfo) {
116 | this.extraInfo = extraInfo;
117 | }
118 |
119 | @Override
120 | public int hashCode() {
121 | final int prime = 31;
122 | int result = 1;
123 | result = prime * result + ((extraInfo == null) ? 0 : extraInfo.hashCode());
124 | result = prime * result + ((id == null) ? 0 : id.hashCode());
125 | return result;
126 | }
127 |
128 | @Override
129 | public boolean equals(Object obj) {
130 | if (this == obj)
131 | return true;
132 | if (obj == null)
133 | return false;
134 | if (getClass() != obj.getClass())
135 | return false;
136 | AlarmInfo other = (AlarmInfo) obj;
137 | if (extraInfo == null) {
138 | if (other.extraInfo != null)
139 | return false;
140 | } else if (!extraInfo.equals(other.extraInfo))
141 | return false;
142 | if (id == null) {
143 | if (other.id != null)
144 | return false;
145 | } else if (!id.equals(other.id))
146 | return false;
147 | return true;
148 | }
149 |
150 | @Override
151 | public String toString() {
152 | return "AlarmInfo [type=" + type + ",id=" + id + ", extraInfo=["
153 | + extraInfo + "]]";
154 | }
155 |
156 | }
157 |
158 | class AlarmAgent {
159 | // 省略其它代码
160 | private volatile boolean connectedToServer = false;
161 |
162 | public void sendAlarm(AlarmInfo alarm) throws Exception {
163 | // 省略其它代码
164 | System.out.println("Sending " + alarm);
165 | try {
166 | Thread.sleep(50);
167 | } catch (Exception e) {
168 |
169 | }
170 | }
171 |
172 | public void init() {
173 | // 省略其它代码
174 | connectedToServer = true;
175 | }
176 |
177 | public void disconnect() {
178 | // 省略其它代码
179 | System.out.println("disconnected from alarm server.");
180 | }
181 |
182 | public boolean waitUntilConnected() {
183 | // 省略其它代码
184 | return connectedToServer;
185 | }
186 | }
--------------------------------------------------------------------------------
/src/io/github/viscent/mtpattern/tpt/example/AlarmType.java:
--------------------------------------------------------------------------------
1 | package io.github.viscent.mtpattern.tpt.example;
2 |
3 | public enum AlarmType {
4 | FAULT,
5 | RESUME,
6 | EVENT
7 | }
8 |
--------------------------------------------------------------------------------
/src/io/github/viscent/mtpattern/tpt/example/ProducerConsumerStop.java:
--------------------------------------------------------------------------------
1 | package io.github.viscent.mtpattern.tpt.example;
2 |
3 | import io.github.viscent.mtpattern.tpt.AbstractTerminatableThread;
4 |
5 | import java.util.concurrent.BlockingQueue;
6 | import java.util.concurrent.LinkedBlockingQueue;
7 |
8 |
9 | public class ProducerConsumerStop {
10 | private static class SampleConsumer {
11 | private final BlockingQueue
queue = new LinkedBlockingQueue
();
12 |
13 | private AbstractTerminatableThread workThread
14 | = new AbstractTerminatableThread() {
15 | @Override
16 | protected void doRun() throws Exception {
17 | terminationToken.reservations.decrementAndGet();
18 | P product = queue.take();
19 | // ...
20 | System.out.println(product);
21 | }
22 |
23 | };
24 |
25 | public void placeProduct(P product) {
26 | if (workThread.terminationToken.isToShutdown()) {
27 | throw new IllegalStateException("Thread shutdown");
28 | }
29 | try {
30 | queue.put(product);
31 | workThread.terminationToken.reservations.incrementAndGet();
32 | } catch (InterruptedException e) {
33 |
34 | }
35 | }
36 |
37 | public void shutdown() {
38 | workThread.terminate();
39 | }
40 |
41 | public void start() {
42 | workThread.start();
43 | }
44 | }
45 |
46 | public void test() {
47 | final SampleConsumer aConsumer = new SampleConsumer();
48 |
49 | AbstractTerminatableThread aProducer = new AbstractTerminatableThread() {
50 | private int i = 0;
51 |
52 | @Override
53 | protected void doRun() throws Exception {
54 | aConsumer.placeProduct(String.valueOf(i));
55 | }
56 |
57 | @Override
58 | protected void doCleanup(Exception cause) {
59 | // 生产者线程停止完毕后再请求停止消费者线程
60 | aConsumer.shutdown();
61 | }
62 |
63 | };
64 |
65 | aProducer.start();
66 | aConsumer.start();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/io/github/viscent/mtpattern/tpt/example/TestAlarmMgrShutdown.java:
--------------------------------------------------------------------------------
1 | package io.github.viscent.mtpattern.tpt.example;
2 |
3 | import static org.junit.Assert.*;
4 | import io.github.viscent.mtpattern.tpt.AbstractTerminatableThread;
5 |
6 | import org.junit.Before;
7 | import org.junit.Test;
8 |
9 |
10 | public class TestAlarmMgrShutdown {
11 | private AlarmMgr alarmMgr;
12 |
13 | @Before
14 | public void setUp() throws Exception {
15 | alarmMgr = AlarmMgr.getInstance();
16 | alarmMgr.init();
17 |
18 | }
19 |
20 |
21 | @Test
22 | public void testShutdown() {
23 | AbstractTerminatableThread producer = new AbstractTerminatableThread() {
24 | private int i = 0;
25 |
26 | @Override
27 | protected void doRun() throws Exception {
28 | alarmMgr.sendAlarm(AlarmType.FAULT, "001", "key1=value" + (i++));
29 | Thread.sleep(30);
30 | }
31 |
32 | @Override
33 | protected void doCleanup(Exception cause) {
34 | System.out.println("Alarms triggered:" + i + ",pending alarm:"
35 | + alarmMgr.pendingAlarms());
36 | alarmMgr.shutdown();
37 | }
38 |
39 | };
40 |
41 | producer.start();
42 |
43 | try {
44 | Thread.sleep(1000);
45 | } catch (InterruptedException e) {
46 | }
47 |
48 | producer.terminate();
49 |
50 | try {
51 | Thread.sleep(1000);
52 | } catch (InterruptedException e) {
53 | }
54 |
55 | assertEquals(0, alarmMgr.pendingAlarms());
56 | }
57 |
58 |
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/log4j.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. 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 |
17 | # An example log4j configuration file that outputs to System.out. The
18 | # output information consists of relative time, log level, thread
19 | # name, logger name, nested diagnostic context and the message in that
20 | # order.
21 |
22 | # For the general syntax of property based configuration files see the
23 | # documenation of org.apache.log4j.PropertyConfigurator.
24 |
25 | log4j.rootLogger=DEBUG, A1,file
26 |
27 | # A1 is set to be a ConsoleAppender which outputs to System.out.
28 | log4j.appender.A1=org.apache.log4j.ConsoleAppender
29 |
30 | # A1 uses PatternLayout.
31 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout
32 |
33 | # The conversion pattern uses format specifiers. You might want to
34 | # change the pattern an watch the output format change.
35 | log4j.appender.A1.layout.ConversionPattern=%-4r %-5p [%t] %37c %3x - %m%n
36 |
37 | # Direct log messages to a log file
38 | log4j.appender.file=org.apache.log4j.RollingFileAppender
39 |
40 | #Redirect to Tomcat logs folder
41 | #log4j.appender.file.File=${catalina.home}/logs/logging.log
42 |
43 | log4j.appender.file.File=/var/tmp/logs/ao.log
44 | log4j.appender.file.MaxFileSize=10MB
45 | log4j.appender.file.MaxBackupIndex=10
46 | log4j.appender.file.layout=org.apache.log4j.PatternLayout
47 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
48 |
49 | # In this example, we are not really interested in INNER loop or SWAP
50 | # messages. See the effects of uncommenting and changing the levels of
51 | # the following loggers.
52 | # log4j.logger.org.apache.log4j.examples.SortAlgo.INNER=WARN
53 |
54 | log4j.logger.vh.activeobject.AsyncRequestPersistence=INFO
55 |
56 |
--------------------------------------------------------------------------------
/src/vh/activeobject/AsyncRequestPersistence.java:
--------------------------------------------------------------------------------
1 | package vh.activeobject;
2 |
3 | import java.util.Timer;
4 | import java.util.TimerTask;
5 | import java.util.concurrent.Callable;
6 | import java.util.concurrent.LinkedBlockingQueue;
7 | import java.util.concurrent.ThreadFactory;
8 | import java.util.concurrent.ThreadPoolExecutor;
9 | import java.util.concurrent.TimeUnit;
10 | import java.util.concurrent.atomic.AtomicInteger;
11 | import java.util.concurrent.atomic.AtomicLong;
12 |
13 | import org.apache.log4j.Logger;
14 |
15 | /**
16 | *
17 | * @author viscent
18 | *
19 | * Active Object, Proxy Benefits of applying Active Object: Simplifying
20 | * implementing fault isolation
21 | *
22 | * 实际运用考虑: 队列监控:监控处理队列是否积压 KeepAliveTime:空闲线程清理 队列容量:做好存储规划
23 | * 处理过慢,队列满的问题:无论是自己实现度列还是直接使用ThreadPoolExecutor
24 | * ,都需要处理该问题。而ThreadPoolExecutor了预置了一些队列满的处理策略。
25 | *
26 | * ThreadPoolExecutor:当线程池中工作线程数逐渐增加到核心工作线程数时,此时新提交的任务会被放入工作队列。
27 | * 当核心工作线程的处理速率小于工作任务的提交速率时,
28 | * 工作队列会逐渐积压。当工作队列满时,ThreadPoolExecutor会追加工作线程直到其数量到达线程池的最大大小
29 | * 。此时,新提交的工作任务会被拒绝(即提交失败)
30 | *
31 | *
32 | */
33 | // ActiveObjectPattern.Proxy
34 | public class AsyncRequestPersistence implements RequestPersistence {
35 | private static final long ONE_MINUTE_IN_SECONDS = 60;
36 | private final Logger logger;
37 | private final AtomicLong taskTimeConsumedPerInterval = new AtomicLong(0);
38 | private final AtomicInteger requestSubmittedPerIterval = new AtomicInteger(0);
39 |
40 | // ActiveObjectPattern.Servant
41 | private final DiskbasedRequestPersistence delegate = new DiskbasedRequestPersistence();
42 |
43 | // ActiveObjectPattern.Scheduler
44 | private final ThreadPoolExecutor scheduler;
45 |
46 | private static class InstanceHolder {
47 | final static RequestPersistence INSTANCE = new AsyncRequestPersistence();
48 | }
49 |
50 | private AsyncRequestPersistence() {
51 | logger = Logger.getLogger(AsyncRequestPersistence.class);
52 | scheduler = new ThreadPoolExecutor(1, 3, 60 * ONE_MINUTE_IN_SECONDS,
53 | TimeUnit.SECONDS,
54 | // ActiveObjectPattern.ActivationQueue
55 | new LinkedBlockingQueue(200), new ThreadFactory() {
56 | @Override
57 | public Thread newThread(Runnable r) {
58 | Thread t;
59 | t = new Thread(r, "AsyncRequestPersistence");
60 | return t;
61 | }
62 |
63 | });
64 |
65 | scheduler
66 | .setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
67 |
68 | // 启动队列监控定时任务
69 | Timer monitorTimer = new Timer(true);
70 | monitorTimer.scheduleAtFixedRate(new TimerTask() {
71 |
72 | @Override
73 | public void run() {
74 | if (logger.isInfoEnabled()) {
75 |
76 | logger.info("task count:" + requestSubmittedPerIterval
77 | + ",Queue size:" + scheduler.getQueue().size()
78 | + ",taskTimeConsumedPerInterval:"
79 | + taskTimeConsumedPerInterval.get() + " ms");
80 | }
81 |
82 | taskTimeConsumedPerInterval.set(0);
83 | requestSubmittedPerIterval.set(0);
84 |
85 | }
86 | }, 0, ONE_MINUTE_IN_SECONDS * 1000);
87 |
88 | }
89 |
90 | public static RequestPersistence getInstance() {
91 | return InstanceHolder.INSTANCE;
92 | }
93 |
94 | @Override
95 | public void store(final MMSDeliverRequest request) {
96 | /*
97 | * 将对store方法的调用封装成MethodRequest对象, 并存入缓冲区。
98 | */
99 | // ActiveObjectPattern.MethodRequest
100 | Callable methodRequest = new Callable() {
101 | @Override
102 | public Boolean call() throws Exception {
103 | long start = System.currentTimeMillis();
104 | try {
105 | delegate.store(request);
106 | } finally {
107 | taskTimeConsumedPerInterval.addAndGet(System.currentTimeMillis()
108 | - start);
109 | }
110 |
111 | return Boolean.TRUE;
112 | }
113 |
114 | };
115 | scheduler.submit(methodRequest);
116 |
117 | requestSubmittedPerIterval.incrementAndGet();
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/vh/activeobject/CustomScheduler.java:
--------------------------------------------------------------------------------
1 | package vh.activeobject;
2 |
3 | import java.util.concurrent.Callable;
4 | import java.util.concurrent.ExecutionException;
5 | import java.util.concurrent.Future;
6 | import java.util.concurrent.FutureTask;
7 | import java.util.concurrent.LinkedBlockingQueue;
8 |
9 | public class CustomScheduler implements Runnable {
10 | private LinkedBlockingQueue activationQueue = new LinkedBlockingQueue();
11 |
12 | @Override
13 | public void run() {
14 | dispatch();
15 | }
16 |
17 | public Future enqueue(Callable methodRequest) {
18 | final FutureTask task = new FutureTask(methodRequest) {
19 |
20 | @Override
21 | public void run() {
22 | try {
23 | super.run();
24 | // 捕获所以可能抛出的对象,避免该任务运行失败而导致其所在的线程终止。
25 | } catch (Throwable t) {
26 | this.setException(t);
27 | }
28 | }
29 |
30 | };
31 |
32 | try {
33 | activationQueue.put(task);
34 | } catch (InterruptedException e) {
35 | Thread.currentThread().interrupt();
36 | }
37 | return task;
38 | }
39 |
40 | public void dispatch() {
41 | while (true) {
42 | Runnable methodRequest;
43 | try {
44 | methodRequest = activationQueue.take();
45 |
46 | // 防止个别任务执行失败导致线程终止的代码在run方法中
47 | methodRequest.run();
48 | } catch (InterruptedException e) {
49 | // 处理该异常
50 | }
51 |
52 | }
53 | }
54 |
55 | public static void main(String[] args) {
56 |
57 | CustomScheduler scheduler = new CustomScheduler();
58 | Thread t = new Thread(scheduler);
59 | t.start();
60 | Future result = scheduler.enqueue(new Callable() {
61 |
62 | @Override
63 | public String call() throws Exception {
64 | Thread.sleep(1500);
65 | int i = 1;
66 | if (1 == i) {
67 | throw new RuntimeException("test");
68 | }
69 | return "ok";
70 | }
71 |
72 | });
73 |
74 | try {
75 | System.out.println(result.get());
76 | ;
77 | } catch (InterruptedException e) {
78 | e.printStackTrace();
79 | } catch (ExecutionException e) {
80 | e.printStackTrace();
81 | }
82 |
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/vh/activeobject/DiskbasedRequestPersistence.java:
--------------------------------------------------------------------------------
1 | package vh.activeobject;
2 |
3 | import java.io.File;
4 | import java.io.FileNotFoundException;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.io.ObjectOutputStream;
8 | import java.text.DecimalFormat;
9 | import java.text.SimpleDateFormat;
10 | import java.util.Date;
11 | import java.util.Deque;
12 | import java.util.HashMap;
13 | import java.util.LinkedList;
14 | import java.util.Map;
15 | import java.util.concurrent.atomic.AtomicInteger;
16 |
17 | import org.apache.log4j.Logger;
18 |
19 | public class DiskbasedRequestPersistence implements RequestPersistence {
20 | // 负责缓存文件的存储管理
21 | private final SectionBasedDiskStorage storage = new SectionBasedDiskStorage();
22 | private final Logger logger = Logger
23 | .getLogger(DiskbasedRequestPersistence.class);
24 |
25 | @Override
26 | public void store(MMSDeliverRequest request) {
27 | // 申请缓存文件的文件名
28 | String[] fileNameParts = storage.apply4Filename(request);
29 | File file = new File(fileNameParts[0]);
30 | try {
31 | ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream(
32 | file));
33 | try {
34 | objOut.writeObject(request);
35 | } finally {
36 | objOut.close();
37 | }
38 | } catch (FileNotFoundException e) {
39 | storage.decrementSectionFileCount(fileNameParts[1]);
40 | logger.error("Failed to store request", e);
41 | } catch (IOException e) {
42 | storage.decrementSectionFileCount(fileNameParts[1]);
43 | logger.error("Failed to store request", e);
44 | }
45 |
46 | }
47 |
48 | class SectionBasedDiskStorage {
49 | private Deque sectionNames = new LinkedList();
50 | /*
51 | * Key->value: 存储子目录名->子目录下缓存文件计数器
52 | */
53 | private Map sectionFileCountMap = new HashMap();
54 | private int maxFilesPerSection = 2000;
55 | private int maxSectionCount = 100;
56 | private String storageBaseDir = System.getProperty("user.dir") + "/vpn";
57 |
58 | private final Object sectionLock = new Object();
59 |
60 | public String[] apply4Filename(MMSDeliverRequest request) {
61 | String sectionName;
62 | int iFileCount;
63 | boolean need2RemoveSection = false;
64 | String[] fileName = new String[2];
65 | synchronized (sectionLock) {
66 | // 获取当前的存储子目录名
67 | sectionName = this.getSectionName();
68 | AtomicInteger fileCount;
69 | fileCount = sectionFileCountMap.get(sectionName);
70 | iFileCount = fileCount.get();
71 | // 当前存储子目录已满
72 | if (iFileCount >= maxFilesPerSection) {
73 | if (sectionNames.size() >= maxSectionCount) {
74 | need2RemoveSection = true;
75 | }
76 | // 创建新的存储子目录
77 | sectionName = this.makeNewSectionDir();
78 | fileCount = sectionFileCountMap.get(sectionName);
79 |
80 | }
81 | iFileCount = fileCount.addAndGet(1);
82 |
83 | }
84 |
85 | fileName[0] = storageBaseDir + "/" + sectionName + "/"
86 | + new DecimalFormat("0000").format(iFileCount) + "-"
87 | + request.getTimeStamp().getTime() / 1000 + "-" + request.getExpiry()
88 | + ".rq";
89 | fileName[1] = sectionName;
90 |
91 | if (need2RemoveSection) {
92 | // 删除最老的存储子目录
93 | String oldestSectionName = sectionNames.removeFirst();
94 | this.removeSection(oldestSectionName);
95 | }
96 |
97 | return fileName;
98 | }
99 |
100 | public void decrementSectionFileCount(String sectionName) {
101 | AtomicInteger fileCount = sectionFileCountMap.get(sectionName);
102 | if (null != fileCount) {
103 | fileCount.decrementAndGet();
104 | }
105 | }
106 |
107 | private boolean removeSection(String sectionName) {
108 | boolean result = true;
109 | File dir = new File(storageBaseDir + "/" + sectionName);
110 | for (File file : dir.listFiles()) {
111 | result = result && file.delete();
112 | }
113 | result = result && dir.delete();
114 | return result;
115 | }
116 |
117 | private String getSectionName() {
118 | String sectionName;
119 |
120 | if (sectionNames.isEmpty()) {
121 | sectionName = this.makeNewSectionDir();
122 |
123 | } else {
124 | sectionName = sectionNames.getLast();
125 | }
126 |
127 | return sectionName;
128 | }
129 |
130 | private String makeNewSectionDir() {
131 | String sectionName;
132 | SimpleDateFormat sdf = new SimpleDateFormat("MMddHHmmss");
133 | sectionName = sdf.format(new Date());
134 | File dir = new File(storageBaseDir + "/" + sectionName);
135 | if (dir.mkdir()) {
136 | sectionNames.addLast(sectionName);
137 | sectionFileCountMap.put(sectionName, new AtomicInteger(0));
138 | } else {
139 | throw new RuntimeException("Cannot create section dir " + sectionName);
140 | }
141 |
142 | return sectionName;
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/vh/activeobject/MMSDeliverRequest.java:
--------------------------------------------------------------------------------
1 | package vh.activeobject;
2 |
3 | import java.io.Serializable;
4 | import java.util.Collections;
5 | import java.util.Date;
6 | import java.util.HashSet;
7 | import java.util.Set;
8 |
9 | public class MMSDeliverRequest implements Serializable {
10 | private String transactionID;
11 | private String messageType = "Delivery.req";
12 | private String senderAddress;
13 | private Recipient recipient = new Recipient();
14 | private String subject;
15 | private Attachment attachment = new Attachment();
16 |
17 | public long getExpiry() {
18 | return expiry;
19 | }
20 |
21 | private long expiry;
22 | private Date timeStamp;
23 |
24 | public MMSDeliverRequest() {
25 |
26 | }
27 |
28 | public void setExpiry(long expiry) {
29 | this.expiry = expiry;
30 | }
31 |
32 | public Date getTimeStamp() {
33 | return timeStamp;
34 | }
35 |
36 | public void setTimeStamp(Date timeStamp) {
37 | this.timeStamp = timeStamp;
38 | }
39 |
40 | public String getTransactionID() {
41 | return transactionID;
42 | }
43 |
44 | public void setTransactionID(String transactionID) {
45 | this.transactionID = transactionID;
46 | }
47 |
48 | public String getMessageType() {
49 | return messageType;
50 | }
51 |
52 | public void setMessageType(String messageType) {
53 | this.messageType = messageType;
54 | }
55 |
56 | public String getSenderAddress() {
57 | return senderAddress;
58 | }
59 |
60 | public void setSenderAddress(String senderAddress) {
61 | this.senderAddress = senderAddress;
62 | }
63 |
64 | public Recipient getRecipient() {
65 | return recipient;
66 | }
67 |
68 | public void setRecipient(Recipient recipient) {
69 | this.recipient = recipient;
70 | }
71 |
72 | public Attachment getAttachment() {
73 | return attachment;
74 | }
75 |
76 | public void setAttachment(Attachment attachment) {
77 | this.attachment = attachment;
78 | }
79 |
80 | public String getSubject() {
81 | return subject;
82 | }
83 |
84 | public void setSubject(String subject) {
85 | this.subject = subject;
86 | }
87 |
88 | @Override
89 | public String toString() {
90 | return "MM7DeliverRequest [transactionID=" + transactionID
91 | + ", messageType=" + messageType + ", senderAddress=" + senderAddress
92 | + ", recipient=" + recipient + ", subject=" + subject + ", attachment="
93 | + attachment + "]";
94 | }
95 |
96 | private static final long serialVersionUID = 302185079311891797L;
97 |
98 | }
99 |
100 | class Recipient implements Serializable {
101 |
102 | private static final long serialVersionUID = -5427696559429827584L;
103 | private Set to = new HashSet();
104 |
105 | public void addTo(String msisdn) {
106 | to.add(msisdn);
107 | }
108 |
109 | public Set getToList() {
110 | return (Set) Collections.unmodifiableCollection(to);
111 | }
112 |
113 | }
114 |
115 | class Attachment implements Serializable {
116 | private static final long serialVersionUID = -313285270497968496L;
117 | private String contentType;
118 | private byte[] content = new byte[0];
119 |
120 | public String getContentType() {
121 | return contentType;
122 | }
123 |
124 | public void setContentType(String contentType) {
125 | this.contentType = contentType;
126 | }
127 |
128 | public byte[] getContent() {
129 | return content;
130 | }
131 |
132 | public void setContent(byte[] content) {
133 | this.content = content;
134 | }
135 |
136 | @Override
137 | public String toString() {
138 | return "Attachment [contentType=" + contentType + ", content="
139 | + content.length + "]";
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/vh/activeobject/RequestPersistence.java:
--------------------------------------------------------------------------------
1 | package vh.activeobject;
2 |
3 |
4 | public interface RequestPersistence {
5 |
6 | void store(MMSDeliverRequest request);
7 | }
8 |
--------------------------------------------------------------------------------
/src/vh/activeobject/Test.java:
--------------------------------------------------------------------------------
1 | package vh.activeobject;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileNotFoundException;
6 | import java.io.IOException;
7 | import java.nio.ByteBuffer;
8 | import java.util.Date;
9 | import java.util.concurrent.LinkedBlockingQueue;
10 | import java.util.concurrent.ThreadPoolExecutor;
11 | import java.util.concurrent.TimeUnit;
12 | import java.util.concurrent.atomic.AtomicInteger;
13 |
14 | import org.junit.Before;
15 |
16 | public class Test {
17 | private RequestPersistence persistence;
18 | private ThreadPoolExecutor executor;
19 | private Attachment attachment;
20 |
21 | @Before
22 | public void setUp() {
23 | persistence = AsyncRequestPersistence.getInstance();
24 | executor = new ThreadPoolExecutor(80, 200, 60 * 3600, TimeUnit.SECONDS,
25 | new LinkedBlockingQueue(300));
26 | try {
27 | File file = new File("/home/viscent/tmp/callstack.png");
28 | ByteBuffer contentBuf = ByteBuffer.allocate((int) file.length());
29 | FileInputStream fin = new FileInputStream(file);
30 | try {
31 | fin.getChannel().read(contentBuf);
32 | } finally {
33 | fin.close();
34 | }
35 | attachment = new Attachment();
36 | attachment.setContentType("image/png");
37 | attachment.setContent(contentBuf.array());
38 | } catch (FileNotFoundException e1) {
39 | e1.printStackTrace();
40 | } catch (IOException e) {
41 | e.printStackTrace();
42 | }
43 | }
44 |
45 | final AtomicInteger counter = new AtomicInteger(0);
46 |
47 | class RequestSenderThread extends Thread {
48 | private int chunkSize;
49 | private int timeSpan;
50 |
51 | public RequestSenderThread(int chunkSize, int timeSpan) {
52 | this.chunkSize = chunkSize;
53 | this.timeSpan = timeSpan;
54 | }
55 |
56 | @Override
57 | public void run() {
58 | int sleepCount = (chunkSize / timeSpan);
59 | for (int i = 0; i < chunkSize; i++) {
60 |
61 | executor.execute(new Runnable() {
62 |
63 | @Override
64 | public void run() {
65 | MMSDeliverRequest request = new MMSDeliverRequest();
66 | request.setTransactionID(String.valueOf(counter.incrementAndGet()));
67 | request.setSenderAddress("13612345678");
68 | request.setTimeStamp(new Date());
69 | request.setExpiry((new Date().getTime() + 3600000) / 1000);
70 |
71 | request.setSubject("Hi");
72 | request.getRecipient().addTo("776");
73 | request.setAttachment(attachment);
74 |
75 | persistence.store(request);
76 |
77 | }
78 | });
79 |
80 | // System.out.println(this.getId()+" sent "+i+1);
81 | if (0 == (i % sleepCount)) {
82 | try {
83 | Thread.sleep(1000);
84 | } catch (InterruptedException e) {
85 | e.printStackTrace();
86 | }
87 | }
88 |
89 | }
90 | }
91 |
92 | }
93 |
94 | @org.junit.Test
95 | public void testFaultIsolation() {
96 | RequestSenderThread sender;
97 | for (int i = 0; i < 10; i++) {
98 | sender = new RequestSenderThread(200000, 10000);
99 | sender.start();
100 | }
101 |
102 | try {
103 | executor.awaitTermination(2L, TimeUnit.HOURS);
104 | } catch (InterruptedException e1) {
105 | e1.printStackTrace();
106 | }
107 |
108 | }
109 |
110 | public void testTimeConsumption() {
111 | MMSDeliverRequest request = new MMSDeliverRequest();
112 | request.setTransactionID(String.valueOf(counter.incrementAndGet()));
113 | request.setSenderAddress("13612345678");
114 | request.setTimeStamp(new Date());
115 | request.setExpiry((new Date().getTime() + 3600000) / 1000);
116 |
117 | request.setSubject("Hi");
118 | request.getRecipient().addTo("776");
119 | request.setAttachment(attachment);
120 | DiskbasedRequestPersistence rp = new DiskbasedRequestPersistence();
121 | long start = System.currentTimeMillis();
122 | rp.store(request);
123 | // About took 15ms to write a single file of 218KB
124 | System.out.println("Took " + (System.currentTimeMillis() - start));
125 | }
126 |
127 | // private void a(){
128 | // ActiveObject ao=...;
129 | // Future future=ao.doSomething("e");
130 | // //其它代码
131 | // String result=future.get();
132 | // System.out.println(result);
133 | // }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/src/vh/activeobject/lib/ActiveObjectProxy.java:
--------------------------------------------------------------------------------
1 | package vh.activeobject.lib;
2 |
3 | import java.lang.reflect.InvocationHandler;
4 | import java.lang.reflect.InvocationTargetException;
5 | import java.lang.reflect.Method;
6 | import java.lang.reflect.Proxy;
7 | import java.util.concurrent.Callable;
8 | import java.util.concurrent.ExecutorService;
9 | import java.util.concurrent.Future;
10 |
11 | public abstract class ActiveObjectProxy {
12 |
13 | private static class DispatchInvocationHandler implements InvocationHandler {
14 | private final Object delegate;
15 | private final ExecutorService scheduler;
16 |
17 | public DispatchInvocationHandler(Object delegate,
18 | ExecutorService executorService) {
19 | this.delegate = delegate;
20 | this.scheduler = executorService;
21 | }
22 |
23 | private String makeDelegateMethodName(final Method method,
24 | final Object[] arg) {
25 | String name = method.getName();
26 | name = "do" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
27 |
28 | return name;
29 | }
30 |
31 | @Override
32 | public Object invoke(final Object proxy, final Method method,
33 | final Object[] args) throws Throwable {
34 |
35 | Object returnValue = null;
36 | final Object delegate = this.delegate;
37 | final Method delegateMethod;
38 |
39 | // 如果拦截到的被调用方法是异步方法,则将其转发到相应的doXXX方法
40 | if (Future.class.isAssignableFrom(method.getReturnType())) {
41 | delegateMethod = delegate.getClass().getMethod(
42 | makeDelegateMethodName(method, args), method.getParameterTypes());
43 |
44 | final ExecutorService scheduler = this.scheduler;
45 |
46 | Callable