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

jadex.bridge.component.streams.OutputConnection Maven / Gradle / Ivy

Go to download

Jadex bridge is a base package for kernels and platforms, i.e., it is used by both and provides commonly used interfaces and classes for active components and their management.

There is a newer version: 4.0.267
Show newest version
package jadex.bridge.component.streams;

import java.io.InputStream;

import jadex.bridge.IComponentIdentifier;
import jadex.bridge.IComponentStep;
import jadex.bridge.IExternalAccess;
import jadex.bridge.IInternalAccess;
import jadex.bridge.IOutputConnection;
import jadex.bridge.component.IExecutionFeature;
import jadex.bridge.service.search.ServiceQuery;
import jadex.bridge.service.types.threadpool.IDaemonThreadPoolService;
import jadex.commons.future.ExceptionDelegationResultListener;
import jadex.commons.future.Future;
import jadex.commons.future.IFuture;
import jadex.commons.future.IResultListener;
import jadex.commons.future.ISubscriptionIntermediateFuture;
import jadex.commons.future.SubscriptionIntermediateFuture;

/**
 *  Output connection for writing data.
 *  
 *  Must synchronized its internal data because the connection handler
 *  and the connection user (i.e. a component) are using the connection
 *  concurrently.
 *  
 *  - the user calls interface methods like write and flush
 *  - the connection handler calls close to signal that the connection should close.
 */
public class OutputConnection extends AbstractConnection implements IOutputConnection
{		
	//-------- constructors --------
	
	/**
	 *  Create a new connection.
	 */
	public OutputConnection(IComponentIdentifier sender, IComponentIdentifier receiver, 
		int id, boolean initiator, IOutputConnectionHandler ch)
	{
		super(sender, receiver, id, false, initiator, ch);
	}
	
	//-------- IOutputConnection methods --------

	/**
	 *  Write the content to the stream.
	 *  @param data The data.
	 */
	public IFuture write(byte[] data)
	{
		synchronized(this)
		{
			if(closing || closed)
				return new Future(new RuntimeException("Connection closed."));
		}
		return ((IOutputConnectionHandler)ch).send(data);
	}
	
	/**
	 *  Flush the data.
	 */
	public void flush()
	{
		synchronized(this)
		{
			if(closing || closed)
				return;
		}
		((IOutputConnectionHandler)ch).flush();
	}
	
	/**
	 *  Wait until the connection is ready for the next write.
	 *  @return Calls future when next data can be written. Provides a value of how much data should be given to the connection for best performance.
	 */
	public IFuture waitForReady()
	{
		return ((IOutputConnectionHandler)ch).waitForReady();
	}
	
	/**
	 *  Close the connection.
	 *  Notifies the other side that the connection has been closed.
	 */
	public void close()
	{
		synchronized(this)
		{
			if(closing || closed)
				return;
		}
		
		flush();
		
		super.close();
	}
	
	/**
	 *  Do write all data from the input stream.  
	 */
	public ISubscriptionIntermediateFuture writeFromInputStream(final InputStream is, final IExternalAccess component)
	{		
		final SubscriptionIntermediateFuture ret = new SubscriptionIntermediateFuture();
		
		final long[] filesize = new long[1];

		component.scheduleStep(new IComponentStep()
		{
			byte[]	buf	= null;
			
			public IFuture execute(final IInternalAccess ia)
			{
				final IComponentStep self = this;
				
				waitForReady().addResultListener(ia.getFeature(IExecutionFeature.class).createResultListener(new IResultListener()
				{
					public void resultAvailable(Integer bytes)
					{
						// Stop transfer on cancel etc.
						if(ret.isDone())
						{
							finished(null);
						}
						else
						{
							try
							{
								int size = Math.min(bytes.intValue(), is.available());
								if(size>0)
								{
									if(buf==null || buf.length!=size)
									{
										buf = new byte[size];
									}
									int read = is.read(buf, 0, buf.length);
									dataRead(read);
								}
								else
								{
									Future	read	= new Future();
									asyncBlockingRead(read);
									
									read.addResultListener(ia.getFeature(IExecutionFeature.class).createResultListener(new IResultListener()
									{
										public void resultAvailable(Integer read)
										{
											dataRead(read.intValue());
										}
										
										public void exceptionOccurred(Exception exception)
										{
											finished(exception);
										}
									}));
								}
							}
							catch(Exception e)
							{
								finished(e);
							}
						}
					}
					
					public void exceptionOccurred(Exception exception)
					{
						finished(exception);
					}
					
					/**
					 *  Called on end of transmission.
					 */
					protected void	finished(Exception ex)
					{
						buf	= null;
						close();
						if(ex!=null)
						{
							ret.setExceptionIfUndone(ex);
						}
						else
						{
							ret.setFinishedIfUndone();
						}
						
						try
						{
							is.close();
						}
						catch(Exception e)
						{
						}
					}
					
					/**
					 *  Called, when read from input stream returned.
					 */
					protected void	dataRead(int read)
					{
						if(read==-1)
						{
							finished(null);							
						}
						else
						{
							// Maybe less bytes read than buffer size;
							assert read<=buf.length;
							if(read()
							{
								public void resultAvailable(Integer result)
								{
									component.scheduleStep(self);
								}
								
								public void exceptionOccurred(Exception exception)
								{
									finished(exception);		
								}
							}));
						}
					}
					
					IDaemonThreadPoolService	dtps;
					
					/**
					 *  Perform blocking read on extra thread.
					 */
					protected void	asyncBlockingRead(final Future read)
					{
						if(dtps==null)
						{
							component.searchService( new ServiceQuery<>(IDaemonThreadPoolService.class))
								.addResultListener(new ExceptionDelegationResultListener(read)
							{
								public void customResultAvailable(IDaemonThreadPoolService result)
								{
									dtps	= result;
									asyncBlockingRead(read);
								}
							});
						}
						else
						{
							dtps.execute(new Runnable()
							{
								public void run()
								{
									try
									{
										if(buf==null)
										{
											buf	= new byte[256];
										}
										int	len	= is.read(buf);
										read.setResult(Integer.valueOf(len));
									}
									catch(Exception e)
									{
										read.setException(e);
									}
								}
							});
						}
					}
				}));

				return IFuture.DONE;
			}
		});
		
		return ret;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy