diff --git a/.gitignore b/.gitignore index 3e99d9e9f..37dc6be91 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,7 @@ buildNumber.properties .mvn/timing.properties nbactions.xml .settings -.project \ No newline at end of file +.project + +# For all you Mac OS users out there +**/.DS_Store diff --git a/bundleJava/pom.xml b/bundleJava/pom.xml index 28bfa8470..18ba1522c 100644 --- a/bundleJava/pom.xml +++ b/bundleJava/pom.xml @@ -309,16 +309,16 @@ - - maven-deploy-plugin - 2.8.2 - - - org.apache.maven.wagon - wagon-ssh - 2.8 - - + + maven-deploy-plugin + 2.8.2 + + + org.apache.maven.wagon + wagon-ssh + 2.8 + + @@ -335,6 +335,7 @@ ${releaseName} false + posix src/assembly/distribution.xml @@ -363,4 +364,4 @@ - \ No newline at end of file + diff --git a/exampleJava b/exampleJava index e9b92a372..d2dd1b46b 160000 --- a/exampleJava +++ b/exampleJava @@ -1 +1 @@ -Subproject commit e9b92a372da740d35e81bf9ae7aa54a721d78422 +Subproject commit d2dd1b46baab148e3a7c846082428655c2b3d9b6 diff --git a/normativeTypesJava/.gitignore b/normativeTypesJava/.gitignore index 3eac4614f..72af6630f 100644 --- a/normativeTypesJava/.gitignore +++ b/normativeTypesJava/.gitignore @@ -1,2 +1,3 @@ /target/ documentation/html +/bin/ diff --git a/pom.xml b/pom.xml index 51b1c898e..0ef054900 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.3.0-SNAPSHOT 2.3.7-SNAPSHOT - + ${project.name} UTF-8 @@ -110,6 +110,14 @@ developer + + Georg Weiss + georgweiss@esss.se + European Spallation Source + + developer + + @@ -263,7 +271,7 @@ bundle-manifest process-classes - + manifest @@ -274,9 +282,9 @@ maven-jar-plugin 3.0.2 - + ${project.build.outputDirectory}/META-INF/MANIFEST.MF - + @@ -308,7 +316,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.0.0-M1 + 3.0.1 documentation/${mainpage.name}.html @@ -322,16 +330,16 @@ - - maven-deploy-plugin - 2.8.2 - - - org.apache.maven.wagon - wagon-ssh - 2.8 - - + + maven-deploy-plugin + 2.8.2 + + + org.apache.maven.wagon + wagon-ssh + 2.8 + + @@ -351,7 +359,7 @@ - + with-examples true @@ -384,6 +392,46 @@ + + java-9+ + + [9,) + + + directoryService + exampleJava + bundleJava + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + documentation/${mainpage.name}.html + + + + org.apache.commons + commons-lang3 + 3.7 + + + + + attach-javadocs + + jar + + + -html5 + + + + + + + diff --git a/pvAccessJava/documentation/pvAccessJava.html b/pvAccessJava/documentation/pvAccessJava.html index ddc8382a3..8a9be518c 100644 --- a/pvAccessJava/documentation/pvAccessJava.html +++ b/pvAccessJava/documentation/pvAccessJava.html @@ -40,14 +40,14 @@

EPICS pvAccessJava

Release 4.0.2, 10-Nov-2014

Editors:
-
Marty Kraimer, BNL
+
Marty Kraimer, BNL
Matej Sekoranja, CosyLab

Abstract

pvAccessJava is the Java implementation of pvAccess, which is one of a -related set of products:
+related set of products:
relatedDocumentsV4.html

@@ -304,7 +304,7 @@

Implementation of ChannelGetRequester:

The implementation above issues actual get request on success by calling "channelGet.get()". User can always destroy a request by calling -"channelGet.destroy()".
+"channelGet.destroy()".
ChannelGetRequester.getDone gets called on get operation completion. On success indicated by status parameter, pvStructure holds the latest data.

