Issue #1843: Return status back to client when contacting one of the

configured ETN partners fails.

Change-Id: I5bd49f6930c317ba21725ff53f6ab65d548f037c

Former-commit-id: 18a09da6aba3caf83f4c4df71f2fcffb63043fb1
This commit is contained in:
David Gillingham 2013-09-24 16:02:04 -05:00
parent 62a5739feb
commit b7b29a1fdb
16 changed files with 1090 additions and 157 deletions

View file

@ -31,7 +31,8 @@ Require-Bundle: org.eclipse.ui,
com.raytheon.uf.common.python;bundle-version="1.12.1174",
com.raytheon.uf.common.colormap;bundle-version="1.12.1174",
com.google.guava;bundle-version="1.0.0",
com.raytheon.uf.common.style;bundle-version="1.0.0"
com.raytheon.uf.common.style;bundle-version="1.0.0",
com.raytheon.uf.common.activetable
Bundle-ActivationPolicy: lazy
Export-Package: com.raytheon.viz.gfe,
com.raytheon.viz.gfe.constants,
@ -52,8 +53,7 @@ Export-Package: com.raytheon.viz.gfe,
com.raytheon.viz.gfe.ui,
com.raytheon.viz.gfe.ui.runtimeui,
com.raytheon.viz.gfe.ui.zoneselector
Import-Package: com.raytheon.uf.common.activetable,
com.raytheon.uf.common.dissemination,
Import-Package: com.raytheon.uf.common.dissemination,
com.raytheon.uf.common.message,
com.raytheon.uf.common.python.concurrent,
com.raytheon.uf.common.serialization.comm,

View file

@ -29,6 +29,7 @@
# ??/??/?? ???????? Initial Creation.
# 05/14/13 1842 dgilling Use GFEVtecUtil to handle NEW
# ETN assignment.
# 09/24/13 1843 dgilling Handle GetNextEtnResponse.
#
#
@ -713,7 +714,7 @@ class HazardsTable(VTECTableUtil.VTECTableUtil):
# Local WFOs do not assign these numbers, so they should have
# numbers < 1000
if phensig not in self.__tpcKeys or self.__siteID4 in self.__sitesIgnoreNatlEtn:
etn_base = GFEVtecUtil.getNextEtn(self.__siteID4, '.'.join(phensig), False) - 1
etn_base = GFEVtecUtil.getNextEtn(self.__siteID4, '.'.join(phensig), False).getNextEtn() - 1
else:
presentyear = time.gmtime(self.__time)[0]
for active in activeTable:

View file

