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.python;bundle-version="1.12.1174",
com.raytheon.uf.common.colormap;bundle-version="1.12.1174", com.raytheon.uf.common.colormap;bundle-version="1.12.1174",
com.google.guava;bundle-version="1.0.0", 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 Bundle-ActivationPolicy: lazy
Export-Package: com.raytheon.viz.gfe, Export-Package: com.raytheon.viz.gfe,
com.raytheon.viz.gfe.constants, 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,
com.raytheon.viz.gfe.ui.runtimeui, com.raytheon.viz.gfe.ui.runtimeui,
com.raytheon.viz.gfe.ui.zoneselector com.raytheon.viz.gfe.ui.zoneselector
Import-Package: com.raytheon.uf.common.activetable, Import-Package: com.raytheon.uf.common.dissemination,
com.raytheon.uf.common.dissemination,
com.raytheon.uf.common.message, com.raytheon.uf.common.message,
com.raytheon.uf.common.python.concurrent, com.raytheon.uf.common.python.concurrent,
com.raytheon.uf.common.serialization.comm, com.raytheon.uf.common.serialization.comm,

View file

@ -29,6 +29,7 @@
# ??/??/?? ???????? Initial Creation. # ??/??/?? ???????? Initial Creation.
# 05/14/13 1842 dgilling Use GFEVtecUtil to handle NEW # 05/14/13 1842 dgilling Use GFEVtecUtil to handle NEW
# ETN assignment. # 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 # Local WFOs do not assign these numbers, so they should have
# numbers < 1000 # numbers < 1000
if phensig not in self.__tpcKeys or self.__siteID4 in self.__sitesIgnoreNatlEtn: 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: else:
presentyear = time.gmtime(self.__time)[0] presentyear = time.gmtime(self.__time)[0]
for active in activeTable: 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; package com.raytheon.viz.gfe.dialogs.formatterlauncher;
import java.text.SimpleDateFormat; 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.SWT;
import org.eclipse.swt.events.SelectionAdapter; 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.PracticeProductOfftimeRequest;
import com.raytheon.uf.common.activetable.SendPracticeProductRequest; 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.OUPRequest;
import com.raytheon.uf.common.dissemination.OUPResponse; import com.raytheon.uf.common.dissemination.OUPResponse;
import com.raytheon.uf.common.dissemination.OfficialUserProduct; 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.core.mode.CAVEMode;
import com.raytheon.viz.gfe.product.TextDBUtil; import com.raytheon.viz.gfe.product.TextDBUtil;
import com.raytheon.viz.gfe.vtec.GFEVtecUtil; import com.raytheon.viz.gfe.vtec.GFEVtecUtil;
import com.raytheon.viz.texteditor.util.VtecObject;
import com.raytheon.viz.ui.dialogs.CaveSWTDialog; 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 * 08 MAY 2013 1842 dgilling Use VtecUtil to set product ETNs, fix
* warnings. * warnings.
* 07 Jun 2013 1981 mpduff Set user's id in OUPRequest as it is now a protected operation. * 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> * </pre>
* *
* @author lvenable * @author lvenable
@ -334,46 +342,116 @@ public class StoreTransmitDlg extends CaveSWTDialog implements
// Store/Transmit the product... // Store/Transmit the product...
if (!countdownThread.threadCancelled()) { if (!countdownThread.threadCancelled()) {
boolean retrieveEtnFailed = false;
try { Set<VtecObject> vtecsToAssignEtn = GFEVtecUtil
productText = GFEVtecUtil.finalizeETNs(productText); .getVtecLinesThatNeedEtn(productText);
} catch (VizException e) { Map<String, Integer> etnCache = new HashMap<String, Integer>();
statusHandler.handle(Priority.CRITICAL,
"Error setting ETNs for product", e); for (VtecObject vtec : vtecsToAssignEtn) {
sendTransmissionStatus(ConfigData.productStateEnum.Failed); try {
VizApp.runAsync(new Runnable() { 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 @Override
public void run() { 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 // 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; package com.raytheon.viz.gfe.vtec;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.Collections;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet; 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.uf.viz.core.exception.VizException;
import com.raytheon.viz.texteditor.util.VtecObject; import com.raytheon.viz.texteditor.util.VtecObject;
import com.raytheon.viz.texteditor.util.VtecUtil; import com.raytheon.viz.texteditor.util.VtecUtil;
@ -50,6 +53,7 @@ import com.raytheon.viz.texteditor.util.VtecUtil;
* multiple NEW segments with the same * multiple NEW segments with the same
* phensig. * phensig.
* Aug 29, 2013 #1843 dgilling Add hooks for inter-site ETN assignment. * Aug 29, 2013 #1843 dgilling Add hooks for inter-site ETN assignment.
* Oct 21, 2013 #1843 dgilling Use new GetNextEtnResponse.
* *
* </pre> * </pre>
* *
@ -78,29 +82,152 @@ public class GFEVtecUtil {
throw new AssertionError(); throw new AssertionError();
} }
public static int getNextEtn(String office, String phensig, boolean lockEtn) /**
throws VizException { * Gets the next available ETN for a specific product and office.
return getNextEtn(office, phensig, lockEtn, false); *
* @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 { 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)) { * Gets the next available ETN for a specific product and office.
return message; *
* @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 Set<VtecObject> phensigs = new HashSet<VtecObject>();
// 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>();
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(); StringBuffer finalOutput = new StringBuffer();
while (vtecMatcher.find()) { while (vtecMatcher.find()) {
VtecObject vtec = new VtecObject(vtecMatcher.group()); VtecObject vtec = new VtecObject(vtecMatcher.group());
@ -111,13 +238,13 @@ public class GFEVtecUtil {
&& ((!NATIONAL_PHENSIGS.contains(vtec.getPhensig())) || (IGNORE_NATIONAL_ETN && ((!NATIONAL_PHENSIGS.contains(vtec.getPhensig())) || (IGNORE_NATIONAL_ETN
.contains(vtec.getOffice()) && TROPICAL_PHENSIGS .contains(vtec.getOffice()) && TROPICAL_PHENSIGS
.contains(vtec.getPhensig())))) { .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(); String cacheKey = vtec.getPhensig();
Integer newEtn = etnCache.get(cacheKey); Integer newEtn = etnCache.get(cacheKey);
if (newEtn == null) {
newEtn = getNextEtn(vtec.getOffice(), vtec.getPhensig(),
true, true);
etnCache.put(cacheKey, newEtn);
}
vtec.setSequence(newEtn); vtec.setSequence(newEtn);
} }
vtecMatcher 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.common.dataplugin.radar;bundle-version="1.0.0",
com.raytheon.uf.viz.localization, com.raytheon.uf.viz.localization,
com.raytheon.uf.common.auth;bundle-version="1.12.1174", 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 Bundle-ActivationPolicy: lazy
Export-Package: com.raytheon.viz.texteditor, Export-Package: com.raytheon.viz.texteditor,
com.raytheon.viz.texteditor.alarmalert.dialogs, 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.scripting.runner,
com.raytheon.viz.texteditor.util com.raytheon.viz.texteditor.util
Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: com.raytheon.uf.common.activetable, Import-Package: com.raytheon.uf.common.comm,
com.raytheon.uf.common.comm,
com.raytheon.uf.common.dataplugin.warning.util, com.raytheon.uf.common.dataplugin.warning.util,
com.raytheon.uf.common.dissemination, com.raytheon.uf.common.dissemination,
com.raytheon.uf.common.message, 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.ActiveTableMode;
import com.raytheon.uf.common.activetable.GetNextEtnRequest; 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.common.time.SimulatedTime;
import com.raytheon.uf.viz.core.exception.VizException; import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.requests.ThriftClient; 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 * Feb 09, 2009 bwoodle Initial creation
* May 08, 2013 #1842 dgilling Code cleanup. * May 08, 2013 #1842 dgilling Code cleanup.
* Aug 29, 2013 #1843 dgilling Use new GetNextEtnRequest constructor. * Aug 29, 2013 #1843 dgilling Use new GetNextEtnRequest constructor.
* Oct 21, 2013 #1843 dgilling Use new GetNextEtnResponse.
* *
* </pre> * </pre>
* *
@ -106,8 +108,10 @@ public class VtecUtil {
} }
if (vtec.getAction().equals("NEW")) { if (vtec.getAction().equals("NEW")) {
vtec.setSequence(getNextEtn(vtec.getOffice(), vtec.getPhenomena() int nextEtn = getNextEtn(vtec.getOffice(),
+ "." + vtec.getSignificance(), lockEtn)); vtec.getPhenomena() + "." + vtec.getSignificance(), lockEtn)
.getNextEtn();
vtec.setSequence(nextEtn);
} }
return replaceFirstVtecString(message, vtec); return replaceFirstVtecString(message, vtec);
} }
@ -133,9 +137,9 @@ public class VtecUtil {
* If an error occurs while submitting or processing the remote * If an error occurs while submitting or processing the remote
* request. * request.
*/ */
public static int getNextEtn(String office, String phensig, boolean lockEtn) public static GetNextEtnResponse getNextEtn(String office, String phensig,
throws VizException { boolean lockEtn) throws VizException {
return getNextEtn(office, phensig, lockEtn, false); return getNextEtn(office, phensig, lockEtn, false, false, null);
} }
/** /**
@ -159,26 +163,37 @@ public class VtecUtil {
* determine the next ETN. See {@link * determine the next ETN. See {@link
* GetNextEtnUtil#getNextEtnFromPartners(String, ActiveTableMode, * GetNextEtnUtil#getNextEtnFromPartners(String, ActiveTableMode,
* String, Calendar, List<IRequestRouter>)} for more information. * 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. * @return The next ETN in sequence, given the office and phensig.
* @throws VizException * @throws VizException
* If an error occurs while submitting or processing the remote * If an error occurs while submitting or processing the remote
* request. * request.
*/ */
public static int getNextEtn(String office, String phensig, public static GetNextEtnResponse getNextEtn(String office, String phensig,
boolean lockEtn, boolean performISC) throws VizException { boolean lockEtn, boolean performISC, boolean reportOnlyConflict,
Integer etnOverride) throws VizException {
Calendar currentTime = Calendar.getInstance(); Calendar currentTime = Calendar.getInstance();
currentTime.setTime(SimulatedTime.getSystemTime().getTime()); currentTime.setTime(SimulatedTime.getSystemTime().getTime());
ActiveTableMode activeTable = (CAVEMode.getMode() ActiveTableMode activeTable = (CAVEMode.getMode()
.equals(CAVEMode.PRACTICE)) ? ActiveTableMode.PRACTICE .equals(CAVEMode.PRACTICE)) ? ActiveTableMode.PRACTICE
: ActiveTableMode.OPERATIONAL; : ActiveTableMode.OPERATIONAL;
GetNextEtnRequest req = new GetNextEtnRequest(office, activeTable, GetNextEtnRequest req = new GetNextEtnRequest(office, activeTable,
phensig, currentTime, lockEtn, performISC); phensig, currentTime, lockEtn, performISC, reportOnlyConflict,
etnOverride);
int rval = 1; GetNextEtnResponse resp = (GetNextEtnResponse) ThriftClient
Integer resp = (Integer) ThriftClient.sendRequest(req); .sendRequest(req);
if (resp != null) { GetNextEtnResponse rval = (resp != null) ? resp
rval = resp; : new GetNextEtnResponse(1, phensig);
}
return rval; 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.message;bundle-version="1.12.1174",
com.raytheon.uf.common.localization;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", 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 * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Feb 14, 2011 rjpeter Initial creation * 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> * </pre>
* *
@ -62,39 +63,89 @@ public class GetNextEtnRequest implements IServerRequest {
@DynamicSerializeElement @DynamicSerializeElement
private boolean performISC; private boolean performISC;
@DynamicSerializeElement
private boolean reportConflictOnly;
@DynamicSerializeElement
private Integer etnOverride;
public GetNextEtnRequest() { public GetNextEtnRequest() {
// no-op, for dynamic serialize support // no-op, for dynamic serialize support
} }
/** /**
* Constructs a GetNextEtnRequest.
*
* @param siteID * @param siteID
* The 4-character site ID of the office.
* @param mode * @param mode
* Active table mode.
* @param phensig * @param phensig
* The phenomenon and significance of the hazard concatenated
* with a '.' (e.g., TO.W or DU.Y)
* @param currentTime * @param currentTime
* <code>Calendar</code> representing time (needed for DRT mode).
* @param lockEtn * @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, public GetNextEtnRequest(String siteID, ActiveTableMode mode,
String phensig, Calendar currentTime, boolean lockEtn) { 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 * @param siteID
* The 4-character site ID of the office.
* @param mode * @param mode
* Active table mode.
* @param phensig * @param phensig
* The phenomenon and significance of the hazard concatenated
* with a '.' (e.g., TO.W or DU.Y)
* @param currentTime * @param currentTime
* <code>Calendar</code> representing time (needed for DRT mode).
* @param lockEtn * @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 * @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, public GetNextEtnRequest(String siteID, ActiveTableMode mode,
String phensig, Calendar currentTime, boolean lockEtn, String phensig, Calendar currentTime, boolean lockEtn,
boolean performISC) { boolean performISC, boolean reportConflictOnly, Integer etnOverride) {
this.siteID = siteID; this.siteID = siteID;
this.mode = mode; this.mode = mode;
this.phensig = phensig; this.phensig = phensig;
this.currentTime = currentTime; this.currentTime = currentTime;
this.lockEtn = lockEtn; this.lockEtn = lockEtn;
this.performISC = performISC; this.performISC = performISC;
this.reportConflictOnly = reportConflictOnly;
this.etnOverride = etnOverride;
} }
public String getSiteID() { public String getSiteID() {
@ -144,4 +195,20 @@ public class GetNextEtnRequest implements IServerRequest {
public void setPerformISC(boolean performISC) { public void setPerformISC(boolean performISC) {
this.performISC = 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 * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Aug 19, 2013 #1843 dgilling Initial creation * Aug 19, 2013 #1843 dgilling Initial creation
* Oct 21, 2013 #1843 dgilling Add ETN override field.
* *
* </pre> * </pre>
* *
@ -61,17 +62,27 @@ public class LockAndGetNextEtnRequest implements IServerRequest {
@DynamicSerializeElement @DynamicSerializeElement
private Calendar currentTime; private Calendar currentTime;
@DynamicSerializeElement
private Integer etnOverride;
public LockAndGetNextEtnRequest() { public LockAndGetNextEtnRequest() {
// default constructor for thrift/dynamicserialize // default constructor for thrift/dynamicserialize
} }
public LockAndGetNextEtnRequest(String siteID, String requestorSiteID, public LockAndGetNextEtnRequest(String siteID, String requestorSiteID,
ActiveTableMode mode, String phensig, Calendar currentTime) { 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.siteID = siteID;
this.requestorSiteID = requestorSiteID; this.requestorSiteID = requestorSiteID;
this.mode = mode; this.mode = mode;
this.phensig = phensig; this.phensig = phensig;
this.currentTime = currentTime; this.currentTime = currentTime;
this.etnOverride = etnOverride;
} }
public String getSiteID() { public String getSiteID() {
@ -113,4 +124,12 @@ public class LockAndGetNextEtnRequest implements IServerRequest {
public void setCurrentTime(Calendar currentTime) { public void setCurrentTime(Calendar currentTime) {
this.currentTime = 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; package com.raytheon.uf.edex.activetable;
import com.raytheon.uf.common.activetable.GetNextEtnRequest; import com.raytheon.uf.common.activetable.GetNextEtnRequest;
import com.raytheon.uf.common.activetable.response.GetNextEtnResponse;
import com.raytheon.uf.common.serialization.comm.IRequestHandler; 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> * <pre>
* *
@ -31,6 +34,7 @@ import com.raytheon.uf.common.serialization.comm.IRequestHandler;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Feb 15, 2011 rjpeter Initial creation * Feb 15, 2011 rjpeter Initial creation
* Oct 21, 2013 #1843 dgilling Change return type.
* *
* </pre> * </pre>
* *
@ -41,12 +45,14 @@ import com.raytheon.uf.common.serialization.comm.IRequestHandler;
public class GetNextEtnHandler implements IRequestHandler<GetNextEtnRequest> { public class GetNextEtnHandler implements IRequestHandler<GetNextEtnRequest> {
@Override @Override
public Integer handleRequest(GetNextEtnRequest request) throws Exception { public GetNextEtnResponse handleRequest(GetNextEtnRequest request)
Integer nextEtn = GetNextEtnUtil.getNextEtn(request.getSiteID(), throws Exception {
request.getMode(), request.getPhensig(), GetNextEtnResponse response = GetNextEtnUtil.getNextEtn(
request.getSiteID(), request.getMode(), request.getPhensig(),
request.getCurrentTime(), request.isLockEtn(), request.getCurrentTime(), request.isLockEtn(),
request.isPerformISC()); request.isPerformISC(), request.isReportConflictOnly(),
return nextEtn; request.getEtnOverride());
return response;
} }
} }

View file

@ -28,9 +28,13 @@ import java.net.UnknownHostException;
import java.rmi.RemoteException; import java.rmi.RemoteException;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties; import java.util.Properties;
import java.util.Queue; import java.util.Queue;
import java.util.SortedMap; 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.ActiveTableMode;
import com.raytheon.uf.common.activetable.request.LockAndGetNextEtnRequest; import com.raytheon.uf.common.activetable.request.LockAndGetNextEtnRequest;
import com.raytheon.uf.common.activetable.request.UnlockAndSetNextEtnRequest; 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.IPathManager;
import com.raytheon.uf.common.localization.LocalizationContext; import com.raytheon.uf.common.localization.LocalizationContext;
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType; import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
@ -79,7 +84,7 @@ public final class GetNextEtnUtil {
private static final transient IUFStatusHandler statusHandler = UFStatus private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(GetNextEtnUtil.class); .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"; 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 * <code>isLock</code> is false, this flag is effectively false
* and your configured remote partners will not be contacted to * and your configured remote partners will not be contacted to
* determine the next ETN. * 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, public static GetNextEtnResponse getNextEtn(String siteId,
String phensig, Calendar currentTime, boolean isLock, ActiveTableMode mode, String phensig, Calendar currentTime,
boolean performISC) { boolean isLock, boolean performISC, boolean reportConflictOnly,
List<IRequestRouter> hostsToQuery = Collections.emptyList(); Integer etnOverride) {
SortedMap<String, IRequestRouter> hostsToQuery = new TreeMap<String, IRequestRouter>();
List<String> readEtnSourcesErrors = Collections.emptyList();
if (performISC) { 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; GetNextEtnResponse response;
if (performISC && isLock && (!hostsToQuery.isEmpty())) { if (performISC && isLock && (!hostsToQuery.isEmpty())
nextEtn = GetNextEtnUtil.getNextEtnFromPartners(siteId, mode, && (readEtnSourcesErrors.isEmpty())) {
phensig, currentTime, hostsToQuery); response = GetNextEtnUtil.getNextEtnFromPartners(siteId, mode,
phensig, currentTime, hostsToQuery, reportConflictOnly,
etnOverride);
} else { } else {
nextEtn = GetNextEtnUtil.getNextEtnFromLocal(siteId, mode, phensig, int nextEtn = GetNextEtnUtil.getNextEtnFromLocal(siteId, mode,
currentTime, isLock); 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(); Properties etnBackupProps = new Properties();
FileInputStream fis = null; FileInputStream fis = null;
try { try {
@ -156,11 +191,11 @@ public final class GetNextEtnUtil {
etnBackupProps.load(fis); etnBackupProps.load(fis);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
statusHandler.error(CONFIG_FILE_NAME + " file does not exist!", e); statusHandler.error(CONFIG_FILE_NAME + " file does not exist!", e);
return Collections.emptyList(); return new TreeMap<String, IRequestRouter>();
} catch (IOException e) { } catch (IOException e) {
statusHandler.error("Error reading " + CONFIG_FILE_NAME + " file!", statusHandler.error("Error reading " + CONFIG_FILE_NAME + " file!",
e); e);
return Collections.emptyList(); throw e;
} finally { } finally {
if (fis != null) { if (fis != null) {
try { try {
@ -172,6 +207,8 @@ public final class GetNextEtnUtil {
} }
} }
String localhostFQDN = getLocalhostFQDN();
String[] tokens = etnBackupProps.getProperty("BACKUP.HOSTS." + siteId, String[] tokens = etnBackupProps.getProperty("BACKUP.HOSTS." + siteId,
"").split(","); "").split(",");
@ -182,17 +219,7 @@ public final class GetNextEtnUtil {
for (String token : tokens) { for (String token : tokens) {
String host = token.trim().toLowerCase(); String host = token.trim().toLowerCase();
if ("localhost".equals(host)) { if ("localhost".equals(host)) {
try { continue;
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;
}
} }
IRequestRouter reqHandler = new RemoteServerRequestRouter("http://" IRequestRouter reqHandler = new RemoteServerRequestRouter("http://"
@ -200,7 +227,11 @@ public final class GetNextEtnUtil {
sources.put(host, reqHandler); 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 * needed if only determining a preliminary ETN. Required to be
* set to <code>true</code> if you want to actually move the * set to <code>true</code> if you want to actually move the
* sequence forward. * sequence forward.
* @param etnOverride
* TODO
* @return The next ETN to be used in sequence. * @return The next ETN to be used in sequence.
*/ */
public static int lockAndGetNextEtn(String siteId, ActiveTableMode mode, 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); String lockName = getEtnClusterLockName(siteId, mode);
ClusterTask ct = null; ClusterTask ct = null;
if (isLock) { if (isLock) {
@ -264,19 +298,25 @@ public final class GetNextEtnUtil {
currentTime); currentTime);
int nextEtn = (lastEtn != null) ? lastEtn + 1 : 1; int nextEtn = (lastEtn != null) ? lastEtn + 1 : 1;
String year = Integer.toString(currentTime.get(Calendar.YEAR)); int sysNextEtn = -1;
String eInfo = ct.getExtraInfo(); if (etnOverride == null) {
if ((!StringUtil.isEmptyString(eInfo)) && (eInfo.startsWith(year))) { String year = Integer.toString(currentTime.get(Calendar.YEAR));
// parse year info String eInfo = ct.getExtraInfo();
try { if ((!StringUtil.isEmptyString(eInfo)) && (eInfo.startsWith(year))) {
int ctNextEtn = Integer // parse year info
.parseInt(eInfo.substring(year.length() + 1)) + 1; try {
nextEtn = Math.max(nextEtn, ctNextEtn); sysNextEtn = Integer
} catch (NumberFormatException e) { .parseInt(eInfo.substring(year.length() + 1)) + 1;
statusHandler.error( } catch (NumberFormatException e) {
"Caught excetion parsing etn from cluster_task", e); statusHandler
.error("Caught exception parsing etn from cluster_task",
e);
}
} }
} else {
sysNextEtn = etnOverride.intValue();
} }
nextEtn = Math.max(nextEtn, sysNextEtn);
return nextEtn; return nextEtn;
} }
@ -330,7 +370,7 @@ public final class GetNextEtnUtil {
ActiveTableMode mode, String phensig, Calendar currentTime, ActiveTableMode mode, String phensig, Calendar currentTime,
boolean isLock) { boolean isLock) {
int nextEtn = lockAndGetNextEtn(siteId, mode, phensig, currentTime, int nextEtn = lockAndGetNextEtn(siteId, mode, phensig, currentTime,
isLock); isLock, null);
if (isLock) { if (isLock) {
setNextEtnAndUnlock(siteId, mode, phensig, setNextEtnAndUnlock(siteId, mode, phensig,
currentTime.get(Calendar.YEAR), nextEtn); currentTime.get(Calendar.YEAR), nextEtn);
@ -360,50 +400,88 @@ public final class GetNextEtnUtil {
* @param hostsToQuery * @param hostsToQuery
* The remote hosts to query. This should also include the local * The remote hosts to query. This should also include the local
* EDEX instance initiating this operation. * 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, ActiveTableMode mode, String phensig, Calendar currentTime,
List<IRequestRouter> hostsToQuery) { SortedMap<String, IRequestRouter> hostsToQuery,
Queue<IRequestRouter> lockQueue = new ArrayDeque<IRequestRouter>( boolean reportConflictOnly, Integer etnOverride) {
hostsToQuery); Queue<Entry<String, IRequestRouter>> unlockQueue = Collections
Queue<IRequestRouter> unlockQueue = Collections .asLifoQueue(new ArrayDeque<Entry<String, IRequestRouter>>(
.asLifoQueue(new ArrayDeque<IRequestRouter>(hostsToQuery.size())); hostsToQuery.size()));
Map<String, Integer> resultsByHost = new HashMap<String, Integer>(
hostsToQuery.size(), 1f);
List<String> errors = new ArrayList<String>();
String mySiteId = SiteUtil.getSite(); String mySiteId = SiteUtil.getSite();
IServerRequest getAndLockReq = new LockAndGetNextEtnRequest(siteId, IServerRequest getAndLockReq = new LockAndGetNextEtnRequest(siteId,
mySiteId, mode, phensig, currentTime); mySiteId, mode, phensig, currentTime, etnOverride);
int nextEtn = 1; for (Entry<String, IRequestRouter> host : hostsToQuery.entrySet()) {
for (IRequestRouter router : lockQueue) { IRequestRouter router = host.getValue();
String hostName = host.getKey();
Integer partnersNextEtn = null;
try { try {
Integer partersNextEtn = (Integer) GetNextEtnUtil partnersNextEtn = (Integer) GetNextEtnUtil.sendThriftRequest(
.sendThriftRequest(router, getAndLockReq); router, getAndLockReq);
nextEtn = Math.max(nextEtn, partersNextEtn); unlockQueue.add(host);
unlockQueue.add(router);
} catch (RemoteException e) { } catch (RemoteException e) {
statusHandler String message = "Error occurred contacting remote ETN partner ["
.handle(Priority.WARN, + hostName + "]: " + e.getLocalizedMessage();
"Error occurred contacting one of the remote ETN partners.", if (!reportConflictOnly) {
e); 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, IServerRequest unlockReq = new UnlockAndSetNextEtnRequest(siteId,
mySiteId, mode, currentTime.get(Calendar.YEAR), phensig, mySiteId, mode, currentTime.get(Calendar.YEAR), phensig,
nextEtn); nextEtn);
for (IRequestRouter router : unlockQueue) { for (Entry<String, IRequestRouter> host : unlockQueue) {
IRequestRouter router = host.getValue();
try { try {
GetNextEtnUtil.sendThriftRequest(router, unlockReq); GetNextEtnUtil.sendThriftRequest(router, unlockReq);
} catch (RemoteException e) { } catch (RemoteException e) {
statusHandler String message = "Error occurred unlocking remote ETN partner ["
.handle(Priority.WARN, + host.getKey() + "]: " + e.getLocalizedMessage();
"Error occurred unlocking one of the remote ETN partners.", if (!reportConflictOnly) {
e); 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, private static Object sendThriftRequest(final IRequestRouter router,
@ -429,4 +507,17 @@ public final class GetNextEtnUtil {
return retVal; 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 * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Aug 19, 2013 #1843 dgilling Initial creation * Aug 19, 2013 #1843 dgilling Initial creation
* Oct 23, 2013 #1843 dgilling Update with ETN override.
* *
* </pre> * </pre>
* *
@ -64,6 +65,6 @@ public class LockAndGetNextEtnHandler implements
+ request.getSiteID() + "]: phensig= " + request.getPhensig()); + request.getSiteID() + "]: phensig= " + request.getPhensig());
return GetNextEtnUtil.lockAndGetNextEtn(request.getSiteID(), return GetNextEtnUtil.lockAndGetNextEtn(request.getSiteID(),
request.getMode(), request.getPhensig(), 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 # 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: # 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 # 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 # ec-yyy and ec-zzz for the last ETN used for KXYZ. Note that "localhost"
# include the host name for this EDEX server in the list of hosts if you want # will automatically be queried for the next ETN so there is no
# "localhost" to be in the hosts queried for the next ETN. # requirement to add it to the list of entries.
# #
# There should only ever be one localized copy of this file on the EDEX server, # There should only ever be one localized copy of this file on the EDEX server,
# and it should be located at # and it should be located at
@ -26,4 +26,4 @@
# querying (say, in case of service backup), you just add a new BACKUP.HOSTS # querying (say, in case of service backup), you just add a new BACKUP.HOSTS
# entry to this file. # entry to this file.
# #
# CONFIGURATION ENTRIES GO BELOW # CONFIGURATION ENTRIES GO BELOW