// Deals with directory
boolean isDirectory = false;
if (file.exists()) {
if (file.isDirectory()) {
isDirectory = true;
response.setStatus(new Status(Status.CLIENT_ERROR_FORBIDDEN,
"Can't put a new representation of a directory"));
return;
}
} else {
// No existing file or directory found
if (path.endsWith("/")) {
isDirectory = true;
// Create a new directory and its parents if necessary
if (file.mkdirs()) {
response.setStatus(Status.SUCCESS_NO_CONTENT);
} else {
getLogger().log(Level.WARNING,
"Unable to create the new directory");
response.setStatus(new Status(Status.SERVER_ERROR_INTERNAL,
"Unable to create the new directory"));
}
return;
}
}
if (!isDirectory) {
// Several checks : first the consistency of the metadata and
// the filename
boolean partialPut = !request.getRanges().isEmpty();
if (!checkMetadataConsistency(file.getName(), metadataService,
request.getEntity())) {
// ask the client to reiterate properly its request
response.setStatus(new Status(Status.REDIRECTION_SEE_OTHER,
"The metadata are not consistent with the URI"));
return;
} else {
// We look for the possible variants
// 1- set up base name as the longest part of the name
// without known extensions (beginning from the left)
final String baseName = Entity.getBaseName(file.getName(),
metadataService);
final Collection<String> extensions = Entity.getExtensions(file
.getName(), metadataService);
// 2- loooking for resources with the same base name
final File[] files = file.getParentFile().listFiles();
File uniqueVariant = null;
final List<File> variantsList = new ArrayList<File>();
if (files != null) {
for (final File entry : files) {
if (baseName.equals(Entity.getBaseName(entry.getName(),
metadataService))) {
final Collection<String> entryExtensions = Entity
.getExtensions(entry.getName(),
metadataService);
if (entryExtensions.containsAll(extensions)) {
variantsList.add(entry);
if (extensions.containsAll(entryExtensions)) {
// The right representation has been found.
uniqueVariant = entry;
}
}
}
}
}
if (uniqueVariant != null) {
file = uniqueVariant;
} else {
if (!variantsList.isEmpty()) {
// Negociated resource (several variants, but not the
// right one).
// Check if the request could be completed or not.
// The request could be more precise
response
.setStatus(new Status(
Status.CLIENT_ERROR_NOT_ACCEPTABLE,
"Unable to process properly the request. Several variants exist but none of them suits precisely."));
return;
} else {
// This resource does not exist, yet.
// Complete it with the default metadata
updateMetadata(metadataService, file.getName(), request
.getEntity());
if (request.getEntity().getLanguages().isEmpty()) {
if (metadataService.getDefaultLanguage() != null) {
request.getEntity().getLanguages().add(
metadataService.getDefaultLanguage());
}
}
if (request.getEntity().getMediaType() == null) {
request.getEntity().setMediaType(
metadataService.getDefaultMediaType());
}
if (request.getEntity().getEncodings().isEmpty()) {
if ((metadataService.getDefaultEncoding() != null)
&& !metadataService.getDefaultEncoding()
.equals(Encoding.IDENTITY)) {
request.getEntity().getEncodings().add(
metadataService.getDefaultEncoding());
}
}
// Update the URI
final StringBuilder fileName = new StringBuilder(
baseName);
if (metadataService.getExtension(request.getEntity()
.getMediaType()) != null) {
fileName.append("."
+ metadataService.getExtension(request
.getEntity().getMediaType()));
}
for (final Language language : request.getEntity()
.getLanguages()) {
if (metadataService.getExtension(language) != null) {
fileName.append("."
+ metadataService
.getExtension(language));
}
}
for (final Encoding encoding : request.getEntity()
.getEncodings()) {
if (metadataService.getExtension(encoding) != null) {
fileName.append("."
+ metadataService
.getExtension(encoding));
}
}
file = new File(file.getParentFile(), fileName
.toString());
}
}
// Before putting the file representation, we check that all
// the extensions are known
if (!checkExtensionsConsistency(file, metadataService)) {
response
.setStatus(new Status(
Status.SERVER_ERROR_INTERNAL,
"Unable to process properly the URI. At least one extension is not known by the server."));
return;
} else {
File tmp = null;
boolean error = false;
if (file.exists()) {
// The PUT call is handled in two phases:
// 1- write a temporary file
// 2- rename the target file
if (partialPut) {
RandomAccessFile raf = null;
// Replace the content of the file
// First, create a temporary file
try {
// The temporary file used for partial PUT.
tmp = new File(file.getCanonicalPath() + "."
+ getTemporaryExtension());
// Support only one range.
Range range = request.getRanges().get(0);
if (tmp.exists() && !isResumeUpload()) {
tmp.delete();
}
if (!tmp.exists()) {
// Copy the target file.
InputStream in = new FileInputStream(file);
OutputStream out = new FileOutputStream(tmp);
ByteUtils.write(in, out);
out.flush();
out.close();
}
raf = new RandomAccessFile(tmp, "rwd");
// Go to the desired offset.
if (range.getIndex() == Range.INDEX_LAST) {
if (raf.length() <= range.getSize()) {
raf.seek(range.getSize());
} else {
raf
.seek(raf.length()
- range.getSize());
}
} else {
raf.seek(range.getIndex());
}
// Write the entity to the temporary file.
if (request.isEntityAvailable()) {
ByteUtils.write(request.getEntity()
.getStream(), raf);
}
} catch (IOException ioe) {
getLogger().log(Level.WARNING,
"Unable to create the temporary file",
ioe);
response.setStatus(new Status(
Status.SERVER_ERROR_INTERNAL,
"Unable to create a temporary file"));
error = true;
} finally {
try {
if (raf != null) {
raf.close();
// Calling the garbage collector helps
// to workaround lock issues on Windows
System.gc();
}
} catch (IOException ioe) {
getLogger()
.log(
Level.WARNING,
"Unable to close the temporary file",
ioe);
response.setStatus(
Status.SERVER_ERROR_INTERNAL, ioe);
error = true;
}
}
} else {
FileOutputStream fos = null;
try {
tmp = File.createTempFile("restlet-upload",
"bin");
if (request.isEntityAvailable()) {
fos = new FileOutputStream(tmp);
ByteUtils.write(request.getEntity()
.getStream(), fos);
}
} catch (IOException ioe) {
getLogger().log(Level.WARNING,
"Unable to create the temporary file",
ioe);
response.setStatus(new Status(
Status.SERVER_ERROR_INTERNAL,
"Unable to create a temporary file"));
error = true;
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException ioe) {
getLogger()
.log(
Level.WARNING,
"Unable to close the temporary file",
ioe);
response.setStatus(
Status.SERVER_ERROR_INTERNAL, ioe);
error = true;
}
}
}
if (error) {
if (tmp.exists() && !isResumeUpload()) {
tmp.delete();
}
return;
}
// Then delete the existing file
if (tmp.exists() && file.delete()) {
// Finally move the temporary file to the
// existing file location
boolean renameSuccessfull = false;
if (tmp.renameTo(file)) {
if (request.getEntity() == null) {
response
.setStatus(Status.SUCCESS_NO_CONTENT);
} else {
response.setStatus(Status.SUCCESS_OK);
}
renameSuccessfull = true;
} else {
// Many aspects of the behavior of the method
// "renameTo" are inherently platform-dependent:
// the rename operation might not be able to
// move a file from one filesystem to another.
if (tmp.exists()) {
try {
InputStream in = new FileInputStream(
tmp);
OutputStream out = new FileOutputStream(
file);
ByteUtils.write(in, out);
out.flush();
out.close();
renameSuccessfull = true;
tmp.delete();
} catch (Exception e) {
renameSuccessfull = false;
}
}
if (!renameSuccessfull) {
getLogger()
.log(Level.WARNING,
"Unable to move the temporary file to replace the existing file");
response
.setStatus(new Status(
Status.SERVER_ERROR_INTERNAL,
"Unable to move the temporary file to replace the existing file"));
}
}
} else {
getLogger().log(Level.WARNING,
"Unable to delete the existing file");
response.setStatus(new Status(
Status.SERVER_ERROR_INTERNAL,
"Unable to delete the existing file"));
if (tmp.exists() && !isResumeUpload()) {
tmp.delete();
}
}
} else {
// The file does not exist yet.
final File parent = file.getParentFile();
if ((parent != null) && !parent.exists()) {
// Create the parent directories then the new file
if (!parent.mkdirs()) {
getLogger()
.log(Level.WARNING,
"Unable to create the parent directory");
response
.setStatus(new Status(
Status.SERVER_ERROR_INTERNAL,
"Unable to create the parent directory"));
}
}
// Create the new file
if (partialPut) {
// This is a partial PUT
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(file, "rwd");
// Support only one range.
Range range = request.getRanges().get(0);
// Go to the desired offset.
if (range.getIndex() == Range.INDEX_LAST) {
if (raf.length() <= range.getSize()) {
raf.seek(range.getSize());
} else {
raf
.seek(raf.length()
- range.getSize());
}
} else {
raf.seek(range.getIndex());
}
// Write the entity to the file.
if (request.isEntityAvailable()) {
ByteUtils.write(request.getEntity()
.getStream(), raf);
}
} catch (FileNotFoundException fnfe) {
getLogger().log(Level.WARNING,
"Unable to create the new file", fnfe);
response.setStatus(
Status.SERVER_ERROR_INTERNAL, fnfe);
} catch (IOException ioe) {
getLogger().log(Level.WARNING,
"Unable to create the new file", ioe);
response.setStatus(
Status.SERVER_ERROR_INTERNAL, ioe);
} finally {
try {
if (raf != null) {
raf.close();
// Calling the garbage collector helps
// to workaround lock issues on Windows
System.gc();
}
} catch (IOException ioe) {
getLogger()
.log(
Level.WARNING,
"Unable to close the new file",
ioe);
response.setStatus(
Status.SERVER_ERROR_INTERNAL, ioe);
}
}
} else {
// This is simple PUT of the full entity
FileOutputStream fos = null;
try {
if (file.createNewFile()) {
if (request.getEntity() == null) {
response
.setStatus(Status.SUCCESS_NO_CONTENT);
} else {
fos = new FileOutputStream(file);
ByteUtils.write(request.getEntity()
.getStream(), fos);
response
.setStatus(Status.SUCCESS_CREATED);
}
} else {
getLogger().log(Level.WARNING,
"Unable to create the new file");
response.setStatus(new Status(
Status.SERVER_ERROR_INTERNAL,
"Unable to create the new file"));
}
} catch (FileNotFoundException fnfe) {
getLogger().log(Level.WARNING,