/* * JAME 6.2.1 * http://jame.sourceforge.net * * Copyright 2001, 2016 Andrea Medeghini * * This file is part of JAME. * * JAME is an application for creating fractals and other graphics artifacts. * * JAME is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * JAME is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with JAME. If not, see <http://www.gnu.org/licenses/>. * */ package net.sf.jame.queue.jxta; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.StringWriter; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ThreadFactory; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import net.jxta.discovery.DiscoveryEvent; import net.jxta.discovery.DiscoveryListener; import net.jxta.discovery.DiscoveryService; import net.jxta.document.Advertisement; import net.jxta.document.AdvertisementFactory; import net.jxta.document.MimeMediaType; import net.jxta.document.StructuredTextDocument; import net.jxta.endpoint.ByteArrayMessageElement; import net.jxta.endpoint.Message; import net.jxta.endpoint.MessageElement; import net.jxta.endpoint.StringMessageElement; import net.jxta.id.IDFactory; import net.jxta.peer.PeerID; import net.jxta.peergroup.PeerGroup; import net.jxta.peergroup.PeerGroupID; import net.jxta.pipe.PipeID; import net.jxta.pipe.PipeMsgEvent; import net.jxta.pipe.PipeMsgListener; import net.jxta.pipe.PipeService; import net.jxta.platform.NetworkConfigurator; import net.jxta.platform.NetworkManager; import net.jxta.protocol.DiscoveryResponseMsg; import net.jxta.protocol.ModuleClassAdvertisement; import net.jxta.protocol.ModuleSpecAdvertisement; import net.jxta.protocol.PipeAdvertisement; import net.jxta.util.JxtaBiDiPipe; import net.jxta.util.JxtaServerPipe; import net.jxta.util.PipeEventListener; import net.jxta.util.PipeStateListener; import net.sf.jame.core.util.DefaultThreadFactory; import net.sf.jame.queue.network.ServiceConsumer; import net.sf.jame.queue.network.ServiceEndpoint; import net.sf.jame.queue.network.ServiceException; import net.sf.jame.queue.network.ServiceListener; import net.sf.jame.queue.network.ServiceMessage; import net.sf.jame.queue.network.ServiceProcessor; import net.sf.jame.queue.network.ServiceProducer; import net.sf.jame.queue.network.ServiceSession; import net.sf.jame.queue.network.SessionHandler; import net.sf.jame.queue.network.SessionIDFactory; /** * @author Andrea Medeghini */ public class JXTANetworkService { public static final String JAME_JXTA_MULTICAST = "jame.jxta.multicast"; public static final String JAME_JXTA_RELAY_SEEDS = "jame.jxta.relay.seeds"; public static final String JAME_JXTA_RENDEZVOUS_SEEDS = "jame.jxta.rendezvous.seeds"; private static final MimeMediaType GZIP_MEDIA_TYPE = new MimeMediaType("application/gzip"); private static final Logger logger = Logger.getLogger(JXTANetworkService.class.getName()); private static final int CONNECTION_RETRY = 2; private static final int CONNECTION_TIMEOUT = 10000; private static final int CONNECTION_PAUSE = 5000; private static final int SERVERPIPE_TIMEOUT = 10 * 60 * 1000; private static final int RENDEZVOUS_TIMEOUT = 20000; private static final long ADVERTISEMENT_LIFETIME = 15 * 60 * 1000L; private static final long ADVERTISEMENT_EXPIRATION = 15 * 60 * 1000L; private static final long PUBLISH_POLLINGTIME = 18 * 60 * 1000L; private static final long DISCOVER_POLLINGTIME = 30 * 1000L; private static final long CLEANUP_POLLINGTIME = 30 * 1000L; private static final long CONTROL_POLLINGTIME = 30 * 1000L; private static final long SESSION_TIMEOUT = 120 * 1000L; private static final int MAX_LENGTH = 32 * 1024; private static final ThreadFactory factory = new DefaultThreadFactory("Thread", true, Thread.MIN_PRIORITY); private final Map<String, JXTAServiceSession> controlSessions = new HashMap<String, JXTAServiceSession>(); private final Map<String, JXTAServiceSession> serverSessions = new HashMap<String, JXTAServiceSession>(); private final Map<String, JXTAServiceSession> clientSessions = new HashMap<String, JXTAServiceSession>(); private final Map<String, PipeAdvertisement> advertisements = new HashMap<String, PipeAdvertisement>(); private final Map<String, ServiceEndpoint> endpoints = new HashMap<String, ServiceEndpoint>(); private final Set<String> invalidAdvertisements = new HashSet<String>(); private final Object monitor = new Object(); private final ServiceProcessor processor; private final String serviceURI; private final String serviceName; private final String serviceAuthor; private final String serviceVersion; private final File workdir; private DiscoveryService discovery; private NetworkManager manager; private JxtaServerPipe serverPipe; private PipeAdvertisement pipeadv; private Thread thread; private boolean running; private boolean dirty; /** * @param workdir * @param serviceURI * @param serviceName * @param serviceAuthor * @param serviceVersion * @param processor */ public JXTANetworkService(final File workdir, final String serviceURI, final String serviceName, final String serviceAuthor, final String serviceVersion, final ServiceProcessor processor) { this.workdir = workdir; this.serviceURI = serviceURI; this.serviceName = serviceName; this.serviceAuthor = serviceAuthor; this.serviceVersion = serviceVersion; this.processor = processor; } /** * */ public void start() { processor.start(); if (thread == null) { thread = factory.newThread(new NetworkHandler()); thread.setName(serviceName + " Service Thread"); running = true; thread.start(); } } /** * */ public void stop() { processor.stop(); if (thread != null) { running = false; thread.interrupt(); try { thread.join(); } catch (final InterruptedException e) { } thread = null; } } /** * @return */ public Collection<ServiceEndpoint> getEndpoints() { synchronized (endpoints) { return endpoints.values(); } } /** * @param pipeadv * @param listener * @return * @throws ServiceException */ public ServiceSession createSession(final PipeAdvertisement pipeadv, final ServiceListener listener) throws ServiceException { try { for (int i = 0; i < CONNECTION_RETRY; i++) { try { return createClientSession(pipeadv, listener); } catch (final Exception e) { } Thread.sleep(CONNECTION_PAUSE); } try { return createClientSession(pipeadv, listener); } catch (final Exception e) { synchronized (invalidAdvertisements) { invalidAdvertisements.add(pipeadv.getID().toString()); } logger.warning("Can't connect to pipe " + pipeadv.getID().toString()); throw new ServiceException(e); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warning("Can't connect to pipe " + pipeadv.getID().toString()); throw new ServiceException(e); } } private JXTAServiceSession createClientSession(final PipeAdvertisement pipeadv, final ServiceListener listener) throws IOException, Exception { final JXTAConnectionHandler handler = new JXTAConnectionHandler(); final JxtaBiDiPipe pipe = new JxtaBiDiPipe(manager.getNetPeerGroup(), pipeadv, CONNECTION_TIMEOUT, handler, true); handler.setPipe(pipe); final String sessionId = SessionIDFactory.newSessionId(); handler.setSessionId(sessionId); final JXTAServiceSession session = new JXTAServiceSession(sessionId, new JXTAClientServiceConsumer(handler, listener), new JXTAClientServiceProducer(handler)); logger.info("Client session created " + session.getSessionId()); synchronized (clientSessions) { clientSessions.put(session.getSessionId(), session); } return session; } /** * @param pipeadv */ public void invalidate(final PipeAdvertisement pipeadv) { synchronized (controlSessions) { synchronized (invalidAdvertisements) { final Iterator<JXTAServiceSession> sessionIterator = controlSessions.values().iterator(); while (sessionIterator.hasNext()) { final ServiceSession session = sessionIterator.next(); final PipeAdvertisement tmpPipeadv = advertisements.get(session.getSessionId()); if ((tmpPipeadv != null) && tmpPipeadv.getID().equals(pipeadv.getID())) { invalidAdvertisements.add(pipeadv.getID().toString()); advertisements.remove(session.getSessionId()); endpoints.remove(session.getSessionId()); sessionIterator.remove(); session.dispose(); } } } } } /** * @param pipeadv * @return */ public boolean isInvalidated(final PipeAdvertisement pipeadv) { synchronized (invalidAdvertisements) { return invalidAdvertisements.contains(pipeadv.getPipeID().toString()); } } private class NetworkHandler implements Runnable { /** * @see java.lang.Runnable#run() */ public void run() { boolean interrupted = false; final Thread discoveryThread = factory.newThread(new DiscoveryHandler()); discoveryThread.setName(serviceName + " Discovery Thread"); final Thread publishThread = factory.newThread(new PublishHandler()); publishThread.setName(serviceName + " Publish Thread"); final Thread cleanupThread = factory.newThread(new CleanupHandler()); cleanupThread.setName(serviceName + " Cleanup Thread"); final Thread controlThread = factory.newThread(new ControlHandler()); controlThread.setName(serviceName + " Control Thread"); try { final File cache = new File(workdir, "jxta"); manager = new NetworkManager(NetworkManager.ConfigMode.EDGE, serviceName, cache.toURI()); configureNetwork(manager); manager.startNetwork(); if (!manager.waitForRendezvousConnection(RENDEZVOUS_TIMEOUT)) { logger.warning("Failed to connect to Rendezvous service"); } final PeerGroup netPeerGroup = manager.getNetPeerGroup(); discovery = netPeerGroup.getDiscoveryService(); pipeadv = createPipeAdvertisement(manager, serviceName); discoveryThread.start(); publishThread.start(); cleanupThread.start(); controlThread.start(); serverPipe = new JxtaServerPipe(netPeerGroup, pipeadv, 100, SERVERPIPE_TIMEOUT); while (running) { try { final JxtaBiDiPipe pipe = serverPipe.accept(); if (pipe != null) { createServerSession(pipe); } } catch (final Exception e) { logger.info(e.getMessage()); } } } catch (final InterruptedException e) { interrupted = true; } catch (final Exception e) { e.printStackTrace(); } finally { discoveryThread.interrupt(); publishThread.interrupt(); cleanupThread.interrupt(); controlThread.interrupt(); try { discoveryThread.join(); } catch (InterruptedException e) { interrupted = true; } try { publishThread.join(); } catch (InterruptedException e) { interrupted = true; } try { cleanupThread.join(); } catch (InterruptedException e) { interrupted = true; } try { controlThread.join(); } catch (InterruptedException e) { interrupted = true; } synchronized (controlSessions) { controlSessions.clear(); } synchronized (serverSessions) { controlSessions.clear(); } synchronized (clientSessions) { controlSessions.clear(); } synchronized (advertisements) { advertisements.clear(); } synchronized (endpoints) { endpoints.clear(); } if (manager != null) { manager.stopNetwork(); } } if (interrupted) { Thread.currentThread().interrupt(); } } private void createServerSession(final JxtaBiDiPipe pipe) throws Exception { final JXTAConnectionHandler handler = new JXTAConnectionHandler(); handler.setPipe(pipe); final String sessionId = SessionIDFactory.newSessionId(); handler.setSessionId(sessionId); final SessionHandler sessionHandler = processor.createSessionHandler(); final JXTAServiceSession session = new JXTAServiceSession(sessionId, new JXTAServerServiceConsumer(handler, sessionHandler), new JXTAServerServiceProducer(handler)); sessionHandler.setSession(session); logger.info("Server session created " + session.getSessionId()); synchronized (serverSessions) { serverSessions.put(session.getSessionId(), session); } } } private static void configureNetwork(final NetworkManager manager) throws Exception { NetworkConfigurator config = manager.getConfigurator(); if (config.exists()) { config.load(); } else { createNewConfig(manager, config); } if (System.getProperty(JAME_JXTA_RELAY_SEEDS) != null) { URI seedingURI = new File(System.getProperty(JAME_JXTA_RELAY_SEEDS)).toURI(); logger.info("Add relay seeds: " + seedingURI); config.clearRelaySeedingURIs(); config.addRelaySeedingURI(seedingURI); } if (System.getProperty(JAME_JXTA_RENDEZVOUS_SEEDS) != null) { URI seedingURI = new File(System.getProperty(JAME_JXTA_RENDEZVOUS_SEEDS)).toURI(); logger.info("Add rendezvous seeds: " + seedingURI); config.clearRendezvousSeedingURIs(); config.addRdvSeedingURI(seedingURI); } if (Boolean.getBoolean(JAME_JXTA_MULTICAST)) { logger.info("Enable multicast"); config.setUseMulticast(true); } } private static void createNewConfig(final NetworkManager manager, final NetworkConfigurator config) throws IOException { config.setName("JAME"); config.setInfrastructureID(createInfrastructurePeerGroupID()); config.setInfrastructureDescriptionStr("JAME"); config.setInfrastructureName("JAME-JXTA"); config.setPeerID(createPeerID(manager.getInfrastructureID())); config.setUseMulticast(false); config.setTcpIncoming(true); config.setTcpOutgoing(true); config.setTcpEnabled(true); config.setTcpStartPort(9701); config.setTcpEndPort(9704); config.setTcpPort(9701); config.setHttpEnabled(false); config.save(); } private static final PeerGroupID createInfrastructurePeerGroupID() { return IDFactory.newPeerGroupID("JAME-PeerGroup".getBytes()); } private static final PeerID createPeerID(final PeerGroupID peerGroupId) { return IDFactory.newPeerID(peerGroupId, String.valueOf(System.currentTimeMillis()).getBytes()); } private static PipeID createPipeId(final PeerGroupID peerGroupId) { return IDFactory.newPipeID(peerGroupId, String.valueOf(System.currentTimeMillis()).getBytes()); } private static PipeAdvertisement createPipeAdvertisement(final NetworkManager manager, final String name) { final PipeAdvertisement advertisement = (PipeAdvertisement) AdvertisementFactory.newAdvertisement(PipeAdvertisement.getAdvertisementType()); advertisement.setPipeID(createPipeId(manager.getInfrastructureID())); advertisement.setType("JAME-JXTA"); advertisement.setType(PipeService.UnicastType); advertisement.setName(name); return advertisement; } @SuppressWarnings("unchecked") private static void printAdvertisement(final Advertisement adv) { try { final StructuredTextDocument doc = (StructuredTextDocument) adv.getDocument(MimeMediaType.XMLUTF8); final StringWriter out = new StringWriter(); doc.sendToWriter(out); logger.fine(out.toString()); out.close(); } catch (final Exception e) { } } private class CleanupHandler implements Runnable { /** * @see java.lang.Runnable#run() */ public void run() { try { while (running) { try { synchronized (controlSessions) { final Iterator<JXTAServiceSession> sessionIterator = controlSessions.values().iterator(); while (sessionIterator.hasNext()) { final JXTAServiceSession session = sessionIterator.next(); if (session.isExpired()) { logger.info("Dispose expired session " + session.getSessionId()); advertisements.remove(session.getSessionId()); endpoints.remove(session.getSessionId()); sessionIterator.remove(); session.dispose(); } else { try { session.consumeMessages(); } catch (final Exception e) { e.printStackTrace(); session.invalidate(); } } } } synchronized (serverSessions) { final Iterator<JXTAServiceSession> sessionIterator = serverSessions.values().iterator(); while (sessionIterator.hasNext()) { final JXTAServiceSession session = sessionIterator.next(); if (session.isExpired()) { logger.info("Dispose expired session " + session.getSessionId()); sessionIterator.remove(); session.dispose(); } else { try { session.consumeMessages(); } catch (final Exception e) { e.printStackTrace(); session.invalidate(); } } } } synchronized (clientSessions) { final Iterator<JXTAServiceSession> sessionIterator = clientSessions.values().iterator(); while (sessionIterator.hasNext()) { final JXTAServiceSession session = sessionIterator.next(); if (session.isExpired()) { logger.info("Dispose expired session " + session.getSessionId()); sessionIterator.remove(); session.dispose(); } else { try { session.consumeMessages(); } catch (final Exception e) { e.printStackTrace(); session.invalidate(); } } } } } catch (final Exception e) { e.printStackTrace(); } synchronized (monitor) { if (!dirty) { monitor.wait(CLEANUP_POLLINGTIME); } // else { // Thread.yield(); // } dirty = false; } } } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } synchronized (controlSessions) { for (final ServiceSession session : controlSessions.values()) { logger.info("Dispose session " + session.getSessionId()); session.dispose(); } controlSessions.clear(); } synchronized (serverSessions) { for (final ServiceSession session : serverSessions.values()) { logger.info("Dispose session " + session.getSessionId()); session.dispose(); } serverSessions.clear(); } synchronized (clientSessions) { for (final ServiceSession session : clientSessions.values()) { logger.info("Dispose session " + session.getSessionId()); session.dispose(); } clientSessions.clear(); } } } private class PublishHandler implements Runnable { /** * @see java.lang.Runnable#run() */ public void run() { final ModuleClassAdvertisement mcadv = (ModuleClassAdvertisement) AdvertisementFactory.newAdvertisement(ModuleClassAdvertisement.getAdvertisementType()); mcadv.setName("JXTAMOD:" + serviceName); mcadv.setDescription(serviceName); mcadv.setModuleClassID(IDFactory.newModuleClassID()); final ModuleSpecAdvertisement msadv = (ModuleSpecAdvertisement) AdvertisementFactory.newAdvertisement(ModuleSpecAdvertisement.getAdvertisementType()); msadv.setName("JXTASPEC:" + serviceName); mcadv.setDescription(serviceName); msadv.setCreator(serviceAuthor); msadv.setVersion(serviceVersion); msadv.setModuleSpecID(IDFactory.newModuleSpecID(mcadv.getModuleClassID())); msadv.setSpecURI(serviceURI); msadv.setPipeAdvertisement(pipeadv); try { while (running) { try { if (logger.isLoggable(Level.FINE)) { logger.fine("Publish module class advertisement"); printAdvertisement(mcadv); } discovery.publish(mcadv, ADVERTISEMENT_LIFETIME, ADVERTISEMENT_EXPIRATION); discovery.remotePublish(mcadv, ADVERTISEMENT_EXPIRATION); if (logger.isLoggable(Level.FINE)) { logger.fine("Publish module spec advertisement"); printAdvertisement(msadv); } discovery.publish(msadv, ADVERTISEMENT_LIFETIME, ADVERTISEMENT_EXPIRATION); discovery.remotePublish(msadv, ADVERTISEMENT_EXPIRATION); } catch (final Exception e) { e.printStackTrace(); } Thread.sleep(PUBLISH_POLLINGTIME); } } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } } } private class ControlHandler implements Runnable { /** * @see java.lang.Runnable#run() */ public void run() { try { while (running) { synchronized (controlSessions) { final Iterator<JXTAServiceSession> sessionIterator = controlSessions.values().iterator(); while (sessionIterator.hasNext()) { final JXTAServiceSession session = sessionIterator.next(); try { session.sendKeepAliveMessage(); } catch (final Exception e) { session.invalidate(); e.printStackTrace(); } } } Thread.sleep(CONTROL_POLLINGTIME); } } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } } } private class DiscoveryHandler implements Runnable, DiscoveryListener, ServiceListener { /** * @see java.lang.Runnable#run() */ public void run() { discovery.addDiscoveryListener(this); try { while (running) { try { discovery.getRemoteAdvertisements(null, DiscoveryService.ADV, "Name", "JXTASPEC:" + serviceName, 100); } catch (final Exception e) { logger.warning(e.getMessage()); } Thread.sleep(DISCOVER_POLLINGTIME); } } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } discovery.removeDiscoveryListener(this); } /** * @see net.jxta.discovery.DiscoveryListener#discoveryEvent(net.jxta.discovery.DiscoveryEvent) */ public void discoveryEvent(final DiscoveryEvent e) { final DiscoveryResponseMsg response = e.getResponse(); logger.fine("Got a discovery response [" + response.getResponseCount() + " elements] from peer " + e.getSource()); final Enumeration<Advertisement> advEnum = response.getAdvertisements(); if (advEnum != null) { while (advEnum.hasMoreElements()) { final Advertisement adv = advEnum.nextElement(); if (adv.getAdvType().equals(ModuleSpecAdvertisement.getAdvertisementType())) { final ModuleSpecAdvertisement msadv = (ModuleSpecAdvertisement) adv; logger.fine("Got a module spec advertisement"); printAdvertisement(msadv); synchronized (controlSessions) { synchronized (invalidAdvertisements) { if (!pipeadv.getID().toString().equals(msadv.getPipeAdvertisement().getID().toString()) && !controlSessions.containsKey(msadv.getPipeAdvertisement().getID().toString()) && !invalidAdvertisements.contains(msadv.getPipeAdvertisement().getID().toString())) { Thread thread = factory.newThread(new DiscoveryRunnable(msadv, this)); thread.setName("Discovery task for pipe " + msadv.getPipeAdvertisement().getPipeID().toString()); thread.start(); } } } } } } } /** * @see net.sf.jame.queue.network.ServiceListener#onMessage(net.sf.jame.queue.network.ServiceMessage) */ public void onMessage(final ServiceMessage message) throws ServiceException { } } private class DiscoveryRunnable implements Runnable, ServiceListener { private final ModuleSpecAdvertisement msadv; private final ServiceListener listener; /** * @param msadv */ public DiscoveryRunnable(final ModuleSpecAdvertisement msadv, final ServiceListener listener) { this.msadv = msadv; this.listener = listener; } /** * @see java.lang.Runnable#run() */ public void run() { try { JXTAServiceSession session = (JXTAServiceSession) createSession(msadv.getPipeAdvertisement(), listener); synchronized (controlSessions) { final JXTAServiceEndpoint endpoint = new JXTAServiceEndpoint(JXTANetworkService.this, msadv.getPipeAdvertisement()); controlSessions.put(msadv.getPipeAdvertisement().getID().toString(), session); advertisements.put(session.getSessionId(), msadv.getPipeAdvertisement()); endpoints.put(session.getSessionId(), endpoint); logger.info("Session " + session.getSessionId() + " bound to Endpoint " + endpoint); } } catch (ServiceException x) { logger.warning("Can't create control session for pipe " + msadv.getPipeAdvertisement().getID().toString()); } } /** * @see net.sf.jame.queue.network.ServiceListener#onMessage(net.sf.jame.queue.network.ServiceMessage) */ public void onMessage(final ServiceMessage message) throws ServiceException { } } private class JXTAConnectionHandler implements PipeMsgListener, PipeStateListener, PipeEventListener { private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); private final List<Message> newMessages = new ArrayList<Message>(); private final List<Message> messages = new ArrayList<Message>(); private final byte[] buffer = new byte[MAX_LENGTH]; private ServiceListener listener; private String lastCorrelationId; private long lastReceivedTime; private int lastPartReceived; private JxtaBiDiPipe pipe; private String sessionId; /** * */ public JXTAConnectionHandler() { lastReceivedTime = System.currentTimeMillis(); } /** * @param pipe */ public void setPipe(final JxtaBiDiPipe pipe) { this.pipe = pipe; pipe.setMessageListener(this); pipe.setPipeEventListener(this); pipe.setPipeStateListener(this); } /** * @param sessionId */ public void setSessionId(final String sessionId) { this.sessionId = sessionId; } /** * @param session */ public void setServiceListener(final ServiceListener listener) { this.listener = listener; } /** * */ public void dispose() { synchronized (this) { if (pipe != null) { pipe.setMessageListener(null); pipe.setPipeEventListener(null); pipe.setPipeStateListener(null); try { if (pipe.isBound()) { pipe.close(); } } catch (final IOException e) { } pipe = null; } } synchronized (messages) { messages.clear(); } } /** * @return */ public synchronized boolean isDisposed() { return pipe == null; } /** * @return */ public synchronized boolean isConnected() { return (pipe != null) && pipe.isBound(); } /** * @return */ public synchronized boolean isTimeout() { return (System.currentTimeMillis() - lastReceivedTime) > SESSION_TIMEOUT; } /** * @param message * @throws Exception */ public synchronized void sendMessage(final ServiceMessage message) throws ServiceException { if (!isConnected()) { return; } ByteArrayOutputStream baos = null; GZIPOutputStream zos = null; ObjectOutputStream oos = null; try { baos = new ByteArrayOutputStream(); zos = new GZIPOutputStream(baos); oos = new ObjectOutputStream(zos); oos.writeObject(message); oos.close(); zos.finish(); zos.close(); baos.close(); byte[] data = baos.toByteArray(); int maxLength = MAX_LENGTH; int offset = 0; int parts = 1; int part = 1; if (logger.isLoggable(Level.FINE)) { logger.fine("[sessionId = " + sessionId + "] Sending message " + message + " (dataLength = " + data.length + " bytes)"); } if (data.length > maxLength) { parts += data.length / maxLength; for (int i = 1; i < parts; i++) { sendDataMessage(message.getMessageId(), parts, part, data, offset, maxLength); offset += maxLength; part += 1; } sendDataMessage(message.getMessageId(), parts, part, data, offset, data.length % maxLength); } else { sendDataMessage(message.getMessageId(), parts, part, data, 0, data.length); } } catch (final Exception e) { throw new ServiceException(e); } finally { if (oos != null) { try { oos.close(); } catch (Exception e) { } } if (zos != null) { try { zos.close(); } catch (Exception e) { } } if (baos != null) { try { baos.close(); } catch (Exception e) { } } } } private void sendDataMessage(final String correlationId, final int parts, final int part, final byte[] data, final int offset, final int length) throws ServiceException { try { final Message msg = new Message(); msg.addMessageElement(new StringMessageElement("correlationId", correlationId, null)); msg.addMessageElement(new StringMessageElement("messageParts", String.valueOf(parts), null)); msg.addMessageElement(new StringMessageElement("messagePart", String.valueOf(part), null)); msg.addMessageElement(new StringMessageElement("messageType", "data", null)); msg.addMessageElement(new ByteArrayMessageElement("messageBody", GZIP_MEDIA_TYPE, data, offset, length, null)); if (logger.isLoggable(Level.FINE)) { logger.fine("[sessionId = " + sessionId + "] Sending data message (correlationId = " + correlationId.toString() + ", part = " + part + " of " + parts + ", size = " + msg.getByteLength() + " bytes)"); } pipe.sendMessage(msg); } catch (final Exception e) { logger.warning("[sessionId = " + sessionId + "] Failed to send message " + correlationId.toString() + " (part = " + part + " of " + parts + ")"); throw new ServiceException(e); } } /** * @throws ServiceException */ public synchronized void sendKeepAliveMessage() throws ServiceException { if (!isConnected()) { return; } try { final Message msg = new Message(); msg.addMessageElement(new StringMessageElement("messageType", "keepalive", null)); if (logger.isLoggable(Level.FINE)) { logger.fine("[sessionId = " + sessionId + "] Sending keepalive message (size = " + msg.getByteLength() + " bytes)"); } pipe.sendMessage(msg); } catch (final Exception e) { throw new ServiceException(e); } } /** * @throws ServiceException */ public synchronized void sendAckMessage() throws ServiceException { if (!isConnected()) { return; } try { final Message msg = new Message(); msg.addMessageElement(new StringMessageElement("messageType", "ack", null)); if (logger.isLoggable(Level.FINE)) { logger.fine("[sessionId = " + sessionId + "] Sending ack message (size = " + msg.getByteLength() + " bytes)"); } pipe.sendMessage(msg); } catch (final Exception e) { throw new ServiceException(e); } } /** * @throws Exception */ public void consumeMessages() throws ServiceException { try { synchronized (messages) { newMessages.clear(); newMessages.addAll(messages); messages.clear(); } for (final Message message : newMessages) { final MessageElement messageType = message.getMessageElement("messageType"); if ("keepalive".equals(messageType.toString())) { logger.fine("[sessionId = " + sessionId + "] Received keepalive message (size = " + message.getByteLength() + " bytes)"); sendAckMessage(); } else if ("ack".equals(messageType.toString())) { logger.fine("[sessionId = " + sessionId + "] Received ack message (size = " + message.getByteLength() + " bytes)"); } else if ("data".equals(messageType.toString())) { final MessageElement correlationId = message.getMessageElement("correlationId"); final MessageElement messageParts = message.getMessageElement("messageParts"); final MessageElement messagePart = message.getMessageElement("messagePart"); final MessageElement messageBody = message.getMessageElement("messageBody"); int parts = Integer.parseInt(messageParts.toString()); int part = Integer.parseInt(messagePart.toString()); if ((lastCorrelationId != null) && !lastCorrelationId.equals(correlationId.toString())) { lastCorrelationId = null; lastPartReceived = 0; baos.reset(); throw new Exception("[sessionId = " + sessionId + "] Invalid correlationId " + correlationId.toString()); } if ((lastCorrelationId == null) || lastCorrelationId.equals(correlationId.toString())) { if (logger.isLoggable(Level.FINE)) { logger.fine("[sessionId = " + sessionId + "] Received data message (correlationId = " + correlationId.toString() + ", part = " + part + " of " + parts + ", size = " + message.getByteLength() + " bytes)"); } lastCorrelationId = correlationId.toString(); if (part > lastPartReceived) { lastPartReceived = part; int length = 0; InputStream is = messageBody.getStream(); while ((length = is.read(buffer)) > 0) { baos.write(buffer, 0, length); } if (lastPartReceived == parts) { byte[] data = baos.toByteArray(); lastCorrelationId = null; lastPartReceived = 0; baos.reset(); if (listener != null) { ByteArrayInputStream bais = null; ObjectInputStream ois = null; GZIPInputStream zis = null; try { bais = new ByteArrayInputStream(data); zis = new GZIPInputStream(bais); ois = new ObjectInputStream(zis); final ServiceMessage msg = (ServiceMessage) ois.readObject(); if (logger.isLoggable(Level.FINE)) { logger.fine("[sessionId = " + sessionId + "] Received message " + msg + " (dataLength = " + data.length + " bytes)"); } consumeMessage(msg); } catch (Exception e) { throw new Exception("[sessionId = " + sessionId + "] Failed to decode message correlationId = " + correlationId.toString() + " (part = " + part + " of " + parts + ")"); } finally { if (ois != null) { try { ois.close(); } catch (Exception e) { } } if (zis != null) { try { zis.close(); } catch (Exception e) { } } if (bais != null) { try { bais.close(); } catch (Exception e) { } } } } } } } } } newMessages.clear(); } catch (final Exception e) { throw new ServiceException(e); } } /** * @param message * @return */ private boolean isMessageValid(final Message message) { final MessageElement correlationId = message.getMessageElement("correlationId"); final MessageElement messageParts = message.getMessageElement("messageParts"); final MessageElement messagePart = message.getMessageElement("messagePart"); final MessageElement messageType = message.getMessageElement("messageType"); final MessageElement messageBody = message.getMessageElement("messageBody"); if ("data".equals(messageType.toString())) { return (correlationId != null) && (messageParts != null) && (messagePart != null) && (messageType != null) && (messageBody != null); } else if ("keepalive".equals(messageType.toString())) { return true; } else if ("ack".equals(messageType.toString())) { return true; } return false; } /** * @see net.jxta.pipe.PipeMsgListener#pipeMsgEvent(net.jxta.pipe.PipeMsgEvent) */ public synchronized void pipeMsgEvent(final PipeMsgEvent e) { try { synchronized (messages) { final Message message = e.getMessage(); if (message != null) { if (isMessageValid(message)) { lastReceivedTime = System.currentTimeMillis(); messages.add(message); } } } synchronized (monitor) { dirty = true; monitor.notify(); } } catch (Exception x) { x.printStackTrace(); } } /** * @see net.jxta.util.PipeStateListener#stateEvent(java.lang.Object, int) */ public void stateEvent(final Object source, final int event) { } /** * @see net.jxta.util.PipeEventListener#pipeEvent(int) */ public void pipeEvent(final int event) { } /** * @param message * @throws ServiceException */ public void consumeMessage(final ServiceMessage message) throws ServiceException { if (listener != null) { listener.onMessage(message); } } } private class JXTAClientServiceProducer implements ServiceProducer { private JXTAConnectionHandler handler; /** * @param handler */ public JXTAClientServiceProducer(final JXTAConnectionHandler handler) { this.handler = handler; } /** * @see net.sf.jame.queue.network.ServiceProducer#sendMessage(net.sf.jame.queue.network.ServiceMessage) */ public void sendMessage(final ServiceMessage message) throws ServiceException { if (handler != null) { handler.sendMessage(message); } } /** * @see net.sf.jame.queue.network.ServiceProducer#dispose() */ public void dispose() { if (handler != null) { handler.dispose(); handler = null; } } /** * @see net.sf.jame.queue.network.ServiceProducer#sendKeepAliveMessage() */ public void sendKeepAliveMessage() throws ServiceException { if (handler != null) { handler.sendKeepAliveMessage(); } } /** * @see net.sf.jame.queue.network.ServiceProducer#sendAckMessage() */ public void sendAckMessage() throws ServiceException { if (handler != null) { handler.sendAckMessage(); } } } private class JXTAServerServiceProducer implements ServiceProducer { private JXTAConnectionHandler handler; /** * @param handler */ public JXTAServerServiceProducer(final JXTAConnectionHandler handler) { this.handler = handler; } /** * @see net.sf.jame.queue.network.ServiceProducer#sendMessage(net.sf.jame.queue.network.ServiceMessage) */ public void sendMessage(final ServiceMessage message) throws ServiceException { if (handler != null) { handler.sendMessage(message); } } /** * @see net.sf.jame.queue.network.ServiceProducer#dispose() */ public void dispose() { if (handler != null) { handler.dispose(); handler = null; } } /** * @see net.sf.jame.queue.network.ServiceProducer#sendKeepAliveMessage() */ public void sendKeepAliveMessage() throws ServiceException { if (handler != null) { handler.sendKeepAliveMessage(); } } /** * @see net.sf.jame.queue.network.ServiceProducer#sendAckMessage() */ public void sendAckMessage() throws ServiceException { if (handler != null) { handler.sendAckMessage(); } } } private class JXTAClientServiceConsumer implements ServiceConsumer { private JXTAConnectionHandler handler; private ServiceListener listener; /** * @param handler * @param listener */ public JXTAClientServiceConsumer(final JXTAConnectionHandler handler, final ServiceListener listener) { this.handler = handler; this.listener = listener; handler.setServiceListener(this); } /** * @see net.sf.jame.queue.network.ServiceConsumer#onMessage(net.sf.jame.queue.network.ServiceMessage) */ public void onMessage(final ServiceMessage message) throws ServiceException { if (listener != null) { listener.onMessage(message); } } /** * @see net.sf.jame.queue.network.ServiceConsumer#consumeMessages() */ public void consumeMessages() throws ServiceException { if (handler != null) { handler.consumeMessages(); } } /** * @see net.sf.jame.queue.network.ServiceConsumer#isTimeout() */ public boolean isTimeout() { if (handler != null) { return handler.isTimeout(); } return true; } /** * @see net.sf.jame.queue.network.ServiceConsumer#dispose() */ public void dispose() { if (handler != null) { handler.dispose(); handler = null; } listener = null; } /** * @see net.sf.jame.queue.network.ServiceConsumer#consumeMessage(net.sf.jame.queue.network.ServiceMessage) */ public void consumeMessage(final ServiceMessage message) throws ServiceException { if (handler != null) { handler.consumeMessage(message); } } } private class JXTAServerServiceConsumer implements ServiceConsumer { private JXTAConnectionHandler handler; private ServiceListener listener; /** * @param handler * @param listener */ public JXTAServerServiceConsumer(final JXTAConnectionHandler handler, final ServiceListener listener) { this.handler = handler; this.listener = listener; handler.setServiceListener(this); } /** * @see net.sf.jame.queue.network.ServiceListener#onMessage(net.sf.jame.queue.network.ServiceMessage) */ public void onMessage(final ServiceMessage message) throws ServiceException { if (listener != null) { listener.onMessage(message); } } /** * @see net.sf.jame.queue.network.ServiceConsumer#consumeMessages() */ public void consumeMessages() throws ServiceException { if (handler != null) { handler.consumeMessages(); } } /** * @see net.sf.jame.queue.network.ServiceConsumer#isTimeout() */ public boolean isTimeout() { if (handler != null) { return handler.isTimeout(); } return true; } /** * @see net.sf.jame.queue.network.ServiceConsumer#dispose() */ public void dispose() { if (handler != null) { handler.dispose(); handler = null; } listener = null; } /** * @see net.sf.jame.queue.network.ServiceConsumer#consumeMessage(net.sf.jame.queue.network.ServiceMessage) */ public void consumeMessage(final ServiceMessage message) throws ServiceException { if (handler != null) { handler.consumeMessage(message); } } } private class JXTAServiceSession extends ServiceSession { /** * @param sessionId * @param consumer * @param producer * @throws Exception */ public JXTAServiceSession(final String sessionId, final JXTAClientServiceConsumer consumer, final JXTAClientServiceProducer producer) throws Exception { super(sessionId, consumer, producer); } /** * @param sessionId * @param consumer * @param producer * @throws Exception */ public JXTAServiceSession(final String sessionId, final JXTAServerServiceConsumer consumer, final JXTAServerServiceProducer producer) throws Exception { super(sessionId, consumer, producer); } /** * @see net.sf.jame.queue.network.ServiceSession#dispose() */ @Override public void dispose() { logger.fine("Disposed session " + getSessionId()); super.dispose(); synchronized (monitor) { dirty = true; monitor.notify(); } } /** * @see net.sf.jame.queue.network.ServiceSession#isLocalSession() */ @Override public boolean isLocalSession() { return false; } /** * @param message * @throws Exception */ public void onMessage(final ServiceMessage message) throws ServiceException { if (isExpired()) { throw new ServiceException("Expired session " + sessionId); } consumer.onMessage(message); } /** * @param message * @throws Exception */ public void consumeMessage(final ServiceMessage message) throws ServiceException { if (isExpired()) { throw new ServiceException("Expired session " + sessionId); } consumer.consumeMessage(message); } /** * @throws Exception */ public void consumeMessages() throws ServiceException { if (isExpired()) { throw new ServiceException("Expired session " + sessionId); } consumer.consumeMessages(); } } }