/*
* Copyright 2013-2014 High-Level Technologies
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.zodiark.service.broadcaster;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.atmosphere.cpr.AtmosphereFramework;
import org.atmosphere.cpr.AtmosphereRequest;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResponse;
import org.atmosphere.cpr.Broadcaster;
import org.atmosphere.cpr.MetaBroadcaster;
import org.atmosphere.cpr.Serializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zodiark.protocol.Envelope;
import org.zodiark.protocol.Message;
import org.zodiark.server.EnvelopeDigester;
import org.zodiark.server.EventBus;
import org.zodiark.server.Reply;
import javax.inject.Inject;
import org.zodiark.server.annotation.On;
import org.zodiark.service.EndpointAdapter;
import org.zodiark.service.publisher.PublisherEndpoint;
import org.zodiark.service.subscriber.SubscriberEndpoint;
import java.io.IOException;
import java.io.OutputStream;
import static org.atmosphere.cpr.ApplicationConfig.SUSPENDED_ATMOSPHERE_RESOURCE_UUID;
import static org.zodiark.protocol.Paths.BROADCASTER_CREATE;
import static org.zodiark.protocol.Paths.BROADCASTER_TRACK;
import static org.zodiark.protocol.Paths.BROADCAST_TO_ALL;
/**
* Create {@link Broadcaster}, or channel, used by {@link PublisherEndpoint} and {@link SubscriberEndpoint} to communicate.
* <p/>
* A call to the database is always executed in order to retrieve the session information like banned words, etc.
*/
@On({"/chat", "/broadcaster"})
public class BroadcastServiceImpl implements BroadcasterService {
private Logger logger = LoggerFactory.getLogger(BroadcastServiceImpl.class);
@Inject
public ObjectMapper mapper;
@Inject
public EventBus eventBus;
/**
* {@inheritDoc}
*/
@Override
public void reactTo(Envelope e, AtmosphereResource r, Reply reply) {
dispatchMessage(e, r);
}
/**
* {@inheritDoc}
*/
@Override
public void reactTo(String path, Object message, Reply reply) {
switch (path) {
case BROADCASTER_CREATE:
createBroadcaster(PublisherEndpoint.class.cast(message));
break;
case BROADCASTER_TRACK:
associatedSubscriber(SubscriberEndpoint.class.cast(message));
break;
case BROADCAST_TO_ALL: {
broadcastToAll(message);
break;
}
default:
logger.error("Unhandled message {}", message);
}
}
/**
* {@inheritDoc}
*/
@Override
public void broadcastToAll(Object message) {
if (PublisherEndpoint.class.isAssignableFrom(message.getClass())) {
PublisherEndpoint p = PublisherEndpoint.class.cast(message);
// TODO: Service for retrieving the proper message.
String m = p + " is about to start a new streaming session";
logger.debug("About to broadcast to all connected Endpoint {}", m);
MetaBroadcaster.getDefault().broadcastTo("/*", m);
}
}
public void dispatchMessage(final Envelope e, final AtmosphereResource r) {
final AtmosphereRequest request = r.getRequest();
final AtmosphereResponse response = r.getResponse();
logger.debug("Dispatch {}", e.getMessage().getPath());
request.pathInfo(e.getMessage().getPath()).body(e.getMessage().getData());
r.getRequest().setAttribute(EnvelopeDigester.REQUEST_REDISPATCHED, Boolean.TRUE);
try {
r.getAtmosphereConfig().framework().doCometSupport(request, response);
} catch (Exception e1) {
eventBus.message("/error", e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void createBroadcaster(final PublisherEndpoint p) {
endpointBroadcaster(p.resource(), p);
}
private void endpointBroadcaster(final AtmosphereResource r, final EndpointAdapter p) {
final String uuid = p == null ? r.uuid() : p.uuid();
AtmosphereFramework f = r.getAtmosphereConfig().framework();
final Broadcaster b = f.getBroadcasterFactory().lookup("/chat/" + uuid, true);
// eventBus.message(DB_GET_WORD_PASSSTHROUGH, p, new Reply<BroadcasterDBResult>() {
//
// @Override
// public void ok(BroadcasterDBResult result) {
// b.getBroadcasterConfig().addFilter(new WordBroadcastFilter(result));
// }
//
// @Override
// public void fail(BroadcasterDBResult result) {
// logger.error("{}", result);
// }
// });
r.setBroadcaster(b);
r.getRequest().setAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID, null);
b.addAtmosphereResource(r);
logger.debug("Created {}", b.getID());
r.setSerializer(new Serializer() {
@Override
public void write(OutputStream os, Object o) throws IOException {
if (Envelope.class.isAssignableFrom(o.getClass())) {
os.write(mapper.writeValueAsBytes(o));
} else {
r.getRequest().removeAttribute(EnvelopeDigester.REQUEST_REDISPATCHED);
b.removeAtmosphereResource(r);
Message m = new Message();
m.setPath("/chat/" + uuid);
m.setData(o.toString());
byte[] message = mapper.writeValueAsBytes(Envelope.newServerToSubscriberResponse(uuid, m));
os.write(message);
}
}
});
logger.debug("Endpoint {} created Broadcaster {}", p, b);
}
/**
* {@inheritDoc}
*/
@Override
public void associatedSubscriber(SubscriberEndpoint s) {
endpointBroadcaster(s.resource(), s.publisherEndpoint());
}
}