From b8c247b46964c1602b737742d4f250efbee3049f Mon Sep 17 00:00:00 2001 From: David Gillingham Date: Tue, 13 Aug 2013 13:25:16 -0500 Subject: [PATCH] Issue #1843: Implement ETN remote partners functionality. Change-Id: I827e312941b4dbd15e3c6c90e04e38ffbf457294 Former-commit-id: f72a420d538ab846952eb5a176eac95c3eb03a62 [formerly b0c40550c1448894915583a0426e78602cbe05c6] Former-commit-id: 03593245239fab793a03f31ee3dc852a846da48c --- .../raytheon/viz/gfe/vtec/GFEVtecUtil.java | 12 +- .../viz/texteditor/util/VtecUtil.java | 71 ++- .../common/activetable/GetNextEtnRequest.java | 46 ++ .../request/LockAndGetNextEtnRequest.java | 116 +++++ .../request/UnlockAndSetNextEtnRequest.java | 126 +++++ .../META-INF/MANIFEST.MF | 4 +- .../res/spring/activetable-request.xml | 12 + .../uf/edex/activetable/ActiveTable.java | 108 ++--- .../edex/activetable/GetNextEtnHandler.java | 5 +- .../uf/edex/activetable/GetNextEtnUtil.java | 432 ++++++++++++++++++ .../handler/LockAndGetNextEtnHandler.java | 69 +++ .../UnlockActiveTablePhenSigHandler.java | 72 +++ .../base/vtec/remote-etn-partners.properties | 29 ++ 13 files changed, 1010 insertions(+), 92 deletions(-) create mode 100644 edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/request/LockAndGetNextEtnRequest.java create mode 100644 edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/request/UnlockAndSetNextEtnRequest.java create mode 100644 edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/GetNextEtnUtil.java create mode 100644 edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/handler/LockAndGetNextEtnHandler.java create mode 100644 edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/handler/UnlockActiveTablePhenSigHandler.java create mode 100644 edexOsgi/com.raytheon.uf.edex.activetable/utility/edex_static/base/vtec/remote-etn-partners.properties diff --git a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/vtec/GFEVtecUtil.java b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/vtec/GFEVtecUtil.java index 65c06b9a35..361e6d9feb 100644 --- a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/vtec/GFEVtecUtil.java +++ b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/vtec/GFEVtecUtil.java @@ -49,6 +49,7 @@ import com.raytheon.viz.texteditor.util.VtecUtil; * Aug 07, 2013 #1842 dgilling Fix ETN assignment for products with * multiple NEW segments with the same * phensig. + * Aug 29, 2013 #1843 dgilling Add hooks for inter-site ETN assignment. * * * @@ -79,7 +80,12 @@ public class GFEVtecUtil { public static int getNextEtn(String office, String phensig, boolean lockEtn) throws VizException { - return VtecUtil.getNextEtn(office, phensig, lockEtn); + return getNextEtn(office, phensig, lockEtn, false); + } + + public static int getNextEtn(String office, String phensig, + boolean lockEtn, boolean performISC) throws VizException { + return VtecUtil.getNextEtn(office, phensig, lockEtn, performISC); } public static String finalizeETNs(String message) throws VizException { @@ -108,8 +114,8 @@ public class GFEVtecUtil { String cacheKey = vtec.getPhensig(); Integer newEtn = etnCache.get(cacheKey); if (newEtn == null) { - newEtn = VtecUtil.getNextEtn(vtec.getOffice(), - vtec.getPhensig(), true); + newEtn = getNextEtn(vtec.getOffice(), vtec.getPhensig(), + true, true); etnCache.put(cacheKey, newEtn); } vtec.setSequence(newEtn); diff --git a/cave/com.raytheon.viz.texteditor/src/com/raytheon/viz/texteditor/util/VtecUtil.java b/cave/com.raytheon.viz.texteditor/src/com/raytheon/viz/texteditor/util/VtecUtil.java index bf3ce19eef..b4f9944d71 100644 --- a/cave/com.raytheon.viz.texteditor/src/com/raytheon/viz/texteditor/util/VtecUtil.java +++ b/cave/com.raytheon.viz.texteditor/src/com/raytheon/viz/texteditor/util/VtecUtil.java @@ -43,6 +43,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. * * * @@ -111,23 +112,69 @@ public class VtecUtil { return replaceFirstVtecString(message, vtec); } + /** + * 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 occurs while submitting or processing the remote + * request. + */ public static int getNextEtn(String office, String phensig, boolean lockEtn) throws VizException { - int rval = 1; - GetNextEtnRequest req = new GetNextEtnRequest(); - req.setSiteID(office); - req.setPhensig(phensig); - req.setLockEtn(lockEtn); + 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. + * @param performISC + * Whether or not to collaborate with neighboring sites to + * determine the next ETN. See {@link + * GetNextEtnUtil#getNextEtnFromPartners(String, ActiveTableMode, + * String, Calendar, List)} for more information. + * @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 { Calendar currentTime = Calendar.getInstance(); currentTime.setTime(SimulatedTime.getSystemTime().getTime()); - req.setCurrentTime(currentTime); - CAVEMode mode = CAVEMode.getMode(); - if (mode.equals(CAVEMode.PRACTICE)) { - req.setMode(ActiveTableMode.PRACTICE); - } else { - req.setMode(ActiveTableMode.OPERATIONAL); - } + ActiveTableMode activeTable = (CAVEMode.getMode() + .equals(CAVEMode.PRACTICE)) ? ActiveTableMode.PRACTICE + : ActiveTableMode.OPERATIONAL; + GetNextEtnRequest req = new GetNextEtnRequest(office, activeTable, + phensig, currentTime, lockEtn, performISC); + int rval = 1; Integer resp = (Integer) ThriftClient.sendRequest(req); if (resp != null) { rval = resp; diff --git a/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/GetNextEtnRequest.java b/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/GetNextEtnRequest.java index 1b4d4484f5..c152b5c307 100644 --- a/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/GetNextEtnRequest.java +++ b/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/GetNextEtnRequest.java @@ -34,6 +34,7 @@ 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. * * * @@ -58,6 +59,44 @@ public class GetNextEtnRequest implements IServerRequest { @DynamicSerializeElement private boolean lockEtn; + @DynamicSerializeElement + private boolean performISC; + + public GetNextEtnRequest() { + // no-op, for dynamic serialize support + } + + /** + * @param siteID + * @param mode + * @param phensig + * @param currentTime + * @param lockEtn + */ + public GetNextEtnRequest(String siteID, ActiveTableMode mode, + String phensig, Calendar currentTime, boolean lockEtn) { + this(siteID, mode, phensig, currentTime, lockEtn, false); + } + + /** + * @param siteID + * @param mode + * @param phensig + * @param currentTime + * @param lockEtn + * @param performISC + */ + public GetNextEtnRequest(String siteID, ActiveTableMode mode, + String phensig, Calendar currentTime, boolean lockEtn, + boolean performISC) { + this.siteID = siteID; + this.mode = mode; + this.phensig = phensig; + this.currentTime = currentTime; + this.lockEtn = lockEtn; + this.performISC = performISC; + } + public String getSiteID() { return siteID; } @@ -98,4 +137,11 @@ public class GetNextEtnRequest implements IServerRequest { this.currentTime = currentTime; } + public boolean isPerformISC() { + return performISC; + } + + public void setPerformISC(boolean performISC) { + this.performISC = performISC; + } } diff --git a/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/request/LockAndGetNextEtnRequest.java b/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/request/LockAndGetNextEtnRequest.java new file mode 100644 index 0000000000..fd40a7048b --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/request/LockAndGetNextEtnRequest.java @@ -0,0 +1,116 @@ +/** + * 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.request; + +import java.util.Calendar; + +import com.raytheon.uf.common.activetable.ActiveTableMode; +import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; +import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; +import com.raytheon.uf.common.serialization.comm.IServerRequest; + +/** + * Request to lock active table and get next ETN to be used. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Aug 19, 2013  #1843     dgilling     Initial creation
+ * 
+ * 
+ * + * @author dgilling + * @version 1.0 + */ + +@DynamicSerialize +public class LockAndGetNextEtnRequest implements IServerRequest { + + @DynamicSerializeElement + private String siteID; + + @DynamicSerializeElement + private String requestorSiteID; + + @DynamicSerializeElement + private ActiveTableMode mode; + + @DynamicSerializeElement + private String phensig; + + @DynamicSerializeElement + private Calendar currentTime; + + public LockAndGetNextEtnRequest() { + // default constructor for thrift/dynamicserialize + } + + public LockAndGetNextEtnRequest(String siteID, String requestorSiteID, + ActiveTableMode mode, String phensig, Calendar currentTime) { + this.siteID = siteID; + this.requestorSiteID = requestorSiteID; + this.mode = mode; + this.phensig = phensig; + this.currentTime = currentTime; + } + + public String getSiteID() { + return siteID; + } + + public void setSiteID(String siteID) { + this.siteID = siteID; + } + + public String getRequestorSiteID() { + return requestorSiteID; + } + + public void setRequestorSiteID(String requestorSiteID) { + this.requestorSiteID = requestorSiteID; + } + + public ActiveTableMode getMode() { + return mode; + } + + public void setMode(ActiveTableMode mode) { + this.mode = mode; + } + + public String getPhensig() { + return phensig; + } + + public void setPhensig(String phensig) { + this.phensig = phensig; + } + + public Calendar getCurrentTime() { + return currentTime; + } + + public void setCurrentTime(Calendar currentTime) { + this.currentTime = currentTime; + } +} diff --git a/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/request/UnlockAndSetNextEtnRequest.java b/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/request/UnlockAndSetNextEtnRequest.java new file mode 100644 index 0000000000..6824a5ffa5 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/request/UnlockAndSetNextEtnRequest.java @@ -0,0 +1,126 @@ +/** + * 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.request; + +import com.raytheon.uf.common.activetable.ActiveTableMode; +import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; +import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; +import com.raytheon.uf.common.serialization.comm.IServerRequest; + +/** + * Request to unlock active table and set next ETN to be used. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Aug 19, 2013  #1843     dgilling     Initial creation
+ * 
+ * 
+ * + * @author dgilling + * @version 1.0 + */ + +@DynamicSerialize +public class UnlockAndSetNextEtnRequest implements IServerRequest { + + @DynamicSerializeElement + private String siteID; + + @DynamicSerializeElement + private String requestorSiteID; + + @DynamicSerializeElement + private ActiveTableMode mode; + + @DynamicSerializeElement + private int year; + + @DynamicSerializeElement + private String phensig; + + @DynamicSerializeElement + private int newEtn; + + public UnlockAndSetNextEtnRequest() { + // default constructor for thrift/dynamicserialize + } + + public UnlockAndSetNextEtnRequest(String siteID, String requestorSiteID, + ActiveTableMode mode, int year, String phensig, int newEtn) { + this.siteID = siteID; + this.requestorSiteID = requestorSiteID; + this.mode = mode; + this.year = year; + this.phensig = phensig; + this.newEtn = newEtn; + } + + public String getSiteID() { + return siteID; + } + + public void setSiteID(String siteID) { + this.siteID = siteID; + } + + public String getRequestorSiteID() { + return requestorSiteID; + } + + public void setRequestorSiteID(String requestorSiteID) { + this.requestorSiteID = requestorSiteID; + } + + public ActiveTableMode getMode() { + return mode; + } + + public void setMode(ActiveTableMode mode) { + this.mode = mode; + } + + public int getYear() { + return year; + } + + public void setYear(int year) { + this.year = year; + } + + public String getPhensig() { + return phensig; + } + + public void setPhensig(String phensig) { + this.phensig = phensig; + } + + public int getNewEtn() { + return newEtn; + } + + public void setNewEtn(int newEtn) { + this.newEtn = newEtn; + } +} diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.edex.activetable/META-INF/MANIFEST.MF index f1d2e7a4a5..39297c5ed9 100644 --- a/edexOsgi/com.raytheon.uf.edex.activetable/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.edex.activetable/META-INF/MANIFEST.MF @@ -24,6 +24,8 @@ Require-Bundle: com.raytheon.uf.common.localization;bundle-version="1.11.1", com.raytheon.uf.common.message;bundle-version="1.12.1174", com.raytheon.uf.common.activetable;bundle-version="1.12.1174", com.raytheon.uf.edex.site;bundle-version="1.0.0", - com.google.guava;bundle-version="1.0.0" + com.google.guava;bundle-version="1.0.0", + com.raytheon.uf.edex.auth;bundle-version="1.12.1174", + com.raytheon.uf.common.serialization.comm Eclipse-RegisterBuddy: com.raytheon.uf.common.serialization Export-Package: com.raytheon.uf.edex.activetable diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/res/spring/activetable-request.xml b/edexOsgi/com.raytheon.uf.edex.activetable/res/spring/activetable-request.xml index 58ac2e4191..53d0d5f4e8 100644 --- a/edexOsgi/com.raytheon.uf.edex.activetable/res/spring/activetable-request.xml +++ b/edexOsgi/com.raytheon.uf.edex.activetable/res/spring/activetable-request.xml @@ -69,6 +69,18 @@ + + + + + + + + + + + + * @@ -95,8 +94,6 @@ public class ActiveTable { private static final Logger changeLog = Logger .getLogger("ActiveTableChange"); - private static final String NEXT_ETN_LOCK = "ActiveTableNextEtn"; - private static String filePath; private static String includePath; @@ -228,73 +225,6 @@ public class ActiveTable { false); } - public static Integer getNextEtn(String siteId, ActiveTableMode mode, - String phensig, Calendar currentTime, boolean isLock) { - String lockName = getEtnClusterLockName(siteId, mode); - ClusterTask ct = null; - CurrentTimeClusterLockHandler lockHandler = null; - if (isLock) { - lockHandler = new CurrentTimeClusterLockHandler(15000, false); - do { - ct = ClusterLockUtils - .lock(lockName, phensig, lockHandler, true); - } while (!ct.getLockState().equals(LockState.SUCCESSFUL)); - statusHandler.info("Locking::[lockName = " + lockName - + ", phensig = " + phensig + "]"); - } else { - ct = ClusterLockUtils.lookupLock(lockName, phensig); - } - - int nextEtn = 1; - List records = queryTable(siteId, mode, phensig, - null, null, currentTime, false, true); - - if (records != null && records.size() > 0) { - // should only be 1 - nextEtn = Integer.parseInt(records.get(0).getEtn()) + 1; - } - - String year = "" + currentTime.get(Calendar.YEAR); - String eInfo = ct.getExtraInfo(); - if (eInfo != null && eInfo.startsWith(year)) { - // parse year info - try { - int ctNextEtn = Integer - .parseInt(eInfo.substring(year.length() + 1)) + 1; - if (ctNextEtn > nextEtn) { - nextEtn = ctNextEtn; - } - } catch (Exception e) { - statusHandler.error( - "Caught excetion parsing etn from cluster_task", e); - } - } - - if (isLock) { - lockHandler.setExtraInfo(year + ":" + nextEtn); - ClusterLockUtils.unlock(ct, false); - statusHandler.info("Unlocking::[nextEtn = " + nextEtn + "]"); - } - - return new Integer(nextEtn); - } - - /** - * Returns the EDEX cluster lock name for the given site and active table - * mode. - * - * @param siteId - * 4-char site identifier - * @param mode - * The active table mode - * @return The cluster lock name for the given site and active table. - */ - private static String getEtnClusterLockName(String siteId, - ActiveTableMode mode) { - String lockName = NEXT_ETN_LOCK + "_" + siteId + "_" + mode.name(); - return lockName; - } - /** * Updates the active table with the new warnings * @@ -602,7 +532,7 @@ public class ActiveTable { dao.executeNativeSql(sql); sql = "delete from cluster_task where name ='" - + getEtnClusterLockName(requestedSiteId, + + GetNextEtnUtil.getEtnClusterLockName(requestedSiteId, ActiveTableMode.PRACTICE) + "';"; dao.executeNativeSql(sql); } @@ -613,4 +543,34 @@ public class ActiveTable { return file; } + /** + * Get the last assigned ETN for the specified site and phensig combination. + * + * @param siteId + * The 4-character site identifier. + * @param mode + * The active table to search. + * @param phensig + * The phenomenon and significance combination to search for. + * @param currentTime + * Calendar representing time to perform search from + * (needed for DRT mode). + * @return The last ETN assigned to the particular site and phensig + * combination, or null if no ETNs have been assigned + * to this combination. + */ + public static Integer getLastUsedEtn(String siteId, ActiveTableMode mode, + String phensig, Calendar currentTime) { + Integer lastEtn = null; + List records = ActiveTable.queryTable(siteId, mode, + phensig, null, null, currentTime, false, true); + if (!CollectionUtil.isNullOrEmpty(records)) { + // queryTable will return ActiveTableRecords sorted by ETN in + // descending order, so the first record's ETN will always be the + // last used ETN. + lastEtn = Integer.parseInt(records.get(0).getEtn()); + } + + return lastEtn; + } } diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/GetNextEtnHandler.java b/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/GetNextEtnHandler.java index 9817dd4b77..10a683d9dc 100644 --- a/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/GetNextEtnHandler.java +++ b/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/GetNextEtnHandler.java @@ -42,9 +42,10 @@ public class GetNextEtnHandler implements IRequestHandler { @Override public Integer handleRequest(GetNextEtnRequest request) throws Exception { - Integer nextEtn = ActiveTable.getNextEtn(request.getSiteID(), + Integer nextEtn = GetNextEtnUtil.getNextEtn(request.getSiteID(), request.getMode(), request.getPhensig(), - request.getCurrentTime(), request.isLockEtn()); + request.getCurrentTime(), request.isLockEtn(), + request.isPerformISC()); return nextEtn; } diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/GetNextEtnUtil.java b/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/GetNextEtnUtil.java new file mode 100644 index 0000000000..bb77d0e776 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/GetNextEtnUtil.java @@ -0,0 +1,432 @@ +/** + * 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.edex.activetable; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.rmi.RemoteException; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import java.util.Queue; +import java.util.SortedMap; +import java.util.TreeMap; + +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.localization.IPathManager; +import com.raytheon.uf.common.localization.LocalizationContext; +import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType; +import com.raytheon.uf.common.localization.PathManagerFactory; +import com.raytheon.uf.common.serialization.ExceptionWrapper; +import com.raytheon.uf.common.serialization.comm.IRequestRouter; +import com.raytheon.uf.common.serialization.comm.IServerRequest; +import com.raytheon.uf.common.serialization.comm.response.ServerErrorResponse; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.common.status.UFStatus.Priority; +import com.raytheon.uf.common.util.StringUtil; +import com.raytheon.uf.edex.auth.RemoteServerRequestRouter; +import com.raytheon.uf.edex.database.cluster.ClusterLockUtils; +import com.raytheon.uf.edex.database.cluster.ClusterLockUtils.LockState; +import com.raytheon.uf.edex.database.cluster.ClusterTask; + +/** + * Library module of functions to support retrieval of next ETN in sequence for + * a given phensig and 4-character site identifier. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Aug 29, 2013  #1843     dgilling     Initial creation
+ * 
+ * 
+ * + * @author dgilling + * @version 1.0 + */ +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"; + + private static final String NEXT_ETN_LOCK = "ActiveTableNextEtn"; + + /** + * Dummy private constructor so this class can't be directly instantiated. + * Every method is static. + */ + private GetNextEtnUtil() { + throw new AssertionError(); + } + + /** + * Determine the next ETN in sequence given an office, phensig, and active + * table. The next ETN will be determined by using the maximum of the last + * ETN found for the given office and phensig in the appropriate active + * table and in the cluster task for the same office, phensig, and active + * table combination. + *

+ * If the performISC flag is set then we will also query a list + * of configured remote ETN partners. This configuration is stored in the + * file edex_static/site/${AW_SITE_IDENTIFIER}/vtec/remote-etn-partners. + * properties. + * + * @param siteId + * The 4-character site identifier. + * @param mode + * The active table to use. + * @param phensig + * The phenomenon and significance combination (e.g., TO.W or + * DU.Y). + * @param currentTime + * Calendar representing time (needed for DRT mode). + * @param isLock + * Whether or not to return a unique ETN--one that has not and + * cannot be used by any other requestor. + * @param performISC + * Whether or not to collaborate with neighboring sites to + * determine the next ETN. If this is true, but + * isLock 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. + */ + public static Integer getNextEtn(String siteId, ActiveTableMode mode, + String phensig, Calendar currentTime, boolean isLock, + boolean performISC) { + List hostsToQuery = Collections.emptyList(); + if (performISC) { + hostsToQuery = GetNextEtnUtil.getRemoteEtnSources(siteId); + } + + int nextEtn; + if (performISC && isLock && (!hostsToQuery.isEmpty())) { + nextEtn = GetNextEtnUtil.getNextEtnFromPartners(siteId, mode, + phensig, currentTime, hostsToQuery); + } else { + nextEtn = GetNextEtnUtil.getNextEtnFromLocal(siteId, mode, phensig, + currentTime, isLock); + } + + return nextEtn; + } + + private static List getRemoteEtnSources(String siteId) { + Properties etnBackupProps = new Properties(); + FileInputStream fis = null; + try { + IPathManager pathMgr = PathManagerFactory.getPathManager(); + LocalizationContext edexSiteCtx = pathMgr.getContextForSite( + LocalizationType.EDEX_STATIC, SiteUtil.getSite()); + File configFile = pathMgr.getFile(edexSiteCtx, "vtec" + + IPathManager.SEPARATOR + CONFIG_FILE_NAME); + fis = new FileInputStream(configFile); + etnBackupProps.load(fis); + } catch (FileNotFoundException e) { + statusHandler.error(CONFIG_FILE_NAME + " file does not exist!", e); + return Collections.emptyList(); + } catch (IOException e) { + statusHandler.error("Error reading " + CONFIG_FILE_NAME + " file!", + e); + return Collections.emptyList(); + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + statusHandler.handle(Priority.WARN, "Error closing " + + CONFIG_FILE_NAME + " file!", e); + } + } + } + + String[] tokens = etnBackupProps.getProperty("BACKUP.HOSTS." + siteId, + "").split(","); + + // To prevent deadlock, we ensure every system uses the same ordering by + // use of this SortedMap. Configuration entries will be ordered + // alphabetically by host name. + SortedMap sources = new TreeMap(); + 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; + } + } + + IRequestRouter reqHandler = new RemoteServerRequestRouter("http://" + + host + ":9581/services"); + sources.put(host, reqHandler); + } + + return new ArrayList(sources.values()); + } + + /** + * Returns the EDEX cluster lock name for the given site and active table + * mode. + * + * @param siteId + * 4-char site identifier + * @param mode + * The active table mode + * @return The cluster lock name for the given site and active table. + */ + protected static String getEtnClusterLockName(String siteId, + ActiveTableMode mode) { + String lockName = GetNextEtnUtil.NEXT_ETN_LOCK + "_" + siteId + "_" + + mode.name(); + return lockName; + } + + /** + * Will obtain a cluster task lock for the given active table, office id, + * and phensig combination and use the current information in the active + * table and that cluster task's metadata to determine the next ETN in + * sequence. + *

+ * It is the responsibility of the caller of this method to later call + * setNextEtnAndUnlock to release the cluster task lock this + * method creates. + * + * @param siteId + * The 4-character site identifier. + * @param mode + * The active table to use. + * @param phensig + * The phenomenon and significance combination (e.g., TO.W or + * DU.Y). + * @param currentTime + * Calendar representing time (needed for DRT mode). + * @param isLock + * Whether or not to actually obtain the cluster task lock. Not + * needed if only determining a preliminary ETN. Required to be + * set to true if you want to actually move the + * sequence forward. + * @return The next ETN to be used in sequence. + */ + public static int lockAndGetNextEtn(String siteId, ActiveTableMode mode, + String phensig, Calendar currentTime, boolean isLock) { + String lockName = getEtnClusterLockName(siteId, mode); + ClusterTask ct = null; + if (isLock) { + do { + ct = ClusterLockUtils.lock(lockName, phensig, 15000, true); + } while (!ct.getLockState().equals(LockState.SUCCESSFUL)); + statusHandler.info("Locking::[lockName = " + lockName + + ", phensig = " + phensig + "]"); + } else { + ct = ClusterLockUtils.lookupLock(lockName, phensig); + } + + Integer lastEtn = ActiveTable.getLastUsedEtn(siteId, mode, phensig, + 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); + } + } + + return nextEtn; + } + + /** + * Will release the cluster lock for the given office, phensig, and active + * table combination and also save the last used ETN for that given + * combination. + * + * @param siteId + * The 4-character site identifier. + * @param mode + * The active table to use. + * @param phensig + * The phenomenon and significance combination (e.g., TO.W or + * DU.Y). + * @param year + * Year the next ETN is effective for. + * @param nextEtn + * The ETN to persist. + */ + public static void setNextEtnAndUnlock(String siteId, ActiveTableMode mode, + String phensig, int year, int nextEtn) { + String lockName = getEtnClusterLockName(siteId, mode); + ClusterLockUtils.updateExtraInfo(lockName, phensig, + Integer.toString(year) + ":" + nextEtn); + ClusterLockUtils.unlock(lockName, phensig); + statusHandler.info("Unlocking::[nextEtn = " + nextEtn + "]"); + } + + /** + * Will retrieve the ETN in sequence for the given site, phensig, and active + * table combination by only checking the internal active table and cluster + * lock metadata. No remote partners will be contacted. + * + * @param siteId + * The 4-character site identifier. + * @param mode + * The active table to use. + * @param phensig + * The phenomenon and significance combination (e.g., TO.W or + * DU.Y). + * @param currentTime + * Calendar representing time (needed for DRT mode). + * @param isLock + * Whether or not to return a unique ETN--one that has not and + * cannot be used by any other requestor. + * @return The next ETN to be used in sequence. + */ + public static Integer getNextEtnFromLocal(String siteId, + ActiveTableMode mode, String phensig, Calendar currentTime, + boolean isLock) { + int nextEtn = lockAndGetNextEtn(siteId, mode, phensig, currentTime, + isLock); + if (isLock) { + setNextEtnAndUnlock(siteId, mode, phensig, + currentTime.get(Calendar.YEAR), nextEtn); + } + return nextEtn; + } + + /** + * Will retrieve the ETN in sequence for the given site, phensig, and active + * table combination by contacting the EDEX hosts defined in the file + * edex_static + * /site/${AW_SITE_IDENTIFIER}/vtec/remote-etn-partners.properties. + *

+ * It is expected that configuration file will list a number of remote + * servers to check, but the host name of the primary EDEX server hosting + * the site being queried for should also be listed. + * + * @param siteId + * The 4-character site identifier. + * @param mode + * The active table to use. + * @param phensig + * The phenomenon and significance combination (e.g., TO.W or + * DU.Y). + * @param currentTime + * Calendar representing time (needed for DRT mode). + * @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. + */ + public static Integer getNextEtnFromPartners(String siteId, + ActiveTableMode mode, String phensig, Calendar currentTime, + List hostsToQuery) { + Queue lockQueue = new ArrayDeque( + hostsToQuery); + Queue unlockQueue = Collections + .asLifoQueue(new ArrayDeque(hostsToQuery.size())); + + String mySiteId = SiteUtil.getSite(); + + IServerRequest getAndLockReq = new LockAndGetNextEtnRequest(siteId, + mySiteId, mode, phensig, currentTime); + int nextEtn = 1; + for (IRequestRouter router : lockQueue) { + try { + Integer partersNextEtn = (Integer) GetNextEtnUtil + .sendThriftRequest(router, getAndLockReq); + nextEtn = Math.max(nextEtn, partersNextEtn); + unlockQueue.add(router); + } catch (RemoteException e) { + statusHandler + .handle(Priority.WARN, + "Error occurred contacting one of the remote ETN partners.", + e); + } + } + + IServerRequest unlockReq = new UnlockAndSetNextEtnRequest(siteId, + mySiteId, mode, currentTime.get(Calendar.YEAR), phensig, + nextEtn); + for (IRequestRouter router : unlockQueue) { + try { + GetNextEtnUtil.sendThriftRequest(router, unlockReq); + } catch (RemoteException e) { + statusHandler + .handle(Priority.WARN, + "Error occurred unlocking one of the remote ETN partners.", + e); + } + } + + return nextEtn; + } + + private static Object sendThriftRequest(final IRequestRouter router, + final IServerRequest request) throws RemoteException { + Object retVal = null; + + try { + retVal = router.route(request); + } catch (Exception e) { + throw new RemoteException( + "Unhandled exception occurred routing request type " + + request.getClass().toString(), e); + } + + if (retVal instanceof ServerErrorResponse) { + Throwable cause = ExceptionWrapper + .unwrapThrowable(((ServerErrorResponse) retVal) + .getException()); + throw new RemoteException( + "Unhandled exception occurred on remote server processing request type " + + request.getClass().toString(), cause); + } + + return retVal; + } +} diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/handler/LockAndGetNextEtnHandler.java b/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/handler/LockAndGetNextEtnHandler.java new file mode 100644 index 0000000000..28d47c6092 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/handler/LockAndGetNextEtnHandler.java @@ -0,0 +1,69 @@ +/** + * 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.edex.activetable.handler; + +import com.raytheon.uf.common.activetable.request.LockAndGetNextEtnRequest; +import com.raytheon.uf.common.serialization.comm.IRequestHandler; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.edex.activetable.GetNextEtnUtil; + +/** + * Request handler that locks the active table for a specific phensig and office + * ID combination. Should only be activated by another EDEX server. + * + *

+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Aug 19, 2013  #1843     dgilling     Initial creation
+ * 
+ * 
+ * + * @author dgilling + * @version 1.0 + */ + +public class LockAndGetNextEtnHandler implements + IRequestHandler { + + private static final transient IUFStatusHandler statusHandler = UFStatus + .getHandler(LockAndGetNextEtnHandler.class); + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.common.serialization.comm.IRequestHandler#handleRequest + * (com.raytheon.uf.common.serialization.comm.IServerRequest) + */ + @Override + public Integer handleRequest(LockAndGetNextEtnRequest request) + throws Exception { + statusHandler.info("Received LockAndGetNextEtnRequest from [" + + request.getRequestorSiteID() + "] for site [" + + request.getSiteID() + "]: phensig= " + request.getPhensig()); + return GetNextEtnUtil.lockAndGetNextEtn(request.getSiteID(), + request.getMode(), request.getPhensig(), + request.getCurrentTime(), true); + } +} diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/handler/UnlockActiveTablePhenSigHandler.java b/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/handler/UnlockActiveTablePhenSigHandler.java new file mode 100644 index 0000000000..61f1005822 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/handler/UnlockActiveTablePhenSigHandler.java @@ -0,0 +1,72 @@ +/** + * 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.edex.activetable.handler; + +import com.raytheon.uf.common.activetable.request.UnlockAndSetNextEtnRequest; +import com.raytheon.uf.common.serialization.comm.IRequestHandler; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.edex.activetable.GetNextEtnUtil; + +/** + * Request handler that unlocks the active table for a specific phensig and + * office ID combination and sets the next ETN to be used. Should only be + * activated by another EDEX server. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Aug 19, 2013  #1843     dgilling     Initial creation
+ * 
+ * 
+ * + * @author dgilling + * @version 1.0 + */ + +public class UnlockActiveTablePhenSigHandler implements + IRequestHandler { + + private static final transient IUFStatusHandler statusHandler = UFStatus + .getHandler(UnlockActiveTablePhenSigHandler.class); + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.common.serialization.comm.IRequestHandler#handleRequest + * (com.raytheon.uf.common.serialization.comm.IServerRequest) + */ + @Override + public Boolean handleRequest(UnlockAndSetNextEtnRequest request) + throws Exception { + statusHandler.info("Received UnlockAndSetNextEtnRequest from [" + + request.getRequestorSiteID() + "] for site [" + + request.getSiteID() + "]: phensig= " + request.getPhensig() + + ", nextETN= " + request.getNewEtn()); + GetNextEtnUtil.setNextEtnAndUnlock(request.getSiteID(), + request.getMode(), request.getPhensig(), request.getYear(), + request.getNewEtn()); + return Boolean.TRUE; + } +} diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/utility/edex_static/base/vtec/remote-etn-partners.properties b/edexOsgi/com.raytheon.uf.edex.activetable/utility/edex_static/base/vtec/remote-etn-partners.properties new file mode 100644 index 0000000000..7699cd19f7 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.activetable/utility/edex_static/base/vtec/remote-etn-partners.properties @@ -0,0 +1,29 @@ +# This file defines lists of partner sites to check when assigning or +# generating the next ETN for a VTEC product in GFE. This functionality is +# completely optional--there's no requirement to configure it. If not +# configured the system will solely look at local active table entries and +# the cluster_task db entry named +# "ActiveTableNextEtn__" for the specific phensig to +# determine the next ETN in sequence. +# +# If configured, when generating GFE VTEC products for the specified 4-char +# site ID, it will query each of entries in the list of specified hosts for +# the last ETN used for the site, phensig, and active table combination. +# +# 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 +# +# 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. +# +# There should only ever be one localized copy of this file on the EDEX server, +# and it should be located at +# /awips2/edex/data/utility/site/${AW_SITE_IDENTIFIER}/vtec/remote-etn-partners.properties. +# If new sites are activated on this server that need to use remote ETN +# querying (say, in case of service backup), you just add a new BACKUP.HOSTS +# entry to this file. +# +# CONFIGURATION ENTRIES GO BELOW \ No newline at end of file