/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
*/
package org.opensolaris.os.vp.client.swing;
import java.awt.event.*;
import java.awt.Window;
import java.net.URL;
import java.security.*;
import java.util.*;
import java.util.logging.*;
import javax.help.*;
import javax.swing.*;
import org.opensolaris.os.vp.client.common.*;
import org.opensolaris.os.vp.panel.common.*;
import org.opensolaris.os.vp.panel.common.action.*;
import org.opensolaris.os.vp.panel.common.control.*;
import org.opensolaris.os.vp.panel.common.control.InvalidParameterException;
import org.opensolaris.os.vp.panel.common.view.*;
import org.opensolaris.os.vp.panel.swing.control.SwingNavigator;
@SuppressWarnings({"serial"})
public class AppInstance implements ClientContext, ConnectionListener {
static {
// Force early load of runtime properties
AppProperties a = AppProperties.singleton;
// Set look and feel unless user has a preference
String gtk = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
if (System.getProperty("swing.defaultlaf") == null) {
String lafName = UIManager.getSystemLookAndFeelClassName();
// Refuse to be a victim of Solaris's Motif default
if (lafName.contains("MotifLookAndFeel"))
lafName = gtk;
try {
UIManager.setLookAndFeel(lafName);
} catch (Exception ignore) {
}
}
LookAndFeel laf = UIManager.getLookAndFeel();
// Handle Gnome-specific bugs
if (laf != null && laf.getClass().getName().equals(gtk)) {
// Gnome L&F doesn't set this correctly
UIManager.put("ToolTip.background", new JToolTip().getBackground());
// Gnome L&F is the only L&F that does this
UIManager.put("Slider.paintValue", Boolean.FALSE);
}
}
//
// Instance data
//
private App app;
private LoginHistory loginHistory;
private BusyIndicator busy;
private ConnectionInfo info;
private HelpBroker helpBroker;
private SwingNavigator navigator;
private Properties hints;
private AppLoginManager loginManager;
private ActionListener showHelpAction;
private ConnectionListeners cListeners = new ConnectionListeners();
//
// Constructors
//
private AppInstance(App app, Properties hints) {
this.app = app;
this.hints = hints;
createNavigator();
createHelp();
loginManager = new AppLoginManager(app.getConnectionManager(),
new HasWindow() {
@Override
public Window getComponent() {
return SwingNavigator.getLastWindow(getNavigator());
}
});
// Initialize login history.
loginHistory = AppLoginHistory.getInstance(
app.getConnectionManager());
}
public AppInstance(App app, Properties hints, ConnectionInfo info) {
this(app, hints);
setConnectionInfo(info);
app.instanceCreated(this);
}
public AppInstance(App app, Properties hints, LoginRequest request)
throws ActionAbortedException, ActionFailedException {
this(app, hints);
// Don't prompt user for acknowledgement for default local connection
ConnectionInfo current = new ConnectionInfo(RadLoginManager.LOCAL_HOST,
RadLoginManager.LOCAL_USER, null, null);
ConnectionInfo[] infos = loginManager.getConnectionInfo(
request, current);
if (infos[1] == null) {
setConnectionInfo(infos[0]);
} else {
setConnectionInfo(infos[1]);
app.getConnectionManager().add(infos[0], null);
}
app.instanceCreated(this);
}
//
// ConnectionListener methods
//
@Override
public void connectionChanged(ConnectionEvent event) {
// This method should only called by the ConnectionManager when a failed
// connection has been restored
assert info == event.getOldConnectionInfo();
setConnectionInfo(event.getConnectionInfo());
}
@Override
public void connectionFailed(ConnectionEvent event) {
cListeners.connectionFailed(
new ConnectionEvent(this, event.getConnectionInfo()));
}
//
// ClientContext methods
//
@Override
public void addConnectionListener(ConnectionListener listener) {
cListeners.add(listener);
}
@Override
public void closeInstance(final boolean isCancel)
throws ActionAbortedException {
// Using PrivilegedExceptionAction offers no compile-time checking of
// thrown Exceptions, so use this mildly awkward method instead
final ActionAbortedException[] err = new ActionAbortedException[1];
AccessController.doPrivileged(
new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
closeInstanceImp(isCancel);
} catch (ActionAbortedException e) {
err[0] = e;
}
return null;
}
});
if (err[0] != null) {
throw err[0];
}
}
@Override
public BusyIndicator getBusyIndicator() {
List<Control> controls = navigator.getPath();
for (int i = controls.size() - 1; i >= 0; i--) {
Control control = controls.get(i);
if (control instanceof HasBusyIndicator) {
BusyIndicator busy =
((HasBusyIndicator)control).getBusyIndicator();
if (busy != null) {
return busy;
}
}
}
if (busy == null) {
busy = new SimpleBusyIndicator();
}
return busy;
}
@Override
public ConnectionInfo getConnectionInfo() {
return info;
}
@Override
public HelpBroker getHelpBroker() {
return helpBroker;
}
@Override
public Navigator getNavigator() {
return navigator;
}
@Override
public Properties getRuntimeHints() {
return hints;
}
@Override
public ClientContext login(final LoginRequest request,
final boolean forceNewContext) throws ActionAbortedException,
ActionFailedException {
// Using PrivilegedExceptionAction offers no compile-time checking of
// thrown Exceptions, so use this mildly awkward method instead
final ActionAbortedException[] abort = new ActionAbortedException[1];
final ActionFailedException[] fail = new ActionFailedException[1];
ClientContext context = AccessController.doPrivileged(
new PrivilegedAction<ClientContext>() {
@Override
public ClientContext run() {
try {
return loginImp(request, forceNewContext);
} catch (ActionAbortedException e) {
abort[0] = e;
} catch (ActionFailedException e) {
fail[0] = e;
}
return null;
}
});
if (abort[0] != null) {
throw abort[0];
}
if (fail[0] != null) {
throw fail[0];
}
return context;
}
@Override
public boolean removeConnectionListener(ConnectionListener listener) {
return cListeners.remove(listener);
}
@Override
public void showHelp() {
Window window = navigator.getLastWindow();
if (window == null) {
// Absurdly, the Swing help viewer needs a non-null window to show,
// even if that window is not visible
window = new Window(null);
}
ActionEvent event = new ActionEvent(
window, ActionEvent.ACTION_PERFORMED, "showHelp");
showHelpAction.actionPerformed(event);
}
@Override
public LoginHistory getLoginHistory() {
return loginHistory;
}
//
// AppInstance methods
//
/**
* Closes this {@code AppInstance} unconditionally.
*/
public void close() {
ConnectionInfo info = getConnectionInfo();
if (info != null) {
app.getConnectionManager().remove(info, this);
}
app.instanceClosed(this);
}
public App getApp() {
return app;
}
/**
* Sets the current connection, notifying any registered {@link
* ConnectionListener}s.
*
* @param info
* a {@link ConnectionInfo}
*/
protected void setConnectionInfo(ConnectionInfo info) {
ConnectionInfo oldInfo = getConnectionInfo();
if (oldInfo != info) {
ConnectionManager connManager = app.getConnectionManager();
if (oldInfo != null) {
connManager.remove(oldInfo, this);
}
this.info = info;
connManager.add(info, this);
// Set and notify any listeners (ie, panels) of change
cListeners.connectionChanged(
new ConnectionEvent(this, info, oldInfo));
}
}
//
// Private methods
//
private void closeInstanceImp(boolean isCancel)
throws ActionAbortedException {
JDialog dialog = loginManager.getDialog();
if (dialog != null && dialog.isVisible()) {
throw new ActionAbortedException();
}
try {
// Unwind navigation stack to force user to handle pending changes
navigator.goToAsyncAndWait(isCancel, null);
} catch (NavigationAbortedException e) {
throw new ActionAbortedException(e);
} catch (InvalidAddressException e) {
// Should not be possible
} catch (InvalidParameterException e) {
// Should not be possible
} catch (EmptyNavigationStackException e) {
// Should not be possible
} catch (RootNavigableNotControlException e) {
// Should not be possible
}
close();
}
private void createHelp() {
String helpSetName =
getClass().getPackage().getName().replaceAll("\\.", "/") +
"/help/app";
HelpSet helpSet;
try {
ClassLoader loader = getClass().getClassLoader();
URL url = HelpSet.findHelpSet(loader, helpSetName);
helpSet = new HelpSet(loader, url);
} catch (HelpSetException e) {
String message = String.format(
"could not load helpset: %s", helpSetName);
Logger.getLogger(getClass().getName()).log(
Level.WARNING, message, e);
helpSet = new HelpSet();
}
helpBroker = helpSet.createHelpBroker();
showHelpAction = new CSH.DisplayHelpFromSource(helpBroker);
}
private void createNavigator() {
AppRootControl root = new AppRootControl(this);
try {
navigator = new SwingNavigator();
// Re-navigate to most appropriate path on error
navigator.addNavigationListener(new NavigationErrorHandler());
navigator.goToAsyncAndWait(false, null, root);
} catch (NavigationException unlikely) {
}
}
private ClientContext loginImp(LoginRequest request,
boolean forceNewContext) throws ActionAbortedException,
ActionFailedException {
if (request == null) {
StringLoginProperty host =
new StringLoginProperty(info.getHost(), false);
StringLoginProperty user =
new StringLoginProperty(info.getUser(), false);
StringLoginProperty role =
new StringLoginProperty(info.getRole(), false);
request = new LoginRequest(host, user, role);
}
ConnectionInfo[] infos = loginManager.getConnectionInfo(
request, this.info);
ConnectionInfo info;
ConnectionInfo userInfo = null;
if (infos[1] == null) {
info = infos[0];
} else {
info = infos[1];
userInfo = infos[0];
}
ClientContext context;
if (forceNewContext || (this.info != null &&
!this.info.matchesHost(info.getHost()))) {
context = new AppInstance(app, hints, info);
} else {
setConnectionInfo(info);
context = this;
}
if (userInfo != null) {
// Add user-based connection to ConnectionManager in case it isn't
// already there
app.getConnectionManager().add(userInfo, null);
}
return context;
}
}