package com.neverwinterdp.sparkngin; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.name.Named; import com.neverwinterdp.buffer.chronicle.MultiSegmentQueue; import com.neverwinterdp.buffer.chronicle.Segment; import com.neverwinterdp.message.Message; import com.neverwinterdp.server.module.ModuleProperties; import com.neverwinterdp.util.FileUtil; import com.neverwinterdp.yara.MetricRegistry; import com.neverwinterdp.yara.Timer; public class Sparkngin { @Inject @Named("sparkngin:forwarder-class") private String forwarderClass = NullDevMessageForwarder.class.getName() ; @Inject(optional = true) @Named("sparkngin:forwarder-reconnect") private long forwardReconnect = 10000 ; @Inject(optional = true) @Named("sparkngin:queue-dir") private String queueDir ; private MultiSegmentQueue<Message> queue ; private ForwarderThread forwarderThread ; private MetricRegistry mRegistry ; public Sparkngin() {} public Sparkngin(MetricRegistry mRegistry, MessageForwarder mforwarder, String storeDir) throws Exception { this.mRegistry = mRegistry ; queue = new MultiSegmentQueue<Message>(storeDir, 10000l) ; forwarderThread = new ForwarderThread(mforwarder) ; forwarderThread.start() ; } @Inject public void init(Injector container, ModuleProperties moduleProperties, MetricRegistry mRegistry) throws Exception { this.mRegistry = mRegistry ; Class<MessageForwarder> type = (Class<MessageForwarder>) Class.forName(forwarderClass) ; if(queueDir == null) { queueDir = moduleProperties.getDataDir() + "/sparkngin/queue" ; } queue = new MultiSegmentQueue<Message>(queueDir, 10000l) ; MessageForwarder forwarder = container.getInstance(type) ; forwarderThread = new ForwarderThread(forwarder) ; forwarderThread.start() ; } public String getQueueDir() { return this.queueDir ; } public boolean cleanup() throws Exception { queue.close(); FileUtil.removeIfExist(queueDir, false); queue = new MultiSegmentQueue<Message>(queueDir, 10000l) ; return true ; } /** * Enqueue the message and the forwarder thread will forward the message later. * @param message * @throws Exception */ public Ack push(Message message) { Timer.Context ctx = mRegistry.timer(Sparkngin.class.getSimpleName(), "push").time() ; Ack ack = new Ack() ; try { queue.writeObject(message); ack.setMessageId(message.getHeader().getKey()); ack.setStatus(Ack.Status.OK); } catch (Exception e) { ack.setMessage(e.getMessage()); ack.setStatus(Ack.Status.ERROR); } ctx.stop() ; return ack ; } public void close() { forwarderThread.exit = true; forwarderThread.interrupt() ; } public class ForwarderThread extends Thread { private MessageForwarder forwarder ; private boolean exit = false; public ForwarderThread(MessageForwarder forwarder) { super("SparknginForwarderThread"); setPriority(Thread.MAX_PRIORITY); this.forwarder = forwarder ; } /** * Forward the message immediatelly * @param message * @throws Exception */ void forward(Message message) throws Exception { Timer.Context forwardCtx = mRegistry.timer(Sparkngin.class.getSimpleName(), "forward").time() ; forwarder.forward(message); forwardCtx.close(); } public void forward() throws InterruptedException, Exception { while(true) { Segment<Message> segment = queue.nextReadSegment(1000l) ; if(segment != null) { segment.open(); Throwable forwardError = null; while(forwardError == null && segment.hasNext()) { Timer.Context readCtx = mRegistry.timer(Sparkngin.class.getSimpleName(), "read").time() ; Message message = segment.nextObject() ; readCtx.close(); try { forward(message) ; } catch(Exception ex) { forwardError = ex ; } } if(forwardError == null) { queue.commitReadSegment(segment); } else { segment.close(); forwarder.setError(forwardError); System.err.println("sparkngin forward error: " + forwardError.getMessage() + ". Retry in " + forwardReconnect + "ms"); Thread.sleep(forwardReconnect); forwarder.reconnect(); } } } } public void run() { try { forward() ; } catch (InterruptedException e) { } catch (Exception e) { e.printStackTrace(); } forwarder.close(); } } }