All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.uhn.hl7v2.hoh.raw.client.HohRawClientMultithreaded Maven / Gradle / Ivy

There is a newer version: 2.5.1
Show newest version
package ca.uhn.hl7v2.hoh.raw.client;

import java.io.IOException;
import java.net.Socket;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import ca.uhn.hl7v2.hoh.api.IClientMultithreaded;
import ca.uhn.hl7v2.hoh.util.Validate;

/**
 * 

* Raw message sender using the HL7 over HTTP specification which uses a * {@link ScheduledExecutorService} to provide advanced functionality such as * persistent connections which time out and close automatically. *

*

* This connector uses an executor service which can start worker threads, so * use caution if embedding within a J2EE container. *

*/ public class HohRawClientMultithreaded extends AbstractRawClient implements IClientMultithreaded { /** * Default {@link #setSocketTimeout(long) Socket Timeout}, 10000ms */ public static final long DEFAULT_SOCKET_TIMEOUT = 10000; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(HohRawClientMultithreaded.class); private final ScheduledExecutorService myExecutorService; private Map myIdleSocketsToTimeBecameIdle = new IdentityHashMap(); private final SimpleDateFormat myLogTimeFormat = new SimpleDateFormat("HH:mm:ss,SSS"); private boolean myReapingScheduled; private long mySocketTimeout = DEFAULT_SOCKET_TIMEOUT; /** * Constructor */ public HohRawClientMultithreaded() { myExecutorService = Executors.newScheduledThreadPool(1); } /** * Constructor * * @param theHost * The HOST (name/address). E.g. "192.168.1.1" * @param thePort * The PORT. E.g. "8080" * @param thePath * The path being requested (must either be blank or start with * '/' and contain a path). E.g. "/Apps/Receiver.jsp" */ public HohRawClientMultithreaded(String theHost, int thePort, String thePath) { this(); setHost(theHost); setPort(thePort); setUriPath(thePath); } /** * Constructor * * @param theHost * The HOST (name/address). E.g. "192.168.1.1" * @param thePort * The PORT. E.g. "8080" * @param theUriPath * The URI path being requested (must either be blank or start with * '/' and contain a path). E.g. "/Apps/Receiver.jsp" * @param theExecutorService * The executor service to use for detecting stale sockets */ public HohRawClientMultithreaded(String theHost, int thePort, String theUriPath, ScheduledExecutorService theExecutorService) { super(theHost, thePort, theUriPath); Validate.notNull(theExecutorService, "executorService"); myExecutorService = theExecutorService; } /** * Constructor * * @param theUrl * The URL to connect to * @param theExecutorService * The executor service to use for detecting stale sockets */ public HohRawClientMultithreaded(URL theUrl) { this(); setUrl(theUrl); } /** * Constructor * * @param theUrl * The URL to connect to * @param theExecutorService * The executor service to use for detecting stale sockets */ public HohRawClientMultithreaded(URL theUrl, ScheduledExecutorService theExecutorService) { super(theUrl); Validate.notNull(theExecutorService, "executorService"); myExecutorService = theExecutorService; } @Override protected synchronized Socket provideSocket() throws IOException { Socket retVal; if (myIdleSocketsToTimeBecameIdle.size() == 0) { ourLog.info("Creating new remote connection to {}:{}", getHost(), getPort()); retVal = connect(); } else { retVal = myIdleSocketsToTimeBecameIdle.keySet().iterator().next(); myIdleSocketsToTimeBecameIdle.remove(retVal); } return retVal; } /** * Returns a socket to the pool. If the socket is closed, it will * not be returned. */ @Override protected synchronized void returnSocket(Socket theSocket) { if (theSocket.isClosed()) { return; } long now = System.currentTimeMillis(); // TODO: reap immediately if timeout is 0 if (ourLog.isDebugEnabled()) { if (mySocketTimeout == -1) { ourLog.debug("Returning socket, will not attempt to reap"); } else { ourLog.debug("Returning socket, will be eligible for reaping at " + myLogTimeFormat.format(new Date(now + mySocketTimeout))); } } myIdleSocketsToTimeBecameIdle.put(theSocket, now); scheduleReaping(); } private void scheduleReaping() { long now = System.currentTimeMillis(); if (myReapingScheduled) { ourLog.debug("Reaping already scheduled"); return; } if (myIdleSocketsToTimeBecameIdle.size() < 1) { return; } if (mySocketTimeout == -1) { return; } long earliestReapingTime = Long.MAX_VALUE; for (Long next : myIdleSocketsToTimeBecameIdle.values()) { long nextReapingTime = next + mySocketTimeout; if (nextReapingTime < earliestReapingTime) { earliestReapingTime = nextReapingTime; } } long delay = earliestReapingTime - now; if (ourLog.isDebugEnabled()) { ourLog.debug("Scheduling socket reaping in {} ms at {}", delay, myLogTimeFormat.format(new Date(earliestReapingTime))); } myExecutorService.schedule(new TimeoutTask(), delay, TimeUnit.MILLISECONDS); myReapingScheduled = true; } /** * {@inheritDoc} */ public long getSocketTimeout() { return mySocketTimeout; } /** * {@inheritDoc} */ public synchronized void setSocketTimeout(long theSocketTimeout) { if (mySocketTimeout < -1) { throw new IllegalArgumentException("Socket timeout must be -1, 0, or a positive integer"); } mySocketTimeout = theSocketTimeout; myReapingScheduled = false; scheduleReaping(); } private class TimeoutTask implements Runnable { public void run() { if (mySocketTimeout == -1) { return; } ourLog.debug("Beginning socket reaping pass"); try { List socketsToClose = new ArrayList(); long closeIfActiveBefore = System.currentTimeMillis() - mySocketTimeout; synchronized (HohRawClientMultithreaded.this) { for (Iterator> iter = myIdleSocketsToTimeBecameIdle.entrySet().iterator(); iter.hasNext();) { Entry nextEntry = iter.next(); if (nextEntry.getValue() < closeIfActiveBefore) { Socket key = nextEntry.getKey(); socketsToClose.add(key); ourLog.info("Closing idle socket with local port {} because it has been idle since {}", key.getLocalPort(), new Date(nextEntry.getValue())); iter.remove(); } else { if (ourLog.isDebugEnabled()) { ourLog.debug("Next socket has " + (nextEntry.getValue() - closeIfActiveBefore) + "ms remaining"); } } } myReapingScheduled = false; scheduleReaping(); } for (Socket next : socketsToClose) { closeSocket(next); } } catch (Throwable e) { ourLog.error("Failure during reaper pass", e); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy