Context getRealPath() for non-existent file - java

Can someone explain what is the difference between the following two calls to ServletContext getRealPath() in Tomcat:
context.getRealPath("/") + "\\songModified.wav";
context.getRealPath("/" + "\\songModified.wav");
I have a very simple GET method on the server which reads a file on the server and copies the bytes into a new file in the location returned by the above call.
On the client side I have an audio tag that references an audio file on the server, calls this method that creates a new file and changes the reference of the audio tag to this new file. The thing is that in the javascript callback this new file is not immediately referenceable if I store the file to the path that is returned from the second case of the above getRealPath call. Basically it returns a 404. If I store it to the returned path of the first case of the call then it is immediately referenceable and the audio tag normaly references the new file.
Both of those calls to getRealPath() return exactly the same string:
C:\Users\Mihael\apache-tomcat-9.0.31\wtpwebapps\AudioSimulator\songModified.wav
I am passing this returned string to the FileOutputStream constructor further in the code.
Thing to note here is that this file does not exist at the moment of the getRealPath() call so I am confused why is it returning anything at all in the second case of the call.
I know this is not the recommended way of storing files so I am asking from a purely educational perspective. How can the second call to this method break my functionality if they both return exactly the same string to the rest of the code?
EDIT:
Here is a very simple Javascript and Java code for anyone who wants to test this.
Javascript:
<body>
<script>
function modifyRequest() {
var xhttp = new XMLHttpRequest();
xhttp.onload = function() {
var audio = document.getElementById("player");
var currentTime = audio.currentTime;
audio.src = "http://localhost:8080/AudioSimulator/bluesModified.wav";
audio.currentTime = currentTime;
audio.play();
};
xhttp.open("GET", "http://localhost:8080/AudioSimulator/rest/Test/testPath");
xhttp.send();
}
</script>
<audio id="player" src="http://localhost:8080/AudioSimulator/blues.wav"
controls>
Your browser does not support the
<code>audio</code> element.
</audio>
<button onclick="modifyRequest()">Test</button>
</body>
Java:
#Path("/Test")
public class Test {
#Context
ServletContext context;
#GET
#Path("/testPath")
public Response testPath() {
File fileIn = new File(context.getRealPath("/") + "\\blues.wav");
File fileOut = new File(context.getRealPath("/" + "\\bluesModified.wav"));
//if i write it like this it would work
//File fileOut = new File(context.getRealPath("/") + "\\bluesModified.wav");
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(fileIn);
fos = new FileOutputStream(fileOut);
byte[] inArray = new byte[(int) fileIn.length()];
try {
fis.read(inArray);
fos.write(inArray);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return Response
.ok()
.entity("Success")
.header("Access-Control-Allow-Origin", "null")
.build();
}
}

I have taken the time to dive into Tomcat source to find the cause for this. It turns out that getRealPath, in addition to retrieving the system path for a given virtual path, also works a bit with the Tomcat cache.
NOTE:
I know that my file separator usage is not good, but Tomcat is smart enough to validate the above call to produce /bluesModified.wav. So even if I call it like #rickz mentioned in the comments, the result would be the same and therefore that was not the issue.
The issues I had with being unable to reference the file in the case of the following call
context.getRealPath("/" + "\\bluesModified.wav")
was the fact that in this case we are passing the file path to the method, while in the case that works we are passing in the directory path.
What happens is that the call to getRealPath() first checks the cache for the existence of the resource identified by the webapppath /bluesModified.wav. Since it does not exist at the moment of the call, Tomcat will create an instance of the EmptyResource class which is basically a wrapper around File class and represents a file that does not exist, and it will then store the reference to this file in its cache.
The issue here is that even though I create a file that will have the correct virtual path Tomcat will still have that empty resource representing a non existent file in its cache. In other words, if I reference the file from the client side like so
http://localhost:8080/AudioSimulator/bluesModified.wav
Tomcat will return the cached resource that represents the empty file, which actually means a 404 to the client even though the file exists.
Waiting for 5 seconds, which is the time to live of Tomcat cache entries, and then trying to reference the file will revalidate the cache entry and produce a FileResource instead of EmptyResource in which case the referencing will work normally.
It works in this case
context.getRealPath("/") + "\\bluesModified.wav"
since the path that is getting cached is a directory and the file name is simply concatenated. So the string I have here is just an absolute path to the file I am going to create with no cache entries colliding with it.
My mistake was assuming that getRealPath() is just some "pure" method that will return a string I can use to create files while in fact it has a bit of side effects. These side effects are not documented and even though I might have done some things incorrectly the bottom line is this method is not that predictable to use when doing File IO stuff.

The String returned by getRealPath from the ServletContext implementation is normalized.
So when you call getRealPath("/") + "\blues.wav") only the String "/" is normalized, and the String concatenation "\blues.wav" is not.
But when you call getRealPath("/" + "\blues.wav")) the full concatened String is normilized.
public String getRealPath(String path) {
if ("".equals(path)) {
path = "/";
}
if (this.resources != null) {
try {
WebResource resource = this.resources.getResource(path);
String canonicalPath = resource.getCanonicalPath();
if (canonicalPath == null) {
return null;
}
if ((resource.isDirectory() && !canonicalPath.endsWith(File.separator) || !resource.exists()) && path.endsWith("/")) {
return canonicalPath + File.separatorChar;
}
return canonicalPath;
} catch (IllegalArgumentException var4) {
}
}
return null;
}
You can see WebResource resource = this.resources.getResource(path) will try to validate your path and will return a validated path :
private String validate(String path) {
if (!this.getState().isAvailable()) {
throw new IllegalStateException(sm.getString("standardRoot.checkStateNotStarted"));
} else if (path != null && path.length() != 0 && path.startsWith("/")) {
String result;
if (File.separatorChar == '\\') {
result = RequestUtil.normalize(path, true);
} else {
result = RequestUtil.normalize(path, false);
}
if (result != null && result.length() != 0 && result.startsWith("/")) {
return result;
} else {
throw new IllegalArgumentException(sm.getString("standardRoot.invalidPathNormal", new Object[]{path, result}));
}
} else {
throw new IllegalArgumentException(sm.getString("standardRoot.invalidPath", new Object[]{path}));
}
}

