Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/synaptics/elvis/Elvis.java


1   /*
2    * The contents of this file are subject to the Mozilla Public License Version 
3    * 1.1 (the "License"); you may not use this file except in compliance with the 
4    * License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ 
5    *
6    * Software distributed under the License is distributed on an "AS IS" basis, 
7    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
8    * the specific language governing rights and limitations under the License.
9    *
10   * The Original Code is com.synaptics.elvis code.
11   *
12   * The Initial Developers of the Original Code are Synaptics, Inc. and Christopher Heiny.
13   * Portions created by Synaptics, Inc. and Christopher Heiny are
14   * Copyright (C) 2002 Synaptics, Inc. and Christopher Heiny. All Rights Reserved.
15   *
16   * Contributor(s):
17   *    Christopher Heiny <cheiny@synaptics.com>
18   */
19  
20  package com.synaptics.elvis;
21  
22  import java.beans.*;
23  
24  /** Elvis is a package for implementing a singleton, not only within a JVM, but for a given
25   * user on a particular machine.
26   * <P><B>Principles of operation.</b>  The model of use for Elvis is simple.
27   * An Elvis-enabled program calls <CODE>Elvis.get()</code>
28   * with a program name and a command handler.  This name is used to (hopefully) uniquely
29   * identify the Elvis-enabled program and its functions.  If Elvis can establish that he
30   * is indeed the only running instance of that particular program (for that user, on that machine),
31   * he will become the <B>One True Elvis</b>, and establish a thread that will listen for requests
32   * from other Elvis's (also known as Elvis impersonators or Elvis clients).
33   * <P>Once the call to <CODE>Elvis.get()</code> returns, the program can call <CODE>isTheOneTrueElvis()</code>
34   * to determine if their
35   * Elvis is the One True Elvis.  If it isn't they probably just want to send the other Elvis
36   * some messages indicating what it wants to do, and then leave the building (exit gracefully).
37   *<P><B><FONT COLOR="red">KNOWN SECURITY HOLES</font></b>  The current version of Elvis is
38   * most emphatically <B>not</b> secure.  The most prominent holes are:
39   * <UL>
40   *     <LI>On multi-user systems (such as Linux/Unix), the Elvis information file
41   * may be created readable by users other than the creator. An attacker to could read the Elvis
42   * information file to determine the port, key, and user.  This information could
43   * be used to:
44   *        <UL>
45   *            <LI>Submit bogus requests to the One True Elvis;
46   *            <LI>Spoof responses from the One True Elvis.
47   *            <LI>If the attacker could determine that the One True Elvis is not
48   * running, he could use this information to set up his own fraudlent Elvis.
49   *        </ul>
50   *    <LI>On multi-user systems (such as Linux/Unix), the Elvis information file
51   * may be created writable by users other than the creator. An attacker to could
52   * substitute his own information in the file, and redirect requests to his own
53   * fraudulent Elvis.
54   *    <LI>In almost all cases, things that are symptomatic of an attack or probe
55   * of Elvis are simply logged, and no further actions are taken.
56   * </ul>
57   * <P>
58   * Right now, my use of Elvis is in a trusted environment, where these situations are
59   * considered very unlikely to occur.  Although updates are planned for upcoming
60   * releases of Elvis, if you are planning to use this release of Elvis in a less secure
61   * environment, you should seriously consider beefing up Elvis's security.
62   *
63   * @author  cheiny
64   * @version $Id: Elvis.java,v 1.1 2002/05/09 07:17:17 clheiny Exp $
65   */
66  public class Elvis extends Object implements java.io.Serializable {
67  
68      private PropertyChangeSupport propertySupport;
69  
70      /** A flag indicating whether we really are the King or not. */
71      private boolean theOneTrueElvis;
72      
73      /** The port associated with this instance of Elvis */
74      private int port;
75      
76      /** This is the name of the Elvis program associated with this instance of Elvis  */
77      private String program;
78   
79      /** Indicates whether this Elvis is active or not.  If Elvis has left the building,
80       * the Elvis listen thread has been cancelled, and most method invocations will throw
81       * an ElvisException.
82       */
83      private boolean inTheBuilding = true;
84      
85      /** The request handler, provided by the Elvis application */
86      private ElvisHandler handler;
87      
88      /** The listener handles most of Elvis's communication. */
89      private ElvisComm listener = null;
90      
91      /** A handy dandy feedback string that the Elvis application can consult. */
92      private String feedback;
93      
94      /** Utility field used by bound properties. */
95      private java.beans.PropertyChangeSupport propertyChangeSupport =  new java.beans.PropertyChangeSupport(this);
96      
97      /** Creates a new Elvis.  This is private to force folks to use the factory method.  We
98       * want them to use the factory, so we can catch attempts to create multiple Elvises on
99       * the same port.
100      * <P>We presume the parameters have already been vetted by the factory, and don't check them here. */
101     private Elvis ( String program, int port, ElvisHandler handler, PropertyChangeListener propListener ) {
102         setProgram ( program );
103         setPort ( port );
104         setHandlerInternal ( handler );
105         propertySupport = new PropertyChangeSupport ( this );
106         if ( propListener != null ) this.addPropertyChangeListener ( propListener );
107         
108         try {
109             listener = new ElvisComm ( program, port, handler );
110             listener.addPropertyChangeListener ( new StatusListener (listener) );
111             listener.start();
112         } catch ( Exception e ) {
113             System.err.println ( "Failed to acquire an ElvisComm - " + e.getMessage() );
114             e.printStackTrace();
115         }
116         
117         if ( listener == null ) {
118             theOneTrueElvis = false;
119         }
120         else if ( listener.getStatus() == ElvisCommStatus.NOT_THE_KING ) {
121             theOneTrueElvis = false;
122         }
123         else {
124             theOneTrueElvis = true;
125         }
126     }
127     
128     
129     /** This is used to monitor the status of the ElvisComm, and adjust the feedback string
130      * as necessary (among other possible, not yet implemented, actions.
131      */
132     private class StatusListener implements PropertyChangeListener {
133         private ElvisComm listener = null;
134         public StatusListener ( ElvisComm listener ) {
135             this.listener = listener;
136         }
137         
138         public void propertyChange(java.beans.PropertyChangeEvent propertyChangeEvent) {
139             System.out.println ( "Elvis listener reports: " + listener.getStatus() );
140             setFeedback ( listener.getStatus().toString() );
141         }
142         
143     }
144 
145     /** Get an instance of the specified Elvis program on the specified port.  The
146      * program name may not contain the <CODE>':'</code> or end of line characters.
147      * <P>If we are indeed the one true Elvis, the specified ElvisHandler will
148      * be used to handle requests.
149      * @param program The program name string.
150      * @param handler The handler.
151      * @return Elvis, or someone who looks just like him.
152      * @throws IllegalArgumentException if you submit an invalid argument (duh).
153      */
154     public static final Elvis get ( String program, int port, ElvisHandler handler ) {
155         if ( (program == null) || ( program.length() == 0) ) throw new IllegalArgumentException ( "invalid program passed to Elvis.get" );
156         if ( handler == null ) throw new IllegalArgumentException ( "null handler passed to Elvis.get" );
157         return new Elvis( program, port, handler, null );
158     }
159     
160     /** Get an instance of the specified Elvis program on the specified port.  The
161      * program name may not contain the <CODE>':'</code> or end of line characters.
162      * <P>If we are indeed the one true Elvis, the specified ElvisHandler will
163      * be used to handle requests.
164      * @param program The program name string.
165      * @param handler The handler.
166      * @return Elvis, or someone who looks just like him.
167      * @throws IllegalArgumentException if you submit an invalid argument (duh).
168      */
169     public static final Elvis get ( String program, int port, ElvisHandler handler, PropertyChangeListener propListener ) {
170         if ( (program == null) || ( program.length() == 0) ) throw new IllegalArgumentException ( "invalid program passed to Elvis.get" );
171         if ( program.indexOf(ElvisMessage.SEPARATOR) != -1 ) throw new IllegalArgumentException ( "invalid program name (contains separator character "
172             + ElvisMessage.SEPARATOR + ")" );
173         if ( handler == null ) throw new IllegalArgumentException ( "null handler passed to Elvis.get" );
174         return new Elvis( program, port, handler, propListener );
175     }
176     
177     /** <CODE>theOneTrueElvis</CODE> indicates whether this Elvis is The King (<CODE>true</code>)
178      * or just an impersonator (<CODE>false</code>).  
179      * <P>If we are indeed the One True Elvis,
180      * then we will be handling all Elvis based interactions for all JVMs.  The client must
181      * be ready to deal with this.
182      * @return Value of property theOneTrueElvis.
183      * @throws ElvisException if Elvis has left the building.
184      */
185     public boolean isTheOneTrueElvis() {
186         if ( this.hasLeftTheBuilding() ) {
187             throw new ElvisException ( "Elvis has left the building" );
188         }
189         return theOneTrueElvis;
190     }
191     
192     /** We can only set this ourselves.  This will prevent someone from deciding they want to
193      * impersonate Elvis.
194      * @param theOneTrueElvis Boolean indicating that we are the king.  Or not.
195      */
196     private void setTheOneTrueElvis(boolean theOneTrueElvis) {
197         this.theOneTrueElvis = theOneTrueElvis;
198     }
199     
200     /** Get the port number that this Elvis is (or should be) listening on.
201      * @return The port we're listening to.
202      */
203     public int getPort() {
204         return listener.getPort();
205     }
206     
207     /** The program name is the unique identifier for this Elvis's functionality.
208      * It is unique (hopefully) across all Elvis-capable programs in all JVMs.
209      * <P>This
210      * uniqueness is actually subject to the discipline exercised by the various
211      * Elvis client programmers.  There is nothing in the Elvis implementation that
212      * prevents two different programmers from attempting to implement two very
213      * different Elvis programs using the name <CODE>Splat</code>.
214      * The best advice I can offer is <I>Don't
215      * do this!</i>  Instead, try using the fully qualified (package name and class
216      * name) for the class implementing the Elvis-based functionality.
217      * @return A String - the program name.
218      */
219     public String getProgram() {
220         return program;
221     }
222     
223     /** setPort should only be called at class instantiation time.
224      * @param port The port to listen on.
225      */
226     private void setPort(int port) {
227         this.port = port;
228     }
229     
230     /** The program name can only be set internally, preferably at creation time.
231      * @param program The Elvis program name.
232      * @see #getProgram() getProgram()
233      */
234     private void setProgram(String program) {
235         this.program = program;
236     }
237     
238     /** Calling <CODE>leaveTheBuilding()</code> will result in the Elvis listener thread
239      * being cancelled (if this Elvis is the one true Elvis) and most Elvis related infrastructure
240      * being dismantled.  Once Elvis has left the building, most method calls will through
241      * an ElvisException.
242      */
243     public void leaveTheBuilding() {
244         inTheBuilding = false;
245     }
246     
247     /** Returns <CODE>true</code> if Elvis has left the building; <CODE>false</code> if he
248      * is still around and ready to perform.
249      */
250     public boolean hasLeftTheBuilding() {
251         return !inTheBuilding;
252     }
253     
254     /** Returns the handler associated with this Elvis.  This will be <CODE>null</code> if
255      * we are not the One True Elvis.
256      * @return The ElvisHandler being used by this Elvis, or <CODE>null</code>.
257      */
258     public ElvisHandler getHandler() {
259         return handler;
260     }
261     
262     /** Set the Handler for this Elvis.
263      * @param handler New value of property handler.
264      * @throws ElvisException if this is not the One True Elvis, or if Elvis has left the building.
265      */
266     public void setHandler(ElvisHandler handler) {
267         if ( !this.isTheOneTrueElvis() ) {
268             throw new ElvisException ( "We are not the One True Elvis" );
269         }
270         if ( this.hasLeftTheBuilding() ) {
271             throw new ElvisException ( "Elvis has left the building" );
272         }
273         setHandlerInternal ( handler );
274     }
275     
276     /** Internal worker that actually does the handler setting.
277      */
278     private void setHandlerInternal ( ElvisHandler handler ) {
279         this.handler = handler;
280     }
281     
282     /** Add a PropertyChangeListener to the listener list.
283      * @param l The listener to add.
284      */
285     public void addPropertyChangeListener(java.beans.PropertyChangeListener l) {
286         propertyChangeSupport.addPropertyChangeListener(l);
287     }
288     
289     /** Removes a PropertyChangeListener from the listener list.
290      * @param l The listener to remove.
291      */
292     public void removePropertyChangeListener(java.beans.PropertyChangeListener l) {
293         propertyChangeSupport.removePropertyChangeListener(l);
294     }
295     
296     /** The feedback property provides information about the state and activities of Elvis.  For
297      * example, various stages of establishing the ports we listen to are reported.
298      *<P>
299      * This is <B>not</b> intended to be a definitive means of determining the
300      * status of Elvis.  It's more intended to give useful text information that can be
301      * presented in status bars and stuff like that.
302      * @return A string with information of varying interest.
303      */
304     public synchronized String getFeedback() {
305         return feedback;
306     }
307     
308     /** Update the feedback string and notify any listeners.
309      * @param feedback New info of interest.
310      */
311     private synchronized void setFeedback(String feedback) {
312         String oldFeedback = this.feedback;
313         this.feedback = feedback;
314         propertyChangeSupport.firePropertyChange("feedback", oldFeedback, feedback);
315     }
316     
317     /** Calling this will cause Elvis to do something.  If we are the one true Elvis, the local
318      * ElvisHandler will be called with the specified data.  If we are not the one true Elvis, the
319      * remote Elvis will be requested to perform and passed the specified data.
320      * @param data A String for Elvis to operate on (if needed, may be null or empty).
321      * @returns An ElvisResult indicating the success or failure of the performance, and including 
322      * any relevant data.
323      */
324     public ElvisResult perform ( java.lang.String data ) {
325         if ( isTheOneTrueElvis() ) {
326             return handler.perform ( data );
327         }
328         else {
329             return listener.perform ( data );
330         }
331     }
332     
333 }