@ -0,0 +1,217 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.viz.gfe.dialogs.formatterlauncher;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import com.raytheon.uf.common.activetable.response.GetNextEtnResponse;
import com.raytheon.viz.ui.dialogs.CaveJFACEDialog;
import com.raytheon.viz.ui.dialogs.TextDisplayDlg;
import com.raytheon.viz.ui.dialogs.TextDisplayDlg.Location;
/**
* Dialog displayed to manually confirm ETN assignment when backup partners can
* not be reached
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 23, 2013 #1843 randerso Initial creation
*
* </pre>
*
* @author randerso
* @version 1.0
*/
public class ETNConfirmationDialog extends CaveJFACEDialog {
// TODO make this configurable
private static final int MAX_ETN_DELTA = 10;
private String phenSig;
private int proposedEtn;
private Map<String, Integer> resultsByHost;
private List<String> errorMessages;
private Spinner proposedEtnSpinner;
/**
* Dialog constructor
*
* @param parentShell
* @param etnResponse
*/
public ETNConfirmationDialog(Shell parentShell,
GetNextEtnResponse etnResponse) {
super(parentShell);
this.phenSig = etnResponse.getPhensig();
this.proposedEtn = etnResponse.getNextEtn();
this.resultsByHost = etnResponse.getResultsByHost();
this.errorMessages = etnResponse.getErrorMessages();
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets
* .Shell)
*/
@Override
protected void configureShell(Shell newShell) {
newShell.setText("Confirm ETN for " + this.phenSig);
super.configureShell(newShell);
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.viz.ui.dialogs.CaveJFACEDialog#createDialogArea(org.eclipse
* .swt.widgets.Composite)
*/
@Override
protected Control createDialogArea(Composite parent) {
Composite top = (Composite) super.createDialogArea(parent);
GridLayout layout = (GridLayout) top.getLayout();
layout.numColumns = 2;
Label label = new Label(top, SWT.LEFT);
GridData layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false);
label.setLayoutData(layoutData);
label.setText("Proposed ETN for " + this.phenSig + ":");
proposedEtnSpinner = new Spinner(top, SWT.BORDER);
layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false);
proposedEtnSpinner.setLayoutData(layoutData);
proposedEtnSpinner.setValues(this.proposedEtn, this.proposedEtn,
this.proposedEtn + MAX_ETN_DELTA, 0, 1, 1);
Group group = new Group(top, SWT.NONE);
layout = new GridLayout(2, false);
group.setLayout(layout);
layoutData = new GridData(SWT.FILL, SWT.DEFAULT, true, false);
layoutData.horizontalSpan = 2;
group.setLayoutData(layoutData);
group.setText("Partner ETNs");
for (Entry<String, Integer> entry : this.resultsByHost.entrySet()) {
Label host = new Label(group, SWT.LEFT);
layoutData = new GridData(SWT.FILL, SWT.DEFAULT, true, false);
host.setLayoutData(layoutData);
host.setText(entry.getKey());
Label etn = new Label(group, SWT.RIGHT);
layoutData = new GridData(SWT.FILL, SWT.DEFAULT, true, false);
etn.setLayoutData(layoutData);
String text = entry.getValue() == null ? "ERR" : entry.getValue()
.toString();
etn.setText(text);
}
final Button showErrors = new Button(top, SWT.PUSH);
layoutData = new GridData(SWT.FILL, SWT.DEFAULT, true, false);
layoutData.horizontalSpan = 2;
showErrors.setLayoutData(layoutData);
showErrors.setText("Show Errors");
showErrors.addSelectionListener(new SelectionAdapter() {
/*
* (non-Javadoc)
*
* @see
* org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
* .swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e) {
showErrors.setEnabled(false);
StringBuilder errorText = new StringBuilder();
for (String s : errorMessages) {
errorText.append(s).append('\n');
}
TextDisplayDlg errorDlg = new TextDisplayDlg(
ETNConfirmationDialog.this.getShell(), "Errors",
errorText.toString(), Location.BELOW);
errorDlg.open();
showErrors.setEnabled(true);
}
});
return top;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse
* .swt.widgets.Composite)
*/
@Override
protected void createButtonsForButtonBar(Composite parent) {
super.createButtonsForButtonBar(parent);
getButton(IDialogConstants.OK_ID).setText("Transmit");
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.Dialog#okPressed()
*/
@Override
protected void okPressed() {
this.proposedEtn = this.proposedEtnSpinner.getSelection();
super.okPressed();
}
/**
* @return the proposedEtn
*/
public int getProposedEtn() {
return proposedEtn;
}
}

View file

@ -20,6 +20,9 @@
package com.raytheon.viz.gfe.dialogs.formatterlauncher;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
@ -37,6 +40,7 @@ import org.eclipse.swt.widgets.Text;
import com.raytheon.uf.common.activetable.PracticeProductOfftimeRequest;
import com.raytheon.uf.common.activetable.SendPracticeProductRequest;
import com.raytheon.uf.common.activetable.response.GetNextEtnResponse;
import com.raytheon.uf.common.dissemination.OUPRequest;
import com.raytheon.uf.common.dissemination.OUPResponse;
import com.raytheon.uf.common.dissemination.OfficialUserProduct;
@ -52,6 +56,7 @@ import com.raytheon.uf.viz.core.requests.ThriftClient;
import com.raytheon.viz.core.mode.CAVEMode;
import com.raytheon.viz.gfe.product.TextDBUtil;
import com.raytheon.viz.gfe.vtec.GFEVtecUtil;
import com.raytheon.viz.texteditor.util.VtecObject;
import com.raytheon.viz.ui.dialogs.CaveSWTDialog;
/**
@ -70,6 +75,9 @@ import com.raytheon.viz.ui.dialogs.CaveSWTDialog;
* 08 MAY 2013 1842 dgilling Use VtecUtil to set product ETNs, fix
* warnings.
* 07 Jun 2013 1981 mpduff Set user's id in OUPRequest as it is now a protected operation.
* 23 Oct 2013 1843 dgilling Ensure that dialog is always closed,
* even on failure, changes for error handling
* of intersite ETN assignment.
* </pre>
*
* @author lvenable
@ -334,46 +342,116 @@ public class StoreTransmitDlg extends CaveSWTDialog implements
// Store/Transmit the product...
if (!countdownThread.threadCancelled()) {
boolean retrieveEtnFailed = false;
try {
productText = GFEVtecUtil.finalizeETNs(productText);
} catch (VizException e) {
statusHandler.handle(Priority.CRITICAL,
"Error setting ETNs for product", e);
sendTransmissionStatus(ConfigData.productStateEnum.Failed);
VizApp.runAsync(new Runnable() {
Set<VtecObject> vtecsToAssignEtn = GFEVtecUtil
.getVtecLinesThatNeedEtn(productText);
Map<String, Integer> etnCache = new HashMap<String, Integer>();
for (VtecObject vtec : vtecsToAssignEtn) {
try {
GetNextEtnResponse serverResponse = GFEVtecUtil.getNextEtn(
vtec.getOffice(), vtec.getPhensig(), true, true);
if (!serverResponse.isOkay()) {
final VtecObject vtecToFix = vtec;
final boolean[] exitLoopContainer = { false };
final Exception[] exceptionContainer = { null };
final GetNextEtnResponse[] responseContainer = { serverResponse };
do {
getDisplay().syncExec(new Runnable() {
@Override
public void run() {
GetNextEtnResponse serverResponse = responseContainer[0];
ETNConfirmationDialog dlg = new ETNConfirmationDialog(
getShell(), serverResponse);
if (dlg.open() == ETNConfirmationDialog.OK) {
int etn = dlg.getProposedEtn();
statusHandler.info(String
.format("User confirmed ETN for %s: %04d",
serverResponse
.getPhensig(),
etn));
try {
GetNextEtnResponse followupResp = GFEVtecUtil.getNextEtn(
vtecToFix.getOffice(),
vtecToFix.getPhensig(),
true, true, true, etn);
responseContainer[0] = followupResp;
} catch (VizException e) {
exceptionContainer[0] = e;
exitLoopContainer[0] = true;
}
} else {
statusHandler
.info("User declined to fix ETN for %s",
serverResponse
.getPhensig());
exitLoopContainer[0] = true;
}
}
});
} while (!responseContainer[0].isOkay()
&& !exitLoopContainer[0]);
if (!responseContainer[0].isOkay()) {
String msg = "Unable to set ETN for phensig "
+ responseContainer[0].getPhensig()
+ "\nStatus: "
+ responseContainer[0].toString();
Exception e = exceptionContainer[0];
if (e == null) {
throw new VizException(msg);
} else {
throw new VizException(msg, e);
}
}
}
etnCache.put(vtec.getPhensig(), serverResponse.getNextEtn());
} catch (VizException e) {
statusHandler.handle(Priority.CRITICAL,
"Error setting ETNs for product", e);
retrieveEtnFailed = true;
VizApp.runAsync(new Runnable() {
@Override
public void run() {
sendTransmissionStatus(ConfigData.productStateEnum.Failed);
StoreTransmitDlg.this.parentEditor.revive();
}
});
break;
}
}
if (!retrieveEtnFailed) {
productText = GFEVtecUtil.finalizeETNs(productText, etnCache);
VizApp.runSync(new Runnable() {
@Override
public void run() {
StoreTransmitDlg.this.parentEditor.revive();
String pid = productIdTF.getText();
if (parentEditor.isTestVTEC()) {
if (isStoreDialog) {
parentEditor.devStore(pid.substring(3));
} else {
parentEditor.devStore(pid.substring(4));
transmitProduct(true);
}
} else {
if (isStoreDialog) {
TextDBUtil.storeProduct(pid, productText,
parentEditor.isTestVTEC());
} else {
transmitProduct(false);
}
}
}
});
return;
}
VizApp.runSync(new Runnable() {
@Override
public void run() {
String pid = productIdTF.getText();
if (parentEditor.isTestVTEC()) {
if (isStoreDialog) {
parentEditor.devStore(pid.substring(3));
} else {
parentEditor.devStore(pid.substring(4));
transmitProduct(true);
}
} else {
if (isStoreDialog) {
TextDBUtil.storeProduct(pid, productText,
parentEditor.isTestVTEC());
} else {
transmitProduct(false);
}
}
}
});
}
// The asyncExec call is used to dispose of the shell since it is

View file

@ -20,12 +20,15 @@
package com.raytheon.viz.gfe.vtec;
import java.util.Collection;
import java.util.HashMap;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.raytheon.uf.common.activetable.response.GetNextEtnResponse;
import com.raytheon.uf.common.util.StringUtil;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.viz.texteditor.util.VtecObject;
import com.raytheon.viz.texteditor.util.VtecUtil;
@ -50,6 +53,7 @@ import com.raytheon.viz.texteditor.util.VtecUtil;
* multiple NEW segments with the same
* phensig.
* Aug 29, 2013 #1843 dgilling Add hooks for inter-site ETN assignment.
* Oct 21, 2013 #1843 dgilling Use new GetNextEtnResponse.
*
* </pre>
*
@ -78,29 +82,152 @@ public class GFEVtecUtil {
throw new AssertionError();
}
public static int getNextEtn(String office, String phensig, boolean lockEtn)
throws VizException {
return getNextEtn(office, phensig, lockEtn, false);
/**
* Gets the next available ETN for a specific product and office.
*
* @param office
* The 4-character site ID of the office.
* @param phensig
* The phenomenon and significance of the hazard concatenated
* with a '.' (e.g., TO.W or DU.Y)
* @param lockEtn
* Whether or not to request an exclusive ETN--if true, this will
* cause the server to increment its running ETN sequence to the
* next number after determining the next ETN for this request.
* If false, the next ETN will be returned, but it will not
* increment the server's running sequence, so the ETN return
* could be used by another client that makes a
* GetNextEtnRequest.
* @return The next ETN in sequence, given the office and phensig.
* @throws VizException
* If an error occurred sending the request to the server.
*/
public static GetNextEtnResponse getNextEtn(String office, String phensig,
boolean lockEtn) throws VizException {
return getNextEtn(office, phensig, lockEtn, false, false, null);
}
public static int getNextEtn(String office, String phensig,
/**
* Gets the next available ETN for a specific product and office.
*
* @param office
* The 4-character site ID of the office.
* @param phensig
* The phenomenon and significance of the hazard concatenated
* with a '.' (e.g., TO.W or DU.Y)
* @param lockEtn
* Whether or not to request an exclusive ETN--if true, this will
* cause the server to increment its running ETN sequence to the
* next number after determining the next ETN for this request.
* If false, the next ETN will be returned, but it will not
* increment the server's running sequence, so the ETN return
* could be used by another client that makes a
* GetNextEtnRequest.
* @param performISC
* Whether or not to collaborate with neighboring sites to
* determine the next ETN. See {@link
* GetNextEtnUtil#getNextEtnFromPartners(String, ActiveTableMode,
* String, Calendar, List<IRequestRouter>)} for more information.
* @return The next ETN in sequence, given the office and phensig.
* @throws VizException
* If an error occurred sending the request to the server.
*/
public static GetNextEtnResponse getNextEtn(String office, String phensig,
boolean lockEtn, boolean performISC) throws VizException {
return VtecUtil.getNextEtn(office, phensig, lockEtn, performISC);
return getNextEtn(office, phensig, lockEtn, performISC, false, null);
}
public static String finalizeETNs(String message) throws VizException {
if (Strings.isNullOrEmpty(message)) {
return message;
/**
* Gets the next available ETN for a specific product and office.
*
* @param office
* The 4-character site ID of the office.
* @param phensig
* The phenomenon and significance of the hazard concatenated
* with a '.' (e.g., TO.W or DU.Y)
* @param lockEtn
* Whether or not to request an exclusive ETN--if true, this will
* cause the server to increment its running ETN sequence to the
* next number after determining the next ETN for this request.
* If false, the next ETN will be returned, but it will not
* increment the server's running sequence, so the ETN return
* could be used by another client that makes a
* GetNextEtnRequest.
* @param performISC
* Whether or not to collaborate with neighboring sites to
* determine the next ETN. See {@link
* GetNextEtnUtil#getNextEtnFromPartners(String, ActiveTableMode,
* String, Calendar, List<IRequestRouter>)} for more information.
* @param reportOnlyConflict
* Affects which kinds of errors get reported back to the
* requestor. If true, only cases where the value of
* <code>etnOverride</code> is less than or equal to the last ETN
* used by this site or any of its partners will be reported.
* Else, all significant errors will be reported back.
* @param etnOverride
* Allows the user to influence the next ETN assigned by using
* this value unless it is less than or equal to the last ETN
* used by this site or one of its partners.
* @return The next ETN in sequence, given the office and phensig.
* @throws VizException
* If an error occurred sending the request to the server.
*/
public static GetNextEtnResponse getNextEtn(String office, String phensig,
boolean lockEtn, boolean performISC, boolean reportOnlyConflict,
Integer etnOverride) throws VizException {
return VtecUtil.getNextEtn(office, phensig, lockEtn, performISC,
reportOnlyConflict, etnOverride);
}
/**
* Reads through a GFE VTEC product and returns VTEC lines with NEW action
* codes that need to be assigned an ETN.
*
* @param product
* The product's text.
* @return A <code>Set</code> of <code>VtecObject</code>s that need to have
* a new ETN assigned to them.
*/
public static Set<VtecObject> getVtecLinesThatNeedEtn(String product) {
if (StringUtil.isEmptyString(product)) {
return Collections.emptySet();
}
// With GFE VTEC products, it's possible to have multiple segments with
// NEW vtec action codes and the same phensig. For this reason,
// HazardsTable.py implemented a "cache" that would ensure all NEWs for
// the same phensig would be assigned the same ETN. This Map replicates
// that legacy behavior.
Map<String, Integer> etnCache = new HashMap<String, Integer>();
Set<VtecObject> phensigs = new HashSet<VtecObject>();
Matcher vtecMatcher = VtecUtil.VTEC_REGEX.matcher(message);
Matcher vtecMatcher = VtecUtil.VTEC_REGEX.matcher(product);
while (vtecMatcher.find()) {
VtecObject vtec = new VtecObject(vtecMatcher.group());
if (("NEW".equals(vtec.getAction()))
&& ((!NATIONAL_PHENSIGS.contains(vtec.getPhensig())) || (IGNORE_NATIONAL_ETN
.contains(vtec.getOffice()) && TROPICAL_PHENSIGS
.contains(vtec.getPhensig())))) {
phensigs.add(vtec);
}
}
return phensigs;
}
/**
* For any NEW VTEC lines contained within the specified product texts,
* generates a <code>GetNextEtnRequest</code> to retrieve the next canonical
* ETN in sequence for the given phensig.
*
* @param product
* The product's text.
* @return The product's text with any NEW VTEC lines having their ETN
* values replaced with the next ETN in sequence.
* @throws VizException
* If an error occurred sending the request to the server.
*/
public static String finalizeETNs(String product,
Map<String, Integer> etnCache) {
if (StringUtil.isEmptyString(product)) {
return product;
}
Matcher vtecMatcher = VtecUtil.VTEC_REGEX.matcher(product);
StringBuffer finalOutput = new StringBuffer();
while (vtecMatcher.find()) {
VtecObject vtec = new VtecObject(vtecMatcher.group());
@ -111,13 +238,13 @@ public class GFEVtecUtil {
&& ((!NATIONAL_PHENSIGS.contains(vtec.getPhensig())) || (IGNORE_NATIONAL_ETN
.contains(vtec.getOffice()) && TROPICAL_PHENSIGS
.contains(vtec.getPhensig())))) {
// With GFE VTEC products, it's possible to have multiple
// segments with NEW vtec action codes and the same phensig. For
// this reason, HazardsTable.py implemented a "cache" that would
// ensure all NEWs for the same phensig would be assigned the
// same ETN. The etnCache Map replicaes this behavior.
String cacheKey = vtec.getPhensig();
Integer newEtn = etnCache.get(cacheKey);
if (newEtn == null) {
newEtn = getNextEtn(vtec.getOffice(), vtec.getPhensig(),
true, true);
etnCache.put(cacheKey, newEtn);
}
vtec.setSequence(newEtn);
}
vtecMatcher

View file

@ -25,7 +25,8 @@ Require-Bundle: org.eclipse.ui,
com.raytheon.uf.common.dataplugin.radar;bundle-version="1.0.0",
com.raytheon.uf.viz.localization,
com.raytheon.uf.common.auth;bundle-version="1.12.1174",
com.raytheon.uf.common.dataplugin.warning;bundle-version="1.12.1174"
com.raytheon.uf.common.dataplugin.warning;bundle-version="1.12.1174",
com.raytheon.uf.common.activetable
Bundle-ActivationPolicy: lazy
Export-Package: com.raytheon.viz.texteditor,
com.raytheon.viz.texteditor.alarmalert.dialogs,
@ -38,8 +39,7 @@ Export-Package: com.raytheon.viz.texteditor,
com.raytheon.viz.texteditor.scripting.runner,
com.raytheon.viz.texteditor.util
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: com.raytheon.uf.common.activetable,
com.raytheon.uf.common.comm,
Import-Package: com.raytheon.uf.common.comm,
com.raytheon.uf.common.dataplugin.warning.util,
com.raytheon.uf.common.dissemination,
com.raytheon.uf.common.message,

View file

@ -27,6 +27,7 @@ import java.util.regex.Pattern;
import com.raytheon.uf.common.activetable.ActiveTableMode;
import com.raytheon.uf.common.activetable.GetNextEtnRequest;
import com.raytheon.uf.common.activetable.response.GetNextEtnResponse;
import com.raytheon.uf.common.time.SimulatedTime;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.requests.ThriftClient;
@ -44,6 +45,7 @@ import com.raytheon.viz.core.mode.CAVEMode;
* Feb 09, 2009 bwoodle Initial creation
* May 08, 2013 #1842 dgilling Code cleanup.
* Aug 29, 2013 #1843 dgilling Use new GetNextEtnRequest constructor.
* Oct 21, 2013 #1843 dgilling Use new GetNextEtnResponse.
*
* </pre>
*
@ -106,8 +108,10 @@ public class VtecUtil {
}
if (vtec.getAction().equals("NEW")) {
vtec.setSequence(getNextEtn(vtec.getOffice(), vtec.getPhenomena()
+ "." + vtec.getSignificance(), lockEtn));
int nextEtn = getNextEtn(vtec.getOffice(),
vtec.getPhenomena() + "." + vtec.getSignificance(), lockEtn)
.getNextEtn();
vtec.setSequence(nextEtn);
}
return replaceFirstVtecString(message, vtec);
}
@ -133,9 +137,9 @@ public class VtecUtil {
* If an error occurs while submitting or processing the remote
* request.
*/
public static int getNextEtn(String office, String phensig, boolean lockEtn)
throws VizException {
return getNextEtn(office, phensig, lockEtn, false);
public static GetNextEtnResponse getNextEtn(String office, String phensig,
boolean lockEtn) throws VizException {
return getNextEtn(office, phensig, lockEtn, false, false, null);
}
/**
@ -159,26 +163,37 @@ public class VtecUtil {
* determine the next ETN. See {@link
* GetNextEtnUtil#getNextEtnFromPartners(String, ActiveTableMode,
* String, Calendar, List<IRequestRouter>)} for more information.
* @param reportOnlyConflict
* Affects which kinds of errors get reported back to the
* requestor. If true, only cases where the value of
* <code>etnOverride</code> is less than or equal to the last ETN
* used by this site or any of its partners will be reported.
* Else, all significant errors will be reported back.
* @param etnOverride
* Allows the user to influence the next ETN assigned by using
* this value unless it is less than or equal to the last ETN
* used by this site or one of its partners.
* @return The next ETN in sequence, given the office and phensig.
* @throws VizException
* If an error occurs while submitting or processing the remote
* request.
*/
public static int getNextEtn(String office, String phensig,
boolean lockEtn, boolean performISC) throws VizException {
public static GetNextEtnResponse getNextEtn(String office, String phensig,
boolean lockEtn, boolean performISC, boolean reportOnlyConflict,
Integer etnOverride) throws VizException {
Calendar currentTime = Calendar.getInstance();
currentTime.setTime(SimulatedTime.getSystemTime().getTime());
ActiveTableMode activeTable = (CAVEMode.getMode()
.equals(CAVEMode.PRACTICE)) ? ActiveTableMode.PRACTICE
: ActiveTableMode.OPERATIONAL;
GetNextEtnRequest req = new GetNextEtnRequest(office, activeTable,
phensig, currentTime, lockEtn, performISC);
phensig, currentTime, lockEtn, performISC, reportOnlyConflict,
etnOverride);
int rval = 1;
Integer resp = (Integer) ThriftClient.sendRequest(req);
if (resp != null) {
rval = resp;
}
GetNextEtnResponse resp = (GetNextEtnResponse) ThriftClient
.sendRequest(req);
GetNextEtnResponse rval = (resp != null) ? resp
: new GetNextEtnResponse(1, phensig);
return rval;
}

View file

@ -0,0 +1,166 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.viz.ui.dialogs;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
/**
* Generic <code>Dialog</code> to display a multi-line block of text in a
* re-sizable and scrollable window.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 23, 2013 #1843 randerso Initial creation
*
* </pre>
*
* @author randerso
* @version 1.0
*/
public class TextDisplayDlg extends CaveJFACEDialog {
/**
* Location of this dialog relative to it's parent.
*/
public static enum Location {
ABOVE, BELOW, LEFT, RIGHT, CENTERED
}
private String title;
private String contents;
private Location location;
/**
* @param parentShell
* @param title
* @param contents
*/
public TextDisplayDlg(Shell parentShell, String title, String contents,
Location location) {
super(parentShell);
this.title = title;
this.contents = contents;
this.location = location;
this.setShellStyle(SWT.DIALOG_TRIM | SWT.RESIZE);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets
* .Shell)
*/
@Override
protected void configureShell(Shell newShell) {
newShell.setText(this.title);
super.configureShell(newShell);
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.viz.ui.dialogs.CaveJFACEDialog#createDialogArea(org.eclipse
* .swt.widgets.Composite)
*/
@Override
protected Control createDialogArea(Composite parent) {
Composite top = (Composite) super.createDialogArea(parent);
Text contents = new Text(top, SWT.MULTI | SWT.BORDER | SWT.READ_ONLY
| SWT.V_SCROLL | SWT.H_SCROLL);
Rectangle screenBounds = this.getShell().getMonitor().getBounds();
GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
layoutData.widthHint = screenBounds.width / 2;
layoutData.heightHint = screenBounds.height / 4;
contents.setLayoutData(layoutData);
contents.setText(this.contents);
return top;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.dialogs.Dialog#getInitialLocation(org.eclipse.swt.graphics
* .Point)
*/
@Override
protected Point getInitialLocation(Point initialSize) {
Rectangle parentBounds = getParentShell().getBounds();
Rectangle myBounds = getShell().getBounds();
int x = parentBounds.x + ((parentBounds.width - myBounds.width) / 2);
int y = parentBounds.y + ((parentBounds.height - myBounds.height) / 2);
;
switch (this.location) {
case ABOVE:
y = parentBounds.y - myBounds.height;
break;
case BELOW:
y = parentBounds.y + parentBounds.height;
break;
case LEFT:
x = parentBounds.x - myBounds.width;
break;
case RIGHT:
x = parentBounds.x + parentBounds.width;
break;
case CENTERED:
break;
}
Point location = new Point(x, y);
return location;
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.viz.ui.dialogs.CaveJFACEDialog#createButtonBar(org.eclipse
* .swt.widgets.Composite)
*/
@Override
protected Control createButtonBar(Composite parent) {
// no buttons
return null;
}
}

View file

@ -21,4 +21,5 @@ Require-Bundle: com.raytheon.uf.common.serialization,
com.raytheon.uf.common.message;bundle-version="1.12.1174",
com.raytheon.uf.common.localization;bundle-version="1.12.1174",
com.raytheon.uf.common.python;bundle-version="1.12.1174",
org.jep;bundle-version="1.0.0"
org.jep;bundle-version="1.0.0",
com.raytheon.uf.common.util;bundle-version="1.12.1174"

View file

@ -34,7 +34,8 @@ import com.raytheon.uf.common.serialization.comm.IServerRequest;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 14, 2011 rjpeter Initial creation
* Aug 26, 2013 #1843 dgilling Add performISC field, proper constructors.
* Oct 21, 2013 #1843 dgilling Add performISC and reportConflictOnly
* fields, proper constructors.
*
* </pre>
*
@ -62,39 +63,89 @@ public class GetNextEtnRequest implements IServerRequest {
@DynamicSerializeElement
private boolean performISC;
@DynamicSerializeElement
private boolean reportConflictOnly;
@DynamicSerializeElement
private Integer etnOverride;
public GetNextEtnRequest() {
// no-op, for dynamic serialize support
}
/**
* Constructs a GetNextEtnRequest.
*
* @param siteID
* The 4-character site ID of the office.
* @param mode
* Active table mode.
* @param phensig
* The phenomenon and significance of the hazard concatenated
* with a '.' (e.g., TO.W or DU.Y)
* @param currentTime
* <code>Calendar</code> representing time (needed for DRT mode).
* @param lockEtn
* Whether or not to request an exclusive ETN--if true, this will
* cause the server to increment its running ETN sequence to the
* next number after determining the next ETN for this request.
* If false, the next ETN will be returned, but it will not
* increment the server's running sequence, so the ETN return
* could be used by another client that makes a
* GetNextEtnRequest.
*/
public GetNextEtnRequest(String siteID, ActiveTableMode mode,
String phensig, Calendar currentTime, boolean lockEtn) {
this(siteID, mode, phensig, currentTime, lockEtn, false);
this(siteID, mode, phensig, currentTime, lockEtn, false, false, null);
}
/**
* Constructs a GetNextEtnRequest.
*
* @param siteID
* The 4-character site ID of the office.
* @param mode
* Active table mode.
* @param phensig
* The phenomenon and significance of the hazard concatenated
* with a '.' (e.g., TO.W or DU.Y)
* @param currentTime
* <code>Calendar</code> representing time (needed for DRT mode).
* @param lockEtn
* Whether or not to request an exclusive ETN--if true, this will
* cause the server to increment its running ETN sequence to the
* next number after determining the next ETN for this request.
* If false, the next ETN will be returned, but it will not
* increment the server's running sequence, so the ETN return
* could be used by another client that makes a
* GetNextEtnRequest.
* @param performISC
* Whether or not to collaborate with neighboring sites to
* determine the next ETN. See {@link
* GetNextEtnUtil#getNextEtnFromPartners(String, ActiveTableMode,
* String, Calendar, List<IRequestRouter>)} for more information.
* @param reportConflictOnly
* Affects which kinds of errors get reported back to the
* requestor. If true, only cases where the value of
* <code>etnOverride</code> is less than or equal to the last ETN
* used by this site or any of its partners will be reported.
* Else, all significant errors will be reported back.
* @param etnOverride
* Allows the user to influence the next ETN assigned by using
* this value unless it is less than or equal to the last ETN
* used by this site or one of its partners.
*/
public GetNextEtnRequest(String siteID, ActiveTableMode mode,
String phensig, Calendar currentTime, boolean lockEtn,
boolean performISC) {
boolean performISC, boolean reportConflictOnly, Integer etnOverride) {
this.siteID = siteID;
this.mode = mode;
this.phensig = phensig;
this.currentTime = currentTime;
this.lockEtn = lockEtn;
this.performISC = performISC;
this.reportConflictOnly = reportConflictOnly;
this.etnOverride = etnOverride;
}
public String getSiteID() {
@ -144,4 +195,20 @@ public class GetNextEtnRequest implements IServerRequest {
public void setPerformISC(boolean performISC) {
this.performISC = performISC;
}
public boolean isReportConflictOnly() {
return reportConflictOnly;
}
public void setReportConflictOnly(boolean reportConflictOnly) {
this.reportConflictOnly = reportConflictOnly;
}
public Integer getEtnOverride() {
return etnOverride;
}
public void setEtnOverride(Integer etnOverride) {
this.etnOverride = etnOverride;
}
}

View file

@ -36,6 +36,7 @@ import com.raytheon.uf.common.serialization.comm.IServerRequest;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 19, 2013 #1843 dgilling Initial creation
* Oct 21, 2013 #1843 dgilling Add ETN override field.
*
* </pre>
*
@ -61,17 +62,27 @@ public class LockAndGetNextEtnRequest implements IServerRequest {
@DynamicSerializeElement
private Calendar currentTime;
@DynamicSerializeElement
private Integer etnOverride;
public LockAndGetNextEtnRequest() {
// default constructor for thrift/dynamicserialize
}
public LockAndGetNextEtnRequest(String siteID, String requestorSiteID,
ActiveTableMode mode, String phensig, Calendar currentTime) {
this(siteID, requestorSiteID, mode, phensig, currentTime, null);
}
public LockAndGetNextEtnRequest(String siteID, String requestorSiteID,
ActiveTableMode mode, String phensig, Calendar currentTime,
Integer etnOverride) {
this.siteID = siteID;
this.requestorSiteID = requestorSiteID;
this.mode = mode;
this.phensig = phensig;
this.currentTime = currentTime;
this.etnOverride = etnOverride;
}
public String getSiteID() {
@ -113,4 +124,12 @@ public class LockAndGetNextEtnRequest implements IServerRequest {
public void setCurrentTime(Calendar currentTime) {
this.currentTime = currentTime;
}
public Integer getEtnOverride() {
return etnOverride;
}
public void setEtnOverride(Integer etnOverride) {
this.etnOverride = etnOverride;
}
}

View file

@ -0,0 +1,144 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.uf.common.activetable.response;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
import com.raytheon.uf.common.util.CollectionUtil;
import com.raytheon.uf.common.util.StringUtil;
/**
* Response type for <code>GetNextEtnRequest</code>. Contains the next ETN to
* use and a list of hosts that could not be contacted during the process to
* determine the next ETN.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 24, 2013 #1843 dgilling Initial creation
*
* </pre>
*
* @author dgilling
* @version 1.0
*/
@DynamicSerialize
public final class GetNextEtnResponse {
@DynamicSerializeElement
private int nextEtn;
@DynamicSerializeElement
private String phensig;
@DynamicSerializeElement
private Map<String, Integer> resultsByHost;
@DynamicSerializeElement
private List<String> errorMessages;
public GetNextEtnResponse() {
// for Dynamic Serialize use only.
}
public GetNextEtnResponse(int nextEtn, String phensig) {
this(nextEtn, phensig, new HashMap<String, Integer>(),
new ArrayList<String>());
this.resultsByHost.put("localhost", this.nextEtn);
}
public GetNextEtnResponse(int nextEtn, String phensig,
Map<String, Integer> resultsByHost, List<String> errorMessages) {
this.nextEtn = nextEtn;
this.phensig = phensig;
this.resultsByHost = resultsByHost;
this.errorMessages = errorMessages;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("GetNextEtnResponse [nextEtn=");
builder.append(nextEtn);
builder.append(", phensig=");
builder.append(phensig);
builder.append(", resultsByHost=");
builder.append(resultsByHost);
builder.append(", errorMessages=");
builder.append(errorMessages);
builder.append("]");
return builder.toString();
}
public boolean isOkay() {
return errorMessages.isEmpty();
}
public String getError() {
return !isOkay() ? StringUtil.join(errorMessages, '\n') : "";
}
public void addErrorMessage(String message) {
if (CollectionUtil.isNullOrEmpty(errorMessages)) {
errorMessages = new ArrayList<String>();
}
errorMessages.add(message);
}
public int getNextEtn() {
return nextEtn;
}
public void setNextEtn(int nextEtn) {
this.nextEtn = nextEtn;
}
public String getPhensig() {
return phensig;
}
public void setPhensig(String phensig) {
this.phensig = phensig;
}
public Map<String, Integer> getResultsByHost() {
return resultsByHost;
}
public void setResultsByHost(Map<String, Integer> resultsByHost) {
this.resultsByHost = resultsByHost;
}
public List<String> getErrorMessages() {
return errorMessages;
}
public void setErrorMessages(List<String> errorMessages) {
this.errorMessages = errorMessages;
}
}

View file

@ -20,10 +20,13 @@
package com.raytheon.uf.edex.activetable;
import com.raytheon.uf.common.activetable.GetNextEtnRequest;
import com.raytheon.uf.common.activetable.response.GetNextEtnResponse;
import com.raytheon.uf.common.serialization.comm.IRequestHandler;
/**
* TODO Add Description
* Request handler for <code>GetNextEtnRequest</code>. Returns the next ETN to
* use for the given office id, phensig, and active table (PRACTICE or
* OPERATIONAL).
*
* <pre>
*
@ -31,6 +34,7 @@ import com.raytheon.uf.common.serialization.comm.IRequestHandler;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 15, 2011 rjpeter Initial creation
* Oct 21, 2013 #1843 dgilling Change return type.
*
* </pre>
*
@ -41,12 +45,14 @@ import com.raytheon.uf.common.serialization.comm.IRequestHandler;
public class GetNextEtnHandler implements IRequestHandler<GetNextEtnRequest> {
@Override
public Integer handleRequest(GetNextEtnRequest request) throws Exception {
Integer nextEtn = GetNextEtnUtil.getNextEtn(request.getSiteID(),
request.getMode(), request.getPhensig(),
public GetNextEtnResponse handleRequest(GetNextEtnRequest request)
throws Exception {
GetNextEtnResponse response = GetNextEtnUtil.getNextEtn(
request.getSiteID(), request.getMode(), request.getPhensig(),
request.getCurrentTime(), request.isLockEtn(),
request.isPerformISC());
return nextEtn;
request.isPerformISC(), request.isReportConflictOnly(),
request.getEtnOverride());
return response;
}
}

View file

@ -28,9 +28,13 @@ import java.net.UnknownHostException;
import java.rmi.RemoteException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Queue;
import java.util.SortedMap;
@ -40,6 +44,7 @@ import com.raytheon.edex.site.SiteUtil;
import com.raytheon.uf.common.activetable.ActiveTableMode;
import com.raytheon.uf.common.activetable.request.LockAndGetNextEtnRequest;
import com.raytheon.uf.common.activetable.request.UnlockAndSetNextEtnRequest;
import com.raytheon.uf.common.activetable.response.GetNextEtnResponse;
import com.raytheon.uf.common.localization.IPathManager;
import com.raytheon.uf.common.localization.LocalizationContext;
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
@ -79,7 +84,7 @@ public final class GetNextEtnUtil {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(GetNextEtnUtil.class);
private static final String CONFIG_FILE_NAME = "remote-etn-partners.properties";
public static final String CONFIG_FILE_NAME = "remote-etn-partners.properties";
private static final String NEXT_ETN_LOCK = "ActiveTableNextEtn";
@ -121,29 +126,59 @@ public final class GetNextEtnUtil {
* <code>isLock</code> is false, this flag is effectively false
* and your configured remote partners will not be contacted to
* determine the next ETN.
* @return The next ETN to be used in sequence.
* @param reportConflictOnly
* Affects which kinds of errors get reported back to the
* requestor. If true, only cases where the value of
* <code>etnOverride</code> is less than or equal to the last ETN
* used by this site or any of its partners will be reported.
* Else, all significant errors will be reported back.
* @param etnOverride
* Allows the user to influence the next ETN assigned by using
* this value unless it is less than or equal to the last ETN
* used by this site or one of its partners.
* @return A <code>GetNextEtnResponse</code> containing the next ETN to use
* and any hosts that couldn't be contacted during this process.
*/
public static Integer getNextEtn(String siteId, ActiveTableMode mode,
String phensig, Calendar currentTime, boolean isLock,
boolean performISC) {
List<IRequestRouter> hostsToQuery = Collections.emptyList();
public static GetNextEtnResponse getNextEtn(String siteId,
ActiveTableMode mode, String phensig, Calendar currentTime,
boolean isLock, boolean performISC, boolean reportConflictOnly,
Integer etnOverride) {
SortedMap<String, IRequestRouter> hostsToQuery = new TreeMap<String, IRequestRouter>();
List<String> readEtnSourcesErrors = Collections.emptyList();
if (performISC) {
hostsToQuery = GetNextEtnUtil.getRemoteEtnSources(siteId);
try {
hostsToQuery = GetNextEtnUtil.getRemoteEtnSources(siteId);
} catch (UnknownHostException e) {
readEtnSourcesErrors = Arrays.asList(
"Falling back to local ETN calculation: ",
"Could not perform reverse lookup for localhost: "
+ e.getLocalizedMessage());
} catch (IOException e) {
readEtnSourcesErrors = Arrays.asList(
"Falling back to local ETN calculation: ",
"Could not read configuration file " + CONFIG_FILE_NAME
+ ": " + e.getLocalizedMessage());
}
}
int nextEtn;
if (performISC && isLock && (!hostsToQuery.isEmpty())) {
nextEtn = GetNextEtnUtil.getNextEtnFromPartners(siteId, mode,
phensig, currentTime, hostsToQuery);
GetNextEtnResponse response;
if (performISC && isLock && (!hostsToQuery.isEmpty())
&& (readEtnSourcesErrors.isEmpty())) {
response = GetNextEtnUtil.getNextEtnFromPartners(siteId, mode,
phensig, currentTime, hostsToQuery, reportConflictOnly,
etnOverride);
} else {
nextEtn = GetNextEtnUtil.getNextEtnFromLocal(siteId, mode, phensig,
currentTime, isLock);
int nextEtn = GetNextEtnUtil.getNextEtnFromLocal(siteId, mode,
phensig, currentTime, isLock);
response = new GetNextEtnResponse(nextEtn, phensig);
response.setErrorMessages(readEtnSourcesErrors);
}
return nextEtn;
return response;
}
private static List<IRequestRouter> getRemoteEtnSources(String siteId) {
private static SortedMap<String, IRequestRouter> getRemoteEtnSources(
String siteId) throws IOException, UnknownHostException {
Properties etnBackupProps = new Properties();
FileInputStream fis = null;
try {
@ -156,11 +191,11 @@ public final class GetNextEtnUtil {
etnBackupProps.load(fis);
} catch (FileNotFoundException e) {
statusHandler.error(CONFIG_FILE_NAME + " file does not exist!", e);
return Collections.emptyList();
return new TreeMap<String, IRequestRouter>();
} catch (IOException e) {
statusHandler.error("Error reading " + CONFIG_FILE_NAME + " file!",
e);
return Collections.emptyList();
throw e;
} finally {
if (fis != null) {
try {
@ -172,6 +207,8 @@ public final class GetNextEtnUtil {
}
}
String localhostFQDN = getLocalhostFQDN();
String[] tokens = etnBackupProps.getProperty("BACKUP.HOSTS." + siteId,
"").split(",");
@ -182,17 +219,7 @@ public final class GetNextEtnUtil {
for (String token : tokens) {
String host = token.trim().toLowerCase();
if ("localhost".equals(host)) {
try {
host = InetAddress.getLocalHost().getCanonicalHostName();
} catch (UnknownHostException e) {
statusHandler.error(
"Unable to retrieve host name for localhost.", e);
statusHandler
.handle(Priority.CRITICAL,
"ETN assignment will not be able to query local server for used ETNs. Please check your network configuration and "
+ CONFIG_FILE_NAME + ".");
continue;
}
continue;
}
IRequestRouter reqHandler = new RemoteServerRequestRouter("http://"
@ -200,7 +227,11 @@ public final class GetNextEtnUtil {
sources.put(host, reqHandler);
}
return new ArrayList<IRequestRouter>(sources.values());
IRequestRouter reqHandler = new RemoteServerRequestRouter("http://"
+ localhostFQDN + ":9581/services");
sources.put(localhostFQDN, reqHandler);
return sources;
}
/**
@ -244,10 +275,13 @@ public final class GetNextEtnUtil {
* needed if only determining a preliminary ETN. Required to be
* set to <code>true</code> if you want to actually move the
* sequence forward.
* @param etnOverride
* TODO
* @return The next ETN to be used in sequence.
*/
public static int lockAndGetNextEtn(String siteId, ActiveTableMode mode,
String phensig, Calendar currentTime, boolean isLock) {
String phensig, Calendar currentTime, boolean isLock,
Integer etnOverride) {
String lockName = getEtnClusterLockName(siteId, mode);
ClusterTask ct = null;
if (isLock) {
@ -264,19 +298,25 @@ public final class GetNextEtnUtil {
currentTime);
int nextEtn = (lastEtn != null) ? lastEtn + 1 : 1;
String year = Integer.toString(currentTime.get(Calendar.YEAR));
String eInfo = ct.getExtraInfo();
if ((!StringUtil.isEmptyString(eInfo)) && (eInfo.startsWith(year))) {
// parse year info
try {
int ctNextEtn = Integer
.parseInt(eInfo.substring(year.length() + 1)) + 1;
nextEtn = Math.max(nextEtn, ctNextEtn);
} catch (NumberFormatException e) {
statusHandler.error(
"Caught excetion parsing etn from cluster_task", e);
int sysNextEtn = -1;
if (etnOverride == null) {
String year = Integer.toString(currentTime.get(Calendar.YEAR));
String eInfo = ct.getExtraInfo();
if ((!StringUtil.isEmptyString(eInfo)) && (eInfo.startsWith(year))) {
// parse year info
try {
sysNextEtn = Integer
.parseInt(eInfo.substring(year.length() + 1)) + 1;
} catch (NumberFormatException e) {
statusHandler
.error("Caught exception parsing etn from cluster_task",
e);
}
}
} else {
sysNextEtn = etnOverride.intValue();
}
nextEtn = Math.max(nextEtn, sysNextEtn);
return nextEtn;
}
@ -330,7 +370,7 @@ public final class GetNextEtnUtil {
ActiveTableMode mode, String phensig, Calendar currentTime,
boolean isLock) {
int nextEtn = lockAndGetNextEtn(siteId, mode, phensig, currentTime,
isLock);
isLock, null);
if (isLock) {
setNextEtnAndUnlock(siteId, mode, phensig,
currentTime.get(Calendar.YEAR), nextEtn);
@ -360,50 +400,88 @@ public final class GetNextEtnUtil {
* @param hostsToQuery
* The remote hosts to query. This should also include the local
* EDEX instance initiating this operation.
* @return The next ETN to be used in sequence.
* @param reportConflictOnly
* Affects which kinds of errors get reported back to the
* requestor. If true, only cases where the value of
* <code>etnOverride</code> is less than or equal to the last ETN
* used by this site or any of its partners will be reported.
* Else, all significant errors will be reported back.
* @param etnOverride
* Allows the user to influence the next ETN assigned by using
* this value unless it is less than or equal to the last ETN
* used by this site or one of its partners.
* @return A <code>GetNextEtnResponse</code> containing the next ETN to use
* and any hosts that couldn't be contacted during this process.
* @throws UnknownHostException
*/
public static Integer getNextEtnFromPartners(String siteId,
public static GetNextEtnResponse getNextEtnFromPartners(String siteId,
ActiveTableMode mode, String phensig, Calendar currentTime,
List<IRequestRouter> hostsToQuery) {
Queue<IRequestRouter> lockQueue = new ArrayDeque<IRequestRouter>(
hostsToQuery);
Queue<IRequestRouter> unlockQueue = Collections
.asLifoQueue(new ArrayDeque<IRequestRouter>(hostsToQuery.size()));
SortedMap<String, IRequestRouter> hostsToQuery,
boolean reportConflictOnly, Integer etnOverride) {
Queue<Entry<String, IRequestRouter>> unlockQueue = Collections
.asLifoQueue(new ArrayDeque<Entry<String, IRequestRouter>>(
hostsToQuery.size()));
Map<String, Integer> resultsByHost = new HashMap<String, Integer>(
hostsToQuery.size(), 1f);
List<String> errors = new ArrayList<String>();
String mySiteId = SiteUtil.getSite();
IServerRequest getAndLockReq = new LockAndGetNextEtnRequest(siteId,
mySiteId, mode, phensig, currentTime);
int nextEtn = 1;
for (IRequestRouter router : lockQueue) {
mySiteId, mode, phensig, currentTime, etnOverride);
for (Entry<String, IRequestRouter> host : hostsToQuery.entrySet()) {
IRequestRouter router = host.getValue();
String hostName = host.getKey();
Integer partnersNextEtn = null;
try {
Integer partersNextEtn = (Integer) GetNextEtnUtil
.sendThriftRequest(router, getAndLockReq);
nextEtn = Math.max(nextEtn, partersNextEtn);
unlockQueue.add(router);
partnersNextEtn = (Integer) GetNextEtnUtil.sendThriftRequest(
router, getAndLockReq);
unlockQueue.add(host);
} catch (RemoteException e) {
statusHandler
.handle(Priority.WARN,
"Error occurred contacting one of the remote ETN partners.",
e);
String message = "Error occurred contacting remote ETN partner ["
+ hostName + "]: " + e.getLocalizedMessage();
if (!reportConflictOnly) {
errors.add(message);
}
statusHandler.handle(Priority.WARN, message, e);
}
resultsByHost.put(hostName, partnersNextEtn);
}
int nextEtn = 1;
for (Entry<String, Integer> entry : resultsByHost.entrySet()) {
int partnerEtn = (entry.getValue() != null) ? entry.getValue() : -1;
nextEtn = Math.max(nextEtn, partnerEtn);
if ((etnOverride != null) && (etnOverride < partnerEtn)) {
String message = "User-provided ETN value of "
+ etnOverride.toString()
+ " conflicts with calculated ETN value " + partnerEtn
+ " from host " + entry.getKey();
errors.add(message);
statusHandler.warn(message);
}
}
IServerRequest unlockReq = new UnlockAndSetNextEtnRequest(siteId,
mySiteId, mode, currentTime.get(Calendar.YEAR), phensig,
nextEtn);
for (IRequestRouter router : unlockQueue) {
for (Entry<String, IRequestRouter> host : unlockQueue) {
IRequestRouter router = host.getValue();
try {
GetNextEtnUtil.sendThriftRequest(router, unlockReq);
} catch (RemoteException e) {
statusHandler
.handle(Priority.WARN,
"Error occurred unlocking one of the remote ETN partners.",
e);
String message = "Error occurred unlocking remote ETN partner ["
+ host.getKey() + "]: " + e.getLocalizedMessage();
if (!reportConflictOnly) {
errors.add(message);
}
statusHandler.handle(Priority.WARN, message, e);
}
}
return nextEtn;
return new GetNextEtnResponse(nextEtn, phensig, resultsByHost, errors);
}
private static Object sendThriftRequest(final IRequestRouter router,
@ -429,4 +507,17 @@ public final class GetNextEtnUtil {
return retVal;
}
private static String getLocalhostFQDN() throws UnknownHostException {
try {
return InetAddress.getLocalHost().getCanonicalHostName();
} catch (UnknownHostException e) {
statusHandler.error("Unable to retrieve host name for localhost.",
e);
statusHandler
.handle(Priority.CRITICAL,
"ETN assignment will not be able to query local server for used ETNs. Please check your network configuration.");
throw e;
}
}
}

View file

@ -36,6 +36,7 @@ import com.raytheon.uf.edex.activetable.GetNextEtnUtil;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 19, 2013 #1843 dgilling Initial creation
* Oct 23, 2013 #1843 dgilling Update with ETN override.
*
* </pre>
*
@ -64,6 +65,6 @@ public class LockAndGetNextEtnHandler implements
+ request.getSiteID() + "]: phensig= " + request.getPhensig());
return GetNextEtnUtil.lockAndGetNextEtn(request.getSiteID(),
request.getMode(), request.getPhensig(),
request.getCurrentTime(), true);
request.getCurrentTime(), true, request.getEtnOverride());
}
}

View file

@ -12,12 +12,12 @@
#
# To configure remote ETN querying for a specific site id: you must add an
# entry to the site version of this file that looks like the following:
# BACKUP.HOSTS.KXYZ=ec-xyz,ec-yyy,ec-zzz
# BACKUP.HOSTS.KXYZ=ec-yyy,ec-zzz
#
# Now, when generating VTEC products for site KXYZ, EDEX will query hosts
# ec-xyz,ec-yyy, and ec-zzz for the last ETN used for KXYZ. Note that you must
# include the host name for this EDEX server in the list of hosts if you want
# "localhost" to be in the hosts queried for the next ETN.
# ec-yyy and ec-zzz for the last ETN used for KXYZ. Note that "localhost"
# will automatically be queried for the next ETN so there is no
# requirement to add it to the list of entries.
#
# There should only ever be one localized copy of this file on the EDEX server,
# and it should be located at
@ -26,4 +26,4 @@
# querying (say, in case of service backup), you just add a new BACKUP.HOSTS
# entry to this file.
#
# CONFIGURATION ENTRIES GO BELOW
# CONFIGURATION ENTRIES GO BELOW