Related

The .delete() method in java is not working

Files inside the (Tracks)directory was not deleted. The method deletes the wav files stored in the directory.
public boolean deleteTrack(response) {
ListIterator<Track> trackListIterator = this.trackList.listIterator();
//tracklist is the linked list on which I'm using list iterator. I'm storing song which is a object inside it. this object has a fn() that returns content root path not absolute path.
String path = "";
while (trackListIterator.hasNext()) {
//RESPONSE == PARAMETER
if (trackListIterator.next().getTrackName().equals(response)) {
trackListIterator.previous();
path = trackListIterator.next().getTrackPath();//this is the fn() that
returns content root path example(src/Exploit/org/Trackstore/Music/Action Movie Music-FesliyanStudios.wav).
break;
}
}
File file = new File(path);
//here I'm taking absolute path for deleting actual wav file from the computer.
File toBeDeleted = new File(file.getAbsolutePath());
return toBeDeleted.delete();// returns false everytime.
}
The old API has many issues. For example, most methods return a boolean to indicate the result which is stupid and unjavalike - fortunately, there is a new API that fixes these issues.
Use it and you'll know WHY it failed. If that's too much effort, well, there isn't much to say. It didn't delete. No idea why, and there's no way to ask that API about why.
The new API lives in the java.nio.file package.
Replace this:
File f = new File("path/to/file");
if (!f.delete()) { ... it didn't work ... }
with:
Path p = Paths.get("path/to/file");
try {
Files.delete(p);
} catch (IOException e) {
// the exception will explain everything there is to be said about why it did not work!
}

Downloading Thymeleaf template from S3

I am currently storing and downloading my Thymeleaf templates in S3.
I am using the following function to retrieve the Template from S3:
public String getTemplateFile(String name, File localFile) {
ObjectMetadata object = s3Client.getObject(new GetObjectRequest(connectionProperties.getBucket(), name), localFile);
boolean success = localFile.exists() && localFile.canRead();
return localFile.getPath();
}
After doing this the file is successfully downloaded in the desired location.
But when trying to access the file from the FlyingSaucer PDF generator the file doesn't exist, despite it is already downloaded in FILE_LOCATION_PATH. (I can open the file... the file is there but the function doesn't see it)
String xHtmlStringDocument =
convertHtmlToXhtml(templateEngine
.process(FILE_LOCATION_PATH,
initializeLetterHtmlTemplateContext(letter)));
When I run the program again and again I get the same result. But when I STOP the program and RUN it AGAIN then everything works because the file form the last execution is now recognized by the program.
This sounds to me like an asynchronous function issue.
Does anybody know how can I fix this?
Thanks in advance.
EDITED (following suggestion)
New function: Same result:
(And the file was created, the Download from S3 was successful)
java.io.FileNotFoundException: ClassLoader resource "static/templates/template.html" could not be resolved
public String getTemplateFileN(String name, File localFile) throws IOException {
S3Object fullObject = null;
InputStream in = null;
try {
fullObject = s3Client.getObject(new GetObjectRequest(connectionProperties.getBucket(), name));
System.out.println("Content-Type: " + fullObject.getObjectMetadata().getContentType());
System.out.println("Content: ");
displayTextInputStream(fullObject.getObjectContent());
in = fullObject.getObjectContent();
System.out.println(localFile.toPath());
Files.copy(in, localFile.toPath());
} //then later
finally {
// To ensure that the network connection doesn't remain open, close any open input streams.
if (fullObject != null) {
fullObject.close();
}
if (in != null) {
in.close();
}
}
return localFile.getPath();
}
Checking javadoc
https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/AmazonS3Client.html#getObject-com.amazonaws.services.s3.model.GetObjectRequest-java.io.File-
I see not method signature ObjectMetadata getObject(GetObjectRequest getObjectRequest,String file)
There is
ObjectMetadata getObject(GetObjectRequest getObjectRequest,
File destinationFile)
Where you provide File (not String) as second argument. Make sure the file is not opened for write before you try reading it!

