Skip to content

Commit

Permalink
Many JavaMonitor bug fixes and improvements
Browse files Browse the repository at this point in the history
Fix scheduling bug causing "midnight" restarts to never happen.

Fix "Bounce" to guarantee that the instances it shuts down are configured to restart.

Keep showing app detail stats after refreshing the page

Add "Rolling Shutdown" bounce type (see comments in code for details)

Add ability to target a specific host for direct actions (start, stop, etc).

Add ability to skip Monitor login page if password is contained in the URL (?pw=).

Warn if entering an ill-advised statistics page password, i.e. not url-friendly.

Add commented-out WOHost property to wotaskd and JavaMonitor Properties for easier tweaking

Add login form for easily viewing the WOAdaptorInfo page

UI Improvements:
- Add title to login page: JavaMonitor for WebObjects
- Rename "Refresh Now" link to just "Refresh"
- Display better error message if bounce cannot be performed
- Add information on Help page about "Bounce"
- Add link to "Bounce" help information in context
- Change "Deaths" links to plain text with a "Details" link next to it
- Change "YES" link for available hosts to plain text with a "View Config" link next to it
  • Loading branch information
johnthuss committed May 10, 2012
1 parent c7e9f88 commit ed0a6f5
Show file tree
Hide file tree
Showing 27 changed files with 442 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,19 @@ <h1>Configuring Application &quot;<webobject name="AppName" />&quot;</h1>
</webobject>
</webobject>
</div>
<script type="text/javascript">
document.getElementById('appSettingsForm').onsubmit = function(event) {
var statsPassword = document.getElementById('statsPassword').value;
if (statsPassword && statsPassword.length != 0) {
var badChars = ['$', '&', '+', ',' ,'/' ,':', ';', '=', '?', '@', ' ', '<', '>', '#', '%'];
for (var i=0; i<badChars.length; i++) {
if (statsPassword.indexOf(badChars[i]) != -1) {
alert('WARNING: "Statistics Page Password" should not contain characters and have special meaning in URLs.');
break;
}
}
}
return true;
}
</script>
</webobject>
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ InstDefaultsForm: WOForm {

AppSettingsForm: WOForm {
multipleSubmit = true;
id = "appSettingsForm";
}

AppName: WOString {
Expand Down Expand Up @@ -344,6 +345,7 @@ SmtpHostConfigured: WOConditional {
StatisticsPasswordField: WOTextField {
size = "20";
value = appDefaults.statisticsPassword;
id = "statsPassword";
}

String1: WOString {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ <h2 class="SectionHeading">
<p>This page automatically updates every <webobject name = "RefreshRate"></webobject> seconds.</p>
</webobject>
<webobject name = "HasBouncer"><div>Bouncer: <webobject name = "Bouncer"/></div></webobject>
<p><webobject name = "RefreshLink">Refresh Now</webobject> |
<p><webobject name = "RefreshLink">Refresh</webobject> |
<webobject name = "ShowDetailStatistics">
<webobject name="ShowStatistics">Hide Details</webobject>
<webobject name="NotShowStatistics">Show Details</webobject>
<webobject name="ShowStatistics">Hide Stats</webobject>
<webobject name="NotShowStatistics">Show Stats</webobject>
</webobject></p>

<span class="error"><webobject name = "MessageString"></webobject>
Expand Down Expand Up @@ -132,8 +132,9 @@ <h2 class="SectionHeading">
</td>
<td>
<webobject name = "ShouldDisplayDeathDetailLink">
<span class="Deaths"><webobject name = "InstDeathCount"></webobject></span>
<webobject name = "InstDeathDetailLink">
<webobject name = "InstDeathCount"></webobject>
(Details)
</webobject>
</webobject>
<webobject name = "ShouldNOTDisplayDeathDetailLink">-</webobject>
Expand Down Expand Up @@ -222,7 +223,7 @@ <h2 class="SectionHeading">
</div>
</td>
<!-- Next Scheduled Shutdown -->
<td class="AllBounce"><webobject name = "BounceLink">Bounce</webobject></td>
<td class="AllBounce"><webobject name = "BounceLink">Bounce</webobject>&nbsp;<webobject name = "BounceHelpLink"><webobject name="HelpImage"/></webobject></td>
<td class="AllClearDeaths"><webobject name = "ClearDeathsLink">Clear Deaths</webobject></td>
<!-- Statistics All -->
<webobject name = "ShowStatistics">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,14 @@ HasBouncer: WOConditional {
Bouncer : WOString {
value = currentBouncer.status;
}
BounceHelpLink : WOHyperlink {
pageName = "HelpPage";
fragmentIdentifier = "18";
}
HelpImage : WOImage {
filename = "help.png";
style = "vertical-align: middle;";
}

Observer : AjaxObserveField {
action = selectOne;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,30 @@ <h2 class="SectionHeading">HTTP Adaptor URL</h2>
<p>URL to Adaptor: <webobject name="AdaptorURLField" /> <webobject name="SubmitAdaptorButton" /></p>
</webobject>
</div>

<div class="Box AdapterInfo">
<h2 class="SectionHeading">View site statistics and WOAdaptor information</h2>
<p class="AdaptorLoginHelp">The username and password are set in the WOAdaptor configuration file, typically
"/System/Library/WebObjects/Adaptors/Apache2.2/apache.conf"
using the directives WebObjectsAdminUsername and WebObjectsAdminPassword.</p>
<webobject name="AdaptorInfoForm">
<table class="FormFieldTable">
<tr>
<th>Username:</th>
<td><webobject name = "AdaptorInfoUsername"/></td>
</tr>
<tr>
<th>Password:</th>
<td><webobject name = "AdaptorInfoPassword"/></td>
</tr>
<tr>
<th></th>
<td><webobject name = "AdaptorInfoSubmit"/></td>
</tr>
</table>
</webobject>
</div>

<div class="Box AdaptorSettingsBox">
<webobject name="AdaptorSettingsAjaxExpansion">
<webobject name="AdaptorSettingsForm">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,20 @@ BackupButton: WOSubmitButton {
action = backupConfiguration;
value = "Backup Now";
}

AdaptorInfoForm: WOForm {
target = "_blank";
}

AdaptorInfoUsername: WOTextField {
value = adaptorInfoUsername;
}

AdaptorInfoPassword: WOTextField {
value = adaptorInfoPassword;
}

AdaptorInfoSubmit: WOSubmitButton {
action = adaptorInfoLoginClicked;
value = "Log In";
}
44 changes: 36 additions & 8 deletions Applications/JavaMonitor/Components/HelpPage.wo/HelpPage.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ <h1><a name="00">Help</a></h1>
<li><a href="#15"><b>What instance defaults and settings are available through Monitor?</b></a></li>
<li><a href="#16"><b>What application settings are available through Monitor?</b></a></li>
<li><a href="#17"><b>What is Instance Scheduling?</b></a></li>
<li><a href="#18"><b>Monitor's Hosts page</b></a></li>
<li><a href="#19"><b>Monitor's Detail View page</b></a></li>
<li><a href="#20"><b>Deployment FAQ</b></a></li>
<li><a href="#18"><b>What does "Bounce" do to an application?</b></a></li>
<li><a href="#19"><b>Monitor's Hosts page</b></a></li>
<li><a href="#20"><b>Monitor's Detail View page</b></a></li>
<li><a href="#21"><b>Deployment FAQ</b></a></li>
</ul>

<p>&nbsp;</p>
Expand Down Expand Up @@ -100,11 +101,12 @@ <h1><a name="00">Help</a></h1>
instance of that application chosen by the adaptor. Load-balancing takes
place between instances with the same name (cf <code>WOApplicationName</code>).
The default load-balancing
scheme is Random, where an instance is chosen arbitrarily,
scheme is Round Robin,
but other load-balancing schemes are available. Round Robin
distributes a request to instances one after another. Load Average
attempts to balance load to the instances by evenly
distributing the sessions between instances.</p>
distributing the sessions between instances.
If using the Random load-balancing scheme, an instance is chosen arbitrarily.</p>
</blockquote>

<p><br />
Expand Down Expand Up @@ -819,7 +821,33 @@ <h1><a name="00">Help</a></h1>
<br />
</p>

<p><a name="18"><font size="+2"><b>Monitor's Hosts page</b></font></a> <a href="#00"><font size="-1">(top)</font></a><br />
<p><a name="18"><font size="+2"><b>What does "Bounce" do to an application?</b></font></a> <a href="#00"><font size="-1">(top)</font></a><br />
</p>

<blockquote>
<p>"Bounce" restarts an application gracefully. It does so by starting at least one inactive instance
per active host (or 10 % of the total active instance count), waiting
until they have started, then refusing sessions for all old instances and
turning scheduling on for all but the number of instances we started
originally. The next effect should be that the new users get the new app,
old instances die in due time and then restart when the sessions stop.<br/>
You must have at least one inactive instance in order to perform a graceful bounce.<br/>
You may or may not need to set ERKillTimer to prevent totally
long-running sessions to keep the app from dying.
</p>
</blockquote>

<p><br />
<br />
</p>

<hr>

<p><br />
<br />
</p>

<p><a name="19"><font size="+2"><b>Monitor's Hosts page</b></font></a> <a href="#00"><font size="-1">(top)</font></a><br />
</p>

<blockquote>
Expand Down Expand Up @@ -850,7 +878,7 @@ <h1><a name="00">Help</a></h1>
<br />
</p>

<p><a name="19"><font size="+2"><b>Monitor's Detail View page</b></font></a> <a href="#00"><font size="-1">(top)</font></a><br />
<p><a name="20"><font size="+2"><b>Monitor's Detail View page</b></font></a> <a href="#00"><font size="-1">(top)</font></a><br />
</p>

<blockquote>
Expand Down Expand Up @@ -964,7 +992,7 @@ <h1><a name="00">Help</a></h1>
<br />
</p>

<p><a name="20"><font size="+2"><b>Deployment FAQ</b></font></a> <a href="#00"><font size="-1">(top)</font></a><br />
<p><a name="21"><font size="+2"><b>Deployment FAQ</b></font></a> <a href="#00"><font size="-1">(top)</font></a><br />
</p>

<blockquote>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ <h2 class="SectionHeading">Hosts</h2>
</td>
<td class="HostAvailability">
<webobject name = "HostIsAvailable">
<webobject name = "wotaskdLink">YES</webobject>
YES | <webobject name = "wotaskdLink">View Config</webobject>
</webobject>
<webobject name = "HostIsNOTAvailable">
<span class="HostNotAvailable">NO</span>
Expand Down
2 changes: 1 addition & 1 deletion Applications/JavaMonitor/Components/Main.wo/Main.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<webobject name = "ERXStyleSheet"/>
</head>
<body id ="LoginPage" onload="document.getElementById('password').focus()">

<h1 style="text-align: center;">JavaMonitor for WebObjects</h1>
<webobject name = "HasLoginError">
<p class="Box ErrorBox LoginErrorBox"><webobject name="ErrorMsg" /></p>
</webobject>
Expand Down
2 changes: 2 additions & 0 deletions Applications/JavaMonitor/Resources/Properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Properties file for JavaMonitor
# Overrides the JavaWebObjects.framework Properties

#WOHost=localhost

WOAutoOpenInBrowser=true

WODebuggingEnabled=false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,11 @@
* </tr>
* <tr>
* <td>bouncetype</td>
* <td>graceful | shutdown</td>
* <td>graceful | shutdown | rolling</td>
* <td>graceful bounces the application by starting a few instances per host and setting the rest to refusing sessions<br />
* shutdown bounces the application by stopping all instances and then restarting them (use this if your<br />
* application will migrate the database so the old application will crash)<br />
* rolling will start a few instances per host, then forcefully restart the existing instances one at a time<br/>
* The default bouncetype is graceful.</td>
* </tr>
* <tr>
Expand Down Expand Up @@ -433,6 +434,8 @@ public WOActionResults bounceAction() {
}
}
applicationsPage().bounceShutdown(applications, maxwait);
} else if (bouncetype.equalsIgnoreCase("rolling")) {
applicationsPage().bounceRolling(applications);
} else {
woresponse.setContent("Unknown bouncetype");
woresponse.setStatus(406);
Expand Down Expand Up @@ -480,10 +483,10 @@ public void startAction() {
applicationsPage().start(instances);
}

protected void prepareApplications(NSArray nsarray) {
if (nsarray == null)
protected void prepareApplications(NSArray<String> appNames) {
if (appNames == null)
throw new DirectActionException("at least one application name needs to be specified for type app", 406);
for (Enumeration enumeration = nsarray.objectEnumerator(); enumeration.hasMoreElements();) {
for (Enumeration enumeration = appNames.objectEnumerator(); enumeration.hasMoreElements();) {
String s = (String) enumeration.nextElement();
MApplication mapplication = siteConfig().applicationWithName(s);
if (mapplication != null) {
Expand All @@ -495,11 +498,27 @@ protected void prepareApplications(NSArray nsarray) {
}

}

protected void prepareApplicationsOnHosts(NSArray<String> appNames, NSArray<String> hostNames) {
if (appNames == null)
throw new DirectActionException("at least one application name needs to be specified for type app", 406);
for (Enumeration enumeration = appNames.objectEnumerator(); enumeration.hasMoreElements();) {
String s = (String) enumeration.nextElement();
MApplication mapplication = siteConfig().applicationWithName(s);
if (mapplication != null) {
NSArray<MInstance> hostInstances = MInstance.HOST_NAME.in(hostNames).filtered(mapplication.instanceArray());
instances.addObjectsFromArray(hostInstances);
}
else
throw new DirectActionException("Unknown application " + s, 404);
}

protected void prepareInstances(NSArray nsarray) {
if (nsarray == null)
}

protected void prepareInstances(NSArray<String> appNamesAndNumbers) {
if (appNamesAndNumbers == null)
throw new DirectActionException("at least one instance name needs to be specified for type ins", 406);
for (Enumeration enumeration = nsarray.objectEnumerator(); enumeration.hasMoreElements();) {
for (Enumeration enumeration = appNamesAndNumbers.objectEnumerator(); enumeration.hasMoreElements();) {
String s = (String) enumeration.nextElement();
MInstance minstance = siteConfig().instanceWithName(s);
if (minstance != null)
Expand Down Expand Up @@ -528,11 +547,17 @@ public WOActionResults performMonitorActionNamed(String s) {
if ("all".equalsIgnoreCase(s1)) {
prepareApplications((NSArray) siteConfig().applicationArray().valueForKey("name"));
} else {
NSArray nsarray = context().request().formValuesForKey("name");
if ("app".equalsIgnoreCase(s1))
prepareApplications(nsarray);
else if ("ins".equalsIgnoreCase(s1))
prepareInstances(nsarray);
NSArray appNames = context().request().formValuesForKey("name");
NSArray hosts = context().request().formValuesForKey("host");

if ("app".equalsIgnoreCase(s1)) {
if (hosts == null || hosts.isEmpty()) {
prepareApplications(appNames);
} else {
prepareApplicationsOnHosts(appNames, hosts);
}
} else if ("ins".equalsIgnoreCase(s1))
prepareInstances(appNames);
else
throw new DirectActionException("Invalid type " + s1, 406);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ public void bounceShutdown(NSArray<MApplication> applications, int maxwait) {
}
}

public void bounceRolling(NSArray<MApplication> applications) {
for (MApplication application : applications) {
AppDetailPage page = AppDetailPage.create(context(), application);
page = (AppDetailPage) page.bounceClickedWithRollingBouncer();
}
}

public WOComponent bounceClicked() {
AppDetailPage page = AppDetailPage.create(context(), currentApplication);
page = (AppDetailPage) page.bounceClicked();
Expand Down Expand Up @@ -326,4 +333,5 @@ public WOComponent addApplicationClicked() {
public static WOComponent create(WOContext context) {
return WOApplication.application().pageWithName(AdminApplicationsPage.class.getName(), context);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.webobjects.monitor._private.StatsUtilities;
import com.webobjects.monitor.application.starter.ApplicationStarter;
import com.webobjects.monitor.application.starter.GracefulBouncer;
import com.webobjects.monitor.application.starter.RollingShutdownBouncer;
import com.webobjects.monitor.application.starter.ShutdownBouncer;

public class AppDetailPage extends MonitorComponent {
Expand Down Expand Up @@ -82,6 +83,10 @@ public WOComponent bounceClickedWithShutdownBouncer(int maxwait) {
return bounceClickedWithBouncer(new ShutdownBouncer(myApplication(), maxwait));
}

public WOComponent bounceClickedWithRollingBouncer() {
return bounceClickedWithBouncer(new RollingShutdownBouncer(myApplication()));
}

private WOComponent bounceClickedWithBouncer(ApplicationStarter bouncer) {
ApplicationStarter old = currentBouncer();
if (old != null) {
Expand Down Expand Up @@ -554,6 +559,13 @@ public WOComponent schedulingEnableAllClicked() {
private WOComponent newDetailPage() {
AppDetailPage nextPage = AppDetailPage.create(context(), myApplication());
nextPage.displayGroup.setSelectedObjects(displayGroup.selectedObjects());
nextPage.showDetailStatistics = this.showDetailStatistics;
if (currentBouncer() != null &&
!"Finished".equals(currentBouncer().status()) &&
!currentBouncer().errors().isEmpty()) {
mySession().addObjectsFromArrayIfAbsentToErrorMessageArray(currentBouncer().errors());
session().removeObjectForKey(bouncerName());
}
return nextPage;
}

Expand Down Expand Up @@ -743,7 +755,7 @@ public boolean hasHosts() {
}
}

public static AppDetailPage create(WOContext context, MApplication currentApplication, NSArray<MInstance> selected) {
public static AppDetailPage create(WOContext context, MApplication currentApplication, NSArray<MInstance> selected) {
AppDetailPage page = (AppDetailPage) WOApplication.application().pageWithName(AppDetailPage.class.getName(), context);
page.setMyApplication(currentApplication);
NSArray instancesArray = currentApplication.instanceArray();
Expand Down

0 comments on commit ed0a6f5

Please sign in to comment.