View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.river.example.browser;
19  
20  import java.awt.BorderLayout;
21  import java.awt.Component;
22  import java.awt.event.ActionEvent;
23  import java.awt.event.ActionListener;
24  import java.awt.event.MouseAdapter;
25  import java.awt.event.MouseEvent;
26  import java.awt.event.MouseListener;
27  import java.awt.event.WindowAdapter;
28  import java.awt.event.WindowEvent;
29  import java.awt.event.WindowListener;
30  import java.io.IOException;
31  import java.io.ObjectStreamException;
32  import java.io.ObjectStreamField;
33  import java.io.Serializable;
34  import java.lang.reflect.Field;
35  import java.lang.reflect.InvocationHandler;
36  import java.lang.reflect.InvocationTargetException;
37  import java.lang.reflect.Method;
38  import java.lang.reflect.Modifier;
39  import java.lang.reflect.Proxy;
40  import java.net.MalformedURLException;
41  import java.security.AccessController;
42  import java.security.PrivilegedActionException;
43  import java.security.PrivilegedExceptionAction;
44  import java.util.Arrays;
45  import java.util.HashSet;
46  import java.util.Iterator;
47  import java.util.LinkedList;
48  import java.util.List;
49  import java.util.Set;
50  import java.util.StringTokenizer;
51  import java.util.logging.Level;
52  import java.util.logging.Logger;
53  import javax.security.auth.Subject;
54  import javax.security.auth.login.LoginContext;
55  import javax.security.auth.login.LoginException;
56  import javax.swing.BorderFactory;
57  import javax.swing.DefaultListModel;
58  import javax.swing.Icon;
59  import javax.swing.JCheckBoxMenuItem;
60  import javax.swing.JFrame;
61  import javax.swing.JLabel;
62  import javax.swing.JList;
63  import javax.swing.JMenu;
64  import javax.swing.JMenuBar;
65  import javax.swing.JMenuItem;
66  import javax.swing.JOptionPane;
67  import javax.swing.JPanel;
68  import javax.swing.JPopupMenu;
69  import javax.swing.JRadioButtonMenuItem;
70  import javax.swing.JScrollPane;
71  import javax.swing.JTextArea;
72  import javax.swing.ListCellRenderer;
73  import javax.swing.SwingUtilities;
74  import javax.swing.border.TitledBorder;
75  import javax.swing.event.MenuEvent;
76  import javax.swing.event.MenuListener;
77  import javax.swing.event.PopupMenuEvent;
78  import javax.swing.event.PopupMenuListener;
79  import net.jini.admin.Administrable;
80  import net.jini.admin.JoinAdmin;
81  import net.jini.config.Configuration;
82  import net.jini.config.ConfigurationException;
83  import net.jini.config.ConfigurationProvider;
84  import net.jini.config.EmptyConfiguration;
85  import net.jini.config.NoSuchEntryException;
86  import net.jini.core.constraint.MethodConstraints;
87  import net.jini.core.discovery.LookupLocator;
88  import net.jini.core.entry.Entry;
89  import net.jini.core.event.EventRegistration;
90  import net.jini.core.event.RemoteEvent;
91  import net.jini.core.event.RemoteEventListener;
92  import net.jini.core.lease.Lease;
93  import net.jini.core.lookup.ServiceID;
94  import net.jini.core.lookup.ServiceItem;
95  import net.jini.core.lookup.ServiceMatches;
96  import net.jini.core.lookup.ServiceRegistrar;
97  import net.jini.core.lookup.ServiceTemplate;
98  import net.jini.discovery.Constants;
99  import net.jini.discovery.ConstrainableLookupLocator;
100 import net.jini.discovery.DiscoveryEvent;
101 import net.jini.discovery.DiscoveryGroupManagement;
102 import net.jini.discovery.DiscoveryListener;
103 import net.jini.discovery.DiscoveryLocatorManagement;
104 import net.jini.discovery.DiscoveryManagement;
105 import net.jini.discovery.LookupDiscoveryManager;
106 import net.jini.export.Exporter;
107 import net.jini.jeri.BasicILFactory;
108 import net.jini.jeri.BasicJeriExporter;
109 import net.jini.jeri.tcp.TcpServerEndpoint;
110 import net.jini.lease.LeaseListener;
111 import net.jini.lease.LeaseRenewalEvent;
112 import net.jini.lease.LeaseRenewalManager;
113 import net.jini.lookup.DiscoveryAdmin;
114 import net.jini.lookup.SafeServiceRegistrar;
115 import net.jini.lookup.entry.UIDescriptor;
116 import net.jini.lookup.ui.factory.JFrameFactory;
117 import net.jini.security.BasicProxyPreparer;
118 import net.jini.security.ProxyPreparer;
119 import net.jini.security.Security;
120 import net.jini.security.SecurityContext;
121 import net.jini.security.TrustVerifier;
122 import net.jini.security.proxytrust.ServerProxyTrust;
123 import net.jini.space.JavaSpace;
124 import net.jini.space.JavaSpace05;
125 import org.apache.river.admin.DestroyAdmin;
126 import org.apache.river.api.util.Startable;
127 import org.apache.river.config.Config;
128 import org.apache.river.logging.Levels;
129 import org.apache.river.admin.JavaSpaceAdmin;
130 import org.apache.river.proxy.BasicProxyTrustVerifier;
131 import org.apache.river.start.lifecycle.LifeCycle;
132 
133 /*
134  * This is not great user interface design. It was a quick-and-dirty hack
135  * and an experiment in on-the-fly menu construction, and it's still
136  * here because we've never had time to do anything better.
137  */
138 /**
139  * Example service browser. See the package documentation for details.
140  *
141  * @author Sun Microsystems, Inc.
142  */
143 public class Browser extends JFrame implements Startable {
144     private static final long serialVersionUID = 2218152788101871451L;
145     /**
146      * By defining serial persistent fields, we don't need to use transient field identifiers.
147      * All fields can be final and this object becomes immutable.
148      */
149     private static final ObjectStreamField[] serialPersistentFields = {};
150     
151     static final String BROWSER = "org.apache.river.example.browser";
152     static final Logger logger = Logger.getLogger(BROWSER);
153 
154     private final SecurityContext ctx;
155     private final ClassLoader ccl;
156     final Configuration config;
157     private final DiscoveryGroupManagement disco;
158     private SafeServiceRegistrar lookup; /* mutable, guarded by this */
159     private Object eventSource; /* mutable, guarded by this */
160     private long eventID; /* mutable, guarded by this */
161     private long seqNo; /* mutable, guarded by this */
162     private final ActionListener exiter;
163     private final ServiceTemplate tmpl;
164     private final Listener listen;
165     private final LookupListener adder;
166     private Lease elease; /* mutable, guarded by this */
167     final ProxyPreparer leasePreparer;
168     final ProxyPreparer servicePreparer;
169     final ProxyPreparer adminPreparer;
170     private final MethodConstraints locatorConstraints;
171     final LeaseRenewalManager leaseMgr;
172     private final LeaseListener lnotify;
173     private final List ignoreInterfaces;
174     private final JTextArea text;
175     private final JMenu registrars;
176     private final JCheckBoxMenuItem esuper;
177     private final JCheckBoxMenuItem ssuper;
178     private final JCheckBoxMenuItem sclass;
179     private final boolean isAdmin;
180     private final boolean autoConfirm;
181     private final JList list;
182     private final DefaultListModel listModel;
183     private final DefaultListModel dummyModel = new DefaultListModel();
184     private final JScrollPane listScrollPane;
185     
186     /**
187      * Creates an instance with the given action listener for the Exit
188      * menu item and the given configuration. The action listener defaults
189      * to an instance of {@link Exit Exit}. The action listener can be
190      * overridden by a configuration entry. The configuration
191      * defaults to an empty configuration.
192      *
193      * @param config the configuration, or <code>null</code>
194      * @throws net.jini.config.ConfigurationException
195      * @throws java.io.IOException
196      */
197     public Browser(Configuration config)
198 	throws ConfigurationException, IOException
199     {
200 	this(new Initializer(null, config == null ? EmptyConfiguration.INSTANCE : config));
201     }
202     
203     private static class Initializer {
204 	private SecurityContext ctx;
205 	private ClassLoader ccl;
206 	private Configuration config;
207 	private DiscoveryGroupManagement disco;
208 	private SafeServiceRegistrar lookup;
209 	private Object eventSource;
210 	private ActionListener exiter;
211 	private ServiceTemplate tmpl;
212 	private ProxyPreparer leasePreparer;
213 	private ProxyPreparer servicePreparer;
214 	private ProxyPreparer adminPreparer;
215 	private MethodConstraints locatorConstraints;
216 	private LeaseRenewalManager leaseMgr;
217 	private List ignoreInterfaces;
218 	private boolean isAdmin;
219 	private boolean autoConfirm;
220 	private final Exporter listenerExporter;
221 	private final LifeCycle lc;
222 
223 	public Initializer(LifeCycle lc, Configuration config) throws ConfigurationException, IOException {
224 	    this.lc = lc;
225 	    this.exiter = 
226 		Config.getNonNullEntry(config, BROWSER, "exitActionListener",
227 						   ActionListener.class, null);
228 	    this.config = config;
229 	    ctx = Security.getContext();
230 	    ccl = Thread.currentThread().getContextClassLoader();
231 	    leaseMgr = 
232 		Config.getNonNullEntry(config, BROWSER, "leaseManager",
233 				       LeaseRenewalManager.class,
234 				       new LeaseRenewalManager(config));
235 	    isAdmin = config.getEntry(
236 		    BROWSER, "folderView",
237 		    boolean.class, Boolean.TRUE);
238 	    leasePreparer = 
239 		Config.getNonNullEntry(config, BROWSER, "leasePreparer",
240 				       ProxyPreparer.class,
241 				       new BasicProxyPreparer());
242 	    servicePreparer = 
243 		Config.getNonNullEntry(config, BROWSER, "servicePreparer",
244 				       ProxyPreparer.class,
245 				       new BasicProxyPreparer());
246 	    adminPreparer = 
247 		Config.getNonNullEntry(config, BROWSER, "adminPreparer",
248 				       ProxyPreparer.class,
249 				       new BasicProxyPreparer());
250 	    locatorConstraints = 
251 		config.getEntry(BROWSER, "locatorConstraints",
252 				MethodConstraints.class, null);
253 	    ignoreInterfaces = Arrays.asList((String[])
254 		Config.getNonNullEntry(config, BROWSER, "uninterestingInterfaces",
255 				       String[].class,
256 				       new String[]{
257 				"java.io.Serializable",
258 				"java.rmi.Remote",
259 				"net.jini.admin.Administrable",
260 				"net.jini.core.constraint.RemoteMethodControl",
261 				"net.jini.id.ReferentUuid",
262 				"net.jini.security.proxytrust.TrustEquivalence"}));
263 	    autoConfirm =  config.getEntry(
264 					    BROWSER, "autoConfirm", boolean.class,
265 					    Boolean.FALSE);
266 	    listenerExporter = 
267 		    Config.getNonNullEntry(config, BROWSER, "listenerExporter",
268 					   Exporter.class,
269 					   new BasicJeriExporter(
270 						 TcpServerEndpoint.getInstance(0),
271 						 new BasicILFactory(),
272 						 false, false));
273 	    try {
274 		DiscoveryManagement discoMan = 
275 		    Config.getNonNullEntry(config, BROWSER, "discoveryManager",
276 					   DiscoveryManagement.class);
277 		if (!(discoMan instanceof DiscoveryGroupManagement)) {
278 		    throw new ConfigurationException(
279 				  "discoveryManager does not " +
280 				  " support DiscoveryGroupManagement");
281 		} else if (!(discoMan instanceof DiscoveryLocatorManagement)) {
282 		    throw new ConfigurationException(
283 				  "discoveryManager does not " +
284 				  " support DiscoveryLocatorManagement");
285 		}
286 		this.disco = (DiscoveryGroupManagement) discoMan;
287 		String[] groups = this.disco.getGroups();
288 		if (groups == null || groups.length > 0) {
289 		    throw new ConfigurationException(
290 				  "discoveryManager cannot have initial groups");
291 		}
292 		if (((DiscoveryLocatorManagement) discoMan).getLocators().length > 0)
293 		{
294 		    throw new ConfigurationException(
295 				  "discoveryManager cannot have initial locators");
296 		}
297 	    } catch (NoSuchEntryException e) {
298 		disco = new LookupDiscoveryManager(new String[0],
299 						   new LookupLocator[0], null,
300 						   config);
301 	    }
302 	    disco.setGroups((String[]) config.getEntry(BROWSER,
303 						       "initialLookupGroups",
304 						       String[].class,
305 						       null));
306 	    ((DiscoveryLocatorManagement) disco).setLocators((LookupLocator[])
307 		Config.getNonNullEntry(config, BROWSER, "initialLookupLocators",
308 				       LookupLocator[].class,
309 				       new LookupLocator[0]));
310 	    tmpl = new ServiceTemplate(null, new Class[0], new Entry[0]);
311 	}
312     }
313 	
314     private Browser(Initializer init){
315 	this.elease = null;
316 	this.seqNo = Long.MAX_VALUE;
317 	this.eventID = 0;
318 	ctx = init.ctx;
319 	ccl = init.ccl;
320 	config = init.config;
321 	disco = init.disco;
322 	lookup = init.lookup;
323 	eventSource = init.eventSource;
324 	final LifeCycle lc = init.lc;
325 	exiter = wrap(
326 	    init.exiter == null ?
327 		lc != null ?
328 		    new ActionListener() {
329 			public void actionPerformed(ActionEvent ev) {
330 			    Browser.this.dispose();
331 			    cancelLease();
332 			    listen.unexport();
333 			    lc.unregister(Browser.this);
334 			}
335 		    }
336 		: new Exit()
337 	    :
338 	    init.exiter
339 	);
340 	tmpl = init.tmpl;
341 	leasePreparer = init.leasePreparer;
342 	servicePreparer = init.servicePreparer;
343 	adminPreparer = init.adminPreparer;
344 	locatorConstraints = init.locatorConstraints;
345 	leaseMgr = init.leaseMgr;
346 	ignoreInterfaces = init.ignoreInterfaces;
347 	isAdmin = init.isAdmin;
348 	autoConfirm = init.autoConfirm;
349 	listen = new Listener(init.listenerExporter);
350 	setTitle("Service Browser");
351 	JMenuBar bar = new JMenuBar();
352 	JMenu file = new JMenu("File");
353 	JMenuItem allfind = new JMenuItem("Find All");
354 	allfind.addActionListener(wrap(new AllFind()));
355 	file.add(allfind);
356 	JMenuItem pubfind = new JMenuItem("Find Public");
357 	pubfind.addActionListener(wrap(new PubFind()));
358 	file.add(pubfind);
359 	JMenuItem multifind = new JMenuItem("Find By Group...");
360 	multifind.addActionListener(wrap(new MultiFind()));
361 	file.add(multifind);
362 	JMenuItem unifind = new JMenuItem("Find By Address...");
363 	unifind.addActionListener(wrap(new UniFind()));
364 	file.add(unifind);
365 	if(! isAdmin){
366 	    JMenuItem show = new JMenuItem("Show Matches");
367 	    show.addActionListener(wrap(new Show()));
368 	    file.add(show);
369 	}
370 	JMenuItem reset = new JMenuItem("Reset");
371 	reset.addActionListener(wrap(new Reset()));
372 	file.add(reset);
373 	JMenuItem exit = new JMenuItem("Exit");
374 	exit.addActionListener(exiter);
375 	file.add(exit);
376 	bar.add(file);
377 	addWindowListener(new Exiter());
378 	registrars = new JMenu("Registrar");
379 	addNone(registrars);
380 	bar.add(registrars);
381 	JMenu options = new JMenu("Options");
382 	esuper = new JCheckBoxMenuItem("Attribute supertypes", false);
383 	options.add(esuper);
384 	ssuper = new JCheckBoxMenuItem("Service supertypes", false);
385 	options.add(ssuper);
386 	sclass = new JCheckBoxMenuItem("Service classes", false);
387 	options.add(sclass);
388 	bar.add(options);
389 	JMenu services = new JMenu("Services");
390 	services.addMenuListener(wrap(new Services(services)));
391 	bar.add(services);
392 	JMenu attrs = new JMenu("Attributes");
393 	attrs.addMenuListener(wrap(new Entries(attrs)));
394 	bar.add(attrs);
395 	setJMenuBar(bar);
396 
397 	getContentPane().setLayout(new BorderLayout());
398 	int textRows = 8;
399 	if(isAdmin){
400 	  textRows = 4;
401 	    JPanel bpanel = new JPanel();
402 	    bpanel.setLayout(new BorderLayout());
403 
404 	    TitledBorder border = BorderFactory.createTitledBorder("Matching Services");
405 	    border.setTitlePosition(TitledBorder.TOP);
406 	    border.setTitleJustification(TitledBorder.LEFT);
407 	    bpanel.setBorder(border);
408 
409 	    listModel = new DefaultListModel();
410 	    list = new JList(listModel);
411 	    list.setFixedCellHeight(20);
412 	    list.setCellRenderer((ListCellRenderer)
413 		   wrap(new ServiceItemRenderer(), ListCellRenderer.class));
414 	    list.addMouseListener(
415 			     wrap(new MouseReceiver(new ServiceListPopup())));
416 	    listScrollPane = new JScrollPane(list);
417 	    bpanel.add(listScrollPane, "Center");
418 	    getContentPane().add(bpanel, "South");
419 	} else {
420 	    list = null;
421 	    listModel = null;
422 	    listScrollPane = null;
423 	}
424 	text = new JTextArea(genText(false), textRows, 40);
425 	text.setEditable(false);
426 	JScrollPane scroll = new JScrollPane(text);
427 	getContentPane().add(scroll, "Center");
428 	validate();
429 	adder = new LookupListener();
430 	lnotify = new LeaseNotify();
431     }
432     
433     public void start() throws Exception {
434 	listen.start();
435 	SwingUtilities.invokeLater(wrap(new Runnable() {
436 	    public void run() {
437 		pack();
438 		setVisible(true);
439 	    }
440 	}));
441 	((DiscoveryManagement) disco).addDiscoveryListener(adder);
442     }
443 
444     /**
445      * Creates an instance with the given command line arguments and
446      * life cycle callback. See the package documentation for details
447      * of the command line arguments. The default action listener for
448      * the Exit menu item calls the {@link #dispose dispose} method of
449      * this instance, cancels any lookup service event registration lease,
450      * unexports any remote event listener, and calls the
451      * {@link LifeCycle#unregister unregister} method of the life cycle
452      * callback. The action listener can be overridden by a configuration
453      * entry.
454      *
455      * @param args command line arguments
456      * @param lc life cycle callback, or <code>null</code>.
457      * @throws net.jini.config.ConfigurationException
458      * @throws javax.security.auth.login.LoginException
459      * @throws java.io.IOException
460      */
461     public Browser(String[] args, final LifeCycle lc)
462 	throws ConfigurationException, LoginException, IOException
463     {
464 	this(init(args, lc));
465     }
466     
467     private static Initializer init(String[] args, final LifeCycle lc) 
468 	    throws ConfigurationException, IOException, LoginException
469     {
470 	final Configuration config =
471 	    ConfigurationProvider.getInstance(
472 				    args, Browser.class.getClassLoader());
473 	LoginContext login =
474 	    (LoginContext) config.getEntry(BROWSER, "loginContext",
475 					   LoginContext.class, null);
476 	if (login == null) {
477 	    return new Initializer(lc, config);
478 	} else {
479 	    login.login();
480 	    try {
481 		return Subject.doAsPrivileged(
482 		    login.getSubject(),
483 		    new PrivilegedExceptionAction<Initializer>() {
484 			    public Initializer run()
485 				throws ConfigurationException, IOException
486 			    {
487 				return new Initializer(lc, config);
488 			    }
489 			},
490 		    null);
491 	    } catch (PrivilegedActionException pae) {
492 		Exception e = pae.getException();
493 		if (e instanceof ConfigurationException)
494 		    throw (ConfigurationException) e;
495 		throw (IOException) e;
496 	    }
497 	}
498     }
499 
500     private static String typeName(Class type) {
501 	String name = type.getName();
502 	int i = name.lastIndexOf('.');
503 	if (i >= 0)
504 	    name = name.substring(i + 1);
505 	return name;
506     }
507 
508     private void setText(boolean match) {
509 	text.setText(genText(match));
510     }
511 
512     private String genText(boolean match) {
513 	StringBuffer buf = new StringBuffer();
514 	if (tmpl.serviceTypes.length > 0) {
515 	    for (int i = 0, l = tmpl.serviceTypes.length ; i < l ; i++) {
516 		buf.append(tmpl.serviceTypes[i].getName());
517 		buf.append("\n");
518 	    }
519 	}
520 	if (tmpl.attributeSetTemplates.length > 0)
521 	    genEntries(buf, tmpl.attributeSetTemplates, false);
522 	genMatches(buf, match);
523 	return buf.toString();
524     }
525 
526     private void genEntries(StringBuffer buf,
527 			    Entry[] entries,
528 			    boolean showNulls)
529     {
530 	for (int i = 0, l = entries.length; i < l; i++) {
531 	    Entry ent = entries[i];
532 	    if (ent == null) {
533 		buf.append("null\n");
534 		continue;
535 	    }
536 	    buf.append(typeName(ent.getClass()));
537 	    buf.append(": ");
538 	    try {
539 		Field[] fields = ent.getClass().getFields();
540 		for (int j = 0, len = fields.length; j < len; j++) {
541 		    if (!valid(fields[j]))
542 			continue;
543 		    Object val = fields[j].get(ent);
544 		    if (val != null || showNulls) {
545 			buf.append(fields[j].getName());
546 			buf.append("=");
547 			buf.append(val);
548 			buf.append(" ");
549 		    }
550 		}
551 	    } catch (Exception e) {
552 		logger.log(Level.FINE, "Exception thrown: ", e);
553 	    }
554 	    buf.append("\n");
555 	}
556     }
557 
558     private static boolean valid(Field f) {
559 	return (f.getModifiers() & (Modifier.STATIC|Modifier.FINAL)) == 0;
560     }
561 
562     private void genMatches(StringBuffer buf, boolean match) {
563         if(isAdmin) {
564 	    list.setModel(dummyModel);	// to keep away from Swing's bug
565 	    
566 	    listModel.removeAllElements();
567 	    list.clearSelection();
568 	    list.ensureIndexIsVisible(0);
569 	    list.repaint();
570 	    list.revalidate();
571 	    listScrollPane.validate();
572 	}
573 	SafeServiceRegistrar lookup;
574 	synchronized (Browser.this){
575 	    lookup = this.lookup;
576 	}
577 	if (lookup == null) {
578 	    String[] groups = disco.getGroups();
579 	    if (groups == null) {
580 		buf.append("Groups: <all>\n");
581 	    } else if (groups.length > 0) {
582 		buf.append("Groups:");
583 		for (int i = 0, l = groups.length; i < l; i++) {
584 		    String group = groups[i];
585 		    if (group.length() == 0)
586 			group = "public";
587 		    buf.append(" ");
588 		    buf.append(group);
589 		}
590 		buf.append("\n");
591 	    }
592 	    LookupLocator[] locators =
593 		((DiscoveryLocatorManagement) disco).getLocators();
594 	    if (locators.length > 0) {
595 		buf.append("Addresses:");
596 		for (int i = 0; i < locators.length; i++) {
597 		    buf.append(" ");
598 		    buf.append(locators[i].getHost());
599 		    if (locators[i].getPort() != Constants.discoveryPort) {
600 			buf.append(":");
601 			buf.append(locators[i].getPort());
602 		    }
603 		}
604 		buf.append("\n");
605 	    }
606 	    if (!(registrars.getMenuComponent(0) instanceof
607 		  JRadioButtonMenuItem))
608 	    {
609 		buf.append("No registrars to select");
610 		return;
611 	    }
612 	    int n = registrars.getMenuComponentCount();
613 	    if (n == 1) {
614 		buf.append("1 registrar, not selected");
615 	    } else {
616 		buf.append(n);
617 		buf.append(" registrars, none selected");
618 	    }
619 	    return;
620 	}
621 	ServiceMatches matches;
622 	try {
623 	    matches = lookup.lookup(tmpl, (match || isAdmin) ? 1000 : 0);
624 	} catch (Throwable t) {
625 	    logger.log(Level.INFO, "lookup failed", t);
626 	    return;
627 	}
628 
629 	if (matches.items != null) {
630 	    for (int i = 0, l = matches.items.length ; i < l ; i++) {
631 		if (matches.items[i].service != null) {
632 		    try {
633 			matches.items[i].service =
634 			    servicePreparer.prepareProxy(
635 						     matches.items[i].service);
636 		    } catch (Throwable t) {
637 			logger.log(Level.INFO, "proxy preparation failed", t);
638 			matches.items[i].service = null;
639 		    }
640 		}
641 	    }
642 	}
643 
644 	if(isAdmin){
645 	    for (int i = 0, l = matches.items.length ; i < l; i++)
646 	        listModel.addElement(new ServiceListItem(matches.items[i]));
647 	    list.setModel(listModel);
648 	    list.clearSelection();
649 	    list.ensureIndexIsVisible(0);
650 	    list.repaint();
651 	    list.revalidate();
652 	    listScrollPane.validate();
653 	}
654 
655 	if (!match &&
656 	    tmpl.serviceTypes.length == 0 &&
657 	    tmpl.attributeSetTemplates.length == 0)
658 	{
659 	    buf.append("Total services registered: ");
660 	    buf.append(matches.totalMatches);
661 	    return;
662 	}
663 	buf.append("\nMatching services: ");
664 	buf.append(matches.totalMatches);
665 
666 	if(! isAdmin){
667 	    if (!match)
668 	        return;
669 	    buf.append("\n\n");
670 	    for (int i = 0, l = matches.items.length; i < l; i++) {
671 	        ServiceItem item = matches.items[i];
672 		buf.append("Service ID: ");
673 		buf.append(item.serviceID);
674 		buf.append("\n");
675 		buf.append("Service instance: ");
676 		buf.append(item.service);
677 		buf.append("\n");
678 		genEntries(buf, item.attributeSets, true);
679 		buf.append("\n");
680 	    }
681 	}
682     }
683 
684     private static void addNone(JMenu menu) {
685 	JMenuItem item = new JMenuItem("(none)");
686 	item.setEnabled(false);
687 	menu.add(item);
688     }
689 
690     private void addOne(ServiceRegistrar registrar) {
691 	if(!(registrar instanceof SafeServiceRegistrar)) return;
692 	LookupLocator loc;
693 	try {
694 	    loc = registrar.getLocator();
695 	} catch (Throwable t) {
696 	    logger.log(Level.INFO, "obtaining locator failed", t);
697 	    return;
698 	}
699 	String host = loc.getHost();
700 	if (loc.getPort() != Constants.discoveryPort)
701 	    host += ":" + loc.getPort();
702 	JRadioButtonMenuItem reg =
703 	    new RegistrarMenuItem(host, registrar.getServiceID());
704 	reg.addActionListener(wrap(new Lookup((SafeServiceRegistrar)registrar)));
705 	if (!(registrars.getMenuComponent(0)
706 	      instanceof JRadioButtonMenuItem))
707 	    registrars.removeAll();
708 	registrars.add(reg);
709     }
710 
711     private static class RegistrarMenuItem extends JRadioButtonMenuItem {
712 	ServiceID id;
713 
714 	RegistrarMenuItem(String host, ServiceID id) {
715 	    super(host);
716 	    this.id = id;
717 	}
718     }
719 
720     static Class[] getInterfaces(Class c) {
721 	Set<Class> set = new HashSet<Class>();
722 	for ( ; c != null; c = c.getSuperclass()) {
723 	    Class[] ifs = c.getInterfaces();
724 	    for (int i = ifs.length; --i >= 0; )
725 		set.add(ifs[i]);
726 	}
727 	return set.toArray(new Class[set.size()]);
728     }
729 
730     private class Services implements MenuListener {
731 	private final JMenu menu;
732 
733 	public Services(JMenu menu) {
734 	    this.menu = menu;
735 	}
736 
737 	public void menuSelected(MenuEvent ev) {
738 	    SafeServiceRegistrar lookup;
739 	    synchronized (Browser.this){
740 		lookup = Browser.this.lookup;
741 	    }
742 	    if (lookup == null) {
743 		addNone(menu);
744 		return;
745 	    }
746 	    List all = new LinkedList();
747 	    Class[] types = tmpl.serviceTypes;
748 	    for (int i = 0; i < types.length; i++) {
749 		all.add(types[i]);
750 		JCheckBoxMenuItem item =
751 		    new JCheckBoxMenuItem(types[i].getName(), true);
752 		    item.addActionListener(wrap(new Service(types[i], i)));
753 		    menu.add(item);
754 	    }
755 	    try {
756 		types = lookup.getServiceTypes(tmpl, "");
757 	    } catch (Throwable t) {
758 		failure(t);
759 		return;
760 	    }
761 	    if (types == null) {
762 		if (all.isEmpty())
763 		    addNone(menu);
764 		return;
765 	    }
766 	    for (int i = 0, len = types.length; i < len; i++) {
767 		Class[] stypes;
768 		if (types[i] == null) {
769 		    all.add(new JMenuItem("null"));
770 		    continue;
771 		}
772 		if (types[i].isInterface() || sclass.getState()) {
773 		    stypes = new Class[]{types[i]};
774 		} else {
775 		    stypes = getInterfaces(types[i]);
776 		}
777 		for (int j = 0, l = stypes.length; j < l; j++) {
778 		    addType(stypes[j], all);
779 		}
780 	    }
781 	}
782 
783 	private void addType(Class type, List all) {
784 	    if (all.contains(type))
785 		return;
786 	    all.add(type);
787 	    JCheckBoxMenuItem item =
788 		new JCheckBoxMenuItem(type.getName(), false);
789 	    item.addActionListener(wrap(new Service(type, -1)));
790 	    menu.add(item);
791 	    if (!ssuper.getState())
792 		return;
793 	    if (sclass.getState() && type.getSuperclass() != null)
794 		addType(type.getSuperclass(), all);
795 	    Class[] stypes = type.getInterfaces();
796 	    for (int i = 0, l = stypes.length; i < l; i++) {
797 		addType(stypes[i], all);
798 	    }
799 	}
800 
801 	public void menuDeselected(MenuEvent ev) {
802 	    menu.removeAll();
803 	}
804 
805 	public void menuCanceled(MenuEvent ev) {
806 	    menu.removeAll();
807 	}
808     }
809 
810     /**
811      * Indicates whether auto confirm is enabled to prevent from the user
812      * having to click the 'Yes' button in the a popup window to confirm a
813      * modification to the service browser pane is allowed to take place as
814      * result of a service being removed, or its lookup attributes being
815      * changed.
816      *
817      * @return <code>true</code> in case no popup is required to have the user
818      *         confirm the modifications, <code>false</code> otherwise
819      */
820     boolean isAutoConfirm() {
821 	return autoConfirm;
822     }
823 
824     final ActionListener wrap(ActionListener l) {
825 	return (ActionListener) wrap((Object) l, ActionListener.class);
826     }
827 
828     final MenuListener wrap(MenuListener l) {
829 	return (MenuListener) wrap((Object) l, MenuListener.class);
830     }
831 
832     final MouseListener wrap(MouseListener l) {
833 	return (MouseListener) wrap((Object) l, MouseListener.class);
834     }
835 
836     WindowListener wrap(WindowListener a) {
837 	return (WindowListener) wrap((Object) a, WindowListener.class);
838     }
839 
840     final Runnable wrap(Runnable r) {
841 	return (Runnable) wrap((Object) r, Runnable.class);
842     }
843 
844     private Object wrap(Object obj, Class iface) {
845 	return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
846 				      new Class[]{iface}, new Handler(obj));
847     }
848 
849     private class Handler implements InvocationHandler {
850 	private final Object obj;
851 
852 	Handler(Object obj) {
853 	    this.obj = obj;
854 	}
855 
856 	public Object invoke(Object proxy,
857 			     final Method method,
858 			     final Object[] args)
859 	    throws Throwable
860 	{
861 	    if (method.getDeclaringClass() == Object.class) {
862 		if ("equals".equals(method.getName()))
863 		    return proxy == args[0];
864 		else if ("hashCode".equals(method.getName()))
865 		    return System.identityHashCode(proxy);
866 	    }
867 	    try {
868 		return AccessController.doPrivileged(
869 		    ctx.wrap(new PrivilegedExceptionAction() {
870 			public Object run() throws Exception {
871 			    Thread t = Thread.currentThread();
872 			    ClassLoader occl = t.getContextClassLoader();
873 			    try {
874 				t.setContextClassLoader(ccl);
875 				try {
876 				    return method.invoke(obj, args);
877 				} catch (InvocationTargetException e) {
878 				    Throwable tt = e.getCause();
879 				    if (tt instanceof Error)
880 					throw (Error) tt;
881 				    throw (Exception) tt;
882 				}
883 			    } finally {
884 				t.setContextClassLoader(occl);
885 			    }
886 			}
887 		    }), ctx.getAccessControlContext());
888 	    } catch (PrivilegedActionException e) {
889 		throw e.getCause();
890 	    }
891 	}
892     }
893 
894     private class Show implements ActionListener {
895 
896 	public void actionPerformed(ActionEvent ev) {
897 	    setText(true);
898 	}
899     }
900 
901     private void resetTmpl() {
902 	tmpl.serviceTypes = new Class[0];
903 	tmpl.attributeSetTemplates = new Entry[0];
904 	update();
905     }
906 
907     private void reset() {
908 	ssuper.setState(false);
909 	esuper.setState(false);
910 	sclass.setState(false);
911 	resetTmpl();
912     }
913 
914     private class Reset implements ActionListener {
915 
916 	public void actionPerformed(ActionEvent ev) {
917 	    reset();
918 	}
919     }
920 
921     private class Service implements ActionListener {
922 	private final Class type;
923 	private final int index;
924 
925 	public Service(Class type, int index) {
926 	    this.type = type;
927 	    this.index = index;
928 	}
929 
930 	public void actionPerformed(ActionEvent ev) {
931 	    int z = tmpl.serviceTypes.length;
932 	    Class[] newTypes;
933 	    if (index < 0) {
934 		newTypes = new Class[z + 1];
935 		System.arraycopy(tmpl.serviceTypes, 0, newTypes, 0, z);
936 		newTypes[z] = type;
937 	    } else {
938 		newTypes = new Class[z - 1];
939 		System.arraycopy(tmpl.serviceTypes, 0,
940 				 newTypes, 0, index);
941 		System.arraycopy(tmpl.serviceTypes, index + 1,
942 				 newTypes, index, z - index - 1);
943 	    }
944 	    tmpl.serviceTypes = newTypes;
945 	    update();
946 	}
947     }
948 
949     private class Entries implements MenuListener {
950 	private final JMenu menu;
951 
952 	public Entries(JMenu menu) {
953 	    this.menu = menu;
954 	}
955 
956 	public void menuSelected(MenuEvent ev) {
957 	    SafeServiceRegistrar lookup;
958 	    synchronized (Browser.this){
959 		lookup = Browser.this.lookup;
960 	    }
961 	    if (lookup == null) {
962 		addNone(menu);
963 		return;
964 	    }
965 	    Entry[] attrs = tmpl.attributeSetTemplates;
966 	    for (int i = 0; i < attrs.length; i++) {
967 		Class type = attrs[i].getClass();
968 		JMenu item = new JMenu(typeName(type));
969 		item.addMenuListener(new Fields(item, i));
970 		menu.add(item);
971 	    }
972 	    Class[] types;
973 	    try {
974 		types = lookup.getEntryClasses(tmpl);
975 	    } catch (Throwable t) {
976 		failure(t);
977 		return;
978 	    }
979 	    if (types == null) {
980 		if (attrs.length == 0)
981 		    addNone(menu);
982 		return;
983 	    }
984 	    List all = new LinkedList();
985 	    for (int i = 0, l = types.length; i < l; i++) {
986 		if (types[i] == null)
987 		    menu.add(new JMenuItem("null"));
988 		else
989 		    addType(types[i], all);
990 	    }
991 	}
992 
993 	private void addType(Class type, List all) {
994 	    if (all.contains(type))
995 		return;
996 	    all.add(type);
997 	    JCheckBoxMenuItem item =
998 		new JCheckBoxMenuItem(typeName(type), false);
999 	    item.addActionListener(wrap(new AttrSet(type)));
1000 	    menu.add(item);
1001 	    if (esuper.getState() &&
1002 		Entry.class.isAssignableFrom(type.getSuperclass()))
1003 		addType(type.getSuperclass(), all);
1004 	}
1005 
1006 	public void menuDeselected(MenuEvent ev) {
1007 	    menu.removeAll();
1008 	}
1009 
1010 	public void menuCanceled(MenuEvent ev) {
1011 	    menu.removeAll();
1012 	}
1013     }
1014 
1015     private class AttrSet implements ActionListener {
1016 	private final Class type;
1017 
1018 	public AttrSet(Class type) {
1019 	    this.type = type;
1020 	}
1021 
1022 	public void actionPerformed(ActionEvent ev) {
1023 	    Entry ent;
1024 	    try {
1025 		ent = (Entry)type.newInstance();
1026 	    } catch (Throwable t) {
1027 		logger.log(Level.INFO, "creating entry failed", t);
1028 		return;
1029 	    }
1030 	    int z = tmpl.attributeSetTemplates.length;
1031 	    Entry[] newSets = new Entry[z + 1];
1032 	    System.arraycopy(tmpl.attributeSetTemplates, 0, newSets, 0, z);
1033 	    newSets[z] = ent;
1034 	    tmpl.attributeSetTemplates = newSets;
1035 	    update();
1036 	}
1037     }
1038 
1039     private class Fields implements MenuListener {
1040 	private final JMenu menu;
1041 	private final int index;
1042 
1043 	public Fields(JMenu menu, int index) {
1044 	    this.menu = menu;
1045 	    this.index = index;
1046 	}
1047 
1048 	public void menuSelected(MenuEvent ev) {
1049 	    JRadioButtonMenuItem match = new JRadioButtonMenuItem("(match)");
1050 	    match.setSelected(true);
1051 	    match.addActionListener(wrap(new Unmatch(index)));
1052 	    menu.add(match);
1053 	    Entry ent = tmpl.attributeSetTemplates[index];
1054 	    Field[] fields = ent.getClass().getFields();
1055 	    for (int i = 0, l = fields.length; i < l; i++) {
1056 		Field field = fields[i];
1057 		if (!valid(field))
1058 		    continue;
1059 		try {
1060 		    if (field.get(ent) != null) {
1061 			JCheckBoxMenuItem item =
1062 			    new JCheckBoxMenuItem(field.getName(), true);
1063 			item.addActionListener(
1064 					wrap(new Value(index, field, null)));
1065 			menu.add(item);
1066 		    } else {
1067 			JMenu item = new JMenu(field.getName());
1068 			item.addMenuListener(
1069 					wrap(new Values(item, index, field)));
1070 			menu.add(item);
1071 		    }
1072 		} catch (Throwable t) {
1073 		    logger.log(Level.INFO, "getting fields failed", t);
1074 		}
1075 	    }
1076 	}
1077 
1078 	public void menuDeselected(MenuEvent ev) {
1079 	    menu.removeAll();
1080 	}
1081 
1082 	public void menuCanceled(MenuEvent ev) {
1083 	    menu.removeAll();
1084 	}
1085     }
1086 
1087     private class Unmatch implements ActionListener {
1088 	private final int index;
1089 
1090 	public Unmatch(int index) {
1091 	    this.index = index;
1092 	}
1093 
1094 	public void actionPerformed(ActionEvent ev) {
1095 	    int z = tmpl.attributeSetTemplates.length;
1096 	    Entry[] newSets = new Entry[z - 1];
1097 	    System.arraycopy(tmpl.attributeSetTemplates, 0,
1098 			     newSets, 0, index);
1099 	    System.arraycopy(tmpl.attributeSetTemplates, index + 1,
1100 			     newSets, index, z - index - 1);
1101 	    tmpl.attributeSetTemplates = newSets;
1102 	    update();
1103 	}
1104     }
1105 
1106     private class Values implements MenuListener {
1107 	private final JMenu menu;
1108 	private final int index;
1109 	private final Field field;
1110 
1111 	public Values(JMenu menu, int index, Field field) {
1112 	    this.menu = menu;
1113 	    this.index = index;
1114 	    this.field = field;
1115 	}
1116 
1117 	public void menuSelected(MenuEvent ev) {
1118 	    SafeServiceRegistrar lookup;
1119 	    synchronized (Browser.this){
1120 		lookup = Browser.this.lookup;
1121 	    }
1122 	    Object[] values;
1123 	    try {
1124 		values = lookup.getFieldValues(tmpl, index, field.getName());
1125 	    } catch (Throwable t) {
1126 		failure(t);
1127 		return;
1128 	    }
1129 	    if (values == null) {
1130 		addNone(menu);
1131 		return;
1132 	    }
1133 	    for (int i = 0; i < values.length; i++) {
1134 		JMenuItem item = new JMenuItem(values[i].toString());
1135 		item.addActionListener(
1136 				   wrap(new Value(index, field, values[i])));
1137 		menu.add(item);
1138 	    }
1139 	}
1140 
1141 	public void menuDeselected(MenuEvent ev) {
1142 	    menu.removeAll();
1143 	}
1144 
1145 	public void menuCanceled(MenuEvent ev) {
1146 	    menu.removeAll();
1147 	}
1148     }
1149 
1150     private class Value implements ActionListener {
1151 	private final int index;
1152 	private final Field field;
1153 	private final Object value;
1154 
1155 	public Value(int index, Field field, Object value) {
1156 	    this.index = index;
1157 	    this.field = field;
1158 	    this.value = value;
1159 	}
1160 
1161 	public void actionPerformed(ActionEvent ev) {
1162 	    try {
1163 		field.set(tmpl.attributeSetTemplates[index], value);
1164 	    } catch (Throwable t) {
1165 		logger.log(Level.INFO, "setting attribute value failed", t);
1166 	    }
1167 	    update();
1168 	}
1169     }
1170 
1171     private class Listener implements RemoteEventListener, ServerProxyTrust, 
1172 	    Startable, Serializable 
1173     {
1174 	private final Exporter exporter;
1175 	RemoteEventListener proxy;
1176 
1177 	public Listener(Exporter exporter) {
1178 	    this.exporter = exporter;
1179 	}
1180 
1181 	public void notify(final RemoteEvent ev) {
1182 	    SwingUtilities.invokeLater(wrap(new Runnable() {
1183 		public void run() {
1184 		    synchronized (Browser.this){
1185 			if (eventID == ev.getID() &&
1186 			    seqNo < ev.getSequenceNumber() &&
1187 			    eventSource != null &&
1188 			    eventSource.equals(ev.getSource()))
1189 			{
1190 			    seqNo = ev.getSequenceNumber();
1191 			    setText(false);
1192 			}
1193 		    }
1194 		}
1195 	    }));
1196 	}
1197 
1198 	public synchronized TrustVerifier getProxyVerifier() {
1199 	    return new BasicProxyTrustVerifier(proxy);
1200 	}
1201 
1202 	void unexport() {
1203 	    exporter.unexport(true);
1204 	}
1205 
1206 	@Override
1207 	public synchronized void start() throws Exception {
1208 	    proxy = (RemoteEventListener) exporter.export(this);
1209 	}
1210 	
1211 	private synchronized Object writeReplace() throws ObjectStreamException {
1212 	    return proxy;
1213 	}
1214     }
1215 
1216     private class LookupListener implements DiscoveryListener {
1217 
1218 	public void discovered(DiscoveryEvent e) {
1219 	    final ServiceRegistrar[] newregs = e.getRegistrars();
1220 	    SwingUtilities.invokeLater(wrap(new Runnable() {
1221 		public void run() {
1222 		    for (int i = 0, l = newregs.length; i < l; i++) {
1223 			addOne(newregs[i]);
1224 		    }
1225 		    synchronized (Browser.this){
1226 			if (lookup == null)
1227 			    setText(false);
1228 		    }
1229 		}
1230 	    }));
1231 	}
1232 
1233 	public void discarded(DiscoveryEvent e) {
1234 	    final ServiceRegistrar[] regs = e.getRegistrars();
1235 	    SwingUtilities.invokeLater(wrap(new Runnable() {
1236 		public void run() {
1237 		    for (int i = 0, l = regs.length; i < l; i++) {
1238 			ServiceID id = regs[i].getServiceID();
1239 			synchronized (Browser.this){
1240 			    if (lookup != null &&
1241 				id.equals(lookup.getServiceID()))
1242 			    {
1243 				lookup = null;
1244 				seqNo = Long.MAX_VALUE;
1245 			    }
1246 			}
1247 			for (int j = 0;
1248 			     j < registrars.getMenuComponentCount();
1249 			     j++)
1250 			{
1251 			    JMenuItem item =
1252 				(JMenuItem) registrars.getMenuComponent(j);
1253 			    if (item instanceof RegistrarMenuItem &&
1254 				id.equals(((RegistrarMenuItem) item).id))
1255 			    {
1256 				item.setSelected(false);
1257 				registrars.remove(item);
1258 				if (registrars.getMenuComponentCount() == 0)
1259 				    addNone(registrars);
1260 				break;
1261 			    }
1262 			}
1263 		    }
1264 		    synchronized (Browser.this){
1265 			if (lookup == null)
1266 			    resetTmpl();
1267 		    }
1268 		}
1269 	    }));
1270 	}
1271     }
1272 
1273     private void setGroups(String[] groups) {
1274 	((DiscoveryLocatorManagement) disco).setLocators(new LookupLocator[0]);
1275 	try {
1276 	    disco.setGroups(groups);
1277 	} catch (Throwable t) {
1278 	    logger.log(Level.INFO, "setting groups failed", t);
1279 	}
1280 	resetTmpl();
1281     }
1282 
1283     private class AllFind implements ActionListener {
1284 
1285 	public void actionPerformed(ActionEvent ev) {
1286 	    setGroups(null);
1287 	}
1288     }
1289 
1290     private class PubFind implements ActionListener {
1291 
1292 	public void actionPerformed(ActionEvent ev) {
1293 	    setGroups(new String[]{""});
1294 	}
1295     }
1296 
1297     private class MultiFind implements ActionListener {
1298 
1299 	public void actionPerformed(ActionEvent ev) {
1300 	    String names = JOptionPane.showInputDialog(Browser.this,
1301 						       "Enter group names");
1302 	    if (names == null)
1303 		return;
1304 	    setGroups(parseList(names, true));
1305 	}
1306     }
1307 
1308     private class UniFind implements ActionListener {
1309 
1310 	public void actionPerformed(ActionEvent ev) {
1311 	    String list =
1312 		JOptionPane.showInputDialog(Browser.this,
1313 					    "Enter host[:port] addresses");
1314 	    if (list == null)
1315 		return;
1316 	    String[] addrs = parseList(list, false);
1317 	    LookupLocator[] locs = new LookupLocator[addrs.length];
1318 	    for (int i = 0; i < addrs.length; i++) {
1319 		try {
1320 		    locs[i] = new ConstrainableLookupLocator(
1321 				    "jini://" + addrs[i], locatorConstraints);
1322 		} catch (MalformedURLException e) {
1323 		    JOptionPane.showMessageDialog(Browser.this,
1324 						  "\"" + addrs[i] + "\": " +
1325 						  e.getMessage(),
1326 						  "Bad Address",
1327 						  JOptionPane.ERROR_MESSAGE);
1328 		    return;
1329 		}
1330 	    }
1331 	    try {
1332 		disco.setGroups(new String[0]);
1333 	    } catch (Throwable t) {
1334 		logger.log(Levels.HANDLED, "setting groups failed", t);
1335 	    }
1336 	    ((DiscoveryLocatorManagement) disco).setLocators(locs);
1337 	    resetTmpl();
1338 	}
1339     }
1340 
1341     private class Lookup implements ActionListener {
1342 	private final SafeServiceRegistrar registrar;
1343 
1344 	public Lookup(SafeServiceRegistrar registrar) {
1345 	    this.registrar = registrar;
1346 	}
1347 
1348 	public void actionPerformed(ActionEvent ev) {
1349 	    synchronized (Browser.this){
1350 		if (lookup == registrar) {
1351 		    lookup = null;
1352 		} else {
1353 		    lookup = registrar;
1354 		}
1355 		seqNo = Long.MAX_VALUE;
1356 	    }
1357 	    for (int i = 0; i < registrars.getMenuComponentCount(); i++) {
1358 		JMenuItem item = (JMenuItem)registrars.getMenuComponent(i);
1359 		if (item != ev.getSource())
1360 		    item.setSelected(false);
1361 	    }
1362 	    resetTmpl();
1363 	}
1364     }
1365 
1366     static class LeaseNotify implements LeaseListener {
1367 	public void notify(LeaseRenewalEvent ev) {
1368 	    if (ev.getException() != null)
1369 		logger.log(Level.INFO, "lease renewal failed",
1370 			   ev.getException());
1371 	    else
1372 		logger.log(Level.INFO, "lease renewal failed");
1373 	}
1374     }
1375 
1376     private static String[] parseList(String names, boolean groups) {
1377 	StringTokenizer st = new StringTokenizer(names, " \t\n\r\f,");
1378 	String[] elts = new String[st.countTokens()];
1379 	for (int i = 0; st.hasMoreTokens(); i++) {
1380 	    elts[i] = st.nextToken();
1381 	    if (groups && elts[i].equalsIgnoreCase("public"))
1382 		elts[i] = "";
1383 	}
1384 	return elts;
1385     }
1386 
1387     private void cancelLease() {
1388 	Lease lease = null;
1389 	synchronized (Browser.this){
1390 	    if (elease != null){
1391 		lease = elease;
1392 		elease = null;
1393 		eventSource = null;
1394 	    }
1395 	}
1396 	if (lease != null) {
1397 	    try {
1398 		leaseMgr.cancel(lease);
1399 	    } catch (Throwable t) {
1400 		logger.log(Levels.HANDLED, "lease cancellation failed", t);
1401 	    }
1402 	}
1403     }
1404 
1405     private void update() {
1406 	setText(false);
1407 	cancelLease();
1408 	synchronized (Browser.this){
1409 	    if (lookup == null)
1410 		return;
1411 	}
1412 	try {
1413 	    EventRegistration reg =
1414 		lookup.notiFy(tmpl,
1415 			      ServiceRegistrar.TRANSITION_MATCH_NOMATCH |
1416 			      ServiceRegistrar.TRANSITION_NOMATCH_MATCH |
1417 			      ServiceRegistrar.TRANSITION_MATCH_MATCH,
1418 			      listen, null, Lease.ANY);
1419 	    Lease lease = (Lease) leasePreparer.prepareProxy(reg.getLease());
1420 	    leaseMgr.renewUntil(lease, Lease.ANY, lnotify);
1421 	    synchronized(Browser.this){
1422 		elease = lease;
1423 		eventSource = reg.getSource();
1424 		eventID = reg.getID();
1425 		seqNo = reg.getSequenceNumber();
1426 	    }
1427 	} catch (Throwable t) {
1428 	    failure(t);
1429 	}
1430     }
1431 
1432     private void failure(Throwable t) {
1433 	logger.log(Level.INFO, "call to lookup service failed", t);
1434 	SafeServiceRegistrar lookup;
1435 	    synchronized (Browser.this){
1436 		lookup = Browser.this.lookup;
1437 	    }
1438 	((DiscoveryManagement) disco).discard(lookup);
1439     }
1440 
1441     class ServiceItemRenderer implements ListCellRenderer {
1442         private JLabel label;
1443 
1444         public ServiceItemRenderer() {
1445 	    label = new JLabel();
1446 	    label.setOpaque(true);
1447 	}
1448     
1449         public Component getListCellRendererComponent(JList list,
1450 						      Object value,
1451 						      int index,
1452 						      boolean isSelected,
1453 						      boolean cellHasFocus){
1454 	    ServiceListItem item = null;
1455 	    if(value instanceof ServiceListItem)
1456 	        item = (ServiceListItem) value;
1457 
1458 	    label.setFont(list.getFont());
1459 
1460 	    if(isSelected){
1461 	        label.setBackground(list.getSelectionBackground());
1462 		label.setForeground(list.getSelectionForeground());
1463 	    } else {
1464 	        label.setBackground(list.getBackground());
1465 		label.setForeground(list.getForeground());
1466 	    }
1467 	    if(item != null){
1468 		// accessible check done in this method
1469 	        label.setIcon(item.getIcon());
1470 	        label.setText(item.getTitle());
1471 	    } else
1472 	        label.setText(value.toString());
1473 
1474 	    return label;
1475 	}
1476     }
1477 
1478     private static Icon[] icons = new Icon[3];
1479     static {
1480 	// Administrable Service, Controllable Attribute
1481         icons[0] = MetalIcons.getBlueFolderIcon();
1482 	// Non-administrable Service
1483 	icons[1] = MetalIcons.getGrayFolderIcon();
1484 	// "Connection Refused" Service
1485 	icons[2] = MetalIcons.getUnusableFolderIcon();
1486     }
1487 
1488     private class ServiceListItem {
1489         private final ServiceItem item;
1490         private boolean isAccessible;
1491 	private Object admin = null;
1492 
1493         public ServiceListItem(ServiceItem item) {
1494 	    this.item = item;
1495 	    isAccessible = (item.service != null);
1496 	}
1497 
1498         public String getTitle() {
1499 	    if(item.service == null)
1500 	        return "Unknown service";
1501 
1502 	    HashSet set = new HashSet();
1503 	    Class[] infs = getInterfaces(item.service.getClass());
1504 	    for(int j = 0, l = infs.length; j < l; j++)
1505 	        set.add(infs[j].getName());
1506 
1507 	    // remove known interfaces
1508 	    set.removeAll(ignoreInterfaces);
1509 
1510 	    String title;
1511 	    if(set.size() == 1) {
1512 	        Iterator iter = set.iterator();
1513 	        title = (String) iter.next();
1514 	    } else {
1515 	        title = item.service.getClass().getName();
1516 		title += " [";
1517 		for (Iterator iter = set.iterator(); iter.hasNext();) {
1518 		    title += (String) iter.next();
1519 		    if (iter.hasNext())
1520 			title += ", ";
1521 		}
1522 		title += "]";
1523 	    }
1524 	    if(! isAccessible)
1525 	        title += " (Stale service)";
1526 	    return title;
1527 	}
1528       
1529         public boolean isAccessible() {
1530 	    getAdmin();
1531 	    return isAccessible;
1532 	}
1533 
1534 	public Object getAdmin() {
1535 	    if(admin == null &&
1536 	       isAccessible &&
1537 	       item.service instanceof Administrable)
1538 	    {
1539 		try {
1540 		    admin = adminPreparer.prepareProxy(
1541 				((Administrable) item.service).getAdmin());
1542 		} catch (Throwable t) {
1543 		    logger.log(Levels.HANDLED, "failed to get admin proxy", t);
1544 		    isAccessible = false;
1545 		}
1546 	    }
1547 	    return admin;
1548 	}
1549 
1550 	public boolean isAdministrable() {
1551 	    getAdmin();
1552 	    return (admin instanceof DestroyAdmin ||
1553 		    admin instanceof JoinAdmin ||
1554 		    admin instanceof DiscoveryAdmin);
1555 	}
1556 
1557 	public boolean isSpaceBrowsable() {
1558 	    return (item.service instanceof JavaSpace05 ||
1559 		    (item.service instanceof JavaSpace &&
1560 		     getAdmin() instanceof JavaSpaceAdmin));
1561 	}
1562 
1563 	private boolean isUI() {
1564 	    Entry[] attrs = item.attributeSets;
1565 	    if ((attrs != null) && (attrs.length != 0)) {
1566 		for (int i = 0, l = attrs.length; i < l; i++) {
1567 		    if (attrs[i] instanceof UIDescriptor) {
1568 			return true;
1569 		    }
1570 		}
1571 	    }
1572 
1573 	    return false;
1574 	}
1575 
1576         public ServiceItem getServiceItem() {
1577 	    return item;
1578 	}
1579 
1580         public Entry[] getAttributes() {
1581 	    return item.attributeSets;
1582 	}
1583 
1584         public Icon getIcon() {
1585 	    if(! isAccessible())
1586 		return icons[2];
1587 	    else if(isAdministrable())
1588 		return icons[0];
1589 	    else
1590 		return icons[1];
1591 	}
1592 
1593 	@Override
1594         public String toString() {
1595 	    return isAccessible() ?
1596 		item.service.getClass().getName() : "Unknown service";
1597 	}
1598     }
1599 
1600     private class MouseReceiver extends MouseAdapter {
1601         private final ServiceListPopup popup;
1602 
1603         public MouseReceiver(ServiceListPopup popup) {
1604 	  this.popup = popup;
1605 	}
1606 
1607 	@Override
1608         public void mouseClicked(MouseEvent ev) {
1609 	    if(ev.getClickCount() >= 2){
1610 		ServiceListItem listItem = getTargetListItem(ev);
1611 		if(listItem != null) {
1612 		    SafeServiceRegistrar lookup;
1613 		    synchronized (Browser.this){
1614 			lookup = Browser.this.lookup;
1615 		    }
1616 		    ServiceItem item = listItem.getServiceItem();
1617 		    if(listItem.isAdministrable())
1618 			new ServiceEditor(item, listItem.getAdmin(), lookup,
1619 					  Browser.this).setVisible(true);
1620 		    else if(listItem.isAccessible())
1621 			new ServiceBrowser(item, lookup,
1622 					   Browser.this).setVisible(true);
1623 		}
1624 	    }
1625 	}
1626 
1627 	@Override
1628         public void mouseReleased(MouseEvent ev) {
1629 	    if(ev.isPopupTrigger() && (getTargetListItem(ev) != null)) {
1630 	        popup.setServiceItem(getTargetListItem(ev));
1631 	        popup.show(ev.getComponent(), ev.getX(), ev.getY());
1632 	    }
1633 	}
1634 
1635 	@Override
1636         public void mousePressed(MouseEvent ev) {
1637 	    if(ev.isPopupTrigger() && (getTargetListItem(ev) != null)) {
1638 	        popup.setServiceItem(getTargetListItem(ev));
1639 	        popup.show(ev.getComponent(), ev.getX(), ev.getY());
1640 	    }
1641 	}
1642 
1643         private ServiceListItem getTargetListItem(MouseEvent ev) {
1644 	    int index = list.locationToIndex(ev.getPoint());
1645 	    if(index >= 0)
1646 	        return (ServiceListItem) listModel.getElementAt(index);
1647 	    else
1648 	        return null;
1649 	}
1650     }
1651 
1652     private class ServiceListPopup
1653 			extends JPopupMenu
1654 			implements ActionListener, PopupMenuListener
1655     {
1656         protected JMenuItem infoItem;
1657         protected JMenuItem browseItem;
1658         protected JMenuItem adminItem;
1659         protected JMenuItem spaceItem;
1660 	protected ServiceListItem listItem;
1661 	protected JMenuItem uiItem;
1662         protected ServiceItem item;
1663 
1664         public ServiceListPopup() {
1665 	    super();
1666 
1667 	    ActionListener me = wrap(this);
1668 	    infoItem = new JMenuItem("Show Info");
1669 	    infoItem.addActionListener(me);
1670 	    infoItem.setActionCommand("showInfo");
1671 	    add(infoItem);
1672 
1673 	    browseItem = new JMenuItem("Browse Service");
1674 	    browseItem.addActionListener(me);
1675 	    browseItem.setActionCommand("browseService");
1676 	    add(browseItem);
1677 
1678 	    adminItem = new JMenuItem("Admin Service");
1679 	    adminItem.addActionListener(me);
1680 	    adminItem.setActionCommand("adminService");
1681 	    add(adminItem);
1682 
1683 	    spaceItem = new JMenuItem("Browse Entries");
1684 	    spaceItem.addActionListener(me);
1685 	    spaceItem.setActionCommand("browseEntry");
1686 	    add(spaceItem);
1687 
1688 	    uiItem = new JMenuItem("Show UI");
1689 	    uiItem.addActionListener(me);
1690 	    uiItem.setActionCommand("showUI");
1691 	    add(uiItem);
1692 
1693 	    addPopupMenuListener(this);
1694 	    setOpaque(true);
1695 	    setLightWeightPopupEnabled(true);
1696 	}
1697 
1698         public void setServiceItem(ServiceListItem listItem) {
1699 	    this.listItem = listItem;
1700 	    item = listItem.getServiceItem();
1701 	    infoItem.setEnabled(listItem.isAccessible());
1702 	    browseItem.setEnabled(listItem.isAccessible());
1703 	    adminItem.setEnabled(listItem.isAdministrable());
1704 	    spaceItem.setEnabled(listItem.isSpaceBrowsable());
1705 	    uiItem.setEnabled(listItem.isUI());
1706 	}
1707 
1708         public void actionPerformed(ActionEvent ev) {
1709 	    String command = ev.getActionCommand();
1710 	    SafeServiceRegistrar lookup;
1711 	    synchronized (Browser.this){
1712 		lookup = Browser.this.lookup;
1713 	    }
1714 	    if(command.equals("showInfo")){
1715 	        Class[] infs = getInterfaces(item.service.getClass());
1716 		String[] msg = new String[3 + infs.length];
1717 		msg[0] = "ServiceID: " + item.serviceID;
1718 		msg[1] = ("Service Instance: " +
1719 			  item.service.getClass().getName());
1720 		if(infs.length == 1)
1721 		    msg[2] = "Implemented Interface:";
1722 		else
1723 		    msg[2] = "Implemented Interfaces:";
1724 		for(int i = 0; i < infs.length; i++)
1725 		    msg[3 + i] = infs[i].getName();
1726 
1727 		JOptionPane.showMessageDialog(Browser.this,
1728 					      msg,
1729 					      "ServiceItem Information",
1730 					      JOptionPane.INFORMATION_MESSAGE);
1731 	    } else if(command.equals("browseService")){
1732 	        new ServiceBrowser(item, lookup,
1733 				   Browser.this).setVisible(true);
1734 	    } else if(command.equals("adminService")){
1735 	        new ServiceEditor(item, listItem.getAdmin(),
1736 				  lookup, Browser.this).setVisible(true);
1737 	    } else if(command.equals("browseEntry")){
1738 		new SpaceBrowser(item.service instanceof JavaSpace05 ?
1739 				 item.service : listItem.getAdmin(),
1740 				 Browser.this).setVisible(true);
1741 	    }
1742 	    else if(command.equals("showUI")){
1743 		UIDescriptor uiDescriptor = getSelectedUIDescriptor();
1744 
1745 		if (uiDescriptor == null) {
1746 		    return;
1747 		}
1748 
1749 		try {
1750 		    JFrameFactory uiFactory = (JFrameFactory)
1751 			uiDescriptor.getUIFactory(
1752 			    Thread.currentThread().getContextClassLoader());
1753 		    JFrame frame = uiFactory.getJFrame(item);
1754 
1755 		    frame.validate();
1756 		    frame.setVisible(true);
1757 		}
1758 		catch (Exception e) {
1759 		    logger.log(Level.INFO, "show ui failed", e);
1760 		    JOptionPane.showMessageDialog(Browser.this,
1761 						  e.getMessage(),
1762 						  e.getClass().getName(),
1763 						  JOptionPane.WARNING_MESSAGE);
1764 		}
1765 	    }
1766 	}
1767 
1768         public void popupMenuWillBecomeVisible(PopupMenuEvent ev) {
1769 	}
1770 
1771         public void popupMenuWillBecomeInvisible(PopupMenuEvent ev) {
1772 	}
1773 
1774         public void popupMenuCanceled(PopupMenuEvent ev) {
1775 	}
1776 
1777         private UIDescriptor getSelectedUIDescriptor() {
1778 
1779 	    if (!(new ServiceListItem(item)).isUI()) {
1780 		return null;
1781 	    }
1782 
1783 	    Entry[] attrs = item.attributeSets;
1784 	    if ((attrs != null) && (attrs.length != 0)) {
1785 		for (int i = 0, l = attrs.length; i < l; i++) {
1786 		    if (attrs[i] instanceof UIDescriptor) {
1787 			UIDescriptor desc = (UIDescriptor) attrs[i];
1788 			if (!"javax.swing".equals(desc.toolkit)) {
1789 				continue;
1790 			}
1791 			return desc;
1792 		    }
1793 		}
1794 	    }
1795 	    return null;
1796 	}
1797     }
1798 
1799     private class Exiter extends WindowAdapter {
1800 
1801 	@Override
1802 	public void windowClosing(WindowEvent e) {
1803 	    Browser.this.exiter.actionPerformed(
1804 		new ActionEvent(e.getSource(), e.getID(),
1805 				"Exit", System.currentTimeMillis(), 0));
1806 	}
1807     }
1808 
1809     /**
1810      * An action listener that cancels any lookup service event registration
1811      * lease and then calls {@link System#exit System.exit}.
1812      */
1813     public static class Exit implements ActionListener {
1814 
1815 	/**
1816 	 * Cancels any lookup service event registration lease and
1817 	 * calls {@link System#exit System.exit}<code>(0)</code>.
1818 	 * @param ev
1819 	 */
1820 	public void actionPerformed(ActionEvent ev) {
1821 	    Object src = ev.getSource();
1822 	    while (!(src instanceof Browser) && src instanceof Component) {
1823 		if (src instanceof JPopupMenu)
1824 		    src = ((JPopupMenu) src).getInvoker();
1825 		else
1826 		    src = ((Component) src).getParent();
1827 	    }
1828 	    if (src instanceof Browser) {
1829 		((Browser) src).cancelLease();
1830 	    }
1831 	    System.exit(0);
1832 	}
1833     }
1834 
1835     /**
1836      * Runs the service browser. See the package documentation for details.
1837      *
1838      * @param args command line arguments
1839      */
1840     public static void main (String[] args) {
1841 	if (System.getSecurityManager() == null)
1842 	    System.setSecurityManager(new SecurityManager());
1843 	try {
1844 	    final Configuration config =
1845 		ConfigurationProvider.getInstance(
1846 					args, Browser.class.getClassLoader());
1847 	    LoginContext login =
1848 		(LoginContext) config.getEntry(BROWSER, "loginContext",
1849 					       LoginContext.class, null);
1850 	    if (login != null) {
1851 		login.login();
1852 	    }
1853 	    PrivilegedExceptionAction action =
1854 		new PrivilegedExceptionAction() {
1855 		    public Object run() throws Exception {
1856 			Browser b = new Browser(config);
1857 			b.start();
1858 			return b;
1859 		    }
1860 		};
1861 	    if (login != null) {
1862 		Subject.doAsPrivileged(login.getSubject(), action, null);
1863 	    } else {
1864 		action.run();
1865 	    }
1866 	} catch (Throwable t) {
1867 	    if (t instanceof PrivilegedActionException)
1868 		t = t.getCause();
1869 	    logger.log(Level.SEVERE, "browser initialization failed", t);
1870 	}
1871     }
1872 }