How to upload a file using af:inputFile in Oracle ADF

Can anyone please tell me how to upload file to server using af:inputFile in Oracel ADF. I searched about this and found we can use the following
<af:form usesUpload="true">
<af:inputFile columns="10"
valueChangeListener="#{backing.fileUploaded}"/>
</af:form>
using the above code I can set a method that executes when some choose some file in the form. So now I need to know in fileUploaded method what should be the java code to upload the given file to the server.
Please help me. How I can achieve this.
Thanks in advance.
As you have already created value change listener in managed bean then use this code -
/**Method to Upload File ,called on ValueChangeEvent of inputFile
* #param vce
*/
public void uploadFileVCE(ValueChangeEvent vce) {
if (vce.getNewValue() != null) {
//Get File Object from VC Event
UploadedFile fileVal = (UploadedFile) vce.getNewValue();
}
}
and this is the method to upload file on server (You have to provide absolute path)
/**Method to upload file to actual path on Server*/
private String uploadFile(UploadedFile file) {
UploadedFile myfile = file;
String path = null;
if (myfile == null) {
} else {
// All uploaded files will be stored in below path
path = "D://FileStore//" + myfile.getFilename();
InputStream inputStream = null;
try {
FileOutputStream out = new FileOutputStream(path);
inputStream = myfile.getInputStream();
byte[] buffer = new byte[8192];
int bytesRead = 0;
while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
out.write(buffer, 0, bytesRead);
}
out.flush();
out.close();
} catch (Exception ex) {
// handle exception
ex.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
//Returns the path where file is stored
return path;
}
Check this thread on OTN Forum
https://community.oracle.com/message/13135474#13135474
Here you can read full implementation and download sample application to test
http://www.awasthiashish.com/2014/08/uploading-and-downloading-files-from.html
Full disclosure on the last link above and its contents: I wrote it and it is my TLD.
Your inputFile should look something like the following where gciuiCheckin is a reference to the backing bean. In this case the inputFile control is in a jsff contained as a region inside a jspx with usesUpload="true", but this would be similar if you are putting your controls directly in the jspx, the main thing is you need to bind the control value to a backing bean variable of type UploadedFile:
<af:inputFile label=" " id="ifDoc" columns="50"
value="#{pageFlowScope.gciuiCheckin.filesToUpload}"
maximumFiles="#{pageFlowScope.gciuiCheckin.maxFilesCanBeUploaded}"
autoHeightRows="0" rows="5" uploadType="auto"/>
Then you also should have a commandButton to call a bean method once user has selected the file (each file typically is uploaded to server as the user selects or drag drops each one):
<af:commandButton text="Commit File(s)" id="cbUpload"
partialSubmit="true" action="#{pageFlowScope.gciuiCheckin.saveUploadedFilesAction}"/>
You will need this import in the backing bean:
import org.apache.myfaces.trinidad.model.UploadedFile;
In the backing bean, create a List with accessors to hold the uploaded files:
private List<UploadedFile> filesToUpload;
In the method called by the commandButton, you will do something like:
public String saveUploadedFilesAction() {
List<UploadedFile> files = this.getFilesToUpload();
if (files == null || files.size() == 0) {
displayMessageToUser(FacesMessage.SEVERITY_WARN, checkinErrorMessage);
return null;
}
//iterate each file and check size, extension, etc...
for (int i = 0; i < files.size(); i++) {
UploadedFile currFile = files.get(i);
//now do something with the file...
}
...
Hope this helps.

java.io.file backslash saving in weblogic's root

I have a java application running into a weblogic server.
The application have to write a file into the path \bla\john doe (for example).
For this, I used the java.io.File library to:
1. Verify if the path exists
2. If not, create it.
3. Verify if the file exists
4. if not, create it
5. Write the bytes into the file.
The correct behavior would be to create the directory bla into the root of the weblogic's current domain and then create a john doe inside it.
The problem is: in my current enviroment it works like a charm, but in the client's one, the application does not consider the backslash as an element of the path, and instead of creating two directories, the application only creates one, literally named as \bla\john does.
So, instead of:
-domain_root
-bla
-john does
I get the following:
-domain_root
-\bla\john does
(and if I escape it, occurres the same but with two backslash)
The odd is that if I use the commom slash (/bla/john doe), it works..
-domain_root
-bla
-john does
Does any one knows what possibly can be happening?
script for check the path
public File checkPath(String path) {
File f = new File(cls_Util.NeutralizeFilePath(path));
if (!(f.exists() && f.isDirectory())) {
try {
f.mkdirs();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
return f;
}
script for check the file:
public File checkFile(String path){
File f = new File(path);
return checkFile(f);
}
public File checkFile(File f) {
if (!(f.exists() && f.isFile())) {
try {
f.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
return f;
}
script for create file
public File writeFile(String path, byte[] binaryfile) {
File file = checkFile(path);
if (file != null) {
FileOutputStream fos;
try {
fos = new FileOutputStream(path);
try {
fos.write(binaryfile);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return file;
}
return null;
}
And to create the file:
String filePathPub = pathPub + newName;
File FilePathPub = writeFile(filePathPub, p_Arquivo);
On Windows the \ starts an absolute path; on Unix/Linux the backslash is a valid filename character (and therefore starts a relative path).
I would suggest you try to avoid using file name concatenation platform specific separators if you are not familiar with the semantic:
File current = new File();
File bla = new File(current, "bla");
(or simply stick to / (forward slash as used by Unix) to separate path components). Java translates this to the Windows character automatically).

Retrieve the web app root path in JSF Managed Bean

Im trying to access the example/web folder (see below in the image) in a jsf managed bean but cant seem to find a way to do it
thx
Try
FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath()
for build relative url's to resources in your app.
If you want the real path...
ServletContext ctx = (ServletContext) FacesContext.getCurrentInstance()
.getExternalContext().getContext();
String realPath = ctx.getRealPath("/");
If you want to get it as a File for some reason, then you need ExternalContext#getRealPath(). This converts a relative web path to an absolute disk file system. Since you need the web's root folder, just pass in /:
String absoluteWebPath = externalContext.getRealPath("/");
File webRoot = new File(absoluteWebPath);
// ...
Unrelated to the concrete problem, whatever functional requirement you've had in mind for which you thought that having an absolute local disk file system path to the web folder is the right solution, it has most definitely to be solved differently. And indeed, as per your comment on the other answer,
because Im trying to upload some file inside the folder and using the relative path
you're going the wrong path. You should not store uploaded files in there if you intend to keep them longer than the webapp's deployment lifetime. Whenever you redeploy the webapp (and on some server configs even when you restart the server), the uploaded files would get completely lost, simply because they are not contained as part of the original WAR file. Even more, some heavy server configs don't expand the WAR on disk at all, but in memory instead, the getRealPath() would then always return null.
Rather store it in a fixed disk file system path outside the server's deploy folder. Add that path in turn as a new server context or docroot, so that it's accessible on a different (virtual) context path. Or homegrow a servlet which gets an InputStream of it from disk and writes it to OutputStream of the response. See also this related answer: Uploaded image only available after refreshing the page
Try:
String relativePath="/resources/temp/";
String absolutePath= FacesContext.getCurrentInstance.getExternalContext().getRealPath(relativePath);
File file = new File(absolutePath);
to get real path.
Create a tmp file in resources/temp/ to avoid any exception.
Just wanted to thank Balus C. Code Java with JSP, in Tomcat/Tomee server I the following code that works:
private Boolean SaveUserItemImage(Part ui, String bid) throws IOException {
Boolean fileCreate = false;
OutputStream out = null;
InputStream filecontent = null;
ExternalContext ctx = context().getExternalContext();
String absoluteWebPath = ctx.getRealPath("/");
String resource_path = absoluteWebPath + "\\resources\\";
String image_path = resource_path + "\\" + this.itemType + "_images\\";
String buildFileName = image_path + bid + "_" + getFileName(ui);
File files = null;
try {
files = new File(buildFileName);
fileCreate = true;
} catch (Exception ex) {
System.out.println("Error in Creating New File");
Logger.getLogger(ItemBean.class.getName()).log(Level.SEVERE, null, ex);
}
if (fileCreate == true) {
if (files.exists()) {
/// User may be using same image file name but has been editted
files.delete();
}
try {
out = new FileOutputStream(files);
filecontent = ui.getInputStream();
int read = 0;
final byte[] bytes = new byte[1024];
while ((read = filecontent.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
fileCreate = true;
} catch (FileNotFoundException fne) {
fileCreate = false;
Logger.getLogger(ItemBean.class.getName()).log(Level.SEVERE, "SaveUserItemImage", fne);
} finally {
if (out != null) {
out.close();
}
if (filecontent != null) {
filecontent.close();
}
files = null;
}
}
return fileCreate;
}

Categories