/************************************************************************** * Copyright (c) 2001 by Punch Telematix. All rights reserved. * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * 1. Redistributions of source code must retain the above copyright * * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * 3. Neither the name of Punch Telematix nor the names of * * other contributors may be used to endorse or promote products * * derived from this software without specific prior written permission.* * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * * IN NO EVENT SHALL PUNCH TELEMATIX OR OTHER CONTRIBUTORS BE LIABLE * * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************/ /* ** $Id: CommPortIdentifier.java,v 1.2 2005/03/19 11:45:06 cvs Exp $ */ package javax.comm; import java.io.FileDescriptor; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import com.acunia.device.uart.UARTDriver; public class CommPortIdentifier { /** The various kinds of comm port. */ //@{ /// public static final int PORT_SERIAL = 1; /// public static final int PORT_PARALLEL = 2; //@} /**@name Hashtables */ //@{ /** maps port names (Strings) onto instances of CommPortIdentifier. */ private static Hashtable idtable = new Hashtable(); /**@name Instance fields of a CommPortIdentifier */ //@{ /** A String which uniquely identifies this port. ** Names are allocated by the system (more ** specifically, by the device driver in question). */ private String name; /** type is either PORT_SERIAL or PORT_PARALLEL. */ private int type; /** The device driver which controls this port. */ private CommDriver driver; /** The instance of a subclass of CommPort which ** acts as a "control block" for this port. */ private CommPort commport; /** The current "owner". ** Owners are identified by simple String's; it is up to the developers of ** classes which make use of this class to agree on a naming convention. */ private String owner; /** ownershiplisteners A list (Vector) of CommPortOwnershipListeners. ** Each member of the list is notified whenever the port is open()ed or ** close()d - or rather ... */ private Vector ownershiplisteners; /** ... a change in ownership status results in notification of all ** ownershiplisteners only if ownershipcallback is zero. ** Before notification begins, ownershipcallback is incremented: it is ** decremented again after notification is complete. ** This is intended to prevent a "notification storm" ** which could occur when many would-be owners are ** contending for ownership of a port. */ private int ownershipcallback; //@} /**@name Static initializer ** The static initializer of this class has the duty of starting up ** all device drivers (it's a kludgey job, but someone has to do it). ** TODO: consider making this data-driven somehow ... */ static { CommDriver driver = UARTDriver.getInstance(); driver.initialize(); } /** ** Constructor. Boring as hell. Get the underlying commport from ** the driver and record the mapping CommPort->CommPortIdentifier */ private CommPortIdentifier(String portName, int portType, CommDriver driver) throws NoSuchPortException { this.name = portName; this.type = portType; this.driver = driver; ownershiplisteners = new Vector(); } /** ** getPortIdentifiers() returns an Enumeration of all instances ** of CommPortIdentifier. */ public static Enumeration getPortIdentifiers() { return idtable.elements(); } /** ** getPortIdentifier(String) retrieves a CommPortIdentifier by name. ** Throws NoSuchPortException if no such CommPortIdentifier exists. */ public static CommPortIdentifier getPortIdentifier(String portName) throws NoSuchPortException { CommPortIdentifier result = (CommPortIdentifier)idtable.get(portName); if (result==null) { throw new NoSuchPortException("No such CommPort as "+portName); } return result; } /** ** getPortIdentifier(CommPort) retrieves a CommPortIdentifier by CommPort. ** Throws NoSuchPortException if no such CommPortIdentifier exists. */ public static CommPortIdentifier getPortIdentifier(CommPort port) throws NoSuchPortException { CommPortIdentifier result = port.commPortIdentifier; if (result==null) { throw new NoSuchPortException(); } return result; } /** ** addPortName(String,int,CommDriver) is used by a CommDriver to register ** a port with the system. */ public static void addPortName (String portName, int portType, CommDriver driver) { try { CommPortIdentifier newportid = new CommPortIdentifier(portName, portType, driver); idtable.put(portName,newportid); } catch ( NoSuchPortException e) { // Now why would the driver call us with a non-existent port name? } } /** ** getName() retrieves the name of this port. */ public String getName() { return this.name; } /** ** getPortType() retrieves the type of this port. */ public int getPortType () { return type; } /** ** getCurrentOwner() retrieves the current owner of this port. ** Returns null if the port is currently ownerless. */ public String getCurrentOwner() { return this.owner; } /** ** isCurrentlyOwned() returns true if this port has an owner,. ** false if the port is currently ownerless. */ public synchronized boolean isCurrentlyOwned() { return owner!=null; } /** ** open(String,int) attempts to open the port in the name of a given ** application, within a given period of time. ** If the port is not free (unowned) when open() is called, then we ** notify all ownership listeners of our desire to open the port; ** if the current owner reacts to this by releasing the port, then ** we can now open it. If the current owner did not release the port, ** then we hang around for a while to see whether it becomes free for ** whatever reason: for this we use wait/notify, as the OwnershipListener ** mechanism has no time dimension. ** ** Note that if an application does not register as an ownership ** listener then it will be deaf to the pleas of all other applications ** that wish to use the port: there is no way we can force it to relinquish ** control. Nor is there any prioritization of ownership candidates, nor ** any pretence of fairness. ** ** The "spec" is silent on such questions, but I (CG) decided ** 1) to throw an exception if the appname is null or the timeout is negative ** 2) zero timeout means don't hang around, either return immediately ** or throw PortInUseException.. */ public synchronized CommPort open (String appname, int timeout) throws PortInUseException { if (appname==null) throw new NullPointerException("appname is null"); if (timeout<0) throw new IllegalArgumentException("nonpositive timeout"); Vector snapshot = (Vector)ownershiplisteners.clone(); Enumeration lstnrz = snapshot.elements(); CommPortOwnershipListener lstnr; if (this.owner!=null && this.owner!=appname) { long deadline = System.currentTimeMillis()+timeout; long remaining = timeout; while (lstnrz.hasMoreElements()) { lstnr = (CommPortOwnershipListener)lstnrz.nextElement(); ++ownershipcallback; lstnr.ownershipChange(CommPortOwnershipListener.PORT_OWNERSHIP_REQUESTED); --ownershipcallback; } while (remaining>0 && this.owner!=null) { try { this.wait(remaining); remaining = deadline - System.currentTimeMillis(); } catch (InterruptedException e) {} } if (this.owner!=null) { PortInUseException piue = new PortInUseException(); piue.currentOwner = this.owner; throw piue; } } if(commport == null) { commport = this.driver.getCommPort(this.name, this.type); if(commport == null) { throw new PortInUseException(); } commport. commPortIdentifier = this; } lstnrz = snapshot.elements(); while (lstnrz.hasMoreElements()) { lstnr = (CommPortOwnershipListener)lstnrz.nextElement(); ++ownershipcallback; lstnr.ownershipChange(CommPortOwnershipListener.PORT_OWNED); --ownershipcallback; } this.owner = appname; if(commport.closed){ commport = this.driver.getCommPort(this.name, this.type); commport. commPortIdentifier = this; } return commport; } /** ** handle_close() is the counterpart to open(): it is called from ** inside the close() method of CommPort. If not already within ** an ownership callback, we notify all ownership listeners that ** the port is now free; we then also issue a notify() for the ** benefit of any thread that may be wait()ing with a timeout. ** (Maybe this should happen the other way around, who knows ...) */ synchronized void handle_close() { Vector snapshot = (Vector)ownershiplisteners.clone(); Enumeration lstnrz = snapshot.elements(); CommPortOwnershipListener lstnr; owner = null; // [CG 20000428] moved this up from just before notify() if (ownershipcallback==0) { while (lstnrz.hasMoreElements()) { lstnr = (CommPortOwnershipListener)lstnrz.nextElement(); ++ownershipcallback; lstnr.ownershipChange(CommPortOwnershipListener.PORT_UNOWNED); --ownershipcallback; } } if (this.type==PORT_SERIAL) { ((SerialPort)this.commport).removeEventListener(); } // Parallel ports we do not have // else { // ((ParallelPort)this.commport).removeEventListener(); // } this.notifyAll(); } /** ** open(FileDescriptor) is not yet supported. */ public CommPort open (FileDescriptor fd) throws UnsupportedCommOperationException { // opens fd as a CommPort. Not supported on platform. :0 throw new UnsupportedCommOperationException(); } /** ** addPortOwnershipListener(CommPortOwnershipListener) adds a ** CommPortOwnershipListener to ownershiplisteners. */ public void addPortOwnershipListener(CommPortOwnershipListener listener) { ownershiplisteners.addElement(listener); } /** ** removePortOwnershipListener(CommPortOwnershipListener) removes a ** CommPortOwnershipListener from ownershiplisteners. */ public void removePortOwnershipListener (CommPortOwnershipListener listener) { ownershiplisteners.removeElement(listener); } }