Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2010-2024 60East Technologies Inc., All Rights Reserved.
//
// This computer software is owned by 60East Technologies Inc. and is
// protected by U.S. copyright laws and other laws and by international
// treaties. This computer software is furnished by 60East Technologies
// Inc. pursuant to a written license agreement and may be used, copied,
// transmitted, and stored only in accordance with the terms of such
// license agreement and with the inclusion of the above copyright notice.
// This computer software or any other copies thereof may not be provided
// or otherwise made available to any other person.
//
// U.S. Government Restricted Rights. This computer software: (a) was
// developed at private expense and is in all respects the proprietary
// information of 60East Technologies Inc.; (b) was not developed with
// government funds; (c) is a trade secret of 60East Technologies Inc.
// for all purposes of the Freedom of Information Act; and (d) is a
// commercial item and thus, pursuant to Section 12.212 of the Federal
// Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
// Government's use, duplication or disclosure of the computer software
// is subject to the restrictions set forth by 60East Technologies Inc..
//
////////////////////////////////////////////////////////////////////////////
package com.crankuptheamps.client;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.UnknownHostException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.URI;
import java.beans.ExceptionListener;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.net.SocketFactory;
import java.net.SocketTimeoutException;
import com.crankuptheamps.client.exception.AlreadyConnectedException;
import com.crankuptheamps.client.exception.ConnectionRefusedException;
import com.crankuptheamps.client.exception.DisconnectedException;
import com.crankuptheamps.client.exception.RetryOperationException;
import com.crankuptheamps.client.exception.TimedOutException;
import com.crankuptheamps.client.exception.InvalidURIException;
/**
* Socket implementation for {@link TCPTransport}. TCP Transport delegates to an instance of this class.
* This is used internally by the client's transport. There is usually no reason to make direct use of it.
* Its functionality is best accessed via the client instance.
*/
public class TCPTransportImpl
{
protected Client _client = null;
protected URI _addr = null;
protected Socket _socket = null;
protected InputStream _inputStream = null;
protected OutputStream _outputStream = null;
public final Lock _lock = new ReentrantLock();
volatile int _connectionVersion = 0;
private volatile boolean _disconnecting = false;
private Protocol _messageType = null;
private MessageHandler _onMessage = DefaultMessageHandler.instance;
private TransportDisconnectHandler _onDisconnect = DefaultDisconnectHandler.instance;
private TCPReaderThread _readerThread = null;
private ExceptionListener _exceptionListener = null;
private Properties _properties = null;
protected TransportFilter _filter = null;
protected volatile AMPSRunnable _idleRunnable = null;
private int _readTimeout = 0;
private int _connectTimeout = 0;
private int _idleReadTimeout = 1000; // 1 second default
private ThreadCreatedHandler _threadCreatedHandler = null;
protected boolean _httpPreflight = false;
protected boolean _handshakeSetStreams = false;
private List _httpPreflightHeaders = null;
/**
* Constructs a TCPTransportImpl object with the specified message type, properties, and transport filter.
* @param messageType The message type associated with the transport.
* @param properties The properties to be applied to the transport.
* @param filter The transport filter to be applied.
*/
public TCPTransportImpl(Protocol messageType, Properties properties, TransportFilter filter)
{
this._messageType = messageType;
this._properties = properties;
this._filter = filter;
this._readTimeout = TCPTransport.getDefaultReadTimeout();
this._connectTimeout = TCPTransport.getDefaultConnectTimeout();
}
/**
* Factory method for creating a new instance of {@link TCPTransport}.
* @param messageType The protocol message type for the transport.
* @param impl The underlying implementation for the new transport.
* @return A new instance of TCPTransport with the specified protocol message type.
*/
public TCPTransport createTransport(Protocol messageType, TCPTransportImpl impl)
{
return TCPTransport.createTransport(messageType, impl);
}
/**
* Sets the message handler for the transport.
* @param h The message handler to be set.
*/
public void setMessageHandler(MessageHandler h)
{
this._onMessage = h;
}
/**
* Sets the disconnect handler for the transport.
* @param h The disconnect handler to be set.
*/
public void setDisconnectHandler(TransportDisconnectHandler h)
{
this._onDisconnect = h;
}
/**
* Sets the exception listener for the transport.
* @param exceptionListener The exception listener to be set.
*/
public void setExceptionListener(ExceptionListener exceptionListener)
{
this._exceptionListener = exceptionListener;
}
/**
* Sets the thread created handler for the transport.
* @param h The thread created handler to be set.
*/
public void setThreadCreatedHandler(ThreadCreatedHandler h)
{
this._threadCreatedHandler = h;
}
/**
* Sets the transport filter for the transport.
* @param filter The transport filter to be set.
*/
public void setTransportFilter(TransportFilter filter)
{
this._filter = filter;
}
/**
* Connects the transport to the specified URI.
* @param addr The URI to connect to.
* @throws ConnectionRefusedException Thrown if the connection is refused.
* @throws AlreadyConnectedException Thrown if the transport is already connected.
* @throws InvalidURIException Thrown if the URI is invalid.
*/
public void connect(URI addr) throws ConnectionRefusedException,
AlreadyConnectedException, InvalidURIException
{
_lock.lock();
_disconnecting = false;
try
{
// clear out the interrupt bit. If we don't do this, and the thread is interrupted,
// EVERY SUBSEQUENT CALL TO connect() WILL FAIL FOREVER FROM THIS THREAD and throw a
// ClosedByInterruptException. Unlike InterruptedException, ClosedByInterruptException
// does not clear the thread's interrupted state.
Thread.interrupted();
if(this._addr != null)
{
throw new AlreadyConnectedException("Already connected to AMPS at " +
this._addr.getHost() + ":" + this._addr.getPort() + "\n");
}
_socket = createSocket();
_addr = addr;
// Merge properties from construction and the ones in this URI.
URIProperties properties = new URIProperties(addr);
if(_properties != null) properties.putAll(_properties);
applySocketProperties(properties);
connectSocket(addr, properties);
handshake();
_socket.setSoTimeout(_idleReadTimeout);
createStream(addr);
_connectionVersion++;
_readerThread = new TCPReaderThread(this, this._messageType);
_readerThread.barrier.await();
}
catch (InvalidURIException iuex)
{
_addr = null;
throw iuex;
}
catch (ClosedByInterruptException e)
{
// Clear the interrupt flag! It is set, and simply catching this exception
// doesn't clear it.
Thread.interrupted();
_addr = null;
throw new ConnectionRefusedException("Interrupted, but please try again.", e);
}
catch (BrokenBarrierException|InterruptedException e)
{
// Clear the interrupt flag! It is set, and simply catching this
// exception doesn't clear it.
Thread.interrupted();
_addr = null;
throw new ConnectionRefusedException("Sync with reader thread interrupted, but please try again.", e);
}
catch (IllegalArgumentException iaex)
{
_addr = null;
throw new InvalidURIException("Error setting socket options", iaex);
}
catch (AlreadyConnectedException e)
{
_addr = null;
throw e;
}
catch (Exception ex)
{
_addr = null;
throw new ConnectionRefusedException("Unable to connect to AMPS at " +
addr.getHost() + ":" + addr.getPort(),
ex);
}
finally
{
_lock.unlock();
}
}
/**
* Creates a socket using the default socket factory.
* @return A newly created socket.
* @throws Exception If an error occurs while creating the socket.
*/
protected Socket createSocket() throws Exception
{
return SocketFactory.getDefault().createSocket();
}
protected void connectSocket(URI addr, URIProperties properties) throws Exception
{
String default_ip_proto_prefer = System.getProperty("com.crankuptheamps.client.DEFAULT_IP_PROTOCOL_PREFER", "ipv4");
if(!("ipv4".equals(default_ip_proto_prefer) || "ipv6".equals(default_ip_proto_prefer)))
{
throw new IllegalArgumentException("Invalid value '" + default_ip_proto_prefer + "' for com.crankuptheamps.client.DEFAULT_IP_PROTOCOL_PREFER System Property");
}
String ip_proto_prefer = properties.getProperty("ip_protocol_prefer", default_ip_proto_prefer);
if(!("ipv4".equals(ip_proto_prefer) || "ipv6".equals(ip_proto_prefer)))
{
throw new InvalidURIException("Invalid value '" + ip_proto_prefer + "' for ip_protocol_prefer URI parameter");
}
int preferredAddrIndex = -1;
InetAddress[] addrCandidates = InetAddress.getAllByName(addr.getHost());
if("ipv4".equals(ip_proto_prefer))
{
for(int i = 0; i < addrCandidates.length; i++)
{
if(addrCandidates[i] instanceof Inet4Address)
{
preferredAddrIndex = i;
break;
}
}
if(preferredAddrIndex == -1)
{
for(int i = 0; i < addrCandidates.length; i++)
{
if(addrCandidates[i] instanceof Inet6Address)
{
preferredAddrIndex = i;
break;
}
}
}
if(preferredAddrIndex == -1)
{
throw new UnknownHostException();
}
}
else
{
for(int i = 0; i < addrCandidates.length; i++)
{
if(addrCandidates[i] instanceof Inet6Address)
{
preferredAddrIndex = i;
break;
}
}
if(preferredAddrIndex == -1)
{
for(int i = 0; i < addrCandidates.length; i++)
{
if(addrCandidates[i] instanceof Inet4Address)
{
preferredAddrIndex = i;
break;
}
}
}
if(preferredAddrIndex == -1)
{
throw new UnknownHostException();
}
}
_socket.connect(new InetSocketAddress(addrCandidates[preferredAddrIndex], addr.getPort()), _connectTimeout);
}
protected void createStream(URI addr) throws IOException
{
if (!_handshakeSetStreams) {
_inputStream = _socket.getInputStream();
_outputStream = _socket.getOutputStream();
}
else {
_handshakeSetStreams = false;
}
}
/**
* Performs the handshake for the TCP connection.
* @throws Exception If an error occurs during the handshake process.
*/
protected void handshake() throws Exception
{
if (_httpPreflight) {
createStream(_addr);
_handshakeSetStreams = true;
StringBuilder get = new StringBuilder();
get.append("GET ").append(_addr.getPath()).append(" HTTP/1.1\r\n").append("Host: ").append(_addr.getHost()).append("\r\nConnection: upgrade\r\nUpgrade: ").append(_addr.getScheme()).append("\r\n");
if (_httpPreflightHeaders != null) {
for (String header : _httpPreflightHeaders) {
get.append(header).append("\r\n");
}
}
get.append("\r\n");
_outputStream.write(get.toString().getBytes(StandardCharsets.UTF_8));
byte[] getResponse = new byte[2046];
int readBytes = _inputStream.read(getResponse, 0, 2046);
int totalBytes = readBytes;
// Make sure we at least read initial line of response
while (totalBytes < 15) {
if(readBytes == -1 || readBytes == 0)
{
throw new DisconnectedException("The remote server has closed the upgrade connection.");
}
readBytes = _inputStream.read(getResponse, readBytes, 2046);
totalBytes += readBytes;
}
if (getResponse[9] != (byte)'1' || getResponse[10] != (byte)'0' || getResponse[11] != (byte)'1') {
throw new ConnectionRefusedException("Failed to upgrade connection failed response code");
}
// Clear any remaining response bytes
long available = (long)_inputStream.available();
while (available > 0) {
_inputStream.skip(available);
available = (long)_inputStream.available();
}
}
}
/**
* Applies socket properties based on the provided properties.
* @param properties_ The properties to be applied to the socket.
* @throws SocketException If an error occurs while setting socket options.
* @throws InvalidURIException If an invalid URI parameter is encountered.
*/
protected void applySocketProperties(Properties properties_)
throws SocketException, InvalidURIException
{
_socket.setKeepAlive(true);
if(properties_ == null) return;
for(Map.Entry