Issue #1843: Implement ETN remote partners functionality.

Change-Id: I827e312941b4dbd15e3c6c90e04e38ffbf457294

Former-commit-id: f72a420d53 [formerly b0c40550c1448894915583a0426e78602cbe05c6]
Former-commit-id: 0359324523
This commit is contained in:
David Gillingham 2013-08-13 13:25:16 -05:00
parent 3bdf7eed9d
commit b8c247b469
13 changed files with 1010 additions and 92 deletions

View file

@ -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.
*
* </pre>
*
@ -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);

View file

@ -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.
*
* </pre>
*
@ -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<IRequestRouter>)} 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;

View file

@ -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.
*
* </pre>
*
@ -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;
}
}

View file

@ -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.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 19, 2013 #1843 dgilling Initial creation
*
* </pre>
*
* @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;
}
}

View file

@ -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.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 19, 2013 #1843 dgilling Initial creation
*
* </pre>
*
* @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;
}
}

View file

@ -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

View file

@ -69,6 +69,18 @@
<constructor-arg ref="ingestATHandler"/>
</bean>
<bean id="lockAndGetEtnHandler" class="com.raytheon.uf.edex.activetable.handler.LockAndGetNextEtnHandler"/>
<bean factory-bean="handlerRegistry" factory-method="register">
<constructor-arg value="com.raytheon.uf.common.activetable.request.LockAndGetNextEtnRequest"/>
<constructor-arg ref="lockAndGetEtnHandler"/>
</bean>
<bean id="setAndUnlockEtnHandler" class="com.raytheon.uf.edex.activetable.handler.UnlockActiveTablePhenSigHandler"/>
<bean factory-bean="handlerRegistry" factory-method="register">
<constructor-arg value="com.raytheon.uf.common.activetable.request.UnlockAndSetNextEtnRequest"/>
<constructor-arg ref="setAndUnlockEtnHandler"/>
</bean>
<bean id="fetchATSrv" class="com.raytheon.uf.edex.activetable.vtecsharing.FetchActiveTableSrv"/>
<camelContext id="activeTableSharingRoutes"
xmlns="http://camel.apache.org/schema/spring"

View file

@ -52,13 +52,10 @@ import com.raytheon.uf.common.site.SiteMap;
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.CollectionUtil;
import com.raytheon.uf.common.util.FileUtil;
import com.raytheon.uf.edex.core.EDEXUtil;
import com.raytheon.uf.edex.database.DataAccessLayerException;
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;
import com.raytheon.uf.edex.database.cluster.handler.CurrentTimeClusterLockHandler;
import com.raytheon.uf.edex.database.dao.CoreDao;
import com.raytheon.uf.edex.database.dao.DaoConfig;
import com.raytheon.uf.edex.database.query.DatabaseQuery;
@ -81,6 +78,8 @@ import com.raytheon.uf.edex.database.query.DatabaseQuery;
* May 14, 2013 1842 dgilling Also delete cluster locks when purging
* PRACTICE active table.
* Jun 11, 2013 2083 randerso Log active table changes
* Aug 29, 2013 1843 dgilling Move ETN related methods to
* GetNextEtnUtil.
*
* </pre>
*
@ -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<ActiveTableRecord> 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
* <code>Calendar</code> representing time to perform search from
* (needed for DRT mode).
* @return The last ETN assigned to the particular site and phensig
* combination, or <code>null</code> 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<ActiveTableRecord> 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;
}
}

View file

@ -42,9 +42,10 @@ public class GetNextEtnHandler implements IRequestHandler<GetNextEtnRequest> {
@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;
}

View file

@ -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.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 29, 2013 #1843 dgilling Initial creation
*
* </pre>
*
* @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.
* <p>
* If the <code>performISC</code> 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
* <code>Calendar</code> 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
* <code>isLock</code> is false, this flag is effectively false
* and your configured remote partners will not be contacted to
* determine the next ETN.
* @return The next ETN to be used in sequence.
*/
public static Integer getNextEtn(String siteId, ActiveTableMode mode,
String phensig, Calendar currentTime, boolean isLock,
boolean performISC) {
List<IRequestRouter> 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<IRequestRouter> 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<String, IRequestRouter> sources = new TreeMap<String, IRequestRouter>();
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<IRequestRouter>(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.
* <p>
* It is the responsibility of the caller of this method to later call
* <code>setNextEtnAndUnlock</code> 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
* <code>Calendar</code> 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 <code>true</code> 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
* <code>Calendar</code> 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.
* <p>
* 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
* <code>Calendar</code> 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<IRequestRouter> hostsToQuery) {
Queue<IRequestRouter> lockQueue = new ArrayDeque<IRequestRouter>(
hostsToQuery);
Queue<IRequestRouter> unlockQueue = Collections
.asLifoQueue(new ArrayDeque<IRequestRouter>(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;
}
}

View file

@ -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.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 19, 2013 #1843 dgilling Initial creation
*
* </pre>
*
* @author dgilling
* @version 1.0
*/
public class LockAndGetNextEtnHandler implements
IRequestHandler<LockAndGetNextEtnRequest> {
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);
}
}

View file

@ -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.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 19, 2013 #1843 dgilling Initial creation
*
* </pre>
*
* @author dgilling
* @version 1.0
*/
public class UnlockActiveTablePhenSigHandler implements
IRequestHandler<UnlockAndSetNextEtnRequest> {
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;
}
}

View file

@ -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_<siteId>_<activeTableName>" 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