Issue #1243 Made localization lock more intelligently to avoid files getting out of sync and causing errors.
Amend: Fixed LocalizationFileOutputStream so it has option to close and save file and made LocalizationFile.write so it is more clear as to what it is doing. Made CloudHeightData and AdaptivePlotResourceData not create FileUpdateMessages. Amend: Rebased Change-Id: I5aff6402004f4b4d3040c8c71f5ee16dfeb33567 Former-commit-id:fe11e31bfb
[formerlydcde9e8951
] [formerly28e61c8285
[formerly 85a0b9bb03887b9ef50e67a1ae3a36afd24b3a58]] Former-commit-id:28e61c8285
Former-commit-id:2e20c3e799
This commit is contained in:
parent
1540334a35
commit
5aa26e4f13
22 changed files with 735 additions and 656 deletions
|
@ -29,12 +29,6 @@ import javax.xml.bind.annotation.XmlElement;
|
|||
import javax.xml.bind.annotation.XmlElementWrapper;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import com.raytheon.uf.common.localization.FileUpdatedMessage;
|
||||
import com.raytheon.uf.common.localization.ILocalizationFileObserver;
|
||||
import com.raytheon.uf.common.localization.IPathManager;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
|
||||
import com.raytheon.uf.common.localization.LocalizationFile;
|
||||
import com.raytheon.uf.common.localization.PathManagerFactory;
|
||||
import com.raytheon.uf.common.serialization.ISerializableObject;
|
||||
import com.raytheon.uf.common.status.IUFStatusHandler;
|
||||
|
@ -68,8 +62,6 @@ public class CloudHeightData implements ISerializableObject {
|
|||
private static final String DATA_FILE = CLOUDHEIGHT_DATA_DIR
|
||||
+ File.separator + "values.xml";
|
||||
|
||||
private static LocalizationFile cloudHeightDir;
|
||||
|
||||
private static CloudHeightData theData = null;
|
||||
|
||||
@XmlAccessorType(XmlAccessType.NONE)
|
||||
|
@ -101,26 +93,9 @@ public class CloudHeightData implements ISerializableObject {
|
|||
|
||||
public static synchronized CloudHeightData getCloudHeightData() {
|
||||
if (theData == null) {
|
||||
IPathManager pm = PathManagerFactory.getPathManager();
|
||||
theData = new CloudHeightData();
|
||||
cloudHeightDir = pm.getLocalizationFile(pm.getContext(
|
||||
LocalizationType.CAVE_STATIC, LocalizationLevel.BASE),
|
||||
CLOUDHEIGHT_DATA_DIR);
|
||||
|
||||
ILocalizationFileObserver observer = new ILocalizationFileObserver() {
|
||||
@Override
|
||||
public void fileUpdated(FileUpdatedMessage message) {
|
||||
if (DATA_FILE.equals(message.getFileName())) {
|
||||
populateData(theData, PathManagerFactory
|
||||
.getPathManager().getStaticFile(DATA_FILE));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// load data
|
||||
observer.fileUpdated(new FileUpdatedMessage(null, DATA_FILE, null));
|
||||
|
||||
// TODO: cloudHeightDir.addFileUpdatedObserver(observer);
|
||||
populateData(theData, PathManagerFactory.getPathManager()
|
||||
.getStaticFile(DATA_FILE));
|
||||
}
|
||||
return theData;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import com.raytheon.uf.common.localization.LocalizationContext;
|
|||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
|
||||
import com.raytheon.uf.common.localization.LocalizationFile;
|
||||
import com.raytheon.uf.common.localization.LocalizationFile.ModifiableLocalizationFile;
|
||||
import com.raytheon.uf.common.localization.exception.LocalizationOpFailedException;
|
||||
|
||||
/**
|
||||
|
@ -47,20 +48,6 @@ import com.raytheon.uf.common.localization.exception.LocalizationOpFailedExcepti
|
|||
|
||||
public class CollaborationLocalizationAdapter implements ILocalizationAdapter {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* com.raytheon.uf.common.localization.ILocalizationAdapter#getDirNameForType
|
||||
* (
|
||||
* com.raytheon.uf.common.localization.LocalizationContext.LocalizationType)
|
||||
*/
|
||||
@Override
|
||||
public String getDirNameForType(LocalizationType type) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
@ -108,13 +95,12 @@ public class CollaborationLocalizationAdapter implements ILocalizationAdapter {
|
|||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* com.raytheon.uf.common.localization.ILocalizationAdapter#save(java.io
|
||||
* .File, com.raytheon.uf.common.localization.LocalizationContext,
|
||||
* java.lang.String)
|
||||
* com.raytheon.uf.common.localization.ILocalizationAdapter#save(com.raytheon
|
||||
* .uf.common.localization.LocalizationFile. ModifiableLocalizationFile)
|
||||
*/
|
||||
@Override
|
||||
public boolean save(File localFile, LocalizationContext context,
|
||||
String fileName) throws LocalizationOpFailedException {
|
||||
public boolean save(ModifiableLocalizationFile file)
|
||||
throws LocalizationOpFailedException {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
@ -182,13 +168,12 @@ public class CollaborationLocalizationAdapter implements ILocalizationAdapter {
|
|||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* com.raytheon.uf.common.localization.ILocalizationAdapter#delete(java.
|
||||
* io.File, com.raytheon.uf.common.localization.LocalizationContext,
|
||||
* java.lang.String)
|
||||
* com.raytheon.uf.common.localization.ILocalizationAdapter#delete(com.raytheon
|
||||
* .uf.common.localization.LocalizationFile. ModifiableLocalizationFile)
|
||||
*/
|
||||
@Override
|
||||
public boolean delete(File file, LocalizationContext context,
|
||||
String fileName) throws LocalizationOpFailedException {
|
||||
public boolean delete(ModifiableLocalizationFile file)
|
||||
throws LocalizationOpFailedException {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
package com.raytheon.uf.viz.core.localization;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
@ -34,7 +37,9 @@ import com.raytheon.uf.common.localization.LocalizationContext;
|
|||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
|
||||
import com.raytheon.uf.common.localization.LocalizationFile;
|
||||
import com.raytheon.uf.common.localization.LocalizationFile.ModifiableLocalizationFile;
|
||||
import com.raytheon.uf.common.localization.LocalizationInternalFile;
|
||||
import com.raytheon.uf.common.localization.LockingFileInputStream;
|
||||
import com.raytheon.uf.common.localization.exception.LocalizationOpFailedException;
|
||||
import com.raytheon.uf.common.localization.msgs.AbstractUtilityCommand;
|
||||
import com.raytheon.uf.common.localization.msgs.AbstractUtilityResponse;
|
||||
|
@ -72,15 +77,12 @@ public class CAVELocalizationAdapter implements ILocalizationAdapter {
|
|||
this.contexts = new HashMap<LocalizationType, LocalizationContext[]>();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
/**
|
||||
* Returns a directory name for the localization type
|
||||
*
|
||||
* @see
|
||||
* com.raytheon.uf.common.localization.ILocalizationAdapter#getDirNameForType
|
||||
* (
|
||||
* com.raytheon.uf.common.localization.LocalizationContext.LocalizationType)
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getDirNameForType(LocalizationType type) {
|
||||
if (type == LocalizationType.COMMON_STATIC) {
|
||||
return "common";
|
||||
|
@ -228,21 +230,39 @@ public class CAVELocalizationAdapter implements ILocalizationAdapter {
|
|||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* com.raytheon.uf.common.localization.ILocalizationAdapter#save(java.io
|
||||
* .File, com.raytheon.uf.common.localization.LocalizationContext,
|
||||
* java.lang.String)
|
||||
* com.raytheon.uf.common.localization.ILocalizationAdapter#save(com.raytheon
|
||||
* .uf.common.localization.LocalizationFile. ModifiableLocalizationFile)
|
||||
*/
|
||||
@Override
|
||||
public boolean save(File localFile, LocalizationContext context,
|
||||
String fileName) throws LocalizationOpFailedException {
|
||||
if (context.getLocalizationLevel().isSystemLevel()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Saving to the System Level, "
|
||||
+ context.getLocalizationLevel()
|
||||
+ ", is not supported.");
|
||||
public boolean save(ModifiableLocalizationFile file)
|
||||
throws LocalizationOpFailedException {
|
||||
File localFile = file.getLocalFile();
|
||||
if (localFile.isDirectory() == false && localFile.exists()) {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new LockingFileInputStream(localFile);
|
||||
long serverModTime = manager.upload(file.getContext(),
|
||||
file.getFileName(), in, localFile.length());
|
||||
// Success! set potentially changed fields
|
||||
file.setTimeStamp(new Date(serverModTime));
|
||||
file.setIsAvailableOnServer(true);
|
||||
file.setIsDirectory(false);
|
||||
return true;
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new LocalizationOpFailedException(
|
||||
"Error saving file, does not exist");
|
||||
} finally {
|
||||
// Make sure to close input stream
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
// Ignore close exception
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return manager.upload(context, fileName, localFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -443,16 +463,26 @@ public class CAVELocalizationAdapter implements ILocalizationAdapter {
|
|||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* com.raytheon.uf.common.localization.ILocalizationAdapter#delete(java.
|
||||
* io.File, com.raytheon.uf.common.localization.LocalizationContext,
|
||||
* java.lang.String)
|
||||
* com.raytheon.uf.common.localization.ILocalizationAdapter#delete(com.raytheon
|
||||
* .uf.common.localization.LocalizationFile. ModifiableLocalizationFile)
|
||||
*/
|
||||
@Override
|
||||
public boolean delete(File file, LocalizationContext context,
|
||||
String fileName) throws LocalizationOpFailedException {
|
||||
file.delete();
|
||||
return !file.exists() && manager.delete(context, fileName);
|
||||
public boolean delete(ModifiableLocalizationFile file)
|
||||
throws LocalizationOpFailedException {
|
||||
long deleteTime = manager.delete(file.getContext(), file.getFileName());
|
||||
|
||||
// Made it here! file on server succesfully deleted! Delete local file
|
||||
// reference. If that fails, doesn't matter since file does not exist!
|
||||
File localFile = file.getLocalFile();
|
||||
localFile.delete();
|
||||
|
||||
// Reset fields
|
||||
file.setTimeStamp(new Date(deleteTime));
|
||||
file.setIsAvailableOnServer(false);
|
||||
file.setFileChecksum(null);
|
||||
file.setIsDirectory(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private ListResponse convertResponse(ListResponseEntry entry,
|
||||
|
|
|
@ -22,10 +22,9 @@ package com.raytheon.uf.viz.core.localization;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -51,8 +50,8 @@ import com.raytheon.uf.common.localization.ILocalizationAdapter;
|
|||
import com.raytheon.uf.common.localization.LocalizationContext;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
|
||||
import com.raytheon.uf.common.localization.LocalizationFile;
|
||||
import com.raytheon.uf.common.localization.LockingFileInputStream;
|
||||
import com.raytheon.uf.common.localization.exception.LocalizationOpFailedException;
|
||||
import com.raytheon.uf.common.localization.msgs.AbstractPrivilegedUtilityCommand;
|
||||
import com.raytheon.uf.common.localization.msgs.AbstractUtilityResponse;
|
||||
import com.raytheon.uf.common.localization.msgs.DeleteUtilityCommand;
|
||||
import com.raytheon.uf.common.localization.msgs.DeleteUtilityResponse;
|
||||
|
@ -76,7 +75,6 @@ import com.raytheon.uf.viz.core.VizApp;
|
|||
import com.raytheon.uf.viz.core.VizServers;
|
||||
import com.raytheon.uf.viz.core.exception.VizException;
|
||||
import com.raytheon.uf.viz.core.requests.PrivilegedRequestFactory;
|
||||
import com.raytheon.uf.viz.core.requests.ServerRequestException;
|
||||
import com.raytheon.uf.viz.core.requests.ThriftClient;
|
||||
|
||||
/**
|
||||
|
@ -131,7 +129,7 @@ public class LocalizationManager implements IPropertyChangeListener {
|
|||
|
||||
/** The current localization site */
|
||||
private String currentSite;
|
||||
|
||||
|
||||
private boolean nationalCenter;
|
||||
|
||||
private boolean overrideSite;
|
||||
|
@ -398,11 +396,11 @@ public class LocalizationManager implements IPropertyChangeListener {
|
|||
.getString("-site").toUpperCase();
|
||||
this.overrideSite = true;
|
||||
}
|
||||
|
||||
|
||||
this.nationalCenter = false;
|
||||
|
||||
|
||||
if (ProgramArguments.getInstance().getString("-nc") != null) {
|
||||
this.nationalCenter = true;
|
||||
this.nationalCenter = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -709,150 +707,145 @@ public class LocalizationManager implements IPropertyChangeListener {
|
|||
}
|
||||
|
||||
/**
|
||||
* Uploads a file to EDEX through the UtilitySrv
|
||||
* Uploads a stream as a file to EDEX through the UtilitySrv. It is the
|
||||
* responsibility of the caller to close the stream
|
||||
*
|
||||
* @param context
|
||||
* the context to upload to
|
||||
* @param filename
|
||||
* the name of the file
|
||||
* @param bytes
|
||||
* the contents of the file
|
||||
* @return
|
||||
* @throws LocalizationCommunicationException
|
||||
* @param in
|
||||
* @param streamLength
|
||||
* @return the new server time stamp
|
||||
* @throws LocalizationOpFailedException
|
||||
*/
|
||||
protected boolean upload(LocalizationContext context, String filename,
|
||||
FileInputStream fin, long totalSize)
|
||||
protected long upload(LocalizationContext context, String filename,
|
||||
InputStream in, long streamLength)
|
||||
throws LocalizationOpFailedException {
|
||||
boolean success = true;
|
||||
if (context.getLocalizationLevel().isSystemLevel()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Saving to the System Level, "
|
||||
+ context.getLocalizationLevel()
|
||||
+ ", is not supported.");
|
||||
}
|
||||
|
||||
LocalizationStreamPutRequest request;
|
||||
try {
|
||||
LocalizationStreamPutRequest request = PrivilegedRequestFactory
|
||||
request = PrivilegedRequestFactory
|
||||
.constructPrivilegedRequest(LocalizationStreamPutRequest.class);
|
||||
request.setMyContextName(getContextName(context
|
||||
request.setMyContextName(LocalizationManager.getContextName(context
|
||||
.getLocalizationLevel()));
|
||||
request.setContext(context);
|
||||
request.setFileName(filename);
|
||||
request.setOffset(0);
|
||||
boolean finished = false;
|
||||
byte[] bytes = new byte[512 * 1024];
|
||||
int totalRead = 0;
|
||||
while (!finished) {
|
||||
request.setOffset(totalRead);
|
||||
int read = fin.read(bytes, 0, bytes.length);
|
||||
} catch (VizException e) {
|
||||
throw new LocalizationOpFailedException(
|
||||
"Could not construct privileged utility request", e);
|
||||
}
|
||||
|
||||
long serverModTime = -1;
|
||||
// Create byte[] buffer
|
||||
byte[] bytes = new byte[512 * 1024];
|
||||
// initial offset = 0
|
||||
int offset = 0;
|
||||
do {
|
||||
// set current offset
|
||||
request.setOffset(offset);
|
||||
try {
|
||||
// read in data from input stream
|
||||
int read = in.read(bytes, 0, bytes.length);
|
||||
if (read > 0) {
|
||||
totalRead += read;
|
||||
bytes = Arrays.copyOf(bytes, read);
|
||||
request.setBytes(bytes);
|
||||
request.setEnd(totalRead == totalSize);
|
||||
// byte read, trim if necessary
|
||||
offset += read;
|
||||
if (read < bytes.length) {
|
||||
bytes = Arrays.copyOf(bytes, read);
|
||||
}
|
||||
} else {
|
||||
request.setBytes(new byte[0]);
|
||||
request.setEnd(true);
|
||||
bytes = new byte[0];
|
||||
// Should be case but paranoia takes over
|
||||
offset = (int) streamLength;
|
||||
}
|
||||
|
||||
if (request.isEnd()) {
|
||||
finished = true;
|
||||
}
|
||||
|
||||
success = (ThriftClient.sendLocalizationRequest(request) != null);
|
||||
}
|
||||
} catch (ServerRequestException e) {
|
||||
statusHandler.handle(Priority.PROBLEM, e.getMessage(), e);
|
||||
success = false;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
statusHandler.handle(Priority.PROBLEM,
|
||||
"Error uploading file to server", e);
|
||||
success = false;
|
||||
} finally {
|
||||
try {
|
||||
fin.close();
|
||||
request.setBytes(bytes);
|
||||
request.setEnd(offset == streamLength);
|
||||
} catch (IOException e) {
|
||||
// ignore message
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a file to the edex through the UtilitySrv
|
||||
*
|
||||
* @param context
|
||||
* the context to upload to
|
||||
* @param filename
|
||||
* the name of the file
|
||||
* @param localFile
|
||||
* the local file, used to send the contents of the file to the
|
||||
* service
|
||||
* @return
|
||||
* @throws LocalizationOpFailedException
|
||||
* @throws LocalizationCommunicationException
|
||||
*/
|
||||
protected boolean upload(LocalizationContext context, String path,
|
||||
File localFile) throws LocalizationOpFailedException {
|
||||
if (localFile.isDirectory()) {
|
||||
// TODO: Someday upload entire directory structure onto
|
||||
// server?
|
||||
} else if (localFile.exists()) {
|
||||
// Upload single file
|
||||
try {
|
||||
return upload(context, path, new LockingFileInputStream(
|
||||
localFile), localFile.length());
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new LocalizationOpFailedException(
|
||||
"Error opening input stream on localization file", e);
|
||||
"Could not save file, failed to read in contents", e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
try {
|
||||
Number modTime = (Number) ThriftClient
|
||||
.sendLocalizationRequest(request);
|
||||
if (modTime != null) {
|
||||
serverModTime = modTime.longValue();
|
||||
}
|
||||
} catch (VizException e) {
|
||||
throw new LocalizationOpFailedException(
|
||||
"Error storing file contents to server: "
|
||||
+ e.getLocalizedMessage(), e);
|
||||
}
|
||||
|
||||
} while (request.isEnd() == false);
|
||||
return serverModTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a localization file
|
||||
* Deletes a localization file from localization server
|
||||
*
|
||||
* @param context
|
||||
* the context to the file
|
||||
* @param filename
|
||||
* the name of the file
|
||||
* @return
|
||||
* @return modified time on server
|
||||
* @throws LocalizationOpFailedException
|
||||
* @throws LocalizationCommunicationException
|
||||
*/
|
||||
protected boolean delete(LocalizationContext context, String filename)
|
||||
protected long delete(LocalizationContext context, String filename)
|
||||
throws LocalizationOpFailedException {
|
||||
// Build list commands
|
||||
DeleteUtilityCommand[] commands = new DeleteUtilityCommand[1];
|
||||
|
||||
commands[0] = new DeleteUtilityCommand(context, filename);
|
||||
commands[0].setMyContextName(getContextName(context
|
||||
.getLocalizationLevel()));
|
||||
|
||||
PrivilegedUtilityRequestMessage deleteRequest = null;
|
||||
PrivilegedUtilityRequestMessage request;
|
||||
try {
|
||||
deleteRequest = PrivilegedRequestFactory
|
||||
request = PrivilegedRequestFactory
|
||||
.constructPrivilegedRequest(PrivilegedUtilityRequestMessage.class);
|
||||
deleteRequest.setCommands(commands);
|
||||
} catch (VizException e) {
|
||||
throw new LocalizationOpFailedException(
|
||||
"Error constructing privileged utility request", e);
|
||||
"Could not construct privileged utility request", e);
|
||||
}
|
||||
|
||||
AbstractUtilityResponse[] responseList = makeRequest(deleteRequest);
|
||||
if (responseList == null) {
|
||||
return false;
|
||||
}
|
||||
for (AbstractUtilityResponse response : responseList) {
|
||||
if (!(response instanceof DeleteUtilityResponse)) {
|
||||
DeleteUtilityCommand command = new DeleteUtilityCommand(context,
|
||||
filename);
|
||||
command.setMyContextName(getContextName(context.getLocalizationLevel()));
|
||||
AbstractPrivilegedUtilityCommand[] commands = new AbstractPrivilegedUtilityCommand[] { command };
|
||||
request.setCommands(commands);
|
||||
try {
|
||||
UtilityResponseMessage response = (UtilityResponseMessage) ThriftClient
|
||||
.sendLocalizationRequest(request);
|
||||
if (response == null) {
|
||||
throw new LocalizationOpFailedException(
|
||||
"Unexpected type returned"
|
||||
+ response.getClass().getName());
|
||||
"No response received for delete command");
|
||||
}
|
||||
|
||||
// DeleteUtilityResponse deleteResponse = (DeleteUtilityResponse)
|
||||
// response;
|
||||
// TODO log this
|
||||
AbstractUtilityResponse[] responses = response.getResponses();
|
||||
if (responses == null || responses.length != commands.length) {
|
||||
throw new LocalizationOpFailedException(
|
||||
"Unexpected return type from delete: Expected "
|
||||
+ commands.length + " responses, received "
|
||||
+ (responses != null ? responses.length : null));
|
||||
}
|
||||
AbstractUtilityResponse rsp = responses[0];
|
||||
if (rsp instanceof DeleteUtilityResponse) {
|
||||
DeleteUtilityResponse dur = (DeleteUtilityResponse) rsp;
|
||||
if (dur.getErrorText() != null) {
|
||||
throw new LocalizationOpFailedException(
|
||||
"Error processing delete command: "
|
||||
+ dur.getErrorText());
|
||||
}
|
||||
// Yay, successful execution!
|
||||
return dur.getTimeStamp();
|
||||
} else {
|
||||
throw new LocalizationOpFailedException(
|
||||
"Unexpected return type from delete: Expected "
|
||||
+ DeleteUtilityResponse.class + " received "
|
||||
+ (rsp != null ? rsp.getClass() : null));
|
||||
}
|
||||
} catch (VizException e) {
|
||||
throw new LocalizationOpFailedException(
|
||||
"Error processing delete command: "
|
||||
+ e.getLocalizedMessage(), e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -958,8 +951,8 @@ public class LocalizationManager implements IPropertyChangeListener {
|
|||
public boolean isOverrideSite() {
|
||||
return overrideSite;
|
||||
}
|
||||
|
||||
|
||||
public boolean isNationalCenter() {
|
||||
return nationalCenter;
|
||||
return nationalCenter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,8 +161,7 @@ public class AdaptivePlotResourceData extends AbstractResourceData implements
|
|||
LoadProperties loadProperties, IDescriptor descriptor)
|
||||
throws VizException {
|
||||
if (file == null) {
|
||||
fileUpdated(new FileUpdatedMessage(null, null,
|
||||
FileChangeType.DELETED));
|
||||
fileUpdated(FileChangeType.ADDED, filePath);
|
||||
}
|
||||
|
||||
if (file == null) {
|
||||
|
@ -186,10 +185,17 @@ public class AdaptivePlotResourceData extends AbstractResourceData implements
|
|||
*/
|
||||
@Override
|
||||
public synchronized void fileUpdated(FileUpdatedMessage message) {
|
||||
fileUpdated(message.getChangeType(), message.getFileName());
|
||||
}
|
||||
|
||||
private void fileUpdated(FileChangeType changeType, String filePath) {
|
||||
Set<PlotObject> newObjects = new HashSet<PlotObject>();
|
||||
switch (message.getChangeType()) {
|
||||
switch (changeType) {
|
||||
case DELETED:
|
||||
case ADDED: {
|
||||
if (file != null) {
|
||||
file.removeFileUpdatedObserver(this);
|
||||
}
|
||||
// Our old file was deleted, search for file
|
||||
file = PathManagerFactory.getPathManager()
|
||||
.getStaticLocalizationFile(filePath);
|
||||
|
|
|
@ -33,6 +33,7 @@ import com.raytheon.uf.common.localization.LocalizationContext;
|
|||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
|
||||
import com.raytheon.uf.common.localization.LocalizationFile;
|
||||
import com.raytheon.uf.common.localization.LocalizationFile.ModifiableLocalizationFile;
|
||||
import com.raytheon.uf.common.localization.exception.LocalizationOpFailedException;
|
||||
import com.raytheon.uf.edex.core.props.EnvProperties;
|
||||
import com.raytheon.uf.edex.core.props.PropertiesFactory;
|
||||
|
@ -73,23 +74,6 @@ public class EDEXLocalizationAdapter implements ILocalizationAdapter {
|
|||
this.contexts = new HashMap<LocalizationType, LocalizationContext[]>();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* com.raytheon.edex.utility.ILocalizationAdapter#getDirNameForType(com.
|
||||
* raytheon.edex.utility.LocalizationContext.LocalizationType)
|
||||
*/
|
||||
@Override
|
||||
public String getDirNameForType(LocalizationType type) {
|
||||
|
||||
if (type == LocalizationType.UNKNOWN) {
|
||||
throw new IllegalArgumentException("Unsupported type: " + type);
|
||||
} else {
|
||||
return type.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
@ -294,43 +278,37 @@ public class EDEXLocalizationAdapter implements ILocalizationAdapter {
|
|||
|
||||
// --- CRUD Operations ---------------------------------------------------
|
||||
|
||||
/**
|
||||
* <EM>UNSUPPORTED</EM> Create a file
|
||||
* <p>
|
||||
* On EDEX all file operations are local and do not need to be created,
|
||||
* read, updated, or deleted from a remote source. Instead of using the CRUD
|
||||
* methods provided by the {@link ILocalizationAdapter} interface use
|
||||
* {@link #getPath(LocalizationContext, String)} and appropriate File object
|
||||
* methods.
|
||||
* </p>
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.raytheon.uf.common.localization.ILocalizationAdapter#save(java.io.File,
|
||||
* com.raytheon.uf.common.localization.LocalizationContext,
|
||||
* java.lang.String)
|
||||
* @see com.raytheon.uf.common.localization.ILocalizationAdapter#save(
|
||||
* com.raytheon.uf.common.localization.LocalizationFile.
|
||||
* ModifiableLocalizationFile)
|
||||
*/
|
||||
@Override
|
||||
public boolean save(File localFile, LocalizationContext context,
|
||||
String fileName) throws LocalizationOpFailedException {
|
||||
|
||||
if (context.getLocalizationLevel().equals(LocalizationLevel.BASE)) {
|
||||
public boolean save(ModifiableLocalizationFile file)
|
||||
throws LocalizationOpFailedException {
|
||||
if (file.getContext().getLocalizationLevel()
|
||||
.equals(LocalizationLevel.BASE)) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Saving to the BASE context is not supported.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file. Deletes the local file.
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.raytheon.uf.common.localization.ILocalizationAdapter#delete(java.io.File,
|
||||
* com.raytheon.uf.common.localization.LocalizationContext,
|
||||
* java.lang.String)
|
||||
* @see com.raytheon.uf.common.localization.ILocalizationAdapter#delete(
|
||||
* com.raytheon.uf.common.localization.LocalizationFile.
|
||||
* ModifiableLocalizationFile)
|
||||
*/
|
||||
@Override
|
||||
public boolean delete(File file, LocalizationContext context,
|
||||
String fileName) throws LocalizationOpFailedException {
|
||||
if (file.exists()) {
|
||||
return file.delete();
|
||||
public boolean delete(ModifiableLocalizationFile file)
|
||||
throws LocalizationOpFailedException {
|
||||
File localFile = file.getLocalFile();
|
||||
if (localFile.exists()) {
|
||||
return localFile.delete();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
<project basedir="." default="deploy" name="com.raytheon.edex.plugin.gfe">
|
||||
<available file="../build.edex" property="build.dir.location" value="../build.edex" />
|
||||
<available file="../../../../../build.edex" property="build.dir.location" value="../../../../../build.edex" />
|
||||
|
||||
<import file="${build.dir.location}/basebuilds/component_deploy_base.xml" />
|
||||
</project>
|
|
@ -105,12 +105,11 @@ public class SaveCombinationsFileHandler implements
|
|||
if (isAdded) {
|
||||
changeType = FileChangeType.ADDED;
|
||||
}
|
||||
EDEXUtil.getMessageProducer()
|
||||
.sendAsync(
|
||||
"utilityNotify",
|
||||
new FileUpdatedMessage(localization, FileUtil.join(
|
||||
COMBO_FILE_DIR, request.getFileName()),
|
||||
changeType));
|
||||
EDEXUtil.getMessageProducer().sendAsync(
|
||||
"utilityNotify",
|
||||
new FileUpdatedMessage(localization, FileUtil.join(
|
||||
COMBO_FILE_DIR, request.getFileName()), changeType,
|
||||
localFile.lastModified()));
|
||||
} finally {
|
||||
if (file != null) {
|
||||
file.close();
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
<project basedir="." default="deploy" name="com.raytheon.edex.utilitysrv">
|
||||
<available file="../build.edex" property="build.dir.location" value="../build.edex"/>
|
||||
<available file="../../../../../build.edex" property="build.dir.location" value="../../../../../build.edex"/>
|
||||
|
||||
<import file="${build.dir.location}/basebuilds/component_deploy_base.xml" />
|
||||
|
||||
</project>
|
|
@ -29,6 +29,8 @@ import java.util.Map;
|
|||
import com.raytheon.edex.utility.ProtectedFiles;
|
||||
import com.raytheon.uf.common.auth.exception.AuthorizationException;
|
||||
import com.raytheon.uf.common.auth.user.IUser;
|
||||
import com.raytheon.uf.common.localization.FileLocker;
|
||||
import com.raytheon.uf.common.localization.FileLocker.Type;
|
||||
import com.raytheon.uf.common.localization.FileUpdatedMessage;
|
||||
import com.raytheon.uf.common.localization.FileUpdatedMessage.FileChangeType;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext;
|
||||
|
@ -169,6 +171,8 @@ public class LocalizationStreamHandler
|
|||
|
||||
private Object handleStreamingGet(LocalizationStreamGetRequest request,
|
||||
File file) throws Exception {
|
||||
// TODO: Copy file to tmp location named from request unique id and
|
||||
// stream that file for the request to avoid put/delete/read issues
|
||||
FileInputStream inputStream = null;
|
||||
try {
|
||||
inputStream = new FileInputStream(file);
|
||||
|
@ -214,8 +218,8 @@ public class LocalizationStreamHandler
|
|||
if (file.getParentFile().exists() == false) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
File tmpFile = new File(file.getParentFile(), file.getName() + "."
|
||||
+ request.getId());
|
||||
File tmpFile = new File(file.getParentFile(), "." + file.getName()
|
||||
+ "." + request.getId());
|
||||
if ((tmpFile.exists() == false) && (request.getOffset() != 0)) {
|
||||
throw new LocalizationException(
|
||||
"Illegal state, request has offset set but file "
|
||||
|
@ -238,12 +242,10 @@ public class LocalizationStreamHandler
|
|||
tmpFile.createNewFile();
|
||||
}
|
||||
FileOutputStream outputStream = null;
|
||||
Integer bytesWritten = 0;
|
||||
|
||||
try {
|
||||
outputStream = new FileOutputStream(tmpFile, true);
|
||||
byte[] bytes = request.getBytes();
|
||||
bytesWritten += bytes.length;
|
||||
outputStream.write(bytes);
|
||||
} finally {
|
||||
if (outputStream != null) {
|
||||
|
@ -252,21 +254,35 @@ public class LocalizationStreamHandler
|
|||
}
|
||||
}
|
||||
if (request.isEnd()) {
|
||||
FileChangeType changeType = FileChangeType.UPDATED;
|
||||
if (!file.exists()) {
|
||||
changeType = FileChangeType.ADDED;
|
||||
try {
|
||||
FileLocker.lock(this, file, Type.WRITE);
|
||||
FileChangeType changeType = FileChangeType.UPDATED;
|
||||
if (!file.exists()) {
|
||||
changeType = FileChangeType.ADDED;
|
||||
}
|
||||
|
||||
tmpFile.renameTo(file);
|
||||
|
||||
try {
|
||||
// attempt to generate checksum after change
|
||||
UtilityManager.writeChecksum(file);
|
||||
} catch (Exception e) {
|
||||
// ignore, will be generated next time requested
|
||||
}
|
||||
|
||||
long timeStamp = file.lastModified();
|
||||
|
||||
EDEXUtil.getMessageProducer().sendAsync(
|
||||
UtilityManager.NOTIFY_ID,
|
||||
new FileUpdatedMessage(request.getContext(), request
|
||||
.getFileName(), changeType, timeStamp));
|
||||
return timeStamp;
|
||||
} finally {
|
||||
FileLocker.unlock(this, file);
|
||||
}
|
||||
|
||||
tmpFile.renameTo(file);
|
||||
// tmpFile.delete();
|
||||
|
||||
EDEXUtil.getMessageProducer().sendAsync(
|
||||
UtilityManager.NOTIFY_ID,
|
||||
new FileUpdatedMessage(request.getContext(), request
|
||||
.getFileName(), changeType));
|
||||
}
|
||||
|
||||
return bytesWritten;
|
||||
return tmpFile.lastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,10 +21,11 @@
|
|||
package com.raytheon.edex.services;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.FileWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
||||
|
@ -33,6 +34,8 @@ import org.apache.commons.logging.LogFactory;
|
|||
|
||||
import com.raytheon.edex.utility.ProtectedFiles;
|
||||
import com.raytheon.uf.common.localization.Checksum;
|
||||
import com.raytheon.uf.common.localization.FileLocker;
|
||||
import com.raytheon.uf.common.localization.FileLocker.Type;
|
||||
import com.raytheon.uf.common.localization.FileUpdatedMessage;
|
||||
import com.raytheon.uf.common.localization.FileUpdatedMessage.FileChangeType;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext;
|
||||
|
@ -41,7 +44,6 @@ import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
|
|||
import com.raytheon.uf.common.localization.msgs.DeleteUtilityResponse;
|
||||
import com.raytheon.uf.common.localization.msgs.ListResponseEntry;
|
||||
import com.raytheon.uf.common.localization.msgs.ListUtilityResponse;
|
||||
import com.raytheon.uf.common.util.FileUtil;
|
||||
import com.raytheon.uf.edex.core.EDEXUtil;
|
||||
import com.raytheon.uf.edex.core.EdexException;
|
||||
|
||||
|
@ -121,35 +123,47 @@ public class UtilityManager {
|
|||
* @throws EdexException
|
||||
*/
|
||||
private static String getFileChecksum(File file) throws EdexException {
|
||||
File checksumFile = new File(file.toString() + CHECKSUM_FILE_EXTENSION);
|
||||
|
||||
FileLocker.lock(UtilityManager.class, file, Type.WRITE);
|
||||
File checksumFile = getChecksumFile(file);
|
||||
String chksum = null;
|
||||
if (!checksumFile.exists()
|
||||
|| (checksumFile.lastModified() < file.lastModified())) {
|
||||
// Create a checksum
|
||||
try {
|
||||
chksum = Checksum.getMD5Checksum(file);
|
||||
FileUtil.bytes2File(chksum.getBytes(), checksumFile);
|
||||
} catch (Exception e) {
|
||||
// ignore, no checksum will be provided
|
||||
}
|
||||
} else {
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
br = new BufferedReader(new FileReader(checksumFile));
|
||||
chksum = br.readLine();
|
||||
} catch (Exception e) {
|
||||
// ignore, no checksum will be provided
|
||||
} finally {
|
||||
if (br != null) {
|
||||
try {
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
// ignore - can't do anything anyway...
|
||||
}
|
||||
try {
|
||||
if (checksumFile.exists()
|
||||
&& checksumFile.lastModified() >= file.lastModified()) {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(
|
||||
checksumFile));
|
||||
try {
|
||||
chksum = reader.readLine();
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (chksum == null) {
|
||||
chksum = writeChecksum(file);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// log, no checksum will be provided
|
||||
logger.error("Error determing file checksum for: " + file, e);
|
||||
} finally {
|
||||
FileLocker.unlock(UtilityManager.class, file);
|
||||
}
|
||||
return chksum;
|
||||
}
|
||||
|
||||
private static File getChecksumFile(File utilityFile) {
|
||||
return new File(utilityFile.getParentFile(), utilityFile.getName()
|
||||
+ CHECKSUM_FILE_EXTENSION);
|
||||
}
|
||||
|
||||
public static String writeChecksum(File file) throws Exception {
|
||||
String chksum = null;
|
||||
File checksumFile = getChecksumFile(file);
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(checksumFile));
|
||||
try {
|
||||
chksum = Checksum.getMD5Checksum(file);
|
||||
bw.write(chksum);
|
||||
} finally {
|
||||
bw.close();
|
||||
}
|
||||
return chksum;
|
||||
}
|
||||
|
@ -176,7 +190,20 @@ public class UtilityManager {
|
|||
File delFile = new File(fullPath);
|
||||
|
||||
if (delFile.exists()) {
|
||||
delFile.delete();
|
||||
if (!delFile.delete()) {
|
||||
// Failed to delete file...
|
||||
msg = "File could not be deleted: ";
|
||||
if (delFile.isDirectory() && delFile.list().length > 0) {
|
||||
msg += "Non-empty directory";
|
||||
} else if (delFile.canWrite() == false) {
|
||||
msg += "Do not have write permission to file";
|
||||
} else if (delFile.getParentFile() != null
|
||||
&& delFile.getParentFile().canWrite() == false) {
|
||||
msg += "Do not have write permission to file's parent directory";
|
||||
} else {
|
||||
msg += "Reason unknown";
|
||||
}
|
||||
}
|
||||
String md5Path = fullPath + ".md5";
|
||||
File md5File = new File(md5Path);
|
||||
if (md5File.exists()) {
|
||||
|
@ -184,20 +211,22 @@ public class UtilityManager {
|
|||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return new DeleteUtilityResponse(context, e.getMessage(), fileName);
|
||||
return new DeleteUtilityResponse(context, e.getMessage(), fileName,
|
||||
System.currentTimeMillis());
|
||||
}
|
||||
|
||||
long timeStamp = System.currentTimeMillis();
|
||||
// send notification
|
||||
try {
|
||||
EDEXUtil.getMessageProducer().sendAsync(
|
||||
NOTIFY_ID,
|
||||
new FileUpdatedMessage(context, fileName,
|
||||
FileChangeType.DELETED));
|
||||
FileChangeType.DELETED, timeStamp));
|
||||
} catch (Exception e) {
|
||||
logger.error("Error sending file updated message", e);
|
||||
}
|
||||
|
||||
return new DeleteUtilityResponse(context, msg, fileName);
|
||||
return new DeleteUtilityResponse(context, msg, fileName, timeStamp);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
<project basedir="." default="deploy" name="com.raytheon.uf.common.localization">
|
||||
<available file="../build.edex" property="build.dir.location" value="../build.edex"/>
|
||||
<available file="../../../../../build.edex" property="build.dir.location" value="../../../../../build.edex"/>
|
||||
|
||||
<import file="${build.dir.location}/basebuilds/component_deploy_base.xml" />
|
||||
|
||||
</project>
|
|
@ -63,14 +63,19 @@ public class FileUpdatedMessage implements ISerializableObject {
|
|||
@XmlElement
|
||||
private FileChangeType changeType;
|
||||
|
||||
@DynamicSerializeElement
|
||||
@XmlAttribute
|
||||
private long timeStamp;
|
||||
|
||||
public FileUpdatedMessage() {
|
||||
}
|
||||
|
||||
public FileUpdatedMessage(LocalizationContext context, String fileName,
|
||||
FileChangeType changeType) {
|
||||
FileChangeType changeType, long timeStamp) {
|
||||
this.context = context;
|
||||
this.fileName = fileName;
|
||||
this.changeType = changeType;
|
||||
this.timeStamp = timeStamp;
|
||||
}
|
||||
|
||||
public LocalizationContext getContext() {
|
||||
|
@ -96,4 +101,20 @@ public class FileUpdatedMessage implements ISerializableObject {
|
|||
public void setChangeType(FileChangeType changeType) {
|
||||
this.changeType = changeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the timeStamp
|
||||
*/
|
||||
public long getTimeStamp() {
|
||||
return timeStamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeStamp
|
||||
* the timeStamp to set
|
||||
*/
|
||||
public void setTimeStamp(long timeStamp) {
|
||||
this.timeStamp = timeStamp;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.util.Date;
|
|||
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
|
||||
import com.raytheon.uf.common.localization.LocalizationFile.ModifiableLocalizationFile;
|
||||
import com.raytheon.uf.common.localization.exception.LocalizationOpFailedException;
|
||||
|
||||
/**
|
||||
|
@ -44,19 +45,6 @@ import com.raytheon.uf.common.localization.exception.LocalizationOpFailedExcepti
|
|||
*/
|
||||
public interface ILocalizationAdapter {
|
||||
|
||||
/**
|
||||
* Retrieves the directory name for the given localization type.
|
||||
*
|
||||
* @param type
|
||||
* the localization type
|
||||
* @return the directory name of the directory where the localization type
|
||||
* should be stored. The returned string is the directory name only,
|
||||
* NOT the fully qualified path.
|
||||
*
|
||||
* @see #getPath(LocalizationContext, String)
|
||||
*/
|
||||
public abstract String getDirNameForType(LocalizationType type);
|
||||
|
||||
/**
|
||||
* Return the fully qualified path given a localization context and a file
|
||||
* name
|
||||
|
@ -93,19 +81,14 @@ public interface ILocalizationAdapter {
|
|||
throws LocalizationOpFailedException;
|
||||
|
||||
/**
|
||||
* Save a file given it's local pointer, a localization context and a file
|
||||
* name
|
||||
* Save a file a modifiable localization file
|
||||
*
|
||||
* @param localFile
|
||||
* the local file pointer
|
||||
* @param context
|
||||
* the localization context
|
||||
* @param fileName
|
||||
* the file name
|
||||
* @param file
|
||||
* the modifiable localization file
|
||||
* @throws LocalizationOpFailedException
|
||||
*/
|
||||
public abstract boolean save(File localFile, LocalizationContext context,
|
||||
String fileName) throws LocalizationOpFailedException;
|
||||
public abstract boolean save(ModifiableLocalizationFile file)
|
||||
throws LocalizationOpFailedException;
|
||||
|
||||
/**
|
||||
* List a directory given a set of contexts and a path.
|
||||
|
@ -162,18 +145,14 @@ public interface ILocalizationAdapter {
|
|||
LocalizationLevel level);
|
||||
|
||||
/**
|
||||
* Delete a file given a local file pointer, context and filename
|
||||
* Delete a file given a modifiable localization file
|
||||
*
|
||||
* @param file
|
||||
* the file pointer
|
||||
* @param context
|
||||
* the localization context
|
||||
* @param fileName
|
||||
* the name of the file on the server
|
||||
* the modifiable localization file
|
||||
* @throws LocalizationOpFailedException
|
||||
*/
|
||||
public abstract boolean delete(File file, LocalizationContext context,
|
||||
String fileName) throws LocalizationOpFailedException;
|
||||
public abstract boolean delete(ModifiableLocalizationFile file)
|
||||
throws LocalizationOpFailedException;
|
||||
|
||||
public abstract String[] getContextList(LocalizationLevel level)
|
||||
throws LocalizationOpFailedException;
|
||||
|
|
|
@ -27,7 +27,10 @@ import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel
|
|||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
|
||||
|
||||
/**
|
||||
* A generalized interface for constructing LocalizationFiles.
|
||||
* A generalized interface for constructing LocalizationFiles. NOTE: There will
|
||||
* only exist a single reference to any LocalizationFile. It is the
|
||||
* IPathManager's responsibility to ensure multiple objects of the same
|
||||
* LocalizationFile are not returned
|
||||
*
|
||||
* Note: Paths should use IPathManager.SEPARATOR as the separator for
|
||||
* consistency. The client OS could potentially differ from the localization
|
||||
|
|
|
@ -23,13 +23,13 @@ import java.io.File;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.raytheon.uf.common.localization.FileLocker.Type;
|
||||
import com.raytheon.uf.common.localization.ILocalizationAdapter.ListResponse;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
|
||||
import com.raytheon.uf.common.localization.exception.LocalizationException;
|
||||
|
@ -85,12 +85,60 @@ import com.raytheon.uf.common.status.UFStatus.Priority;
|
|||
* @version 1.0
|
||||
*/
|
||||
|
||||
public class LocalizationFile implements Comparable<LocalizationFile> {
|
||||
public final class LocalizationFile implements Comparable<LocalizationFile> {
|
||||
private static transient IUFStatusHandler statusHandler = UFStatus
|
||||
.getHandler(LocalizationFile.class);
|
||||
|
||||
/**
|
||||
* Class {@link LocalizationFile} exposes to the
|
||||
* {@link ILocalizationAdapter} objects so they can modify the file if
|
||||
* anything changes. Don't want to expose ability to modify
|
||||
* {@link LocalizationFile} contents to everyone
|
||||
*
|
||||
* @author mschenke
|
||||
* @version 1.0
|
||||
*/
|
||||
public class ModifiableLocalizationFile {
|
||||
|
||||
private ModifiableLocalizationFile() {
|
||||
// Private constructor
|
||||
}
|
||||
|
||||
public LocalizationFile getLocalizationFile() {
|
||||
return LocalizationFile.this;
|
||||
}
|
||||
|
||||
public void setTimeStamp(Date timeStamp) {
|
||||
getLocalizationFile().fileTimestamp = timeStamp;
|
||||
}
|
||||
|
||||
public void setFileChecksum(String checksum) {
|
||||
getLocalizationFile().fileCheckSum = checksum;
|
||||
}
|
||||
|
||||
public void setIsAvailableOnServer(boolean isAvailableOnServer) {
|
||||
getLocalizationFile().isAvailableOnServer = isAvailableOnServer;
|
||||
}
|
||||
|
||||
public void setIsDirectory(boolean isDirectory) {
|
||||
getLocalizationFile().isDirectory = isDirectory;
|
||||
}
|
||||
|
||||
public File getLocalFile() {
|
||||
return getLocalizationFile().file;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return getLocalizationFile().path;
|
||||
}
|
||||
|
||||
public LocalizationContext getContext() {
|
||||
return getLocalizationFile().context;
|
||||
}
|
||||
}
|
||||
|
||||
/** Local file pointer to localization file, will never be null */
|
||||
private File file;
|
||||
protected final File file;
|
||||
|
||||
/** The file timestamp on the server, may be null if file doesn't exist yet */
|
||||
private Date fileTimestamp;
|
||||
|
@ -108,7 +156,7 @@ public class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
private boolean isAvailableOnServer;
|
||||
|
||||
/** The localization adapter for the file */
|
||||
final ILocalizationAdapter adapter;
|
||||
protected final ILocalizationAdapter adapter;
|
||||
|
||||
/** The localization path of the file */
|
||||
private final String path;
|
||||
|
@ -120,13 +168,14 @@ public class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
private Set<ILocalizationFileObserver> observers = new HashSet<ILocalizationFileObserver>();
|
||||
|
||||
/** Flag to set if file has been requested */
|
||||
boolean fileRequested = false;
|
||||
protected boolean fileRequested = false;
|
||||
|
||||
/**
|
||||
* Construct a null localization file, used to keep track of files that
|
||||
* cannot exist.
|
||||
*/
|
||||
LocalizationFile() {
|
||||
file = null;
|
||||
path = null;
|
||||
adapter = null;
|
||||
context = null;
|
||||
|
@ -138,7 +187,8 @@ public class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
* @return
|
||||
*/
|
||||
boolean isNull() {
|
||||
return adapter == null && path == null && context == null;
|
||||
return adapter == null && path == null && context == null
|
||||
&& file == null;
|
||||
}
|
||||
|
||||
LocalizationFile(ILocalizationAdapter adapter, LocalizationContext context,
|
||||
|
@ -173,6 +223,16 @@ public class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a modifiable version of the localization file. Meant to be used
|
||||
* internally within localization only which is why package level
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
ModifiableLocalizationFile getModifiableFile() {
|
||||
return new ModifiableLocalizationFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns the time stamp of the file where it is stored, not the local
|
||||
* version of the file
|
||||
|
@ -209,27 +269,33 @@ public class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
* @return the file
|
||||
*/
|
||||
public File getFile(boolean retrieveFile) throws LocalizationException {
|
||||
if (retrieveFile) {
|
||||
fileRequested = true;
|
||||
}
|
||||
if (isAvailableOnServer && retrieveFile) {
|
||||
if (isDirectory) {
|
||||
file.mkdirs();
|
||||
try {
|
||||
FileLocker.lock(this, file, Type.WRITE);
|
||||
if (retrieveFile) {
|
||||
fileRequested = true;
|
||||
}
|
||||
adapter.retrieve(this);
|
||||
}
|
||||
|
||||
if (isDirectory == false && !file.exists()) {
|
||||
try {
|
||||
file.getParentFile().mkdirs();
|
||||
} catch (Throwable t) {
|
||||
// try to create the file's directory automatically, but if it
|
||||
// fails, don't report it as it is just something to do to help
|
||||
// the user of the file for easier creation of the file
|
||||
if (isAvailableOnServer && retrieveFile) {
|
||||
if (isDirectory) {
|
||||
file.mkdirs();
|
||||
}
|
||||
adapter.retrieve(this);
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
if (isDirectory == false && !file.exists()) {
|
||||
try {
|
||||
file.getParentFile().mkdirs();
|
||||
} catch (Throwable t) {
|
||||
// try to create the file's directory automatically, but if
|
||||
// it fails, don't report it as it is just something to do
|
||||
// to help the user of the file for easier creation of the
|
||||
// file
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
} finally {
|
||||
FileLocker.unlock(this, file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -255,7 +321,8 @@ public class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
* @return the InputStream to be used for reading the file
|
||||
* @throws LocalizationException
|
||||
*/
|
||||
public InputStream openInputStream() throws LocalizationException {
|
||||
public LocalizationFileInputStream openInputStream()
|
||||
throws LocalizationException {
|
||||
try {
|
||||
return new LocalizationFileInputStream(this);
|
||||
} catch (FileNotFoundException e) {
|
||||
|
@ -324,7 +391,8 @@ public class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
* @return the OutputStream to be used for writing to the file
|
||||
* @throws LocalizationException
|
||||
*/
|
||||
public OutputStream openOutputStream() throws LocalizationException {
|
||||
public LocalizationFileOutputStream openOutputStream()
|
||||
throws LocalizationException {
|
||||
return openOutputStream(false);
|
||||
}
|
||||
|
||||
|
@ -335,7 +403,7 @@ public class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
* @return the OutputStream to be used for writing to the file
|
||||
* @throws LocalizationException
|
||||
*/
|
||||
public OutputStream openOutputStream(boolean isAppending)
|
||||
public LocalizationFileOutputStream openOutputStream(boolean isAppending)
|
||||
throws LocalizationException {
|
||||
try {
|
||||
return new LocalizationFileOutputStream(this, isAppending);
|
||||
|
@ -352,23 +420,20 @@ public class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
* @throws LocalizationException
|
||||
*/
|
||||
public void write(byte[] bytes) throws LocalizationException {
|
||||
OutputStream os = null;
|
||||
LocalizationFileOutputStream os = null;
|
||||
try {
|
||||
os = openOutputStream();
|
||||
os.write(bytes);
|
||||
save();
|
||||
} catch (IOException e) {
|
||||
throw new LocalizationException("Could not write to file "
|
||||
+ file.getName(), e);
|
||||
} finally {
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
os.closeAndSave();
|
||||
} catch (IOException e) {
|
||||
statusHandler
|
||||
.handle(Priority.INFO,
|
||||
"Failed to close output stream to area geometries file",
|
||||
e);
|
||||
statusHandler.handle(Priority.INFO,
|
||||
"Failed to close output stream for file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -426,23 +491,28 @@ public class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
* @throws LocalizationOpFailedException
|
||||
*/
|
||||
public boolean save() throws LocalizationOpFailedException {
|
||||
String checksum = "";
|
||||
try {
|
||||
checksum = Checksum.getMD5Checksum(file);
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
// Check if file differs from server file
|
||||
if (!checksum.equals(fileCheckSum)) {
|
||||
boolean rval = adapter.save(file, context, path);
|
||||
if (rval) {
|
||||
fileCheckSum = checksum;
|
||||
FileLocker.lock(this, file, Type.WRITE);
|
||||
String checksum = "";
|
||||
try {
|
||||
checksum = Checksum.getMD5Checksum(file);
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
// Check if file differs from server file
|
||||
if (!checksum.equals(fileCheckSum)) {
|
||||
boolean rval = adapter.save(getModifiableFile());
|
||||
if (rval) {
|
||||
fileCheckSum = checksum;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
// Local file matches server file, success technically
|
||||
return true;
|
||||
// Local file matches server file, success technically
|
||||
return true;
|
||||
} finally {
|
||||
FileLocker.unlock(this, file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -461,15 +531,20 @@ public class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
* @throws LocalizationOpFailedException
|
||||
*/
|
||||
public boolean delete() throws LocalizationOpFailedException {
|
||||
if (exists()) {
|
||||
return adapter.delete(file, context, path);
|
||||
} else if (file.exists()) {
|
||||
// Local file does actually exist, delete manually
|
||||
return file.delete();
|
||||
}
|
||||
try {
|
||||
FileLocker.lock(this, file, Type.WRITE);
|
||||
if (exists()) {
|
||||
return adapter.delete(getModifiableFile());
|
||||
} else if (file.exists()) {
|
||||
// Local file does actually exist, delete manually
|
||||
return file.delete();
|
||||
}
|
||||
|
||||
// File doesn't exist, it is deleted, so technically success?
|
||||
return true;
|
||||
// File doesn't exist, it is deleted, so technically success?
|
||||
return true;
|
||||
} finally {
|
||||
FileLocker.unlock(this, file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -478,7 +553,7 @@ public class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
* @return true if the file exists
|
||||
*/
|
||||
public boolean exists() {
|
||||
return adapter.exists(this);
|
||||
return isNull() == false && adapter.exists(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.io.FileNotFoundException;
|
|||
import java.io.IOException;
|
||||
|
||||
import com.raytheon.uf.common.localization.exception.LocalizationException;
|
||||
import com.raytheon.uf.common.localization.exception.LocalizationOpFailedException;
|
||||
|
||||
/**
|
||||
* Class which opens a LocalizationFile for writing to
|
||||
|
@ -43,6 +44,8 @@ import com.raytheon.uf.common.localization.exception.LocalizationException;
|
|||
|
||||
public class LocalizationFileOutputStream extends LockingFileOutputStream {
|
||||
|
||||
private LocalizationFile file;
|
||||
|
||||
/**
|
||||
* @param file
|
||||
* @param isAppending
|
||||
|
@ -53,6 +56,25 @@ public class LocalizationFileOutputStream extends LockingFileOutputStream {
|
|||
LocalizationFileOutputStream(LocalizationFile file, boolean isAppending)
|
||||
throws FileNotFoundException, LocalizationException {
|
||||
super(file.getFile(false), isAppending);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes input stream for the {@link LocalizationFile} and calls
|
||||
* {@link LocalizationFile#save()} on the file to ensure contents are
|
||||
* persisted. Calling {@link #close()} does not trigger a save
|
||||
*
|
||||
* @param save
|
||||
* @throws IOException
|
||||
*/
|
||||
public void closeAndSave() throws IOException,
|
||||
LocalizationOpFailedException {
|
||||
try {
|
||||
closeWithoutUnlocking();
|
||||
file.save();
|
||||
} finally {
|
||||
// Make sure we unlock the file
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
/**
|
||||
* 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.localization;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* WeakReference object to Localization File
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jun 21, 2011 mschenke Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author mschenke
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
public class LocalizationFileRef extends WeakReference<LocalizationFile> {
|
||||
|
||||
private final LocalizationFileKey key;
|
||||
|
||||
/**
|
||||
* @param referent
|
||||
*/
|
||||
public LocalizationFileRef(LocalizationFile referent) {
|
||||
super(referent);
|
||||
key = new LocalizationFileKey(referent.getName(), referent.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
public Object getKey() {
|
||||
return key;
|
||||
}
|
||||
}
|
|
@ -20,14 +20,14 @@
|
|||
package com.raytheon.uf.common.localization;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.raytheon.uf.common.localization.FileLocker.Type;
|
||||
import com.raytheon.uf.common.localization.FileUpdatedMessage.FileChangeType;
|
||||
import com.raytheon.uf.common.localization.ILocalizationAdapter.ListResponse;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
|
||||
|
@ -58,12 +58,12 @@ import com.raytheon.uf.common.status.UFStatus.Priority;
|
|||
|
||||
public class LocalizationNotificationObserver {
|
||||
|
||||
private static class LocalizationFileKey {
|
||||
private static class LocalizationTypeFileKey {
|
||||
private final LocalizationType type;
|
||||
|
||||
private final String path;
|
||||
|
||||
public LocalizationFileKey(LocalizationType type, String path) {
|
||||
public LocalizationTypeFileKey(LocalizationType type, String path) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.path = path;
|
||||
|
@ -89,7 +89,7 @@ public class LocalizationNotificationObserver {
|
|||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final LocalizationFileKey other = (LocalizationFileKey) obj;
|
||||
final LocalizationTypeFileKey other = (LocalizationTypeFileKey) obj;
|
||||
if (path == null) {
|
||||
if (other.path != null) {
|
||||
return false;
|
||||
|
@ -119,7 +119,7 @@ public class LocalizationNotificationObserver {
|
|||
|
||||
private Set<ILocalizationFileObserver> globalObservers = new HashSet<ILocalizationFileObserver>();
|
||||
|
||||
private final Map<LocalizationFileKey, Set<LocalizationFileRef>> observedFiles;
|
||||
private final Map<LocalizationTypeFileKey, Set<LocalizationFile>> observedFiles;
|
||||
|
||||
private PathManager pm;
|
||||
|
||||
|
@ -137,24 +137,25 @@ public class LocalizationNotificationObserver {
|
|||
* @param lf
|
||||
*/
|
||||
void addObservedFile(LocalizationFile lf) {
|
||||
LocalizationFileKey key = new LocalizationFileKey(lf.getContext()
|
||||
.getLocalizationType(), lf.getName());
|
||||
LocalizationTypeFileKey key = new LocalizationTypeFileKey(lf
|
||||
.getContext().getLocalizationType(), lf.getName());
|
||||
|
||||
LocalizationFileRef ref = new LocalizationFileRef(lf);
|
||||
Set<LocalizationFileRef> lfList;
|
||||
Set<LocalizationFile> lfList;
|
||||
synchronized (observedFiles) {
|
||||
lfList = observedFiles.get(key);
|
||||
if (lfList == null) {
|
||||
lfList = new HashSet<LocalizationFileRef>();
|
||||
// add now so cleanUpRefs() cannot remove the set
|
||||
lfList.add(ref);
|
||||
lfList = new HashSet<LocalizationFile>();
|
||||
observedFiles.put(key, lfList);
|
||||
}
|
||||
}
|
||||
synchronized (lfList) {
|
||||
lfList.add(ref);
|
||||
if (lfList.add(lf) == false) {
|
||||
// Contract between IPathManager/LocalizationFile will force
|
||||
// this to never occur and will be developer mistake if so
|
||||
throw new RuntimeException(
|
||||
"Internal Error: Attempted to register LocalizationFile which had already been registered");
|
||||
}
|
||||
}
|
||||
cleanUpRefs();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -182,118 +183,79 @@ public class LocalizationNotificationObserver {
|
|||
}
|
||||
|
||||
private LocalizationNotificationObserver() {
|
||||
observedFiles = new ConcurrentHashMap<LocalizationFileKey, Set<LocalizationFileRef>>();
|
||||
observedFiles = new ConcurrentHashMap<LocalizationTypeFileKey, Set<LocalizationFile>>();
|
||||
pm = (PathManager) PathManagerFactory.getPathManager();
|
||||
}
|
||||
|
||||
public void fileUpdateMessageRecieved(FileUpdatedMessage fum) {
|
||||
public synchronized void fileUpdateMessageRecieved(FileUpdatedMessage fum) {
|
||||
LocalizationType type = fum.getContext().getLocalizationType();
|
||||
LocalizationLevel level = fum.getContext().getLocalizationLevel();
|
||||
String contextName = fum.getContext().getContextName();
|
||||
String filename = LocalizationUtil.getSplitUnique(fum.getFileName());
|
||||
|
||||
// Cleanup LocalizationFiles that have been GC'd
|
||||
cleanUpRefs();
|
||||
// Check if file update is older than latest file data
|
||||
Collection<LocalizationFile> potentialFiles = getLocalizationFiles(
|
||||
type, filename);
|
||||
|
||||
// Find exact match first:
|
||||
for (LocalizationFile file : potentialFiles) {
|
||||
if (file.getContext().equals(fum.getContext())) {
|
||||
// exact match found, skip old updates (in case we have changed
|
||||
// the file since this update)
|
||||
try {
|
||||
FileLocker.lock(this, file.file, Type.WRITE);
|
||||
Date fileTS = file.getTimeStamp();
|
||||
if (fileTS != null && fileTS.getTime() > fum.getTimeStamp()) {
|
||||
// Update is older than latest file data, skip update as
|
||||
// a newer one should be coming
|
||||
return;
|
||||
} else {
|
||||
// Proceed with update process
|
||||
processUpdate(fum, file);
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
FileLocker.unlock(this, file.file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If file deleted, delete from filesystem if non directory
|
||||
if (fum.getChangeType() == FileChangeType.DELETED) {
|
||||
File local = pm.adapter.getPath(fum.getContext(), filename);
|
||||
if (local != null && local.isDirectory() == false && local.exists()) {
|
||||
local.delete();
|
||||
try {
|
||||
FileLocker.lock(this, local, Type.WRITE);
|
||||
local.delete();
|
||||
} finally {
|
||||
FileLocker.unlock(this, local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Response map, only request updated data once per file reference key
|
||||
Map<Object, ListResponse> responseMap = new HashMap<Object, ListResponse>();
|
||||
// Process other file, skipping context match that was processed above
|
||||
for (LocalizationFile file : potentialFiles) {
|
||||
if (file.getContext().equals(fum.getContext()) == false) {
|
||||
processUpdate(fum, file);
|
||||
}
|
||||
}
|
||||
|
||||
// Split parts so we update sub directories
|
||||
String[] parts = LocalizationUtil.splitUnique(filename);
|
||||
for (int idx = parts.length - 1; idx > 0; --idx) {
|
||||
String subpath = "";
|
||||
for (int i = 0; i < idx; ++i) {
|
||||
subpath += parts[i];
|
||||
if (i < (idx - 1)) {
|
||||
subpath += IPathManager.SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
LocalizationFileKey key = new LocalizationFileKey(type, filename);
|
||||
// Get the file references for the key to notify
|
||||
Set<LocalizationFileRef> lfList;
|
||||
synchronized (observedFiles) {
|
||||
lfList = observedFiles.get(key);
|
||||
Collection<LocalizationFile> files = getLocalizationFiles(type,
|
||||
subpath);
|
||||
for (LocalizationFile file : files) {
|
||||
processUpdate(fum, file);
|
||||
}
|
||||
if (lfList != null) {
|
||||
Set<LocalizationFileRef> copy;
|
||||
synchronized (lfList) {
|
||||
copy = new HashSet<LocalizationFileRef>(lfList);
|
||||
}
|
||||
// Flags so we only delete or request once
|
||||
boolean requested = false;
|
||||
for (LocalizationFileRef ref : copy) {
|
||||
LocalizationFile lf = ref.get();
|
||||
if (lf != null) {
|
||||
// If null, means it was garbage collected, will be
|
||||
// caught next update
|
||||
int compVal = lf.getContext().getLocalizationLevel()
|
||||
.compareTo(level);
|
||||
if (compVal <= 0) {
|
||||
boolean notify = false;
|
||||
|
||||
if (compVal < 0) {
|
||||
// Different level, check our context name to
|
||||
// make sure it matches update message
|
||||
String ourContextName = pm.getContext(type,
|
||||
level).getContextName();
|
||||
if ((ourContextName == null && contextName == null)
|
||||
|| (ourContextName != null && ourContextName
|
||||
.equals(contextName))) {
|
||||
notify = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This file should be NOTIFEID based on update...
|
||||
ListResponse resp = null;
|
||||
if (fum.getContext().equals(lf.getContext())) {
|
||||
// context perfectly matches, make sure we
|
||||
// notify
|
||||
notify = true;
|
||||
// This file should be MODIFIED based on
|
||||
// update...
|
||||
if (lf.isDirectory() == false) {
|
||||
resp = responseMap.get(ref.getKey());
|
||||
if (resp == null) {
|
||||
// Make sure we only request metadata
|
||||
// once per file update
|
||||
resp = getMetadata(lf);
|
||||
responseMap.put(ref.getKey(), resp);
|
||||
}
|
||||
|
||||
// Update file with new metadata
|
||||
lf.update(resp);
|
||||
|
||||
// If we are still not a directoy and we
|
||||
// should request the file, request it
|
||||
if (lf.isDirectory() == false
|
||||
&& lf.fileRequested && !requested) {
|
||||
switch (fum.getChangeType()) {
|
||||
case UPDATED:
|
||||
case ADDED: {
|
||||
requested = true;
|
||||
lf.getFile();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (notify) {
|
||||
// Notify file of change
|
||||
lf.notifyObservers(fum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int pos = filename.lastIndexOf(IPathManager.SEPARATOR);
|
||||
if (pos > 0) {
|
||||
filename = filename.substring(0, pos);
|
||||
} else {
|
||||
filename = "";
|
||||
}
|
||||
} while (!filename.isEmpty());
|
||||
}
|
||||
|
||||
// Notify system wide listeners
|
||||
synchronized (globalObservers) {
|
||||
|
@ -303,6 +265,74 @@ public class LocalizationNotificationObserver {
|
|||
}
|
||||
}
|
||||
|
||||
private void processUpdate(FileUpdatedMessage fum, LocalizationFile file) {
|
||||
LocalizationContext context = fum.getContext();
|
||||
LocalizationLevel level = context.getLocalizationLevel();
|
||||
LocalizationType type = context.getLocalizationType();
|
||||
String contextName = context.getContextName();
|
||||
|
||||
int compVal = file.getContext().getLocalizationLevel().compareTo(level);
|
||||
if (compVal <= 0) {
|
||||
boolean notify = false;
|
||||
if (compVal < 0) {
|
||||
// Different level, check our context name to
|
||||
// make sure it matches update message
|
||||
String ourContextName = pm.getContext(type, level)
|
||||
.getContextName();
|
||||
if ((ourContextName == null && contextName == null)
|
||||
|| (ourContextName != null && ourContextName
|
||||
.equals(contextName))) {
|
||||
notify = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fum.getContext().equals(file.getContext())) {
|
||||
// context perfectly matches, make sure we
|
||||
// notify
|
||||
notify = true;
|
||||
// This file should be MODIFIED based on
|
||||
// update...
|
||||
if (file.isDirectory() == false) {
|
||||
// Update file with new metadata
|
||||
file.update(getMetadata(file));
|
||||
|
||||
// If we are still not a directoy and we
|
||||
// should request the file, request it
|
||||
if (file.isDirectory() == false && file.fileRequested) {
|
||||
switch (fum.getChangeType()) {
|
||||
case UPDATED:
|
||||
case ADDED: {
|
||||
file.getFile();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (notify) {
|
||||
// Notify file of change
|
||||
file.notifyObservers(fum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<LocalizationFile> getLocalizationFiles(
|
||||
LocalizationType type, String fileName) {
|
||||
Set<LocalizationFile> copy = new HashSet<LocalizationFile>();
|
||||
Set<LocalizationFile> lfList;
|
||||
synchronized (observedFiles) {
|
||||
lfList = observedFiles.get(new LocalizationTypeFileKey(type,
|
||||
fileName));
|
||||
}
|
||||
if (lfList != null) {
|
||||
synchronized (lfList) {
|
||||
copy = new HashSet<LocalizationFile>(lfList);
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lf
|
||||
* @return
|
||||
|
@ -328,33 +358,4 @@ public class LocalizationNotificationObserver {
|
|||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up stale references
|
||||
*/
|
||||
private void cleanUpRefs() {
|
||||
List<LocalizationFileKey> keysToRemove = new ArrayList<LocalizationFileKey>();
|
||||
for (LocalizationFileKey key : observedFiles.keySet()) {
|
||||
Set<LocalizationFileRef> refs = observedFiles.get(key);
|
||||
if (refs == null || refs.size() == 0) {
|
||||
keysToRemove.add(key);
|
||||
} else {
|
||||
synchronized (refs) {
|
||||
List<LocalizationFileRef> toRemove = new ArrayList<LocalizationFileRef>();
|
||||
for (LocalizationFileRef ref : refs) {
|
||||
if (ref.get() == null) {
|
||||
toRemove.add(ref);
|
||||
}
|
||||
}
|
||||
refs.removeAll(toRemove);
|
||||
if (refs.size() == 0) {
|
||||
keysToRemove.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (LocalizationFileKey key : keysToRemove) {
|
||||
observedFiles.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,11 +77,36 @@ public class LockingFileOutputStream extends FileOutputStream {
|
|||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
close(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the output stream without unlocking the file. It is the
|
||||
* responsibility of the caller to call {@link #unlock()} when they are done
|
||||
* with the lock.
|
||||
*/
|
||||
public void closeWithoutUnlocking() throws IOException {
|
||||
close(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the stream, flag designates if lock will be released or not. By
|
||||
* default {@link #close()} will unlock the file
|
||||
*
|
||||
* @param unlock
|
||||
* @throws IOException
|
||||
*/
|
||||
private void close(boolean unlock) throws IOException {
|
||||
try {
|
||||
super.close();
|
||||
} finally {
|
||||
FileLocker.unlock(this, file);
|
||||
if (unlock) {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unlock() {
|
||||
FileLocker.unlock(this, file);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -223,22 +223,13 @@ public class PathManager implements IPathManager {
|
|||
}
|
||||
|
||||
if (entry != null) {
|
||||
for (ListResponse lr : entry) {
|
||||
// A null File means the file can never exist, therefore we
|
||||
// set
|
||||
// the context/name key as a null file object to avoid
|
||||
// requesting data about the non-existent file again
|
||||
LocalizationFile file = new LocalizationFile();
|
||||
File local = adapter.getPath(lr.context, name);
|
||||
if (local != null) {
|
||||
file = new LocalizationFile(this.adapter, lr.context,
|
||||
local, lr.date, name, lr.checkSum,
|
||||
lr.isDirectory, lr.existsOnServer,
|
||||
lr.protectedLevel);
|
||||
availableFiles.put(file.getContext(), file);
|
||||
synchronized (fileCache) {
|
||||
for (ListResponse lr : entry) {
|
||||
LocalizationFile file = createFromResponse(lr);
|
||||
if (file.isNull() == false) {
|
||||
availableFiles.put(file.getContext(), file);
|
||||
}
|
||||
}
|
||||
fileCache.put(new LocalizationFileKey(lr.fileName,
|
||||
lr.context), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -255,6 +246,43 @@ public class PathManager implements IPathManager {
|
|||
return rval.toArray(new LocalizationFile[rval.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LocalizationFile from a {@link ListResponse}, callers need to
|
||||
* synchronize on fileCache before calling
|
||||
*
|
||||
* @param response
|
||||
* @return LocalizationFile for response (never null), but be sure to check
|
||||
* isNull() on file object
|
||||
*/
|
||||
private LocalizationFile createFromResponse(ListResponse response) {
|
||||
// able to resolve file, lf will be set, check cache
|
||||
LocalizationFileKey key = new LocalizationFileKey(response.fileName,
|
||||
response.context);
|
||||
LocalizationFile lf = fileCache.get(key);
|
||||
if (lf != null && lf.isNull() == false) {
|
||||
// Ensure latest data for file, will only be null if no File can be
|
||||
// returned for path/context.
|
||||
lf.update(response);
|
||||
} else {
|
||||
// Not in cache or null reference, see if file can be resolved
|
||||
if (lf == null) {
|
||||
// Default to null file if not from cache
|
||||
lf = new LocalizationFile();
|
||||
}
|
||||
File file = this.adapter.getPath(response.context,
|
||||
response.fileName);
|
||||
if (file != null) {
|
||||
// No cache file available and path is resolved, create
|
||||
lf = new LocalizationFile(this.adapter, response.context, file,
|
||||
response.date, response.fileName, response.checkSum,
|
||||
response.isDirectory, response.existsOnServer,
|
||||
response.protectedLevel);
|
||||
}
|
||||
fileCache.put(key, lf);
|
||||
}
|
||||
return lf;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
@ -288,41 +316,13 @@ public class PathManager implements IPathManager {
|
|||
ListResponse[] entries = this.adapter.listDirectory(contexts, name,
|
||||
recursive, filesOnly);
|
||||
|
||||
for (ListResponse entry : entries) {
|
||||
if (entry.isDirectory
|
||||
|| matchesExtension(entry.fileName, filter)) {
|
||||
File file = this.adapter.getPath(entry.context,
|
||||
entry.fileName);
|
||||
if (file != null) {
|
||||
LocalizationFileKey key = new LocalizationFileKey(
|
||||
entry.fileName, entry.context);
|
||||
LocalizationFile lf = fileCache.get(key);
|
||||
boolean fromCache = true;
|
||||
if (lf == null) {
|
||||
fromCache = false;
|
||||
// No cache file available
|
||||
lf = new LocalizationFile(this.adapter,
|
||||
entry.context, file, entry.date,
|
||||
entry.fileName, entry.checkSum,
|
||||
entry.isDirectory, entry.existsOnServer,
|
||||
entry.protectedLevel);
|
||||
fileCache.put(key, lf);
|
||||
}
|
||||
if (lf.exists()) {
|
||||
files.add(lf);
|
||||
} else if (fromCache) {
|
||||
// File from cache does not exist, check if entry
|
||||
// from response exists. If so update cache with new
|
||||
// file created from entry
|
||||
lf = new LocalizationFile(this.adapter,
|
||||
entry.context, file, entry.date,
|
||||
entry.fileName, entry.checkSum,
|
||||
entry.isDirectory, entry.existsOnServer,
|
||||
entry.protectedLevel);
|
||||
if (lf.exists()) {
|
||||
files.add(lf);
|
||||
fileCache.put(key, lf);
|
||||
}
|
||||
synchronized (fileCache) {
|
||||
for (ListResponse entry : entries) {
|
||||
if (entry.isDirectory
|
||||
|| matchesExtension(entry.fileName, filter)) {
|
||||
LocalizationFile file = createFromResponse(entry);
|
||||
if (file.exists()) {
|
||||
files.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,12 @@ package com.raytheon.uf.common.localization.msgs;
|
|||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import com.raytheon.uf.common.localization.LocalizationContext;
|
||||
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
|
||||
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
|
||||
|
||||
/**
|
||||
* Defines the delete localization response
|
||||
|
@ -47,19 +49,40 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
|
|||
@DynamicSerialize
|
||||
public class DeleteUtilityResponse extends AbstractUtilityResponse {
|
||||
|
||||
@XmlAttribute
|
||||
@DynamicSerializeElement
|
||||
private long timeStamp;
|
||||
|
||||
public DeleteUtilityResponse() {
|
||||
|
||||
}
|
||||
|
||||
public DeleteUtilityResponse(LocalizationContext context, String errorText,
|
||||
String fileName) {
|
||||
String fileName, long timeStamp) {
|
||||
super(context, fileName, errorText);
|
||||
this.timeStamp = timeStamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the timeStamp
|
||||
*/
|
||||
public long getTimeStamp() {
|
||||
return timeStamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeStamp
|
||||
* the timeStamp to set
|
||||
*/
|
||||
public void setTimeStamp(long timeStamp) {
|
||||
this.timeStamp = timeStamp;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.raytheon.edex.msg.utility.AbstractUtilityResponse#getFormattedErrorMessage()
|
||||
* @see com.raytheon.edex.msg.utility.AbstractUtilityResponse#
|
||||
* getFormattedErrorMessage()
|
||||
*/
|
||||
@Override
|
||||
public String getFormattedErrorMessage() {
|
||||
|
|
Loading…
Add table
Reference in a new issue