У меня Flex 3.6 + BlazeDS + Java 1.6 веб-приложение, работающее на Tomcat 6 . Мне нужно, чтобы служба сообщений BlazeDS передавала с сервера на клиент для создания детерминированного индикатора выполнения, когда пользователь запускает какую-то разработку из внешнего интерфейса Flex.
Тогда у меня есть n модуль, построенный следующим образом:
public static const JOBN:String = "JOBN";
private var _dm:DataManager;
public function init():void {
_dm = new DataManager;
}
private function btnN_OnClick(event:MouseEvent):void {
_dm.addEventListener(JOBN, onJobNResult);
_dm.jobN();
}
private function onJobNResult(dataEvent:MyEvent):void {
var resN:int = dataEvent.result as int;
_dm.removeEventListener(JOBN, onJobNResult);
}
и DataManager, построенный следующим образом:
public static const JOBN:String = "JOBN";
public function DataManager() {
var loCs:ChannelSet = new ChannelSet();
loCs.addChannel(new AMFChannel("canale", "messagebroker/amf"));
_service = new RemoteObject("dataManager");
_service.channelSet = loCs;
}
public function jobN():void {
var token:AsyncToken = _service.jobN();
token.addResponder(new AsyncResponder(jobNOnResult,jobNOnFault));
runProgressBar();
}
private function jobNOnFault(event:FaultEvent,token:Object):void {
var _fail:String = "Error";
}
private function jobNOnResult(event:ResultEvent,token:Object):void {
var jobNResult:int = event.result as int;
dispatchEvent(new MyEvent(JOBN,jobNResult));
stopProgressBar();
}
метод runProgressBar () и stopProgressBar () соответственно создает и удаляет всплывающее окно, которое содержит индикатор выполнения и помещается на холсте приложения. Всплывающие методы запускают и останавливают поток сообщений:
private function init():void {
start();
}
private function start():void {
var msg:AsyncMessage = new AsyncMessage();
msg.body = "START";
producer.send(msg);
consumer.subscribe();
}
private function stop():void {
var msg:AsyncMessage = new AsyncMessage();
msg.body = "STOP";
producer.send(msg);
}
private function messageHandler(message:IMessage):void {
values = message.body as String;
value = (int) (values.substr(0, values.lastIndexOf(";")));
max = (int) (values.substr(values.indexOf(";")+1,values.length));
if (value == -1) {
value = 0;
increaseProgress(max,max);
stop();
} else {
increaseProgress(value, max);
}
}
private function ack(event:MessageAckEvent):void {
if ( (event != null) && (event.message.body != null) ) {
values = event.message.body as String;
}
}
private function increaseProgress(num:int, max:int):void {
trace(num+" "+max);
prgBar.setProgress(num, max);
}
Java-класс, который отправляет данные:
public class ProgressMessSender extends ServiceAdapter {
private ProgressDataSender thread;
public void startMessaging() {
if (thread == null) {
thread = new ProgressDataSender();
thread.start();
}
}
public void stop() {
thread.running = false;
MsgConstant.ProgValues = 0;
MsgConstant.MaxValues = -2;
thread = null;
}
@Override
public Object invoke(Message message) {
if (message.getBody().equals("STOP")) {
stop();
} else if (message.getBody().equals("START")) {
startMessaging();
}
return null;
}
public class ProgressDataSender extends Thread {
public volatile boolean running = true;
private Message createTestMessage() {
AsyncMessage msg = new AsyncMessage();
msg.setDestination("RandomDataPush");
msg.setClientId(UUIDUtils.createUUID());
msg.setMessageId(UUIDUtils.createUUID());
msg.setBody(MsgConstant.ProgValues+";"+MsgConstant.MaxValues);
return msg;
}
public void run() {
while(running) {
sendMessageToClients(createTestMessage());
secondsToSleep(5);
}
}
public void sendMessageToClients(Message msg) {
((MessageService) getDestination().getService()).pushMessageToClients(msg, false);
}
private void secondsToSleep(int seconds) {
try{
Thread.sleep(seconds * 10);
}catch(InterruptedException e){
System.out.println("TestServiceAdapter Interrupted while sending messages");
e.printStackTrace();
}
}
}
}
MsgConstant.MaxValues и MsgConstant.ProgValues - это статические переменные, которые соответственно устанавливаются и увеличиваются в классах заданий. Файлы конфигурации BlazeDS messaging-config.xml и services-config.xml изменены в соответствии с этим руководством .
Проблема :
Если я вызываю задание из модуля, индикатор выполнения запускается и останавливается правильно, все работает нормально. Однажды вызвав работу из модуля, если я вызываю другую работу из другого модуля, я всегда получаю следующее исключение:
[BlazeDS]Unexpected error encountered in Message Broker servlet
java.lang.IllegalStateException
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:421)
at flex.messaging.endpoints.BaseStreamingHTTPEndpoint.handleFlexClientStreamingOpenRequest(BaseStreamingHTTPEndpoint.java:764)
at flex.messaging.endpoints.BaseStreamingHTTPEndpoint.serviceStreamingRequest(BaseStreamingHTTPEndpoint.java:1055)
at flex.messaging.endpoints.BaseStreamingHTTPEndpoint.service(BaseStreamingHTTPEndpoint.java:460)
at flex.messaging.MessageBrokerServlet.service(MessageBrokerServlet.java:353)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:723)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:861)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:620)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Unknown Source)
Я искал в Интернете, но не могу найти работающее решение, и я не знаю, как решить эту проблему.
Более простой вопрос. Я пробовал другой способ отправить сообщение без расширений ServiceAdapter :
сообщений-config.xml:
<adapters>
<adapter-definition id="actionscript" class="flex.messaging.services.messaging.adapters.ActionScriptAdapter" default="true" />
</adapters>
<destination id="feed">
<properties>
<network>
<session-timeout>0</session-timeout>
</network>
<server>
<message-time-to-live>0</message-time-to-live>
<durable>false</durable>
</server>
</properties>
</destination>
services-config.xml совпадает с первым вопросом:
<channel-definition id="my-streaming-amf" class="mx.messaging.channels.StreamingAMFChannel">
<endpoint url="http://localhost:8080/ProgressBar2/messagebroker/streamingamf" class="flex.messaging.endpoints.StreamingAMFEndpoint"/>
<properties>
<!-- you don't need to set all these properties, this is just what we set, included for illustration, only -->
<idle-timeout-minutes>0</idle-timeout-minutes>
<max-streaming-clients>10</max-streaming-clients>
<server-to-client-heartbeat-millis>5000</server-to-client-heartbeat-millis>
<user-agent-settings>
<user-agent match-on="Safari" kickstart-bytes="2048" max-streaming-connections-per-session="10"/>
<user-agent match-on="MSIE" kickstart-bytes="2048" max-streaming-connections-per-session="15"/>
<user-agent match-on="Firefox" kickstart-bytes="2048" max-streaming-connections-per-session="10"/>
</user-agent-settings>
</properties>
</channel-definition>
и класс, который выдвигает сообщение:
private Message createTestMessage() {
AsyncMessage msg = new AsyncMessage();
msg.setDestination("feed");
msg.setClientId(clientID);
msg.setMessageId(UUIDUtils.createUUID());
msg.setBody(MsgConstant.ProgValues+";"+MsgConstant.MaxValues);
msgBroker.routeMessageToService(msg, null);
System.out.println("CREATE MESSAGE FOR CLIENT: "+msg.getBody().toString());
return msg;
}
public void run() {
while(running) {
createTestMessage();
secondsToSleep(1);
}
}
private void secondsToSleep(int seconds) {
try{
Thread.sleep(seconds * 100);
}catch(InterruptedException e){
System.out.println("TestServiceAdapter Interrupted while sending messages");
e.printStackTrace();
}
}
И этот способ работает лучше, чем первый, но метод msgBroker.routeMessageToService () отправляет сообщение (или блок сообщений) клиенту каждую секунду, и через некоторое время индикатор выполнения увеличивается с 10% до 50% или более, и это нехорошо. Есть ли способ быстро отправить сообщение?
Решение
Мне удается отправить сообщение с сервера на клиент. без определить пользовательский SeviceAdapter (класс Thread внутри расширяет возможности запуска ServiceAdapter при запуске приложения, и я не знаю, как этого избежать) следующим образом:
сообщения-config.xml
<default-channels>
<channel ref="my-streaming-amf"/>
<channel ref="my-polling-amf"/>
</default-channels>
<destination id="dest">
<properties>
<network>
<session-timeout>0</session-timeout>
</network>
<server>
<message-time-to-live>0</message-time-to-live>
<durable>false</durable>
</server>
</properties>
</destination>
услуга-config.xml
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://localhost:8080/ProgressBar2/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>false</polling-enabled>
<polling-interval-millis>4</polling-interval-millis>
</properties>
</channel-definition>
<channel-definition id="my-polling-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://localhost:8080/ProgressBar2/messagebroker/amfpolling" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>false</polling-enabled>
<polling-interval-seconds>4</polling-interval-seconds>
</properties>
</channel-definition>
<!-- definito per il push di dati asincroni da java a flex -->
<channel-definition id="my-streaming-amf" class="mx.messaging.channels.StreamingAMFChannel">
<endpoint url="http://localhost:8080/ProgressBar2/messagebroker/streamingamf" class="flex.messaging.endpoints.StreamingAMFEndpoint"/>
<properties>
<!-- you don't need to set all these properties, this is just what we set, included for illustration, only -->
<idle-timeout-minutes>0</idle-timeout-minutes>
<max-streaming-clients>10</max-streaming-clients>
<server-to-client-heartbeat-millis>5000</server-to-client-heartbeat-millis>
<user-agent-settings>
<user-agent match-on="Safari" kickstart-bytes="2048" max-streaming-connections-per-session="10"/>
<user-agent match-on="MSIE" kickstart-bytes="2048" max-streaming-connections-per-session="15"/>
<user-agent match-on="Firefox" kickstart-bytes="2048" max-streaming-connections-per-session="10"/>
</user-agent-settings>
</properties>
</channel-definition>
Java-класс расширяет поток, который отправляет сообщения:
MessageBroker msgBroker = MessageBroker.getMessageBroker(null);
public Message createTestMessage() {
AsyncMessage msg = new AsyncMessage();
msg.setDestination("feed");
msg.setClientId(UUIDUtils.createUUID());
msg.setMessageId(UUIDUtils.createUUID());
msg.setBody(MsgConstant.ProgValues+";"+MsgConstant.MaxValues);
msgBroker.routeMessageToService(msg, null);
return msg;
}
public void run() {
while(running) {
sendMessageToClients(createTestMessage());
secondsToSleep(1);
}
}
public void sendMessageToClients(Message msg) {
MessageService service = (MessageService) msgBroker.getService("message-service");
service.pushMessageToClients(msg, false);
}
Чем я определяю тег потребитель внутри .mxml, и я запускаю (и останавливаю) поток с помощью DataManager , когда начинается разработка.