diff --git a/pvAccessJava/src/org/epics/pvaccess/client/impl/remote/ClientContextImpl.java b/pvAccessJava/src/org/epics/pvaccess/client/impl/remote/ClientContextImpl.java index 935a5dfae..c95cb5e1a 100644 --- a/pvAccessJava/src/org/epics/pvaccess/client/impl/remote/ClientContextImpl.java +++ b/pvAccessJava/src/org/epics/pvaccess/client/impl/remote/ClientContextImpl.java @@ -21,6 +21,7 @@ import java.net.NetworkInterface; import java.net.SocketException; import java.nio.channels.SocketChannel; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -48,7 +49,6 @@ import org.epics.pvaccess.client.impl.remote.tcp.BlockingClientTCPTransport; import org.epics.pvaccess.client.impl.remote.tcp.BlockingTCPConnector; import org.epics.pvaccess.client.impl.remote.tcp.BlockingTCPConnector.TransportFactory; -import org.epics.pvaccess.client.impl.remote.tcp.NonBlockingClientTCPTransport; import org.epics.pvaccess.impl.remote.ConnectionException; import org.epics.pvaccess.impl.remote.Context; import org.epics.pvaccess.impl.remote.ProtocolType; @@ -64,7 +64,6 @@ import org.epics.pvaccess.plugins.SecurityPlugin; import org.epics.pvaccess.plugins.impl.client.CAClientSecurityPlugin; import org.epics.pvaccess.util.InetAddressUtil; -import org.epics.pvaccess.util.IntHashMap; import org.epics.pvaccess.util.configuration.Configuration; import org.epics.pvaccess.util.configuration.ConfigurationProvider; import org.epics.pvaccess.util.configuration.impl.ConfigurationFactory; @@ -79,142 +78,131 @@ import org.epics.pvdata.pv.StatusCreate; /** - * Implementation of PVAJ JCA Context. + * Implementation of PVAJ JCA Context. + * * @author Matej Sekoranja * @version $Id$ */ -public class ClientContextImpl implements Context/*, Configurable*/ { +public class ClientContextImpl implements Context { - static - { + static { // force only IPv4 sockets, since EPICS does not work right with IPv6 sockets // see http://java.sun.com/j2se/1.5.0/docs/guide/net/properties.html System.setProperty("java.net.preferIPv4Stack", "true"); } - + /** * Name if the provider this context provides. */ public static final String PROVIDER_NAME = "pva"; - - /** - * Version. - */ - public static final Version VERSION = new Version( - "pvAccess Client", "Java", - PVAVersion.VERSION_MAJOR, PVAVersion.VERSION_MINOR, - PVAVersion.VERSION_MAINTENANCE, PVAVersion.VERSION_DEVELOPMENT); - - /** - * Server state enum. - */ - enum State { + + /** + * Version. + */ + public static final Version VERSION = new Version("pvAccess Client", "Java", PVAVersion.VERSION_MAJOR, + PVAVersion.VERSION_MINOR, PVAVersion.VERSION_MAINTENANCE, PVAVersion.VERSION_DEVELOPMENT); + + /** + * Server state enum. + */ + enum State { /** * State value of non-initialized context. */ NOT_INITIALIZED, - + /** * State value of initialized context. */ INITIALIZED, - + /** * State value of destroyed context. */ DESTROYED; - } - + } + /** * Initialization status. */ private volatile State state = State.NOT_INITIALIZED; - + /** * Context logger. */ protected Logger logger; - + /** * Debug level. */ protected int debugLevel; /** - * A space-separated list of broadcast address for process variable name resolution. - * Each address must be of the form: ip.number:port or host.name:port + * A space-separated list of broadcast address for process variable name + * resolution. Each address must be of the form: ip.number:port or + * host.name:port */ protected String addressList = ""; - + /** - * Define whether or not the network interfaces should be discovered at runtime. + * Define whether or not the network interfaces should be discovered at runtime. */ protected boolean autoAddressList = true; - + /** * If the context doesn't see a beacon from a server that it is connected to for - * connectionTimeout seconds then a state-of-health message is sent to the server over TCP/IP. - * If this state-of-health message isn't promptly replied to then the context will assume that - * the server is no longer present on the network and disconnect. + * connectionTimeout seconds then a state-of-health message is sent to the + * server over TCP/IP. If this state-of-health message isn't promptly replied to + * then the context will assume that the server is no longer present on the + * network and disconnect. */ protected float connectionTimeout = 30.0f; - + /** * Period in second between two beacon signals. */ protected float beaconPeriod = 15.0f; - + /** * Broadcast (beacon, search) port number to listen to. */ protected int broadcastPort = PVAConstants.PVA_BROADCAST_PORT; - + /** * Receive buffer size (max size of payload). */ protected int receiveBufferSize = PVAConstants.MAX_TCP_RECV; - + /** * Timer. */ protected Timer timer = null; - /** - * Reactor. - */ - //protected Reactor reactor = null; - - /** - * Leader/followers thread pool. - */ - //protected LeaderFollowersThreadPool leaderFollowersThreadPool = null; - /** * Broadcast transport needed to listen for broadcasts. */ -// protected UDPTransport broadcastTransport = null; + protected BlockingUDPTransport broadcastTransport = null; - + /** * UDP transport needed for channel searches. */ -// protected UDPTransport searchTransport = null; + protected BlockingUDPTransport searchTransport = null; /** * Local multicast address. */ protected InetSocketAddress localBroadcastAddress = null; - + /** * PVA connector (creates PVA virtual circuit). */ protected BlockingTCPConnector connector = null; -// protected TCPConnector connector = null; /** - * PVA transport (virtual circuit) registry. - * This registry contains all active transports - connections to PVA servers. + * PVA transport (virtual circuit) registry. This registry contains all active + * transports - connections to PVA servers. */ protected TransportRegistry transportRegistry = null; @@ -226,39 +214,35 @@ enum State { /** * Context instance. */ - private static final int LOCK_TIMEOUT = 20 * 1000; // 20s + private static final int LOCK_TIMEOUT = 20 * 1000; // 20s /** * Map of channels (keys are CIDs). */ - // TODO consider using WeakHashMap (and call Channel.destroy() in finalize() method). - protected final IntHashMap channelsByCID = new IntHashMap(); - - /** - * Map of channels (keys are names). - */ - // TODO consider using WeakHashMap (and call Channel.destroy() in finalize() method). - //protected final Map channelsByName = new HashMap(); + // TODO consider using WeakHashMap (and call Channel.destroy() in finalize() + // method). + protected final Map channelsByCID = Collections.synchronizedMap(new HashMap()); /** - * Last CID cache. + * Last CID cache. */ private int lastCID = 0; /** * Map of pending response requests (keys are IOID). */ - // TODO consider using WeakHashMap (and call ResponseRequest.destroy() in finalize() method). - protected final IntHashMap pendingResponseRequests = new IntHashMap(); + // TODO consider using WeakHashMap (and call ResponseRequest.destroy() in + // finalize() method). + protected final Map pendingResponseRequests = Collections + .synchronizedMap(new HashMap()); /** - * Last IOID cache. + * Last IOID cache. */ private int lastIOID = 0; /** - * Channel search manager. - * Manages UDP search requests. + * Channel search manager. Manages UDP search requests. */ private ChannelSearchManager channelSearchManager; @@ -271,68 +255,64 @@ enum State { * Response handler. */ private final ResponseHandler clientResponseHandler; - + /** * Provider implementation. */ protected ChannelProvider channelProvider = new ChannelProviderImpl(); - + /** * Constructor. */ - public ClientContextImpl() - { + public ClientContextImpl() { loadConfiguration(); initializeLogger(); initializeSecutiryPlugins(); - + clientResponseHandler = new ClientResponseHandler(this); } - - /* (non-Javadoc) - * @see org.epics.pvaccess.client.ClientContext#getVersion() - */ - public Version getVersion() - { - return VERSION; - } - + + /* + * (non-Javadoc) + * + * @see org.epics.pvaccess.client.ClientContext#getVersion() + */ + public Version getVersion() { + return VERSION; + } + /** * Initialize context logger. */ - protected void initializeLogger() - { + protected void initializeLogger() { logger = Logger.getLogger(this.getClass().getName()); - - if (debugLevel > 0) - { + + if (debugLevel > 0) { logger.setLevel(Level.ALL); - + // install console logger only if there is no already installed Logger inspectedLogger = logger; boolean found = false; - while (inspectedLogger != null) - { + while (inspectedLogger != null) { for (Handler handler : inspectedLogger.getHandlers()) - if (handler instanceof ConsoleLogHandler) - { + if (handler instanceof ConsoleLogHandler) { found = true; break; } inspectedLogger = inspectedLogger.getParent(); } - + if (!found) logger.addHandler(new ConsoleLogHandler()); } } - + /** * Get configuration instance. + * * @return the configuration. */ - public Configuration getConfiguration() - { + public Configuration getConfiguration() { final ConfigurationProvider configurationProvider = ConfigurationFactory.getProvider(); Configuration config = configurationProvider.getConfiguration("pvAccess-client"); if (config == null) @@ -343,12 +323,11 @@ public Configuration getConfiguration() /** * Load configuration. */ - protected void loadConfiguration() - { + protected void loadConfiguration() { final Configuration config = getConfiguration(); - + debugLevel = config.getPropertyAsInteger(PVAConstants.PVACCESS_DEBUG, 0); - + addressList = config.getPropertyAsString("EPICS_PVA_ADDR_LIST", addressList); autoAddressList = config.getPropertyAsBoolean("EPICS_PVA_AUTO_ADDR_LIST", autoAddressList); connectionTimeout = config.getPropertyAsFloat("EPICS_PVA_CONN_TMO", connectionTimeout); @@ -359,147 +338,75 @@ protected void loadConfiguration() /** * Check context state and tries to establish necessary state. - * @throws PVAException any PVA exception. - * @throws IllegalStateException thrown if context is already destroyed. + * + * @throws PVAException + * any PVA exception. + * @throws IllegalStateException + * thrown if context is already destroyed. */ protected void checkState() throws PVAException, IllegalStateException { if (state == State.DESTROYED) throw new IllegalStateException("Context destroyed."); - else if (state == State.NOT_INITIALIZED) - { - // double-locking pattern used to prevent unnecessary initialization calls - synchronized (this) - { + else if (state == State.NOT_INITIALIZED) { + // double-locking pattern used to prevent unnecessary initialization calls + synchronized (this) { if (state == State.NOT_INITIALIZED) initialize(); } } } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.client.ClientContext#initialize() */ public synchronized void initialize() throws PVAException { - + if (state == State.DESTROYED) throw new IllegalStateException("Context destroyed."); else if (state == State.INITIALIZED) throw new IllegalStateException("Context already initialized."); - - //super.initialize(); - + internalInitialize(); - + state = State.INITIALIZED; - + } // TODO remove final AtomicBoolean pollerInitialized = new AtomicBoolean(); PollerImpl poller; - + /** * @throws PVAException */ private void internalInitialize() throws PVAException { - + timer = TimerFactory.create("pvAccess-client timer", ThreadPriority.lower); -// connector = new TCPConnector(this, receiveBufferSize, connectionTimeout); - TransportFactory transportFactory = new TransportFactory() { - + @Override - public Transport create(Context context, SocketChannel channel, - ResponseHandler responseHandler, int receiveBufferSize, - TransportClient client, short transportRevision, - float heartbeatInterval, short priority) { + public Transport create(Context context, SocketChannel channel, ResponseHandler responseHandler, + int receiveBufferSize, TransportClient client, short transportRevision, float heartbeatInterval, + short priority) { try { - return new BlockingClientTCPTransport(context, channel, responseHandler, receiveBufferSize, client, transportRevision, heartbeatInterval, priority); + return new BlockingClientTCPTransport(context, channel, responseHandler, receiveBufferSize, client, + transportRevision, heartbeatInterval, priority); } catch (SocketException e) { throw new RuntimeException("Failed to create transport."); } } }; - - // TODO not used yet - @SuppressWarnings("unused") - TransportFactory nonBlockingTransportFactory = new TransportFactory() { - - @Override - public Transport create(Context context, SocketChannel channel, - ResponseHandler responseHandler, int receiveBufferSize, - TransportClient client, short transportRevision, - float heartbeatInterval, short priority) { - try { - // TODO !!! - if (!pollerInitialized.getAndSet(true)) - { - poller = new PollerImpl(); - poller.start(); - } - return new NonBlockingClientTCPTransport(context, poller, channel, responseHandler, receiveBufferSize, client, transportRevision, heartbeatInterval, priority); - } catch (IOException e) { - throw new RuntimeException("Failed to create transport."); - } - } - }; connector = new BlockingTCPConnector(this, transportFactory, receiveBufferSize, connectionTimeout); - //connector = new BlockingTCPConnector(this, nonBlockingTransportFactory, receiveBufferSize, connectionTimeout); transportRegistry = new TransportRegistry(); namedLocker = new NamedLockPattern(); -/* - try - { - reactor = new Reactor(); - - if (System.getProperties().containsKey(CAJ_SINGLE_THREADED_MODEL)) - { - logger.config("Using single threaded model."); - - // single thread processing - new Thread( - new Runnable() { - /** - * @see java.lang.Runnable#run() - * - public void run() { - // do the work - while (reactor.process()); - } - - }, "CA reactor").start(); - } - else - { - // leader/followers processing - leaderFollowersThreadPool = new LeaderFollowersThreadPool(); - // spawn initial leader - leaderFollowersThreadPool.promoteLeader( - new Runnable() { - /** - * @see java.lang.Runnable#run() - * - public void run() { - reactor.process(); - } - } - ); - } - - } - catch (IOException ioex) - { - throw new PVAException("Failed to initialize reactor.", ioex); - } - */ - + // setup UDP transport initializeUDPTransport(); // setup search manager - // TODO -// channelSearchManager = new ChannelSearchManagerImpl(this); channelSearchManager = new SimpleChannelSearchManagerImpl(this); } @@ -508,40 +415,31 @@ public void run() { */ private void initializeUDPTransport() { // setup UDP transport - try - { + try { // where to bind (listen) address InetSocketAddress listenLocalAddress = new InetSocketAddress(broadcastPort); - + // where to send address InetSocketAddress[] broadcastAddresses = InetAddressUtil.getBroadcastAddresses(broadcastPort); -// UDPConnector broadcastConnector = new UDPConnector(this, true, broadcastAddresses, true); BlockingUDPConnector broadcastConnector = new BlockingUDPConnector(this, true, broadcastAddresses, true); - - broadcastTransport = (BlockingUDPTransport)broadcastConnector.connect( -// broadcastTransport = (UDPTransport)broadcastConnector.connect( - null, new ClientResponseHandler(this), - listenLocalAddress, PVAConstants.PVA_PROTOCOL_REVISION, - PVAConstants.PVA_DEFAULT_PRIORITY); - -// UDPConnector searchConnector = new UDPConnector(this, false, broadcastAddresses, true); + + broadcastTransport = (BlockingUDPTransport) broadcastConnector.connect(null, + new ClientResponseHandler(this), listenLocalAddress, PVAConstants.PVA_PROTOCOL_REVISION, + PVAConstants.PVA_DEFAULT_PRIORITY); + BlockingUDPConnector searchConnector = new BlockingUDPConnector(this, false, broadcastAddresses, true); - - searchTransport = (BlockingUDPTransport)searchConnector.connect( -// searchTransport = (UDPTransport)searchConnector.connect( - null, new ClientResponseHandler(this), - new InetSocketAddress(0), PVAConstants.PVA_PROTOCOL_REVISION, - PVAConstants.PVA_DEFAULT_PRIORITY); + + searchTransport = (BlockingUDPTransport) searchConnector.connect(null, new ClientResponseHandler(this), + new InetSocketAddress(0), PVAConstants.PVA_PROTOCOL_REVISION, PVAConstants.PVA_DEFAULT_PRIORITY); // set broadcast address list - if (addressList != null && addressList.length() > 0) - { + if (addressList != null && addressList.length() > 0) { // if auto is true, add it to specified list InetSocketAddress[] appendList = null; if (autoAddressList == true) appendList = broadcastTransport.getSendAddresses(); - + InetSocketAddress[] list = InetAddressUtil.getSocketAddressList(addressList, broadcastPort, appendList); if (list != null && list.length > 0) { broadcastTransport.setSendAddresses(list); @@ -552,51 +450,41 @@ null, new ClientResponseHandler(this), final InetSocketAddress[] broadcastAddressList = broadcastTransport.getSendAddresses(); if (broadcastAddressList != null) for (int i = 0; i < broadcastAddressList.length; i++) - logger.finer("Broadcast address #" + i + ": " + broadcastAddressList[i] + '.'); - - // TODO do not use searchBroadcast in future + logger.finer("Broadcast address #" + i + ": " + broadcastAddressList[i] + '.'); + + // TODO do not use searchBroadcast in future // TODO configurable local NIF, address // setup local broadcasting NetworkInterface localNIF = InetAddressUtil.getLoopbackNIF(); - if (localNIF != null) - { - try - { + if (localNIF != null) { + try { InetAddress group = InetAddress.getByName("224.0.0.128"); localBroadcastAddress = new InetSocketAddress(group, broadcastPort); - /*MembershipKey key =*/ searchTransport.join(group, localNIF); - - // NOTE: this disables usage of multicast addresses in EPICS_PVA_ADDR_LIST + searchTransport.join(group, localNIF); + + // NOTE: this disables usage of multicast addresses in EPICS_PVA_ADDR_LIST searchTransport.setMutlicastNIF(localNIF, true); - - //searchTransport.setSendAddresses(new InetSocketAddress[] { - // new InetSocketAddress(group, broadcastPort) - //}); - logger.config("Local multicast enabled on " + localBroadcastAddress + ":" + broadcastPort + " using " + localNIF.getDisplayName() + "."); - } - catch (Throwable th) - { + logger.config("Local multicast enabled on " + localBroadcastAddress + ":" + broadcastPort + + " using " + localNIF.getDisplayName() + "."); + } catch (Exception th) { logger.log(Level.CONFIG, "Failed to initialize local multicast, funcionality disabled.", th); } - } - else - { + } else { logger.config("Failed to detect a loopback network interface, local multicast disabled."); } - broadcastTransport.start(); searchTransport.start(); - } - catch (ConnectionException ce) - { + } catch (ConnectionException ce) { logger.log(Level.SEVERE, "Failed to initialize UDP transport.", ce); } } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.client.ClientContext#destroy() */ public synchronized void destroy() { @@ -604,11 +492,11 @@ public synchronized void destroy() { if (state == State.DESTROYED) throw new IllegalStateException("Context already destroyed."); - // go into destroyed state ASAP - state = State.DESTROYED; - + // go into destroyed state ASAP + state = State.DESTROYED; + internalDestroy(); - + } /** @@ -621,19 +509,18 @@ private void internalDestroy() { channelSearchManager.cancel(); // stop timer - if (timer != null) + if (timer != null) timer.stop(); - + // // cleanup // - + // this will also close all PVA transports destroyAllChannels(); - + // close broadcast transport - if (broadcastTransport != null) - { + if (broadcastTransport != null) { try { broadcastTransport.close(); } catch (IOException e) { @@ -641,9 +528,8 @@ private void internalDestroy() { e.printStackTrace(); } } - - if (searchTransport != null) - { + + if (searchTransport != null) { try { searchTransport.close(); } catch (IOException e) { @@ -651,58 +537,31 @@ private void internalDestroy() { e.printStackTrace(); } } - - /* - // shutdown reactor - if (reactor != null) - reactor.shutdown(); - - // shutdown LF thread pool - if (leaderFollowersThreadPool != null) - leaderFollowersThreadPool.shutdown(); - */ - // TODO still some events can be in queue (e.g. channel destroyed) - // reposibility of the event dispatcher? - // shutdown dispatcher - //if (eventDispatcher != null) - // eventDispatcher.dispose(); - + } /** * Destroy all channels. */ private void destroyAllChannels() { - - Channel[] channelsArray; - synchronized (channelsByCID) - { - // not some elements might be null - Channel[] ch = new Channel[channelsByCID.size()]; - channelsArray = (Channel[])channelsByCID.toArray(ch); - channelsByCID.clear(); - //channelsByName.clear(); - } - - for (int i = 0; i < channelsArray.length; i++) - { - try - { - // force destruction regardless of reference count - final ChannelImpl channel = (ChannelImpl)channelsArray[i]; - if (channel != null) - channel.destroy(true); - } - catch (Throwable th) - { - th.printStackTrace(); + + for (Channel channel : channelsByCID.values()) { + try { + channel.destroy(); + } catch (Exception e) { + e.printStackTrace(); } } + + channelsByCID.clear(); + } /** * Check channel name. - * @param name name to check. + * + * @param name + * name to check. * @throws IllegalArgumentException */ private final void checkChannelName(String name) throws IllegalArgumentException { @@ -721,163 +580,101 @@ public Channel createChannelInternal(String name, ChannelRequester requester, sh if (requester == null) throw new IllegalArgumentException("null requester"); - + if (priority < ChannelProvider.PRIORITY_MIN || priority > ChannelProvider.PRIORITY_MAX) throw new IllegalArgumentException("priority out of bounds"); - /* - // lookup for channel w/o named lock - ChannelImpl channel = getChannel(name, priority, true); - if (channel != null) - { - if (l != null) - channel.addConnectionListenerAndFireIfConnected(l); - return channel; - } - */ - boolean lockAcquired = namedLocker.acquireSynchronizationObject(name, LOCK_TIMEOUT); - if (lockAcquired) - { - try - { - /* - // ... lookup for channel, if created while acquiring lock - channel = getChannel(name, priority, true); - if (channel != null) - { - if (l != null) - channel.addConnectionListenerAndFireIfConnected(l); - return channel; - } - */ + if (lockAcquired) { + try { + int cid = generateCID(); return new ChannelImpl(this, cid, name, requester, priority, addresses); + } finally { + namedLocker.releaseSynchronizationObject(name); } - finally - { - namedLocker.releaseSynchronizationObject(name); - } - } - else - { - throw new PVAException("Failed to obtain synchronization lock for '" + name + "', possible deadlock.", null); + } else { + throw new PVAException("Failed to obtain synchronization lock for '" + name + "', possible deadlock.", + null); } } - + /** * Destroy channel. - * @param channel the channel to destroy. - * @param force force-full (non-user) destruction. - * @throws PVAException unexpected exception. - * @throws IllegalStateException if channel is already destroyed. - */ - public void destroyChannel(ChannelImpl channel, boolean force) - throws PVAException, IllegalStateException { - + * + * @param channel + * the channel to destroy. + * @param force + * force-full (non-user) destruction. + * @throws PVAException + * unexpected exception. + * @throws IllegalStateException + * if channel is already destroyed. + */ + public void destroyChannel(ChannelImpl channel, boolean force) throws PVAException, IllegalStateException { + boolean lockAcquired = namedLocker.acquireSynchronizationObject(channel.getChannelName(), LOCK_TIMEOUT); - if (lockAcquired) - { - try - { + if (lockAcquired) { + try { channel.destroyChannel(force); - } - catch (IOException ioex) - { + } catch (IOException ioex) { logger.log(Level.SEVERE, "Failed to cleanly destroy channel.", ioex); throw new PVAException("Failed to cleanly destroy channel.", ioex); + } finally { + namedLocker.releaseSynchronizationObject(channel.getChannelName()); } - finally - { - namedLocker.releaseSynchronizationObject(channel.getChannelName()); - } - } - else - { - throw new PVAException("Failed to obtain synchronization lock for '" + channel.getChannelName() + "', possible deadlock.", null); + } else { + throw new PVAException( + "Failed to obtain synchronization lock for '" + channel.getChannelName() + "', possible deadlock.", + null); } } /** * Register channel. + * * @param channel */ - void registerChannel(ChannelImpl channel) - { - synchronized (channelsByCID) - { - channelsByCID.put(channel.getChannelID(), channel); - //channelsByName.put(getUniqueChannelName(channel.getChannelName(), channel.getPriority()), channel); - } + void registerChannel(ChannelImpl channel) { + channelsByCID.put(channel.getChannelID(), channel); } /** * Unregister channel. + * * @param channel */ - void unregisterChannel(ChannelImpl channel) - { - synchronized (channelsByCID) - { - channelsByCID.remove(channel.getChannelID()); - //channelsByName.remove(getUniqueChannelName(channel.getChannelName(), channel.getPriority())); - } + void unregisterChannel(ChannelImpl channel) { + channelsByCID.remove(channel.getChannelID()); } /** * Searches for a channel with given channel ID. - * @param channelID CID. + * + * @param channelID + * CID. * @return channel with given CID, null if non-existent. */ - public ChannelImpl getChannel(int channelID) - { - synchronized (channelsByCID) - { - return (ChannelImpl)channelsByCID.get(channelID); - } + public ChannelImpl getChannel(int channelID) { + + return (ChannelImpl) channelsByCID.get(channelID); } - - /* - * Generate unique channel string from channel name and priority. - * @param name channel name. - * @param priority channel priority. - * @return unique channel string. - * - private final String getUniqueChannelName(String name, short priority) - { - // this name is illegal for PVA, so this function is unique - return name + '\0' + priority; - }*/ - - /* - * Searches for a channel with given channel name. - * @param name channel name. - * @param priority channel priority. - * @param acquire whether to acquire ownership (increment ref. counting) - * @return channel with given name, null if non-existant. - * - public ChannelImpl getChannel(String name, short priority, boolean acquire) - { - synchronized (channelsByName) - { - ChannelImpl channel = (ChannelImpl)channelsByName.get(getUniqueChannelName(name, priority)); - if (channel != null && acquire) - channel.acquire(); - return channel; - } - }*/ - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.client.ClientContext#printInfo() */ public void printInfo() { printInfo(System.out); } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.client.ClientContext#printInfo(java.io.PrintStream) */ - public void printInfo(PrintStream out) { + public void printInfo(PrintStream out) { out.println("CLASS : " + getClass().getName()); out.println("VERSION : " + getVersion()); out.println("ADDR_LIST : " + addressList); @@ -886,26 +683,26 @@ public void printInfo(PrintStream out) { out.println("BEACON_PERIOD : " + beaconPeriod); out.println("BROADCAST_PORT : " + broadcastPort); out.println("RCV_BUFFER_SIZE : " + receiveBufferSize); - //out.println("EVENT_DISPATCHER: " + eventDispatcher); + // out.println("EVENT_DISPATCHER: " + eventDispatcher); out.print("STATE : "); - switch (state) - { - case NOT_INITIALIZED: - out.println("NOT_INITIALIZED"); - break; - case INITIALIZED: - out.println("INITIALIZED"); - break; - case DESTROYED: - out.println("DESTROYED"); - break; - default: - out.println("UNKNOWN"); + switch (state) { + case NOT_INITIALIZED: + out.println("NOT_INITIALIZED"); + break; + case INITIALIZED: + out.println("INITIALIZED"); + break; + case DESTROYED: + out.println("DESTROYED"); + break; + default: + out.println("UNKNOWN"); } } /** * Get initialization status. + * * @return initialization status. */ public boolean isInitialized() { @@ -914,14 +711,16 @@ public boolean isInitialized() { /** * Get destruction status. + * * @return destruction status. */ public boolean isDestroyed() { return state == State.DESTROYED; } - + /** * Get search address list. + * * @return get search address list. */ public String getAddressList() { @@ -930,6 +729,7 @@ public String getAddressList() { /** * Get auto search-list flag. + * * @return auto search-list flag. */ public boolean isAutoAddressList() { @@ -938,6 +738,7 @@ public boolean isAutoAddressList() { /** * Get beacon period (in seconds). + * * @return beacon period (in seconds). */ public float getBeaconPeriod() { @@ -946,7 +747,8 @@ public float getBeaconPeriod() { /** * Get connection timeout (in seconds). - * @return connection timeout (in seconds). + * + * @return connection timeout (in seconds). */ public float getConnectionTimeout() { return connectionTimeout; @@ -954,6 +756,7 @@ public float getConnectionTimeout() { /** * Get logger. + * * @return logger. */ public Logger getLogger() { @@ -967,6 +770,7 @@ public int getDebugLevel() { /** * Get receive buffer size (max size of payload). + * * @return receive buffer size (max size of payload). */ public int getReceiveBufferSize() { @@ -975,6 +779,7 @@ public int getReceiveBufferSize() { /** * Get broadcast port. + * * @return broadcast port. */ public int getBroadcastPort() { @@ -982,14 +787,8 @@ public int getBroadcastPort() { } /* - * Get event dispatcher. - * @return event dispatcher. - * - public final EventDispatcher getEventDispatcher() { - return eventDispatcher; - }*/ - - /* (non-Javadoc) + * (non-Javadoc) + * * @see org.epics.pvaccess.client.ClientContext#dispose() */ public void dispose() { @@ -1000,37 +799,36 @@ public void dispose() { } } - // ************************************************************************** // - /** * Broadcast transport. + * * @return broadcast transport. */ - //public UDPTransport getBroadcastTransport() { public BlockingUDPTransport getBroadcastTransport() { return broadcastTransport; } /** * Broadcast transport. + * * @return broadcast transport. */ -// public UDPTransport getSearchTransport() { public BlockingUDPTransport getSearchTransport() { return searchTransport; } /** * Get local multicast address (group). + * * @return the address. */ - public InetSocketAddress getLocalMulticastAddress() - { + public InetSocketAddress getLocalMulticastAddress() { return localBroadcastAddress; } - + /** * Get PVA transport (virtual circuit) registry. + * * @return PVA transport (virtual circuit) registry. */ public TransportRegistry getTransportRegistry() { @@ -1039,6 +837,7 @@ public TransportRegistry getTransportRegistry() { /** * Get timer. + * * @return timer. */ public Timer getTimer() { @@ -1052,22 +851,19 @@ public Map getSecurityPlugins() { return securityPlugins; } - private void initializeSecutiryPlugins() - { + private void initializeSecutiryPlugins() { String classes = System.getProperty(SecurityPlugin.SECURITY_PLUGINS_CLIENT_KEY); - if (classes != null) - { + if (classes != null) { StringTokenizer tokens = new StringTokenizer(classes, ","); - - while (tokens.hasMoreElements()) - { + + while (tokens.hasMoreElements()) { String className = tokens.nextToken().trim(); logger.log(Level.FINER, "Loading security plug-in '" + className + "'..."); - - try - { - final Class c = Class.forName(className); - SecurityPlugin p = (SecurityPlugin)c.newInstance(); // TODO in the future any specific method can be used + + try { + final Class c = Class.forName(className); + SecurityPlugin p = (SecurityPlugin) c.newInstance(); // TODO in the future any specific method can + // be used securityPlugins.put(p.getId(), p); logger.log(Level.FINER, "Security plug-in '" + className + "' [" + p.getId() + "] loaded."); } catch (Throwable th) { @@ -1075,171 +871,158 @@ private void initializeSecutiryPlugins() } } } - + // load by default - if (!securityPlugins.containsKey("ca")) - { + if (!securityPlugins.containsKey("ca")) { SecurityPlugin p = new CAClientSecurityPlugin(); securityPlugins.put(p.getId(), p); } logger.log(Level.FINE, "Installed security plug-ins: " + securityPlugins.keySet() + "."); } - + /** * Get channel search manager. + * * @return channel search manager. */ public ChannelSearchManager getChannelSearchManager() { return channelSearchManager; } - /** - * Get LF thread pool. - * @return LF thread pool, can be null if disabled. - * - public LeaderFollowersThreadPool getLeaderFollowersThreadPool() { - return leaderFollowersThreadPool; - }*/ - /** - * Called each time new server is detected. + * Get LF thread pool. + * + * @return LF thread pool, can be null if disabled. + * + * public LeaderFollowersThreadPool getLeaderFollowersThreadPool() { + * return leaderFollowersThreadPool; } + */ + + /** + * Called each time new server is detected. */ - public void newServerDetected() - { + public void newServerDetected() { if (channelSearchManager != null) channelSearchManager.newServerDetected(); } - + /** - * Get, or create if necessary, transport of given server address. - * Note that this method might block (creating TCP connection, verifying it). - * @param serverAddress required transport address - * @param priority process priority. + * Get, or create if necessary, transport of given server address. Note that + * this method might block (creating TCP connection, verifying it). + * + * @param serverAddress + * required transport address + * @param priority + * process priority. * @return transport for given address */ - Transport getTransport(TransportClient client, InetSocketAddress serverAddress, byte minorRevision, short priority) - { - try - { + Transport getTransport(TransportClient client, InetSocketAddress serverAddress, byte minorRevision, + short priority) { + try { return connector.connect(client, clientResponseHandler, serverAddress, minorRevision, priority); - } - catch (ConnectionException cex) - { + } catch (ConnectionException cex) { logger.log(Level.SEVERE, "Failed to create transport for: " + serverAddress, cex); } - + return null; } - + /** * Generate Client channel ID (CID). - * @return Client channel ID (CID). - */ - private int generateCID() - { - synchronized (channelsByCID) - { - // search first free (theoretically possible loop of death) - while (getChannel(++lastCID) != null); - // reserve CID - channelsByCID.put(lastCID, null); - return lastCID; - } + * + * @return Client channel ID (CID). + */ + private int generateCID() { + // reserve CID + channelsByCID.put(lastCID, null); + return lastCID; } /** * Free generated channel ID (CID). */ - private void freeCID(int cid) - { - synchronized (channelsByCID) - { - channelsByCID.remove(cid); - } + private void freeCID(int cid) { + channelsByCID.remove(cid); + } /** * Searches for a response request with given channel IOID. - * @param ioid I/O ID. + * + * @param ioid + * I/O ID. * @return request response with given I/O ID. */ - public ResponseRequest getResponseRequest(int ioid) - { - synchronized (pendingResponseRequests) - { - return (ResponseRequest)pendingResponseRequests.get(ioid); - } + public ResponseRequest getResponseRequest(int ioid) { + + return (ResponseRequest) pendingResponseRequests.get(ioid); } /** * Register response request. - * @param request request to register. + * + * @param request + * request to register. * @return request ID (IOID). */ - public int registerResponseRequest(ResponseRequest request) - { - synchronized (pendingResponseRequests) - { - int ioid = generateIOID(); - pendingResponseRequests.put(ioid, request); - return ioid; - } + public int registerResponseRequest(ResponseRequest request) { + + int ioid = generateIOID(); + pendingResponseRequests.put(ioid, request); + return ioid; + } /** * Unregister response request. - * @param request request to unregister. + * + * @param request + * request to unregister. * @return removed object, can be null */ - public ResponseRequest unregisterResponseRequest(ResponseRequest request) - { - synchronized (pendingResponseRequests) - { - return (ResponseRequest)pendingResponseRequests.remove(request.getIOID()); - } + public ResponseRequest unregisterResponseRequest(ResponseRequest request) { + return (ResponseRequest) pendingResponseRequests.remove(request.getIOID()); } /** * Generate IOID. - * @return IOID. - */ - private int generateIOID() - { - synchronized (pendingResponseRequests) - { - // search first free (theoretically possible loop of death) - while (pendingResponseRequests.get(++lastIOID) != null || lastIOID == PVAConstants.PVA_INVALID_IOID); - // reserve IOID - pendingResponseRequests.put(lastIOID, null); - return lastIOID; - } + * + * @return IOID. + */ + private int generateIOID() { + + // search first free (theoretically possible loop of death) + while (pendingResponseRequests.get(++lastIOID) != null || lastIOID == PVAConstants.PVA_INVALID_IOID) + ; + // reserve IOID + pendingResponseRequests.put(lastIOID, null); + return lastIOID; } /** * Get (and if necessary create) beacon handler. - * @param protocol protocol used. - * @param responseFrom remote source address of received beacon. + * + * @param protocol + * protocol used. + * @param responseFrom + * remote source address of received beacon. * @return beacon handler for particular server. */ - public BeaconHandler getBeaconHandler(String protocol, InetSocketAddress responseFrom) - { + public BeaconHandler getBeaconHandler(String protocol, InetSocketAddress responseFrom) { // TODO for now we monitor only TCP responses if (!protocol.equals(ProtocolType.tcp.name())) return null; - + synchronized (beaconHandlers) { - Map protocolBeaconHandlersMap = - beaconHandlers.get(protocol); - if (protocolBeaconHandlersMap == null) - { + Map protocolBeaconHandlersMap = beaconHandlers.get(protocol); + if (protocolBeaconHandlersMap == null) { protocolBeaconHandlersMap = new HashMap(); beaconHandlers.get(protocolBeaconHandlersMap); } - + BeaconHandlerImpl handler = protocolBeaconHandlersMap.get(responseFrom); - if (handler == null) - { + if (handler == null) { handler = new BeaconHandlerImpl(this, protocol, responseFrom); protocolBeaconHandlersMap.put(responseFrom, handler); } @@ -1252,46 +1035,49 @@ public ChannelProvider getProvider() { } private static final StatusCreate statusCreate = PVFactory.getStatusCreate(); - private static final Status okStatus = statusCreate.getStatusOK(); - - private static final Status listNotSupported = - StatusFactory.getStatusCreate() - .createStatus(StatusType.ERROR, "channelList not supported", null); - - private static final Status findNotSupported = - StatusFactory.getStatusCreate() - .createStatus(StatusType.ERROR, "channelFind not supported", null); - - - private class ChannelProviderImpl implements ChannelProvider - { - @SuppressWarnings("unused") - private class ChannelFindImpl implements ChannelFind, SearchInstance - { - final String channelName; - final ChannelFindRequester requester; - final int channelID; - final AtomicInteger userValue = new AtomicInteger(); + private static final Status okStatus = statusCreate.getStatusOK(); + + private static final Status listNotSupported = StatusFactory.getStatusCreate().createStatus(StatusType.ERROR, + "channelList not supported", null); + + private static final Status findNotSupported = StatusFactory.getStatusCreate().createStatus(StatusType.ERROR, + "channelFind not supported", null); + + private class ChannelProviderImpl implements ChannelProvider { + @SuppressWarnings("unused") + private class ChannelFindImpl implements ChannelFind, SearchInstance { + final String channelName; + final ChannelFindRequester requester; + final int channelID; + final AtomicInteger userValue = new AtomicInteger(); public ChannelFindImpl(String channelName, ChannelFindRequester requester) { this.channelName = channelName; this.requester = requester; - + channelID = generateCID(); - + getChannelSearchManager().register(this); } - /* (non-Javadoc) - * @see org.epics.pvaccess.client.impl.remote.ChannelSearchManager.SearchInstance#getChannelID() + /* + * (non-Javadoc) + * + * @see + * org.epics.pvaccess.client.impl.remote.ChannelSearchManager.SearchInstance# + * getChannelID() */ @Override public int getChannelID() { return channelID; } - /* (non-Javadoc) - * @see org.epics.pvaccess.client.impl.remote.ChannelSearchManager.SearchInstance#getChannelName() + /* + * (non-Javadoc) + * + * @see + * org.epics.pvaccess.client.impl.remote.ChannelSearchManager.SearchInstance# + * getChannelName() */ @Override public String getChannelName() { @@ -1304,15 +1090,20 @@ public void searchResponse(GUID guid, byte minorRevision, InetSocketAddress serv requester.channelFindResult(okStatus, this, true); } - /* (non-Javadoc) - * @see org.epics.pvaccess.client.impl.remote.search.SearchInstance#getUserValue() + /* + * (non-Javadoc) + * + * @see + * org.epics.pvaccess.client.impl.remote.search.SearchInstance#getUserValue() */ @Override public AtomicInteger getUserValue() { return userValue; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.client.ChannelFind#cancel() */ @Override @@ -1321,105 +1112,107 @@ public void cancel() { getChannelSearchManager().unregister(this); } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.client.ChannelFind#getChannelProvider() */ @Override public ChannelProvider getChannelProvider() { return getProvider(); } - - } - - /* (non-Javadoc) - * @see org.epics.pvaccess.client.ChannelProvider#channelFind(java.lang.String, org.epics.pvaccess.client.ChannelFindRequester) + + } + + /* + * (non-Javadoc) + * + * @see org.epics.pvaccess.client.ChannelProvider#channelFind(java.lang.String, + * org.epics.pvaccess.client.ChannelFindRequester) */ @Override - public ChannelFind channelFind(String channelName, - ChannelFindRequester channelFindRequester) { + public ChannelFind channelFind(String channelName, ChannelFindRequester channelFindRequester) { checkChannelName(channelName); if (channelFindRequester == null) throw new IllegalArgumentException("null requester"); -/* - synchronized (this) { - try { - checkState(); - return new ChannelFindImpl(channelName, channelFindRequester); - } catch (Throwable th) { - channelFindRequester.channelFindResult(statusCreate.createStatus(StatusType.ERROR, "failed to find channel", th), null, false); - return null; - } - } -*/ - // code above is commented-out to avoid server->client->server looks when - // this provider is used by server provider serving it + channelFindRequester.channelFindResult(findNotSupported, null, false); return null; - + } - - @Override + + @Override public ChannelFind channelList(ChannelListRequester channelListRequester) { - if (channelListRequester == null) + if (channelListRequester == null) throw new IllegalArgumentException("null requester"); channelListRequester.channelListResult(listNotSupported, null, null, false); return null; } - /* (non-Javadoc) - * @see org.epics.pvaccess.client.ChannelProvider#createChannel(java.lang.String, org.epics.pvaccess.client.ChannelRequester, short) + /* + * (non-Javadoc) + * + * @see + * org.epics.pvaccess.client.ChannelProvider#createChannel(java.lang.String, + * org.epics.pvaccess.client.ChannelRequester, short) */ @Override - public Channel createChannel(String channelName, - ChannelRequester channelRequester, short priority) { - return createChannel(channelName, channelRequester, priority, null); + public Channel createChannel(String channelName, ChannelRequester channelRequester, short priority) { + return createChannel(channelName, channelRequester, priority, null); } - - /* (non-Javadoc) - * @see org.epics.pvaccess.client.ChannelProvider#createChannel(java.lang.String, org.epics.pvaccess.client.ChannelRequester, short, java.lang.String[]) - */ - @Override - public Channel createChannel(String channelName, - ChannelRequester channelRequester, short priority, + + /* + * (non-Javadoc) + * + * @see + * org.epics.pvaccess.client.ChannelProvider#createChannel(java.lang.String, + * org.epics.pvaccess.client.ChannelRequester, short, java.lang.String[]) + */ + @Override + public Channel createChannel(String channelName, ChannelRequester channelRequester, short priority, String address) { - Channel channel; + Channel channel; try { // TODO configurable short defaultPort = PVAConstants.PVA_SERVER_PORT; - InetSocketAddress[] addressList = (address == null) ? null : InetAddressUtil.getSocketAddressList(address, defaultPort); + InetSocketAddress[] addressList = (address == null) ? null + : InetAddressUtil.getSocketAddressList(address, defaultPort); channel = createChannelInternal(channelName, channelRequester, priority, addressList); } catch (IllegalArgumentException iae) { throw iae; } catch (Throwable th) { - channelRequester.channelCreated(statusCreate.createStatus(StatusType.ERROR, "failed to create channel", th), null); + channelRequester.channelCreated( + statusCreate.createStatus(StatusType.ERROR, "failed to create channel", th), null); return null; } - channelRequester.channelCreated(okStatus, channel); - return channel; + channelRequester.channelCreated(okStatus, channel); + return channel; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.client.ChannelProvider#getProviderName() */ @Override public String getProviderName() { return PROVIDER_NAME; } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.client.ChannelProvider#destroy() */ @Override public void destroy() { dispose(); } - + } - - } diff --git a/pvAccessJava/src/org/epics/pvaccess/client/impl/remote/search/SimpleChannelSearchManagerImpl.java b/pvAccessJava/src/org/epics/pvaccess/client/impl/remote/search/SimpleChannelSearchManagerImpl.java index 5dae40f81..e3a891fc2 100644 --- a/pvAccessJava/src/org/epics/pvaccess/client/impl/remote/search/SimpleChannelSearchManagerImpl.java +++ b/pvAccessJava/src/org/epics/pvaccess/client/impl/remote/search/SimpleChannelSearchManagerImpl.java @@ -18,6 +18,9 @@ import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; @@ -28,13 +31,13 @@ import org.epics.pvaccess.impl.remote.udp.BlockingUDPTransport.InetAddressType; import org.epics.pvaccess.impl.remote.utils.GUID; import org.epics.pvaccess.util.InetAddressUtil; -import org.epics.pvaccess.util.IntHashMap; import org.epics.pvdata.misc.SerializeHelper; import org.epics.pvdata.misc.Timer.TimerCallback; import org.epics.pvdata.misc.Timer.TimerNode; import org.epics.pvdata.misc.TimerFactory; import org.epics.pvdata.pv.Field; + /** * @author Matej Sekoranja * @version $Id$ @@ -64,7 +67,8 @@ public class SimpleChannelSearchManagerImpl implements ChannelSearchManager, Tim /** * Set of registered channels. */ - private final IntHashMap channels = new IntHashMap(); + private final Map channels = + Collections.synchronizedMap(new HashMap()); private final ArrayList immediateSearch = new ArrayList(128); @@ -133,7 +137,7 @@ public void run() send(sis); } - catch (Throwable th) + catch (Exception th) { // should never happen, be we are careful and verbose th.printStackTrace(); @@ -402,17 +406,21 @@ public void newServerDetected() private void boost() { - synchronized (channels) { - if (channels.size() == 0) - return; - - // TODO no copy when iterator is supported - SearchInstance[] sis = new SearchInstance[channels.size()]; - channels.toArray(sis); - - - for (SearchInstance si : sis) - si.getUserValue().set(BOOST_VALUE); +// synchronized (channels) { +// if (channels.size() == 0) +// return; +// +// // TODO no copy when iterator is supported +// SearchInstance[] sis = new SearchInstance[channels.size()]; +// channels.values().toArray(sis); +// +// +// for (SearchInstance si : sis) +// si.getUserValue().set(BOOST_VALUE); +// } + + for(SearchInstance searchInstance : channels.values()) { + searchInstance.getUserValue().set(BOOST_VALUE); } } @@ -431,12 +439,12 @@ public void callback() { try { SearchInstance[] sis; - synchronized (channels) { + //synchronized (channels) { if (channels.size() == 0) return; sis = new SearchInstance[channels.size()]; - channels.toArray(sis); - } + channels.values().toArray(sis); + //} send(sis); } diff --git a/pvAccessJava/src/org/epics/pvaccess/client/pvms/IncomingMulticastIntrospectionRegistry.java b/pvAccessJava/src/org/epics/pvaccess/client/pvms/IncomingMulticastIntrospectionRegistry.java index 292273cb7..f97e5ca0c 100644 --- a/pvAccessJava/src/org/epics/pvaccess/client/pvms/IncomingMulticastIntrospectionRegistry.java +++ b/pvAccessJava/src/org/epics/pvaccess/client/pvms/IncomingMulticastIntrospectionRegistry.java @@ -15,15 +15,18 @@ package org.epics.pvaccess.client.pvms; import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.epics.pvaccess.PVFactory; import org.epics.pvaccess.impl.remote.IntrospectionRegistry; -import org.epics.pvaccess.util.ShortHashMap; import org.epics.pvdata.pv.DeserializableControl; import org.epics.pvdata.pv.Field; import org.epics.pvdata.pv.FieldCreate; + /** * Incoming (receive) introspection registry. * Registry is used to cache introspection interfaces to minimize network traffic. @@ -60,7 +63,8 @@ public int getSerializationSize() { } // TODO generics - protected final ShortHashMap registry = new ShortHashMap(); + protected final Map registry = + Collections.synchronizedMap(new HashMap()); public IncomingMulticastIntrospectionRegistry() { @@ -82,7 +86,7 @@ public void reset() */ public FieldEntry getIntrospectionInterface(short id) { - return (FieldEntry)registry.get(id); + return registry.get(id); } /** diff --git a/pvAccessJava/src/org/epics/pvaccess/client/rpc/package.html b/pvAccessJava/src/org/epics/pvaccess/client/rpc/package.html index b47ba6bd9..9b33f8671 100644 --- a/pvAccessJava/src/org/epics/pvaccess/client/rpc/package.html +++ b/pvAccessJava/src/org/epics/pvaccess/client/rpc/package.html @@ -10,7 +10,8 @@

Support code for implementing channelRPC.

-

PVService
This page documentation needs to be updated!
+

PVService
+This page documentation needs to be updated!
2011.08.22

CONTENTS @@ -28,11 +29,10 @@

PVService
This page documentation needs to b -

-
+

Introduction

-
+

This package provides support for services that are implemented as an RPC (Remote Procedure Call). PVAccess provideds two flavors of RPC: putProcessGet @@ -102,25 +102,25 @@

Introduction

The rest of this document describes the following:

    -
  • client
    +
  • client
    The java interfaces and factories this project provides for a service client.
  • -
  • server
    +
  • server
    The java interfaces and factories this project provides for a service server.
  • Examples:
      -
    • table
      +
    • table
      An example that returns data that can be interpeted as a table.
    • -
    • example
      +
    • example
      A example of a service that returns a somewhat complex structure.
-
+

Client

-
+

The client is implemented via the following interfaces and factory:

interface ServiceClientRequester extends Requester{
@@ -196,10 +196,10 @@ 

Client

-
+

Server

-
+

The service is implemented via the following interfaces and factory:

//RPCServer defined in org.epics.ca.server.impl.local
@@ -259,10 +259,10 @@ 

Server

Called by org.epics.ca.server.impl.local.ChannelServerFactory when the PVRecord for the service is initialized.
-
+

Examples

-
+

The project provides examples in package (org.epics.pvService.example). This section describes ExampleClient and ExampleServiceFactory, which are the client @@ -402,7 +402,7 @@

The service record

This is the record for clientExample
tableSevice
This is the record for clientTable
- You should see: +

Note that both records are created by extending org.epics.pvService.service. This is defined in pvService.xml.structure.service.xml:

diff --git a/pvAccessJava/src/org/epics/pvaccess/impl/remote/IntrospectionRegistry.java b/pvAccessJava/src/org/epics/pvaccess/impl/remote/IntrospectionRegistry.java index ea5acb4e3..dd2bf338f 100644 --- a/pvAccessJava/src/org/epics/pvaccess/impl/remote/IntrospectionRegistry.java +++ b/pvAccessJava/src/org/epics/pvaccess/impl/remote/IntrospectionRegistry.java @@ -15,16 +15,17 @@ package org.epics.pvaccess.impl.remote; import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; import org.epics.pvaccess.PVFactory; -import org.epics.pvaccess.util.ShortHashMap; +import org.epics.pvaccess.util.BooleanHolder; import org.epics.pvdata.pv.DeserializableControl; import org.epics.pvdata.pv.Field; import org.epics.pvdata.pv.FieldCreate; import org.epics.pvdata.pv.SerializableControl; import org.epics.pvdata.pv.Type; -import org.omg.CORBA.BooleanHolder; -import org.omg.CORBA.ShortHolder; + /** @@ -35,7 +36,8 @@ */ public final class IntrospectionRegistry { - protected ShortHashMap registry = new ShortHashMap(); + protected Map registry = + new HashMap<>(); protected short pointer; public IntrospectionRegistry() @@ -58,7 +60,7 @@ public void reset() */ public Field getIntrospectionInterface(short id) { - return (Field)registry.get(id); + return registry.get(id); } /** @@ -74,7 +76,7 @@ public void registerIntrospectionInterface(short id, Field field) /** * Private helper variable (optimization). */ - private ShortHolder shortHolder = new ShortHolder(); + private Short shortHolder = Short.valueOf((short)0); /** * Register introspection interface and get it's ID. Always OUTGOING. @@ -85,11 +87,10 @@ public void registerIntrospectionInterface(short id, Field field) */ public short registerIntrospectionInterface(Field field, BooleanHolder existing) { - // TODO this is slow - if (registry.contains(field, shortHolder)) - { + Field potentiallyExistingField = registry.get(shortHolder); + if(potentiallyExistingField != null && potentiallyExistingField.equals(field)) { existing.value = true; - return shortHolder.value; + return shortHolder; } else { diff --git a/pvAccessJava/src/org/epics/pvaccess/impl/remote/TransportRegistry.java b/pvAccessJava/src/org/epics/pvaccess/impl/remote/TransportRegistry.java index 2f87cc8a4..4fc5dd91b 100644 --- a/pvAccessJava/src/org/epics/pvaccess/impl/remote/TransportRegistry.java +++ b/pvAccessJava/src/org/epics/pvaccess/impl/remote/TransportRegistry.java @@ -16,14 +16,14 @@ import java.net.InetSocketAddress; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; -import org.epics.pvaccess.util.IntHashMap; - - /** * Class to cache PVA transports (connections to other hosts). + * * @author Matej Sekoranja * @version $Id$ */ @@ -32,153 +32,149 @@ public final class TransportRegistry { /** * Map caching transports. */ - private Map transports; - + private Map> transports; + /** * Array of all transports. */ - private ArrayList allTransports; + private List allTransports; /** * Constructor. */ public TransportRegistry() { - transports = new HashMap(); - allTransports = new ArrayList(); + transports = Collections.synchronizedMap(new HashMap>()); + allTransports = Collections.synchronizedList(new ArrayList()); } /** * Save/cache new transport into the registry. - * @param transport transport to be registered. + * + * @param transport + * transport to be registered. */ - public void put(Transport transport) - { + public void put(Transport transport) { // TODO support type - //final String type = transport.getType(); final short priority = transport.getPriority(); final InetSocketAddress address = transport.getRemoteAddress(); - synchronized (transports) { - IntHashMap priorities = (IntHashMap)transports.get(address); - if (priorities == null) { - priorities = new IntHashMap(); - transports.put(address, priorities); - } - priorities.put(priority, transport); - allTransports.add(transport); + Map priorities = transports.get(address); + if (priorities == null) { + priorities = Collections.synchronizedMap(new HashMap()); + transports.put(address, priorities); } + priorities.put(priority, transport); + allTransports.add(transport); + } /** * Lookup for a transport for given address. - * @param type protocol type. - * @param address address of the host computer. - * @param priority priority of the transport. + * + * @param type + * protocol type. + * @param address + * address of the host computer. + * @param priority + * priority of the transport. * @return corresponding transport, null if none found. */ - public Transport get(String type, InetSocketAddress address, short priority) - { + public Transport get(String type, InetSocketAddress address, short priority) { // TODO support type - synchronized (transports) { - IntHashMap priorities = transports.get(address); - if (priorities != null) - return (Transport)priorities.get(priority); - else - return null; - } + Map priorities = transports.get(address); + if (priorities != null) + return priorities.get(priority); + else + return null; } /** * Lookup for a transport for given address (all priorities). - * @param type protocol type (e.g. tcp, udp, ssl, etc.). - * @param address address of the host computer. + * + * @param type + * protocol type (e.g. tcp, udp, ssl, etc.). + * @param address + * address of the host computer. * @return array of corresponding transports, null if none found. */ - public Transport[] get(String type, InetSocketAddress address) - { + public Transport[] get(String type, InetSocketAddress address) { // TODO support type - synchronized (transports) { - IntHashMap priorities = transports.get(address); - if (priorities != null) - { - // TODO optimize - Transport[] ts = new Transport[priorities.size()]; - priorities.toArray(ts); - return ts; - } - else - return null; - } + Map priorities = transports.get(address); + if (priorities != null) { + // TODO optimize + Transport[] ts = new Transport[priorities.size()]; + priorities.values().toArray(ts); + return ts; + } else + return null; } /** * Remove transport from the registry. - * @param transport transport to remove. + * + * @param transport + * transport to remove. * @return removed transport, null if none found. */ - public Transport remove(Transport transport) - { + public Transport remove(Transport transport) { // TODO support type - //final String type = transport.getType(); final short priority = transport.getPriority(); final InetSocketAddress address = transport.getRemoteAddress(); - synchronized (transports) { - IntHashMap priorities = transports.get(address); - if (priorities != null) { - transport = (Transport)priorities.remove(priority); - if (priorities.size() == 0) - transports.remove(address); - if (transport != null) - allTransports.remove(transport); - return transport; - } - else - return null; - } + + Map priorities = transports.get(address); + if (priorities != null) { + transport = priorities.remove(priority); + if (priorities.size() == 0) + transports.remove(address); + if (transport != null) + allTransports.remove(transport); + return transport; + } else + return null; } /** * Clear cache. */ - public void clear() - { - synchronized (transports) { - transports.clear(); - allTransports.clear(); - } + public void clear() { + + transports.clear(); + + allTransports.clear(); } - + /** * Get number of active (cached) transports. + * * @return number of active (cached) transports. */ - public int numberOfActiveTransports() - { - synchronized (transports) { - return allTransports.size(); - } + public int numberOfActiveTransports() { + + return allTransports.size(); + } /** * Get array of all active (cached) transports. - * @param type protocol type (e.g. tcp, udp, ssl, etc.). + * + * @param type + * protocol type (e.g. tcp, udp, ssl, etc.). * @return array of all active (cached) transports. */ - public Transport[] toArray(String type) - { + public Transport[] toArray(String type) { // TODO support type - synchronized (transports) { - return (Transport[]) allTransports.toArray(new Transport[transports.size()]); - } + + return allTransports.toArray(new Transport[transports.size()]); + } /** * Get array of all active (cached) transports. + * * @return array of all active (cached) transports. */ - public Transport[] toArray() - { - synchronized (transports) { - return (Transport[]) allTransports.toArray(new Transport[transports.size()]); - } + public Transport[] toArray() { + + return allTransports.toArray(new Transport[transports.size()]); + } } diff --git a/pvAccessJava/src/org/epics/pvaccess/server/impl/remote/ServerChannelImpl.java b/pvAccessJava/src/org/epics/pvaccess/server/impl/remote/ServerChannelImpl.java index f50018873..7833c75f1 100644 --- a/pvAccessJava/src/org/epics/pvaccess/server/impl/remote/ServerChannelImpl.java +++ b/pvAccessJava/src/org/epics/pvaccess/server/impl/remote/ServerChannelImpl.java @@ -15,16 +15,19 @@ package org.epics.pvaccess.server.impl.remote; import java.io.PrintStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.epics.pvaccess.client.Channel; import org.epics.pvaccess.impl.remote.server.ServerChannel; import org.epics.pvaccess.plugins.SecurityPlugin.ChannelSecuritySession; -import org.epics.pvaccess.util.IntHashMap; import org.epics.pvdata.misc.Destroyable; /** - * Server channel (client connection to local channel). - * This (default) implementation grants all access rights. + * Server channel (client connection to local channel). This (default) + * implementation grants all access rights. + * * @author Matej Sekoranja * @version $Id$ */ @@ -44,7 +47,7 @@ public class ServerChannelImpl implements ServerChannel { * Channel CID. */ protected final int cid; - + /** * Channel secutiry session. */ @@ -53,7 +56,8 @@ public class ServerChannelImpl implements ServerChannel { /** * Requests. */ - protected final IntHashMap requests = new IntHashMap(); + protected final Map requests = Collections + .synchronizedMap(new HashMap()); /** * Destroy state. @@ -62,15 +66,17 @@ public class ServerChannelImpl implements ServerChannel { /** * Create server channel for given process variable. - * @param channel local channel. - * @param cid channel CID. - * @param sid channel SID. - * @param css channel security session. - */ - public ServerChannelImpl(Channel channel, - int cid, int sid, - ChannelSecuritySession css) - { + * + * @param channel + * local channel. + * @param cid + * channel CID. + * @param sid + * channel SID. + * @param css + * channel security session. + */ + public ServerChannelImpl(Channel channel, int cid, int sid, ChannelSecuritySession css) { if (channel == null) throw new IllegalArgumentException("non null local channel required"); @@ -79,25 +85,28 @@ public ServerChannelImpl(Channel channel, this.channel = channel; this.channelSecuritySession = css; } - + /** * Get local channel. + * * @return local channel. */ - public Channel getChannel() - { + public Channel getChannel() { return channel; } - + /** * Get channel CID. + * * @return channel CID. */ public int getCID() { return cid; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.server.impl.remote.ServerChannel#getSID() */ public int getSID() { @@ -108,123 +117,123 @@ public ChannelSecuritySession getChannelSecuritySession() { return channelSecuritySession; } - /** - * Register request - * @param id request ID. - * @param request request to be registered. - */ - public void registerRequest(int id, Destroyable request) - { - if (request == null) - throw new IllegalArgumentException("request == null"); - - synchronized (requests) { - requests.put(id, request); - } - } - - /** - * Unregister request. - * @param id request ID. - */ - public void unregisterRequest(int id) - { - synchronized (requests) { - requests.remove(id); - } - } - - /** - * Get request by its ID. - * @param id request ID. - * @return request with given ID, null if there is no request with such ID. - */ - public Destroyable getRequest(int id) - { - synchronized (requests) { - return (Destroyable)requests.get(id); - } - } - - public Destroyable[] getRequests() - { - synchronized (requests) { - Destroyable[] reqs = new Destroyable[requests.size()]; - requests.toArray(reqs); - return reqs; - } + /** + * Register request + * + * @param id + * request ID. + * @param request + * request to be registered. + */ + public void registerRequest(int id, Destroyable request) { + if (request == null) + throw new IllegalArgumentException("request == null"); + + requests.put(id, request); + } + + /** + * Unregister request. + * + * @param id + * request ID. + */ + public void unregisterRequest(int id) { + + requests.remove(id); + + } + + /** + * Get request by its ID. + * + * @param id + * request ID. + * @return request with given ID, null if there is no request with + * such ID. + */ + public Destroyable getRequest(int id) { + + return (Destroyable) requests.get(id); + } - /** - * Destroy all registered requests. - */ - protected void destroyAllRequests() - { - int[] keys; - synchronized (requests) { - - // resource allocation optimization - if (requests.size() == 0) - return; - - keys = requests.keysArray(); - for (int i = 0; i < keys.length; i++) { - final Destroyable cr = (Destroyable)requests.remove(keys[i]); - cr.destroy(); - } + public Destroyable[] getRequests() { + + Destroyable[] reqs = new Destroyable[requests.size()]; + requests.values().toArray(reqs); + return reqs; + + } + + /** + * Destroy all registered requests. + */ + protected void destroyAllRequests() { + Integer[] keys; + + // resource allocation optimization + if (requests.size() == 0) + return; + + keys = new Integer[requests.keySet().size()]; + requests.keySet().toArray(keys); + for (int i = 0; i < keys.length; i++) { + final Destroyable cr = (Destroyable) requests.remove(keys[i]); + cr.destroy(); } - - } - /* (non-Javadoc) + } + + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.server.impl.remote.ServerChannel#destroy() - */ - public synchronized void destroy() - { + */ + public synchronized void destroy() { if (destroyed) return; destroyed = true; - + // destroy all requests destroyAllRequests(); - try - { + try { channelSecuritySession.close(); } catch (Throwable th) { // guard from bad plug-on // TODO th.printStackTrace(); } - + // TODO make impl that does shares channels (and does ref counting)!!! // try catch? channel.destroy(); } - /** - * Prints detailed information about the process variable to the standard output stream. - * @throws IllegalStateException if the context has been destroyed. - */ - public void printInfo() throws IllegalStateException - { - printInfo(System.out); - } - - /** - * Prints detailed information about the process variable to the specified output - * stream. - * @param out the output stream. - * @throws IllegalStateException if the context has been destroyed. - */ - public void printInfo(PrintStream out) - { - out.println("CLASS : " + getClass().getName()); - out.println("CHANNEL : " + channel); - //out.println("RIGHTS : " + AccessRights.getEnumSet(getAccessRights())); - } + /** + * Prints detailed information about the process variable to the standard output + * stream. + * + * @throws IllegalStateException + * if the context has been destroyed. + */ + public void printInfo() throws IllegalStateException { + printInfo(System.out); + } + /** + * Prints detailed information about the process variable to the specified + * output stream. + * + * @param out + * the output stream. + * @throws IllegalStateException + * if the context has been destroyed. + */ + public void printInfo(PrintStream out) { + out.println("CLASS : " + getClass().getName()); + out.println("CHANNEL : " + channel); + } } - - diff --git a/pvAccessJava/src/org/epics/pvaccess/server/impl/remote/tcp/BlockingServerTCPTransport.java b/pvAccessJava/src/org/epics/pvaccess/server/impl/remote/tcp/BlockingServerTCPTransport.java index 8a9c7597c..8dcf1f0e4 100644 --- a/pvAccessJava/src/org/epics/pvaccess/server/impl/remote/tcp/BlockingServerTCPTransport.java +++ b/pvAccessJava/src/org/epics/pvaccess/server/impl/remote/tcp/BlockingServerTCPTransport.java @@ -20,6 +20,8 @@ import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -39,7 +41,6 @@ import org.epics.pvaccess.plugins.SecurityPlugin; import org.epics.pvaccess.plugins.SecurityPlugin.SecurityPluginControl; import org.epics.pvaccess.plugins.SecurityPlugin.SecuritySession; -import org.epics.pvaccess.util.IntHashMap; import org.epics.pvdata.factory.StatusFactory; import org.epics.pvdata.misc.SerializeHelper; import org.epics.pvdata.pv.PVField; @@ -48,45 +49,50 @@ /** * Server TCP transport implementation. + * * @author Matej Sekoranja * @version $Id$ */ public class BlockingServerTCPTransport extends BlockingTCPTransport - implements ChannelHostingTransport, TransportSender, SecurityPluginControl { + implements ChannelHostingTransport, TransportSender, SecurityPluginControl { /** - * Last SID cache. + * Last SID cache. */ private AtomicInteger lastChannelSID = new AtomicInteger(0); /** * Channel table (SID -> channel mapping). */ - private IntHashMap channels; + private Map channels; /** * Server TCP transport constructor. - * @param context context where transport lives in. - * @param channel used socket channel. - * @param responseHandler response handler used to process PVA headers. - * @param receiveBufferSize receive buffer size. - * @throws SocketException thrown on any socket exception. + * + * @param context + * context where transport lives in. + * @param channel + * used socket channel. + * @param responseHandler + * response handler used to process PVA headers. + * @param receiveBufferSize + * receive buffer size. + * @throws SocketException + * thrown on any socket exception. */ - public BlockingServerTCPTransport(Context context, - SocketChannel channel, - ResponseHandler responseHandler, - int receiveBufferSize) throws SocketException { + public BlockingServerTCPTransport(Context context, SocketChannel channel, ResponseHandler responseHandler, + int receiveBufferSize) throws SocketException { super(context, channel, responseHandler, receiveBufferSize, PVAConstants.PVA_DEFAULT_PRIORITY); // NOTE: priority not yet known, default priority is used to register/unregister - // TODO implement priorities in Reactor... not that user will change it.. still getPriority() must return "registered" priority! - + // TODO implement priorities in Reactor... not that user will change it.. still + // getPriority() must return "registered" priority! + final int INITIAL_SIZE = 64; - channels = new IntHashMap(INITIAL_SIZE); - + channels = Collections.synchronizedMap(new HashMap(INITIAL_SIZE)); + start(); } - - + /** * @see org.epics.pvaccess.impl.remote.tcp.BlockingTCPTransport#internalClose() */ @@ -101,115 +107,96 @@ protected void internalClose() { */ private void destroyAllChannels() { - ServerChannel[] channelsArray; - synchronized (channels) - { - // resource allocation optimization - if (channels.size() == 0) - return; - - channelsArray = new ServerChannel[channels.size()]; - channels.toArray(channelsArray); + context.getLogger().fine("Transport to " + socketAddress + " still has " + channels.size() + + " channel(s) active and closing..."); - channels.clear(); + for (ServerChannel serverChannel : channels.values()) { + serverChannel.destroy(); } - context.getLogger().fine("Transport to " + socketAddress + " still has " + channelsArray.length + " channel(s) active and closing..."); - - for (int i = 0; i < channelsArray.length; i++) - { - try - { - channelsArray[i].destroy(); - } - catch (Throwable th) - { - th.printStackTrace(); - } - } + channels.clear(); } /** * Preallocate new channel SID. + * * @return new channel server id (SID). */ - public int preallocateChannelSID() - { - synchronized (channels) { - // search first free (theoretically possible loop of death) - int sid = lastChannelSID.incrementAndGet(); - while (channels.containsKey(sid)) - sid = lastChannelSID.incrementAndGet(); - return sid; - } + public int preallocateChannelSID() { + // search first free (theoretically possible loop of death) + int sid = lastChannelSID.incrementAndGet(); + while (channels.containsKey(sid)) + sid = lastChannelSID.incrementAndGet(); + return sid; } /** * De-preallocate new channel SID. - * @param sid preallocated channel SID. + * + * @param sid + * preallocated channel SID. */ - public void depreallocateChannelSID(int sid) - { + public void depreallocateChannelSID(int sid) { // noop } /** * Register a new channel. - * @param sid preallocated channel SID. - * @param channel channel to register. + * + * @param sid + * preallocated channel SID. + * @param channel + * channel to register. */ - public void registerChannel(int sid, ServerChannel channel) - { - synchronized (channels) { - channels.put(sid, channel); - } + public void registerChannel(int sid, ServerChannel channel) { + channels.put(sid, channel); } - + /** * Unregister a new channel (and deallocates its handle). - * @param sid SID + * + * @param sid + * SID */ - public void unregisterChannel(int sid) - { - synchronized (channels) { - channels.remove(sid); - } + public void unregisterChannel(int sid) { + channels.remove(sid); } /** * Get channel by its SID. - * @param sid channel SID + * + * @param sid + * channel SID * @return channel with given SID, null otherwise */ - public ServerChannel getChannel(int sid) - { - synchronized (channels) { - return (ServerChannel)channels.get(sid); - } + public ServerChannel getChannel(int sid) { + + return channels.get(sid); + } @Override - public ServerChannel[] getChannels() - { - synchronized (channels) { - ServerChannel[] sca = new ServerChannel[channels.size()]; - channels.toArray(sca); - return sca; - } + public ServerChannel[] getChannels() { + + ServerChannel[] sca = new ServerChannel[channels.size()]; + channels.values().toArray(sca); + return sca; + } /** * Get channel count. + * * @return channel count. */ - public int getChannelCount() - { - synchronized (channels) { - return channels.size(); - } + public int getChannelCount() { + return channels.size(); + } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.impl.remote.TransportSender#lock() */ @Override @@ -217,7 +204,9 @@ public void lock() { // noop } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.impl.remote.TransportSender#unlock() */ @Override @@ -227,119 +216,122 @@ public void unlock() { // always called from the same thread, therefore no sync needed private boolean verifyOrVerified = false; - + /** - * PVA connection validation request. - * A server sends a validate connection message when it receives a new connection. - * The message indicates that the server is ready to receive requests; the client must - * not send any messages on the connection until it has received the validate connection message - * from the server. No reply to the message is expected by the server. - * The purpose of the validate connection message is two-fold: - * It informs the client of the protocol version supported by the server. - * It prevents the client from writing a request message to its local transport - * buffers until after the server has acknowledged that it can actually process the - * request. This avoids a race condition caused by the server's TCP/IP stack - * accepting connections in its backlog while the server is in the process of shutting down: - * if the client were to send a request in this situation, the request - * would be lost but the client could not safely re-issue the request because that - * might violate at-most-once semantics. - * The validate connection message guarantees that a server is not in the middle - * of shutting down when the server's TCP/IP stack accepts an incoming connection - * and so avoids the race condition. - * @see org.epics.pvaccess.impl.remote.TransportSender#send(java.nio.ByteBuffer, org.epics.pvaccess.impl.remote.TransportSendControl) + * PVA connection validation request. A server sends a validate connection + * message when it receives a new connection. The message indicates that the + * server is ready to receive requests; the client must not send any messages on + * the connection until it has received the validate connection message from the + * server. No reply to the message is expected by the server. The purpose of the + * validate connection message is two-fold: It informs the client of the + * protocol version supported by the server. It prevents the client from writing + * a request message to its local transport buffers until after the server has + * acknowledged that it can actually process the request. This avoids a race + * condition caused by the server's TCP/IP stack accepting connections in its + * backlog while the server is in the process of shutting down: if the client + * were to send a request in this situation, the request would be lost but the + * client could not safely re-issue the request because that might violate + * at-most-once semantics. The validate connection message guarantees that a + * server is not in the middle of shutting down when the server's TCP/IP stack + * accepts an incoming connection and so avoids the race condition. + * + * @see org.epics.pvaccess.impl.remote.TransportSender#send(java.nio.ByteBuffer, + * org.epics.pvaccess.impl.remote.TransportSendControl) */ @Override public void send(ByteBuffer buffer, TransportSendControl control) { - if (!verifyOrVerified) - { + if (!verifyOrVerified) { verifyOrVerified = true; - + // - // set byte order control message + // set byte order control message // - + ensureBuffer(PVAConstants.PVA_MESSAGE_HEADER_SIZE); sendBuffer.put(PVAConstants.PVA_MAGIC); sendBuffer.put(PVAConstants.PVA_VERSION); - sendBuffer.put((byte)0xc1); // control + server + big endian - sendBuffer.put((byte)2); // set byte order - sendBuffer.putInt(0); - - + sendBuffer.put((byte) 0xc1); // control + server + big endian + sendBuffer.put((byte) 2); // set byte order + sendBuffer.putInt(0); + // // send verification message // - - control.startMessage((byte)1, 4+2); - + + control.startMessage((byte) 1, 4 + 2); + // receive buffer size buffer.putInt(getReceiveBufferSize()); - + // server introspection registry max size // TODO buffer.putShort(Short.MAX_VALUE); - + // list of authNZ plugin names Map securityPlugins = context.getSecurityPlugins(); List validSPNames = new ArrayList(securityPlugins.size()); - InetSocketAddress remoteAddress = (InetSocketAddress)channel.socket().getRemoteSocketAddress(); - for (SecurityPlugin securityPlugin : securityPlugins.values()) - { - try - { + InetSocketAddress remoteAddress = (InetSocketAddress) channel.socket().getRemoteSocketAddress(); + for (SecurityPlugin securityPlugin : securityPlugins.values()) { + try { if (securityPlugin.isValidFor(remoteAddress)) validSPNames.add(securityPlugin.getId()); } catch (Throwable th) { - context.getLogger().log(Level.SEVERE, "Unexpected exception caught while calling SecurityPluin.isValidFor(InetAddress)/getId() methods.", th); + context.getLogger().log(Level.SEVERE, + "Unexpected exception caught while calling SecurityPluin.isValidFor(InetAddress)/getId() methods.", + th); } } - + int validSPCount = validSPNames.size(); - + SerializeHelper.writeSize(validSPCount, buffer, this); for (String spName : validSPNames) SerializeHelper.serializeString(spName, buffer, this); - + securityRequired = (validSPCount > 0); - + // send immediately control.flush(true); - } - else - { + } else { // // send verified message // - - control.startMessage((byte)9, 0); + + control.startMessage((byte) 9, 0); verificationStatus.serialize(buffer, control); - + // send immediately control.flush(true); } } - /* (non-Javadoc) - * @see org.epics.pvaccess.impl.remote.Transport#acquire(org.epics.pvaccess.impl.remote.TransportClient) + /* + * (non-Javadoc) + * + * @see + * org.epics.pvaccess.impl.remote.Transport#acquire(org.epics.pvaccess.impl. + * remote.TransportClient) */ @Override public boolean acquire(TransportClient client) { return false; } - - /* (non-Javadoc) - * @see org.epics.pvaccess.impl.remote.Transport#release(org.epics.pvaccess.impl.remote.TransportClient) + /* + * (non-Javadoc) + * + * @see + * org.epics.pvaccess.impl.remote.Transport#release(org.epics.pvaccess.impl. + * remote.TransportClient) */ @Override public void release(TransportClient client) { } - - private static Status validationTimeoutStatus = - StatusFactory.getStatusCreate() - .createStatus(StatusType.ERROR, "server-side validation timeout", null); + + private static Status validationTimeoutStatus = StatusFactory.getStatusCreate().createStatus(StatusType.ERROR, + "server-side validation timeout", null); private volatile Status verificationStatus = validationTimeoutStatus; @@ -348,7 +340,7 @@ public void verified(Status status) { verificationStatus = status; super.verified(status); } - + @Override public boolean verify(long timeoutMs) { enqueueSendRequest(this); @@ -359,54 +351,51 @@ public boolean verify(long timeoutMs) { return verified; } - - private static Status invalidSecurityPluginNameStatus = - StatusFactory.getStatusCreate() - .createStatus(StatusType.ERROR, "invalid security plug-in name", null); - + + private static Status invalidSecurityPluginNameStatus = StatusFactory.getStatusCreate() + .createStatus(StatusType.ERROR, "invalid security plug-in name", null); + @Override public void authNZInitialize(Object data) { - - Object[] dataArray = (Object[])data; - String securityPluginName = (String)dataArray[0]; - PVField initializationData = (PVField)dataArray[1]; - - InetSocketAddress remoteAddress = (InetSocketAddress)channel.socket().getRemoteSocketAddress(); - + + Object[] dataArray = (Object[]) data; + String securityPluginName = (String) dataArray[0]; + PVField initializationData = (PVField) dataArray[1]; + + InetSocketAddress remoteAddress = (InetSocketAddress) channel.socket().getRemoteSocketAddress(); + // check if plug-in name is valid SecurityPlugin securityPlugin = context.getSecurityPlugins().get(securityPluginName); - if (securityPlugin == null) - { - if (securityRequired) - { + if (securityPlugin == null) { + if (securityRequired) { verified(invalidSecurityPluginNameStatus); return; - } - else - { + } else { securityPlugin = NoSecurityPlugin.INSTANCE; - context.getLogger().finer("No security plug-in installed, selecting default plug-in '" + securityPlugin.getId() + "' for PVA client: " + remoteAddress); + context.getLogger().finer("No security plug-in installed, selecting default plug-in '" + + securityPlugin.getId() + "' for PVA client: " + remoteAddress); } } - - try - { + + try { if (!securityPlugin.isValidFor(remoteAddress)) verified(invalidSecurityPluginNameStatus); } catch (Throwable th) { - context.getLogger().log(Level.SEVERE, "Unexpected exception caught while calling SecurityPluin.isValidFor(InetAddress) methods.", th); - - verified(StatusFactory.getStatusCreate() - .createStatus(StatusType.ERROR, "disfunctional security plug-in", th)); + context.getLogger().log(Level.SEVERE, + "Unexpected exception caught while calling SecurityPluin.isValidFor(InetAddress) methods.", th); + + verified(StatusFactory.getStatusCreate().createStatus(StatusType.ERROR, "disfunctional security plug-in", + th)); return; } - context.getLogger().finer("Accepted security plug-in '" + securityPluginName + "' for PVA client: " + remoteAddress); - + context.getLogger() + .finer("Accepted security plug-in '" + securityPluginName + "' for PVA client: " + remoteAddress); + // create session securitySession = securityPlugin.createSession(remoteAddress, this, initializationData); } - + private volatile SecuritySession securitySession = null; private volatile boolean securityRequired = true; @@ -418,26 +407,26 @@ public void authNZMessage(PVField data) { else context.getLogger().warning("authNZ message received but no security plug-in session active"); } - + @Override public void sendSecurityPluginMessage(PVField data) { // TODO not optimal since it allocates a new object every time enqueueSendRequest(new SecurityPluginMessageTransportSender(data)); } - + @Override public void authenticationCompleted(Status status) { try { - context.getLogger().finer("Authentication completed with status '" + status.getType() + "' for PVA client: " + channel.getRemoteAddress()); + context.getLogger().finer("Authentication completed with status '" + status.getType() + "' for PVA client: " + + channel.getRemoteAddress()); } catch (IOException e1) { // noop } - + if (!verified) verified(status); - else if (!status.isSuccess()) - { + else if (!status.isSuccess()) { String msg = "Re-authentication failed: " + status.getMessage(); String stackDump = status.getStackDump(); if (stackDump != null && !stackDump.isEmpty()) @@ -452,8 +441,9 @@ else if (!status.isSuccess()) } } - - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.epics.pvaccess.impl.remote.Transport#aliveNotification() */ @Override @@ -464,25 +454,24 @@ public void aliveNotification() { // TODO move to proper place @Override public void close() throws IOException { - - if (securitySession != null) - { + + if (securitySession != null) { try { securitySession.close(); } catch (Throwable th) { - context.getLogger().log(Level.WARNING, "Unexpection exception caight while closing secutiry session.", th); + context.getLogger().log(Level.WARNING, "Unexpection exception caight while closing secutiry session.", + th); } - + securitySession = null; } - + super.close(); } - @Override public SecuritySession getSecuritySession() { return securitySession; } - + } diff --git a/pvAccessJava/src/org/epics/pvaccess/server/impl/remote/tcp/NonBlockingServerTCPTransport.java b/pvAccessJava/src/org/epics/pvaccess/server/impl/remote/tcp/NonBlockingServerTCPTransport.java index 89ef07c5f..186487b15 100644 --- a/pvAccessJava/src/org/epics/pvaccess/server/impl/remote/tcp/NonBlockingServerTCPTransport.java +++ b/pvAccessJava/src/org/epics/pvaccess/server/impl/remote/tcp/NonBlockingServerTCPTransport.java @@ -20,6 +20,8 @@ import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -40,7 +42,6 @@ import org.epics.pvaccess.plugins.SecurityPlugin; import org.epics.pvaccess.plugins.SecurityPlugin.SecurityPluginControl; import org.epics.pvaccess.plugins.SecurityPlugin.SecuritySession; -import org.epics.pvaccess.util.IntHashMap; import org.epics.pvdata.factory.StatusFactory; import org.epics.pvdata.misc.SerializeHelper; import org.epics.pvdata.pv.PVField; @@ -63,7 +64,7 @@ public class NonBlockingServerTCPTransport extends NonBlockingTCPTransport /** * Channel table (SID -> channel mapping). */ - private IntHashMap channels; + private Map channels; /** * Server TCP transport constructor. @@ -85,9 +86,8 @@ public NonBlockingServerTCPTransport(Context context, // TODO implement priorities in Reactor... not that user will change it.. still getPriority() must return "registered" priority! final int INITIAL_SIZE = 64; - channels = new IntHashMap(INITIAL_SIZE); + channels = Collections.synchronizedMap(new HashMap(INITIAL_SIZE)); - //start(); } @@ -105,32 +105,15 @@ protected void internalClose() { */ private void destroyAllChannels() { - ServerChannel[] channelsArray; - synchronized (channels) - { - // resource allocation optimization - if (channels.size() == 0) - return; - - channelsArray = new ServerChannel[channels.size()]; - channels.toArray(channelsArray); - - channels.clear(); - } - - context.getLogger().fine("Transport to " + socketAddress + " still has " + channelsArray.length + " channel(s) active and closing..."); - for (int i = 0; i < channelsArray.length; i++) - { - try - { - channelsArray[i].destroy(); - } - catch (Throwable th) - { - th.printStackTrace(); - } + context.getLogger().fine("Transport to " + socketAddress + " still has " + channels.size() + " channel(s) active and closing..."); + + + for(ServerChannel serverChannel : channels.values()) { + serverChannel.destroy(); } + + channels.clear(); } /** @@ -139,13 +122,13 @@ private void destroyAllChannels() { */ public int preallocateChannelSID() { - synchronized (channels) { + // search first free (theoretically possible loop of death) int sid = lastChannelSID.incrementAndGet(); while (channels.containsKey(sid)) sid = lastChannelSID.incrementAndGet(); return sid; - } + } /** @@ -164,9 +147,9 @@ public void depreallocateChannelSID(int sid) */ public void registerChannel(int sid, ServerChannel channel) { - synchronized (channels) { + channels.put(sid, channel); - } + } /** @@ -175,9 +158,9 @@ public void registerChannel(int sid, ServerChannel channel) */ public void unregisterChannel(int sid) { - synchronized (channels) { + channels.remove(sid); - } + } /** @@ -187,19 +170,20 @@ public void unregisterChannel(int sid) */ public ServerChannel getChannel(int sid) { - synchronized (channels) { + return (ServerChannel)channels.get(sid); - } + } @Override public ServerChannel[] getChannels() { - synchronized (channels) { + synchronized(channels) { ServerChannel[] sca = new ServerChannel[channels.size()]; - channels.toArray(sca); + channels.values().toArray(sca); return sca; } + } /** @@ -208,9 +192,7 @@ public ServerChannel[] getChannels() */ public int getChannelCount() { - synchronized (channels) { return channels.size(); - } } /* (non-Javadoc) diff --git a/pvAccessJava/src/org/epics/pvaccess/util/BooleanHolder.java b/pvAccessJava/src/org/epics/pvaccess/util/BooleanHolder.java new file mode 100644 index 000000000..742472a70 --- /dev/null +++ b/pvAccessJava/src/org/epics/pvaccess/util/BooleanHolder.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 European Spallation Source ERIC. + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package org.epics.pvaccess.util; + +/** + * This class is a simple wrapper around a boolean value. It can be used in lieu of + * the class org.omg.CORBA.BooleanHolder in order to avoid the dependency to the + * Java 10 module java.corba, which is deprecated since Java 9 and subject to + * removal in future versions. + */ +public class BooleanHolder { + + public boolean value = false; + + /** + * Instantiates an object of this class where the initial value is set to false. + */ + public BooleanHolder() { + } + + /** + * Instantiates an object of this class. + * @param value The initial value. + */ + public BooleanHolder(boolean value) { + this.value = value; + } +} diff --git a/pvAccessJava/src/org/epics/pvaccess/util/IntHashMap.java b/pvAccessJava/src/org/epics/pvaccess/util/IntHashMap.java deleted file mode 100644 index 29a5d501c..000000000 --- a/pvAccessJava/src/org/epics/pvaccess/util/IntHashMap.java +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Copyright (c) 2004 by Cosylab - * - * The full license specifying the redistribution, modification, usage and other - * rights and obligations is included with the distribution of this project in - * the file "LICENSE-CAJ". If the license is not included visit Cosylab web site, - * . - * - * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, NOT EVEN THE - * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, ASSUMES - * _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, - * OR REDISTRIBUTION OF THIS SOFTWARE. - */ - -package org.epics.pvaccess.util; - -import java.util.ArrayList; - -import org.omg.CORBA.IntHolder; - -/** - *

A hash map that uses primitive ints for the key instead of objects. Also Entry objects are reusable.

- * NOTE: this implementatio is not synced, client has to care of that. - * - *

This implemenation is based on original java implementation.

- * @author Matej Sekoranja - * @version $Id$ - * @see java.util.HashMap - */ -public class IntHashMap { - - /** - * The hash table data. - */ - private transient Entry table[]; - - /** - * The total number of entries in the hash table. - */ - private transient int count; - - /** - * The table is rehashed when its size exceeds this threshold. (The - * value of this field is (int)(capacity * loadFactor).) - * - * @serial - */ - private int threshold; - - /** - * The load factor for the hashtable. - * - * @serial - */ - private float loadFactor; - - /** - * Entry object pool. - * - */ - private transient EntryObjectPool pool; - - /** - *

Innerclass that acts as a datastructure to create a new entry in the - * table.

- */ - private static class Entry { - int hash; - int key; - Object value; - Entry next; - - /** - *

Create a new entry with the given values.

- * - * @param hash The code used to hash the object with - * @param key The key used to enter this in the table - * @param value The value for this key - * @param next A reference to the next entry in the table - */ - protected Entry(int hash, int key, Object value, Entry next) { - initialize(hash, key, value, next); - } - - /** - *

Set given values.

- * - * @param hash The code used to hash the object with - * @param key The key used to enter this in the table - * @param value The value for this key - * @param next A reference to the next entry in the table - */ - public void initialize(int hash, int key, Object value, Entry next) { - this.hash = hash; - this.key = key; - this.value = value; - this.next = next; - } - - } - - /** - *

Object pool implemenation for Entry class.

- */ - // TODO timed clean-up - private static class EntryObjectPool { - - /** - * Pool of reusable objects. - */ - private ArrayList pool; - private int lastPos = -1; - - /** - * Constructor. - * @param initialCapacity initial capacity of the pool. - */ - public EntryObjectPool(int initialCapacity) { - pool = new ArrayList(initialCapacity); - } - - /** - * Get Entry object from the object pool. - * - * @param hash The code used to hash the object with - * @param key The key used to enter this in the table - * @param value The value for this key - * @param next A reference to the next entry in the table - * @return Entry instance. - */ - public Entry getEntry(int hash, int key, Object value, Entry next) { - if (lastPos == -1) { - return new Entry(hash, key, value, next); - } - else { - Entry entry = pool.remove(lastPos--); - entry.initialize(hash, key, value, next); - return entry; - } - } - - /** - * Put (return) Entry object to the object pool. - * @param entry entry to put. - */ - public void putEntry(Entry entry) { - // NOTE: be sure that entry.value and entry.next are null - pool.add(entry); - lastPos++; - } - } - - /** - *

Constructs a new, empty hashtable with a default capacity and load - * factor, which is 20 and 0.75 respectively.

- */ - public IntHashMap() { - this(20, 0.75f); - } - - /** - *

Constructs a new, empty hashtable with the specified initial capacity - * and default load factor, which is 0.75.

- * - * @param initialCapacity the initial capacity of the hashtable. - * @throws IllegalArgumentException if the initial capacity is less - * than zero. - */ - public IntHashMap(int initialCapacity) { - this(initialCapacity, 0.75f); - } - - /** - *

Constructs a new, empty hashtable with the specified initial - * capacity and the specified load factor.

- * - * @param initialCapacity the initial capacity of the hashtable. - * @param loadFactor the load factor of the hashtable. - * @throws IllegalArgumentException if the initial capacity is less - * than zero, or if the load factor is nonpositive. - */ - public IntHashMap(int initialCapacity, float loadFactor) { - super(); - if (initialCapacity < 0) { - throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); - } - if (loadFactor <= 0) { - throw new IllegalArgumentException("Illegal Load: " + loadFactor); - } - if (initialCapacity == 0) { - initialCapacity = 1; - } - - this.loadFactor = loadFactor; - table = new Entry[initialCapacity]; - threshold = (int) (initialCapacity * loadFactor); - - pool = new EntryObjectPool(initialCapacity); - } - - /** - *

Returns the number of keys in this hashtable.

- * - * @return the number of keys in this hashtable. - */ - public int size() { - return count; - } - - /** - *

Tests if this hashtable maps no keys to values.

- * - * @return true if this hashtable maps no keys to values; - * false otherwise. - */ - public boolean isEmpty() { - return count == 0; - } - - /** - *

Tests if some key maps into the specified value in this hashtable. - * This operation is more expensive than the containsKey - * method.

- * - *

Note that this method is identical in functionality to containsValue, - * (which is part of the Map interface in the collections framework).

- * - * @param value a value to search for. - * @return true if and only if some key maps to the - * value argument in this hashtable as - * determined by the equals method; - * false otherwise. - * @throws NullPointerException if the value is null. - * @see #containsKey(int) - * @see #containsValue(Object) - * @see java.util.Map - */ - public boolean contains(Object value) { - if (value == null) { - throw new NullPointerException(); - } - - Entry tab[] = table; - for (int i = tab.length; i-- > 0;) { - for (Entry e = tab[i]; e != null; e = e.next) { - if (e.value.equals(value)) { - return true; - } - } - } - return false; - } - - /** - *

Tests if some key maps into the specified value in this hashtable. - * This operation is more expensive than the containsKey - * method.

- * - *

Note that this method is identical in functionality to containsValue, - * (which is part of the Map interface in the collections framework).

- * - * This method also returns key (using key as aka by-reference parameter); - * key is only valid if method returns true. - * - * @param value a value to search for. - * @param key will contain key fo an object, if the method returs true. - * @return true if and only if some key maps to the - * value argument in this hashtable as - * determined by the equals method; - * false otherwise. - * @throws NullPointerException if the value is null. - * @see #containsKey(int) - * @see #containsValue(Object) - * @see java.util.Map - */ - public boolean contains(Object value, IntHolder key) { - if (value == null) { - throw new NullPointerException(); - } - - Entry tab[] = table; - for (int i = tab.length; i-- > 0;) { - for (Entry e = tab[i]; e != null; e = e.next) { - if (e.value.equals(value)) { - key.value = e.key; - return true; - } - } - } - return false; - } - - /** - *

Returns true if this HashMap maps one or more keys - * to this value.

- * - *

Note that this method is identical in functionality to contains - * (which predates the Map interface).

- * - * @param value value whose presence in this HashMap is to be tested. - * @return boolean true if the value is contained - * @see java.util.Map - * @since JDK1.2 - */ - public boolean containsValue(Object value) { - return contains(value); - } - - /** - *

Tests if the specified object is a key in this hashtable.

- * - * @param key possible key. - * @return true if and only if the specified object is a - * key in this hashtable, as determined by the equals - * method; false otherwise. - * @see #contains(Object) - */ - public boolean containsKey(int key) { - Entry tab[] = table; - int hash = key; - int index = (hash & 0x7FFFFFFF) % tab.length; - for (Entry e = tab[index]; e != null; e = e.next) { - if (e.hash == hash) { - return true; - } - } - return false; - } - - /** - *

Returns the value to which the specified key is mapped in this map.

- * - * @param key a key in the hashtable. - * @return the value to which the key is mapped in this hashtable; - * null if the key is not mapped to any value in - * this hashtable. - * @see #put(int, Object) - */ - public Object get(int key) { - Entry tab[] = table; - int hash = key; - int index = (hash & 0x7FFFFFFF) % tab.length; - for (Entry e = tab[index]; e != null; e = e.next) { - if (e.hash == hash) { - return e.value; - } - } - return null; - } - - /** - *

Increases the capacity of and internally reorganizes this - * hashtable, in order to accommodate and access its entries more - * efficiently.

- * - *

This method is called automatically when the number of keys - * in the hashtable exceeds this hashtable's capacity and load - * factor.

- */ - protected void rehash() { - int oldCapacity = table.length; - Entry oldMap[] = table; - - int newCapacity = oldCapacity * 2 + 1; - Entry newMap[] = new Entry[newCapacity]; - - threshold = (int) (newCapacity * loadFactor); - table = newMap; - - for (int i = oldCapacity; i-- > 0;) { - for (Entry old = oldMap[i]; old != null;) { - Entry e = old; - old = old.next; - - int index = (e.hash & 0x7FFFFFFF) % newCapacity; - e.next = newMap[index]; - newMap[index] = e; - } - } - } - - /** - *

Maps the specified key to the specified - * value in this hashtable. The key cannot be - * null.

- * - *

The value can be retrieved by calling the get method - * with a key that is equal to the original key.

- * - * @param key the hashtable key. - * @param value the value. - * @return the previous value of the specified key in this hashtable, - * or null if it did not have one. - * @throws NullPointerException if the key is null. - * @see #get(int) - */ - public Object put(int key, Object value) { - // Makes sure the key is not already in the hashtable. - Entry tab[] = table; - int hash = key; - int index = (hash & 0x7FFFFFFF) % tab.length; - for (Entry e = tab[index]; e != null; e = e.next) { - if (e.hash == hash) { - Object old = e.value; - e.value = value; - return old; - } - } - - if (count >= threshold) { - // Rehash the table if the threshold is exceeded - rehash(); - - tab = table; - index = (hash & 0x7FFFFFFF) % tab.length; - } - - // Creates the new entry. - //Entry e = new Entry(hash, key, value, tab[index]); - Entry e = pool.getEntry(hash, key, value, tab[index]); - tab[index] = e; - count++; - return null; - } - - /** - *

Removes the key (and its corresponding value) from this - * hashtable.

- * - *

This method does nothing if the key is not present in the - * hashtable.

- * - * @param key the key that needs to be removed. - * @return the value to which the key had been mapped in this hashtable, - * or null if the key did not have a mapping. - */ - public Object remove(int key) { - Entry tab[] = table; - int hash = key; - int index = (hash & 0x7FFFFFFF) % tab.length; - for (Entry e = tab[index], prev = null; e != null; prev = e, e = e.next) { - if (e.hash == hash) { - if (prev != null) { - prev.next = e.next; - } else { - tab[index] = e.next; - } - count--; - Object oldValue = e.value; - e.value = null; - - e.next = null; - pool.putEntry(e); - - return oldValue; - } - } - return null; - } - - /** - *

Clears this hashtable so that it contains no keys.

- */ - public void clear() { - Entry tab[] = table; - for (int index = tab.length; --index >= 0;) { - - Entry e = tab[index]; - if (e != null) { - e.value = null; - e.next = null; - pool.putEntry(e); - } - - tab[index] = null; - } - count = 0; - } - - /** - *

Copies values to array. Note that array capacity has to be large enough.

- * @param arr array to be filled, new instance returned if given not large enough or null. - * @return array of values. - */ - public Object[] toArray(Object[] arr) { - if (arr == null || arr.length < count) { - arr = new Object[count]; - } - - int pos = 0; - Entry tab[] = table; - for (int i = tab.length; i-- > 0;) - for (Entry e = tab[i]; e != null; e = e.next) - arr[pos++] = e.value; - - return arr; - } - - /** - *

Copies keys to array.

- * @return array of keys. - */ - public int[] keysArray() { - int pos = 0; - Entry tab[] = table; - int[] keys = new int[count]; - for (int i = tab.length; i-- > 0;) - for (Entry e = tab[i]; e != null; e = e.next) - keys[pos++] = e.key; - - return keys; - } -} diff --git a/pvAccessJava/src/org/epics/pvaccess/util/ShortHashMap.java b/pvAccessJava/src/org/epics/pvaccess/util/ShortHashMap.java deleted file mode 100644 index 9addde646..000000000 --- a/pvAccessJava/src/org/epics/pvaccess/util/ShortHashMap.java +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright (c) 2004 by Cosylab - * - * The full license specifying the redistribution, modification, usage and other - * rights and obligations is included with the distribution of this project in - * the file "LICENSE-CAJ". If the license is not included visit Cosylab web site, - * . - * - * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, NOT EVEN THE - * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, ASSUMES - * _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, - * OR REDISTRIBUTION OF THIS SOFTWARE. - */ - -package org.epics.pvaccess.util; - -import java.util.ArrayList; - -import org.omg.CORBA.ShortHolder; - -/** - *

A hash map that uses primitive ints for the key instead of objects. Also Entry objects are reusable.

- * NOTE: this implementatio is not synced, client has to care of that. - * - *

This implemenation is based on original java implementation.

- * @author Matej Sekoranja - * @version $Id$ - * @see java.util.HashMap - */ -public class ShortHashMap { - - /** - * The hash table data. - */ - private transient Entry table[]; - - /** - * The total number of entries in the hash table. - */ - private transient int count; - - /** - * The table is rehashed when its size exceeds this threshold. (The - * value of this field is (int)(capacity * loadFactor).) - * - * @serial - */ - private int threshold; - - /** - * The load factor for the hashtable. - * - * @serial - */ - private float loadFactor; - - /** - * Entry object pool. - * - */ - private transient EntryObjectPool pool; - - /** - *

Innerclass that acts as a datastructure to create a new entry in the - * table.

- */ - private static class Entry { - int hash; - short key; - Object value; - Entry next; - - /** - *

Create a new entry with the given values.

- * - * @param hash The code used to hash the object with - * @param key The key used to enter this in the table - * @param value The value for this key - * @param next A reference to the next entry in the table - */ - protected Entry(int hash, short key, Object value, Entry next) { - initialize(hash, key, value, next); - } - - /** - *

Set given values.

- * - * @param hash The code used to hash the object with - * @param key The key used to enter this in the table - * @param value The value for this key - * @param next A reference to the next entry in the table - */ - public void initialize(int hash, short key, Object value, Entry next) { - this.hash = hash; - this.key = key; - this.value = value; - this.next = next; - } - - } - - /** - *

Object pool implemenation for Entry class.

- */ - // TODO timed clean-up - private static class EntryObjectPool { - - /** - * Pool of reusable objects. - */ - private ArrayList pool; - private int lastPos = -1; - - /** - * Constructor. - * @param initialCapacity initial capacity of the pool. - */ - public EntryObjectPool(int initialCapacity) { - pool = new ArrayList(initialCapacity); - } - - /** - * Get Entry object from the object pool. - * - * @param hash The code used to hash the object with - * @param key The key used to enter this in the table - * @param value The value for this key - * @param next A reference to the next entry in the table - * @return Entry instance. - */ - public Entry getEntry(int hash, short key, Object value, Entry next) { - if (lastPos == -1) { - return new Entry(hash, key, value, next); - } - else { - Entry entry = pool.remove(lastPos--); - entry.initialize(hash, key, value, next); - return entry; - } - } - - /** - * Put (return) Entry object to the object pool. - * @param entry entry to put. - */ - public void putEntry(Entry entry) { - // NOTE: be sure that entry.value and entry.next are null - pool.add(entry); - lastPos++; - } - } - - /** - *

Constructs a new, empty hashtable with a default capacity and load - * factor, which is 20 and 0.75 respectively.

- */ - public ShortHashMap() { - this(20, 0.75f); - } - - /** - *

Constructs a new, empty hashtable with the specified initial capacity - * and default load factor, which is 0.75.

- * - * @param initialCapacity the initial capacity of the hashtable. - * @throws IllegalArgumentException if the initial capacity is less - * than zero. - */ - public ShortHashMap(int initialCapacity) { - this(initialCapacity, 0.75f); - } - - /** - *

Constructs a new, empty hashtable with the specified initial - * capacity and the specified load factor.

- * - * @param initialCapacity the initial capacity of the hashtable. - * @param loadFactor the load factor of the hashtable. - * @throws IllegalArgumentException if the initial capacity is less - * than zero, or if the load factor is nonpositive. - */ - public ShortHashMap(int initialCapacity, float loadFactor) { - super(); - if (initialCapacity < 0) { - throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); - } - if (loadFactor <= 0) { - throw new IllegalArgumentException("Illegal Load: " + loadFactor); - } - if (initialCapacity == 0) { - initialCapacity = 1; - } - - this.loadFactor = loadFactor; - table = new Entry[initialCapacity]; - threshold = (int) (initialCapacity * loadFactor); - - pool = new EntryObjectPool(initialCapacity); - } - - /** - *

Returns the number of keys in this hashtable.

- * - * @return the number of keys in this hashtable. - */ - public int size() { - return count; - } - - /** - *

Tests if this hashtable maps no keys to values.

- * - * @return true if this hashtable maps no keys to values; - * false otherwise. - */ - public boolean isEmpty() { - return count == 0; - } - - /** - *

Tests if some key maps into the specified value in this hashtable. - * This operation is more expensive than the containsKey - * method.

- * - *

Note that this method is identical in functionality to containsValue, - * (which is part of the Map interface in the collections framework).

- * - * @param value a value to search for. - * @return true if and only if some key maps to the - * value argument in this hashtable as - * determined by the equals method; - * false otherwise. - * @throws NullPointerException if the value is null. - * @see #containsValue(Object) - * @see java.util.Map - */ - public boolean contains(Object value) { - if (value == null) { - throw new NullPointerException(); - } - - Entry tab[] = table; - for (int i = tab.length; i-- > 0;) { - for (Entry e = tab[i]; e != null; e = e.next) { - if (e.value.equals(value)) { - return true; - } - } - } - return false; - } - - /** - *

Tests if some key maps into the specified value in this hashtable. - * This operation is more expensive than the containsKey - * method.

- * - *

Note that this method is identical in functionality to containsValue, - * (which is part of the Map interface in the collections framework).

- * - * This method also returns key (using key as aka by-reference parameter); - * key is only valid if method returns true. - * - * @param value a value to search for. - * @param key contains objet key, if the method returns true. - * @return true if and only if some key maps to the - * value argument in this hashtable as - * determined by the equals method; - * false otherwise. - * @throws NullPointerException if the value is null. - * @see #containsValue(Object) - * @see java.util.Map - */ - public boolean contains(Object value, ShortHolder key) { - if (value == null) { - throw new NullPointerException(); - } - - Entry tab[] = table; - for (int i = tab.length; i-- > 0;) { - for (Entry e = tab[i]; e != null; e = e.next) { - if (e.value.equals(value)) { - key.value = e.key; - return true; - } - } - } - return false; - } - - /** - *

Returns true if this HashMap maps one or more keys - * to this value.

- * - *

Note that this method is identical in functionality to contains - * (which predates the Map interface).

- * - * @param value value whose presence in this HashMap is to be tested. - * @return boolean true if the value is contained - * @see java.util.Map - * @since JDK1.2 - */ - public boolean containsValue(Object value) { - return contains(value); - } - - /** - *

Tests if the specified object is a key in this hashtable.

- * - * @param key possible key. - * @return true if and only if the specified object is a - * key in this hashtable, as determined by the equals - * method; false otherwise. - * @see #contains(Object) - */ - public boolean containsKey(short key) { - Entry tab[] = table; - int hash = key; - int index = (hash & 0x7FFFFFFF) % tab.length; - for (Entry e = tab[index]; e != null; e = e.next) { - if (e.hash == hash) { - return true; - } - } - return false; - } - - /** - *

Returns the value to which the specified key is mapped in this map.

- * - * @param key a key in the hashtable. - * @return the value to which the key is mapped in this hashtable; - * null if the key is not mapped to any value in - * this hashtable. - */ - public Object get(short key) { - Entry tab[] = table; - int hash = key; - int index = (hash & 0x7FFFFFFF) % tab.length; - for (Entry e = tab[index]; e != null; e = e.next) { - if (e.hash == hash) { - return e.value; - } - } - return null; - } - - /** - *

Increases the capacity of and internally reorganizes this - * hashtable, in order to accommodate and access its entries more - * efficiently.

- * - *

This method is called automatically when the number of keys - * in the hashtable exceeds this hashtable's capacity and load - * factor.

- */ - protected void rehash() { - int oldCapacity = table.length; - Entry oldMap[] = table; - - int newCapacity = oldCapacity * 2 + 1; - Entry newMap[] = new Entry[newCapacity]; - - threshold = (int) (newCapacity * loadFactor); - table = newMap; - - for (int i = oldCapacity; i-- > 0;) { - for (Entry old = oldMap[i]; old != null;) { - Entry e = old; - old = old.next; - - int index = (e.hash & 0x7FFFFFFF) % newCapacity; - e.next = newMap[index]; - newMap[index] = e; - } - } - } - - /** - *

Maps the specified key to the specified - * value in this hashtable. The key cannot be - * null.

- * - *

The value can be retrieved by calling the get method - * with a key that is equal to the original key.

- * - * @param key the hashtable key. - * @param value the value. - * @return the previous value of the specified key in this hashtable, - * or null if it did not have one. - * @throws NullPointerException if the key is null. - */ - public Object put(short key, Object value) { - // Makes sure the key is not already in the hashtable. - Entry tab[] = table; - int hash = key; - int index = (hash & 0x7FFFFFFF) % tab.length; - for (Entry e = tab[index]; e != null; e = e.next) { - if (e.hash == hash) { - Object old = e.value; - e.value = value; - return old; - } - } - - if (count >= threshold) { - // Rehash the table if the threshold is exceeded - rehash(); - - tab = table; - index = (hash & 0x7FFFFFFF) % tab.length; - } - - // Creates the new entry. - //Entry e = new Entry(hash, key, value, tab[index]); - Entry e = pool.getEntry(hash, key, value, tab[index]); - tab[index] = e; - count++; - return null; - } - - /** - *

Removes the key (and its corresponding value) from this - * hashtable.

- * - *

This method does nothing if the key is not present in the - * hashtable.

- * - * @param key the key that needs to be removed. - * @return the value to which the key had been mapped in this hashtable, - * or null if the key did not have a mapping. - */ - public Object remove(short key) { - Entry tab[] = table; - int hash = key; - int index = (hash & 0x7FFFFFFF) % tab.length; - for (Entry e = tab[index], prev = null; e != null; prev = e, e = e.next) { - if (e.hash == hash) { - if (prev != null) { - prev.next = e.next; - } else { - tab[index] = e.next; - } - count--; - Object oldValue = e.value; - e.value = null; - - e.next = null; - pool.putEntry(e); - - return oldValue; - } - } - return null; - } - - /** - *

Clears this hashtable so that it contains no keys.

- */ - public void clear() { - Entry tab[] = table; - for (int index = tab.length; --index >= 0;) { - - Entry e = tab[index]; - if (e != null) { - e.value = null; - e.next = null; - pool.putEntry(e); - } - - tab[index] = null; - } - count = 0; - } - - /** - *

Copies values to array. Note that array capacity has to be large enough.

- * @param arr array to be filled, new instance returned if given not large enough or null. - * @return array of values. - */ - public Object[] toArray(Object[] arr) { - if (arr == null || arr.length < count) { - arr = new Object[count]; - } - - int pos = 0; - Entry tab[] = table; - for (int i = tab.length; i-- > 0;) - for (Entry e = tab[i]; e != null; e = e.next) - arr[pos++] = e.value; - - return arr; - } - -} diff --git a/pvAccessJava/test/org/epics/pvaccess/impl/remote/test/IRSerializationTest.java b/pvAccessJava/test/org/epics/pvaccess/impl/remote/test/IRSerializationTest.java index 55be3a62f..86ee5a0dc 100644 --- a/pvAccessJava/test/org/epics/pvaccess/impl/remote/test/IRSerializationTest.java +++ b/pvAccessJava/test/org/epics/pvaccess/impl/remote/test/IRSerializationTest.java @@ -11,12 +11,18 @@ import org.epics.pvaccess.PVFactory; import org.epics.pvaccess.impl.remote.IntrospectionRegistry; +import org.epics.pvaccess.util.BooleanHolder; +import org.epics.pvdata.factory.FieldFactory; import org.epics.pvdata.pv.DeserializableControl; import org.epics.pvdata.pv.Field; import org.epics.pvdata.pv.PVField; import org.epics.pvdata.pv.PVStructure; +import org.epics.pvdata.pv.ScalarType; import org.epics.pvdata.pv.SerializableControl; +import static org.junit.Assert.*; + + /** * JUnit test for IR supported serialization. * @author mse @@ -105,4 +111,20 @@ public void testDecode() System.out.println(pvField); } + public void testRegisterIntrospectionInterface() { + + IntrospectionRegistry introspectionRegistry = + new IntrospectionRegistry(); + + Field field1 = FieldFactory.getFieldCreate().createScalar(ScalarType.pvDouble); + + BooleanHolder existing = new BooleanHolder(true); + + short key = introspectionRegistry.registerIntrospectionInterface(field1, existing); + + assertEquals(1, key); + assertFalse(existing.value); + + } + } diff --git a/pvDataJava/documentation/pvDataJava.html b/pvDataJava/documentation/pvDataJava.html index 8ddbb9318..c82b61a0d 100644 --- a/pvDataJava/documentation/pvDataJava.html +++ b/pvDataJava/documentation/pvDataJava.html @@ -45,15 +45,15 @@

Release 5.1-DEV - 2017.03.29

-
+

Abstract

-

pvDataJava is a computer software package for the efficient +

pvDataJava is a computer software package for the efficient storage, access, and communication, of structured data. It is specifically the Java implementation of pvData, which is one part of the set of related products in the EPICS -V4 control system programming environment:
+V4 control system programming environment:
relatedDocumentsV4.html

@@ -426,9 +426,9 @@

Introspection and Data creation

public FieldBuilder addArray(String name, Field element); public Structure createStructure(); public Union createUnion(); - FieldBuilder addNestedStructure(String name); + FieldBuilder addNestedStructure(String name); FieldBuilder addNestedUnion(String name); - FieldBuilder addNestedStructureArray(String name); + FieldBuilder addNestedStructureArray(String name); FieldBuilder addNestedUnionArray(String name); FieldBuilder endNested(); } @@ -456,7 +456,7 @@

Introspection and Data creation

PVUnion createPVUnion(Union union); PVUnion createPVVariantUnion(); PVUnion createPVUnion(PVUnion unionToClone); - + PVStructureArray createPVStructureArray(Structure structure); PVUnionArray createPVUnionArray(Union union); PVField[] flattenPVStructure(PVStructure pvStructure); @@ -635,7 +635,7 @@

Serializable

Align buffer.Note that this takes care only current buffer alignment. If streaming protocol is used, care must be taken that entire stream is aligned.
cachedSerialize
-
Serialize Field instance via cache.
+
Serialize Field instance via cache.
 interface DeserializableControl {
@@ -652,12 +652,12 @@ 

Serializable

Align buffer.Note that this takes care only current buffer alignment. If streaming protocol is used, care must be taken that entire stream is aligned.
cachedDeserialize
-
Deserialize Field instance via cache.
+
Deserialize Field instance via cache.
 interface SerializableArray extends Serializable {
     void serialize(ByteBuffer buffer, SerializableControl flusher, int offset, int count);
-}       
+}
 
where
@@ -684,7 +684,7 @@

Reflection

Reflection consists of the following:

Field
-
A Field has an ID and a type. It can be converted to a string. +
A Field has an ID and a type. It can be converted to a string. The format is the metadata format described in the overview.
Scalar
@@ -711,14 +711,14 @@

Reflection

interfaces. A factory is provided to create a FieldCreate.
Field
-
    
+
 interface Field extends Serializable {
     String getID();
     Type getType();
     void toString(StringBuilder buf));
     void toString(StringBuilder buf,int indentLevel);
     String toString();
-} 
+}
 
where
@@ -902,7 +902,7 @@
FieldCreate
interface for each array element.
createUnionArray
Return a UnionArray with the specified introspection interface.
-
createVariantUnionArray
+
createVariantUnionArray
Return a UnionArray of variant union elements.
createStructure
Return a Structure. There are two methods. @@ -961,9 +961,9 @@
FieldBuilder
public FieldBuilder addArray(String name, Field element); public Structure createStructure(); public Union createUnion(); - FieldBuilder addNestedStructure(String name); + FieldBuilder addNestedStructure(String name); FieldBuilder addNestedUnion(String name); - FieldBuilder addNestedStructureArray(String name); + FieldBuilder addNestedStructureArray(String name); FieldBuilder addNestedUnionArray(String name); FieldBuilder endNested(); } @@ -1019,8 +1019,7 @@
FieldBuilder
This is followed by calls to other methods and ended by a call to endNested.
endNested
-
-
+
 
@@ -1058,7 +1057,7 @@

Status

StatusCreate

 interface StatusCreate {
-    Status getStatusOK(); 
+    Status getStatusOK();
     Status createStatus(StatusType type, String message, Throwable cause);
     Status deserializeStatus(ByteBuffer buffer, DeserializableControl control);
 }
@@ -1092,15 +1091,15 @@ 

Requester

where

-
+
 
MessageType
Type of message.
Requester
-
The default implementation is: +
The default implementation is:
    -
  • getRequesterName
    +
  • getRequesterName
    This is the full field name concatenated to the record name.
  • -
  • message
    +
  • message
    For the default implementation, PVField prepends the full field name to the message and calls PVRecord.message. The default implementation for PVRecord either displays the message on stdout or stderr or gives @@ -1174,7 +1173,7 @@

    PVField

    PVStructure.
postPut
If a PostHandler is registered it is called otherwise no action is - taken.
+ taken.
NOTE: The implementation of the various data interfaces automatically call postPut when a field is changed. However this is not true for a subField of a PVUnion, PVUnionArray, or PVStructureArray. @@ -1414,7 +1413,7 @@
PVArray Extensions

shareData results in the PVArray using the primitive array that is passed to this method. This is most useful for immutable arrays. In this case the caller -must set the PVArray to be immutable. If the PVArray is not immutable then +must set the PVArray to be immutable. If the PVArray is not immutable then the application is responsibility for coordinating access to the array. This violates the principle that objects should not expose their internal data but is important for immutable arrays. For example pvData defines @@ -1653,12 +1652,12 @@

PVDataCreate

PVUnion createPVUnion(Union union); PVUnion createPVVariantUnion(); PVUnion createPVUnion(PVUnion unionToClone); - + PVStructureArray createPVStructureArray(Structure structure); PVUnionArray createPVUnionArray(Union union); PVField[] flattenPVStructure(PVStructure pvStructure); }
-where +where
createPVField
The PVField is created reusing the Field interface. Two methods are @@ -1736,14 +1735,14 @@

Convert

megabyte array to be converted to a string expect a lot of output.
  • Conversion from a string to a scalar type.
  • Conversion from an array of strings to an array of scalar types.
  • -
  • Copy between the following types of scalar PVs +
  • Copy between the following types of scalar PVs
    • Numeric type to another numeric type
    • Both have the same type.
    • Either is a string
  • -
  • Copy between PVArrays that satisfy one of the following. +
  • Copy between PVArrays that satisfy one of the following.
    • Numeric to numeric
    • Both have the same type.
    • @@ -2048,7 +2047,7 @@

      Creating pvData

  • Create a structure.

    -
        
    +
     FieldCreate fieldCreate = FieldFactory.getFieldCreate();
     PVDataCreate pvDataCreate = PVDataFactory.getPVDataCreate();
     
    @@ -2172,7 +2171,7 @@ 

    Standard Properties

    This is normally defined since most general purpose clients access this field. All other fields in the structure support or describe the value field. The type can any supported type but is usually one of the - following: + following:
    scalar
    Any of the scalar types.
    @@ -2521,7 +2520,7 @@

    control

    double minStep
    Control
    -The java definition for Control is: +The java definition for Control is:
    class Control {
         Control();
         double getLow();
    @@ -2856,8 +2855,8 @@ 

    BitSetUtil

    currently has only one method:

    compress
    -
    Compress the bits in a BitSet related to a structure.
    - For each structure: +
    Compress the bits in a BitSet related to a structure.
    + For each structure:
    1. If the bit for the structure is set then the bit for all subfields of the structure are cleared.
    2. @@ -2944,7 +2943,7 @@

      ThreadPriority

      high, higher, highest; - + public static final int[] javaPriority; public int getJavaPriority(); public static int getJavaPriority(ThreadPriority threadPriority); @@ -3007,7 +3006,7 @@
      ExecutorFactory
      public class ExecutorFactory { static public Executor create(String name,ScanPriority priority); }
    -where +where
    createNode
    Create a ExecutorNode that can be passed to execute.
    @@ -3061,7 +3060,7 @@

    LinkedListNode

    public interface LinkedListNode<T> { public T getObject(); boolean isOnList(); -} +}

    LinkedList

    @@ -3157,10 +3156,10 @@ 

    timer

    This provides a general purpose timer. It provides the following features not provided by java.util.Timer and java.util.TimerTask:

      -
    1. Priority
      +
    2. Priority
      The java.util implementation does not allow the user to specify the priority of the timer thread. This implementation does.
    3. -
    4. TimerNode
      +
    5. TimerNode
      A java.util.TimerTask is not reusable. Once a TimerTask has been canceled or a delay TimerTask has run, the TimerTask can not be reused. Instead a new TimerTask must be created. A TimerNode can be reused.
    6. @@ -3387,7 +3386,7 @@

      src/copy

      It is provided with this project because the code depends only on pvData itself.

      -

      copy provides the ability to create a structure that has +

      copy provides the ability to create a structure that has a copy of an arbitrary subset of the fields in an existing top level structure. In addition it allows global options and field specific options. It has two main components: createRequest and pvCopy. @@ -3410,10 +3409,10 @@

      src/copy

      createRequest
      The Channel create methods in pvAccess all have an argument - PVStructure pvRequest.
      + PVStructure pvRequest.
      Given an ascii string createRequest creates a PVStructure that provides a pvData representation of the information from the ascii string. - It is this structure that can be passed to the channel create methods.
      + It is this structure that can be passed to the channel create methods.
      The information in a pvRequest selects an arbitrary subset of the fields in a top level structure that resides in the server. In addition options can be specified. Both global and field specific @@ -3424,7 +3423,7 @@

      src/copy

      It provides client specific code that manages a copy of an arbitrary subset of the fields in a top level structure that resides in the provider. It also allows provider access to options specified - by the client.
      + by the client.
      pvCopy also provides filter plugin support.
    @@ -3432,7 +3431,7 @@

    src/copy

    createRequest

    This is mainly used by pvAccess clients. Given a request string it creates a pvRequest structure that can be passed to the pvAccess create methods. -In turn pvAccess passes the pvRequest to a local channel provider which +In turn pvAccess passes the pvRequest to a local channel provider which then passes it to pvCopy.

    The definition of the public members is:

    @@ -3503,13 +3502,13 @@

    pvCopy

    PVCopyTraverseMasterCallback is a callback which must be implemented by the code that uses pvCopy, normally the channel provider. It has the single method nextMasterPVField -
    +
    nextMasterPVField is called for each field in the master as a result of a call to traverseMaster.
    create
    - This is the method for creating a PVCopy instance.
    + This is the method for creating a PVCopy instance.
    pvMaster
    the top-level structure managed by the server.
    @@ -3574,13 +3573,13 @@

    pvCopy

    For each set bit in bitSet set the field in pvMaster to the value of the corresponding field in copyPVStructure. - +
    getOptions
    Get the options for the field at the specified offset. A NULL is returned if no options were specified for the field. - If options were specified, the returnedPVStructure is + If options were specified, the returnedPVStructure is a structure with a set of PVString subfields that specify name,value pairs. name is the subField name and value is the subField value.
    diff --git a/pvDataJava/src/org/epics/pvdata/copy/package.html b/pvDataJava/src/org/epics/pvdata/copy/package.html index 4399d065a..ebb177af4 100644 --- a/pvDataJava/src/org/epics/pvdata/copy/package.html +++ b/pvDataJava/src/org/epics/pvdata/copy/package.html @@ -18,7 +18,5 @@
    - -

    diff --git a/pvDataJava/src/org/epics/pvdata/misc/BitSet.java b/pvDataJava/src/org/epics/pvdata/misc/BitSet.java index ef5c203b8..81b22aefc 100644 --- a/pvDataJava/src/org/epics/pvdata/misc/BitSet.java +++ b/pvDataJava/src/org/epics/pvdata/misc/BitSet.java @@ -214,7 +214,6 @@ public static BitSet valueOf(long[] longs) { * @param lb a long buffer containing a little-endian representation * of a sequence of bits between its position and limit, to be * used as the initial bits of the new bit set - * @return the new bit set * @return a new bit set containing all the bits in the given long buffer between its position and limit * @since 1.7 */ @@ -462,11 +461,11 @@ public boolean getAndSet(int bitIndex) { final int wordIndex = wordIndex(bitIndex); final long mask = 1L << bitIndex; final boolean retVal = (wordIndex < wordsInUse) && ((words[wordIndex] & mask) != 0); - + expandTo(wordIndex); words[wordIndex] |= mask; // Restores invariants - + return retVal; } @@ -478,11 +477,11 @@ public void set(BitSet src) { // we ensure that words array size is adequate (and not wordsInUse to ensure capacity to the future) if (src.words.length > this.words.length) this.words = new long[src.words.length]; - + System.arraycopy(src.words, 0, this.words, 0, src.wordsInUse); this.wordsInUse = src.wordsInUse; } - + /** * Sets the bit at the specified index to the specified value. * @@ -950,12 +949,12 @@ public void or_and(BitSet set1, BitSet set2) { words = Arrays.copyOf(words, inUse); wordsInUse = inUse; } - + // Perform logical AND on words in common for (int i = 0; i < inUse; i++) words[i] |= (set1.words[i] & set2.words[i]); } - + /** * Performs a logical OR of this bit set with the bit set * argument. This bit set is modified so that a bit in it has the @@ -1219,7 +1218,7 @@ public String toString() { b.append('}'); return b.toString(); } - + /** * Get long[] that represents this BitSet. * @return long[] that represts this BitSet. @@ -1228,9 +1227,9 @@ public long[] getBitArray() { return words; } - + /** - * NOTE: word is atomic unit here; some bytes might be saved, but it's not worth it. + * NOTE: word is atomic unit here; some bytes might be saved, but it's not worth it. * @see org.epics.pvdata.pv.Serializable#serialize(java.nio.ByteBuffer, org.epics.pvdata.pv.SerializableControl) */ @Override @@ -1244,12 +1243,12 @@ public void serialize(ByteBuffer buffer, SerializableControl flusher) { final int maxIndex = Math.min(wordsInUse, i + spaceLeft); for (; i < maxIndex; i++) buffer.putLong(words[i]); - + if (i < wordsInUse) flusher.flushSerializeBuffer(); } */ - + int n = wordsInUse; if (n == 0) { SerializeHelper.writeSize(0, buffer, flusher); @@ -1258,14 +1257,14 @@ public void serialize(ByteBuffer buffer, SerializableControl flusher) { int len = 8 * (n-1); for (long x = words[n - 1]; x != 0; x >>>= 8) len++; - + SerializeHelper.writeSize(len, buffer, flusher); flusher.ensureBuffer(len); n = len / 8; for (int i = 0; i < n; i++) buffer.putLong(words[i]); - + if (n < wordsInUse) for (long x = words[wordsInUse - 1]; x != 0; x >>>= 8) buffer.put((byte) (x & 0xff)); @@ -1278,29 +1277,29 @@ public void serialize(ByteBuffer buffer, SerializableControl flusher) { public void deserialize(ByteBuffer buffer, DeserializableControl control) { final int bytes = SerializeHelper.readSize(buffer, control); // in bytes - + wordsInUse = (bytes + 7) / 8; if (wordsInUse > words.length) words = new long[wordsInUse]; if (wordsInUse == 0) return; - + control.ensureData(bytes); - + int i = 0; final int longs = bytes / 8; while (i < longs) words[i++] = buffer.getLong(); - + for (int j = i; j < wordsInUse; j++) words[j] = 0; - + for (int remaining = (bytes - longs * 8), j = 0; j < remaining; j++) words[i] |= (buffer.get() & 0xffL) << (8 * j); - + /* - + wordsInUse = SerializeHelper.readSize(buffer, control); if (wordsInUse > words.length) words = new long[wordsInUse]; diff --git a/pvDataJava/src/org/epics/pvdata/property/package.html b/pvDataJava/src/org/epics/pvdata/property/package.html index 454c38ff7..091715ea8 100644 --- a/pvDataJava/src/org/epics/pvdata/property/package.html +++ b/pvDataJava/src/org/epics/pvdata/property/package.html @@ -14,6 +14,6 @@
    Provides support for setting limits for scalar double values.
    display
    Provides support for for displaying scalar double values.
    -
    +
    diff --git a/pvDataJava/src/org/epics/pvdata/pv/StandardPVField.java b/pvDataJava/src/org/epics/pvdata/pv/StandardPVField.java index 6d523584f..2288eaf84 100644 --- a/pvDataJava/src/org/epics/pvdata/pv/StandardPVField.java +++ b/pvDataJava/src/org/epics/pvdata/pv/StandardPVField.java @@ -12,7 +12,7 @@ public interface StandardPVField { /** * Create a PVStructure with a scalar value field. - * + * * @param type The scalarType * @param properties the list of additional properties, which is some * combination of the strings alarm, timeStamp, display, @@ -23,7 +23,7 @@ public interface StandardPVField { /** * Create a PVStructure with a scalarArray value field. - * + * * @param elementType the scalarType for each element. * @param properties the list of additional properties, which is some * combination of the strings alarm, timeStamp, display, @@ -35,7 +35,6 @@ public interface StandardPVField { /** * Create a PVStructure with a structureArray value field. * - * @param properties Some combination of alarm,timeStamp * @param properties the list of additional properties, which is some * combination of the strings alarm and timeStamp * separated by commas @@ -46,7 +45,7 @@ public interface StandardPVField { /** * Create a PVStructure with an enumerated value field - * + * * @param choices the array of choices. * @return the PVStructure with field value and choices field containing * the supplied choices @@ -55,7 +54,7 @@ public interface StandardPVField { /** * Create a PVStructure with an enumerated value field - * + * * @param choices the array of choices. * @param properties the list of additional properties, which is some * combination of the strings alarm and timeStamp diff --git a/pvDataJava/src/org/epics/pvdata/pv/package.html b/pvDataJava/src/org/epics/pvdata/pv/package.html index 7d678e71b..352f35e33 100644 --- a/pvDataJava/src/org/epics/pvdata/pv/package.html +++ b/pvDataJava/src/org/epics/pvdata/pv/package.html @@ -1,19 +1,18 @@ +

    This package contains the interface definitions for the Java implementation of pvData.

    This package has the enum, interface, and class definitions that define pvData. This -section provides a complete definition of what pvData is and how data is accessed. +section provides a complete definition of what pvData is and how data is accessed.

    This package defines the following:

    ScalarType
    One of: pvBoolean, pvByte, pvShort, pvInt, pvLong, pvUByte, pvUShort, pvUInt, pvULong, - pvFloat, pvDouble, pvString -
    + pvFloat, pvDouble, pvString
    Type
    -
    One of scalar, union, structure, scalarArray, unionArray, structureArray -
    -
    Introspection Interfaces
    +
    One of scalar, union, structure, scalarArray, unionArray, structureArray
    +
    Introspection Interfaces
    Field
    @@ -32,7 +31,7 @@
    Introspection interface for type structureArray.
    -
    Data Interfaces
    +
    Data Interfaces
    PVField
    @@ -65,6 +64,7 @@
    PVString
    Data interface for scalar of type pvString.
    +
    PVUnion
    Data interface for type pvUnion.
    PVStructure
    @@ -105,7 +105,6 @@
    -

    diff --git a/pvDataJava/test/org/epics/pvdata/NumberFormatTest.java b/pvDataJava/test/org/epics/pvdata/NumberFormatTest.java deleted file mode 100644 index 20e0c6432..000000000 --- a/pvDataJava/test/org/epics/pvdata/NumberFormatTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright information and license terms for this software can be - * found in the file LICENSE that is included with the distribution - */ -package org.epics.pvdata; - -import java.text.FieldPosition; -import java.text.NumberFormat; -import java.text.ParsePosition; - -import junit.framework.TestCase; - -/** - * JUnit test for BitSet. - * NOTE not complete. - * @author mse - * - */ -public class NumberFormatTest extends TestCase { - - public void testNumberFormat() { - NumberFormat nf = new NumberFormatDouble("%12.2f"); - StringBuffer sb = new StringBuffer(); - FieldPosition fp = new FieldPosition(0); - sb = nf.format(1.12, sb, fp); - System.out.println(sb.toString()); - ParsePosition pp = new ParsePosition(0); - double xxx = (nf.parse(sb.toString(),pp)).doubleValue(); - System.out.println("parse is "+ xxx); - } - - static class NumberFormatDouble extends NumberFormat { - private static final long serialVersionUID = -609491739577318372L; - private final String format; - - private NumberFormatDouble (String format) { - this.format = format; - } - @Override - public StringBuffer format(double arg0, StringBuffer arg1,FieldPosition arg2) { - return arg1.append(String.format(format, arg0)); - } - @Override - public StringBuffer format(long arg0, StringBuffer arg1,FieldPosition arg2) { - throw new IllegalArgumentException("long not supported"); - } - @Override - public Number parse(String arg0, ParsePosition arg1) { - return Double.parseDouble(arg0.substring(arg1.getIndex())); - } - } -} diff --git a/pvaClientJava b/pvaClientJava index 751b27cef..62d5194d9 160000 --- a/pvaClientJava +++ b/pvaClientJava @@ -1 +1 @@ -Subproject commit 751b27cef3cbaf05e7a1cc086862366eefde92a4 +Subproject commit 62d5194d90f7960d495dbe55e4023118eb65b39d