Apache commons NET FTP retrieve not working after file reading - java

I'm trying to make a launcher for my Java desktop application (separated applications), which has to look for an updated version of the main application on the server. My idea is to store the app version inside of a text file on each side.
I found (thanks to Google san) the way to read the version from the text file and download the jar directory with all of it's content (both on server side). I'm using the Apache Commons Net FTP library btw.
The problem comes when I try to download the jar directory from the server after beeing reading the text file. I get the text file content correctly, the files download fails though.
If I switch code lines to download the stuff first and then read the text file, both of them works well, but we all know that's not the way an update check should be.
I've been looking and I don't get what I'm doing wrong. It's my first time working with this library.
This is the code I'm working with:
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
class FTPUtil{
private String server = "www.server.host";
private int port = 21;
private String user = "user";
private String pass = "password";
private FTPClient ftpClient = new FTPClient();
public void Connect() throws IOException{
ftpClient.connect(server, port);
ftpClient.login(user, pass);
ftpClient.enterLocalPassiveMode();
System.out.println("Connected");
}
public void Disconnect() throws IOException{
ftpClient.logout();
ftpClient.disconnect();
System.out.println("Disconnected");
}
public double getServerVersion(String remoteDirPath) throws IOException{
InputStream inputStream = ftpClient.retrieveFileStream(remoteDirPath + "/version.txt");
return Double.parseDouble(IOUtils.toString(inputStream, "UTF-8"));
}
public boolean downloadSingleFile(String remoteFilePath, String savePath) throws IOException{
File downloadFile = new File(savePath);
File parentDir = downloadFile.getParentFile();
if(!parentDir.exists())
parentDir.mkdir();
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(downloadFile));
try{
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
return ftpClient.retrieveFile(remoteFilePath, outputStream);
}catch(IOException e){
throw e;
}finally{
if(outputStream != null)
outputStream.close();
}
}
public void downloadDirectory(String parentDir, String currentDir, String saveDir) throws IOException{
String dirToList = parentDir;
if(!currentDir.equals(""))
dirToList += "/" + currentDir;
FTPFile[] subFiles = ftpClient.listFiles(dirToList);
if(subFiles != null && subFiles.length > 0){
for(FTPFile aFile : subFiles){
String currentFileName = aFile.getName();
// skip parent directory and the directory itself
if(currentFileName.equals(".") || currentFileName.equals(".."))
continue;
String filePath = parentDir + "/" + currentDir + "/" + currentFileName;
if(currentDir.equals(""))
filePath = parentDir + "/" + currentFileName;
String newDirPath = saveDir + parentDir + File.separator + currentDir + File.separator + currentFileName;
if(currentDir.equals(""))
newDirPath = saveDir + parentDir + File.separator + currentFileName;
if(aFile.isDirectory()){
// create the directory in saveDir
File newDir = new File(newDirPath);
boolean created = newDir.mkdirs();
if(created)
System.out.println("CREATED the directory: " + newDirPath);
else
System.out.println("COULD NOT create the directory: " + newDirPath);
// download the sub directory
downloadDirectory(dirToList, currentFileName, saveDir);
}else{
// download the file
boolean success = downloadSingleFile(filePath, newDirPath);
if(success)
System.out.println("DOWNLOADED the file: " + filePath);
else
System.out.println("COULD NOT download the file: " + filePath);
}
}
}
}
}
import java.io.IOException;
public class Main{
public static void main(String[] args){
String project = "ServerFolderName";
String remoteDirPath = "/" + project;
String saveDirPath = "C:/Users/username/Desktop";
FTPUtil ob = new FTPUtil();
try{
ob.Connect();
System.out.println(ob.getServerVersion(remoteDirPath));
ob.downloadDirectory(remoteDirPath, "", saveDirPath);
ob.Disconnect();
}catch(IOException e){
e.printStackTrace();
}
}
}
And the console output:
run:
Connected
1.0
Exception in thread "main" org.apache.commons.net.ftp.parser.ParserInitializationException: Unknown parser type: 0.000 seconds (measured here), 37.12 Kbytes per second
at org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory.createFileEntryParser(DefaultFTPFileEntryParserFactory.java:170)
at org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory.createFileEntryParser(DefaultFTPFileEntryParserFactory.java:94)
at org.apache.commons.net.ftp.FTPClient.__createParser(FTPClient.java:3381)
at org.apache.commons.net.ftp.FTPClient.initiateListParsing(FTPClient.java:3338)
at org.apache.commons.net.ftp.FTPClient.listFiles(FTPClient.java:3016)
at generateupdateinfo.FTPUtil.downloadDirectory(FTPUtil.java:58)
at generateupdateinfo.Main.main(Main.java:13)
C:\Users\username\AppData\Local\NetBeans\Cache\8.2\executor-snippets\run.xml:53: Java returned: 1
BUILD FAILED (total time: 0 seconds)
Maybe I'm going the wrong way doing this launcher/updater, I'm open to suggestions and new ideas to achieve it.
Edit:
I noticed that it works if I renew the server connection with Disconnect() and Connect() after reading the text file and before downloading the files. I still feel should be another way, doesn't look quite good.
ob.Connect();
System.out.println(ob.getServerVersion(remoteDirPath));
ob.Disconnect();
ob.Connect();
ob.downloadDirectory(remoteDirPath, "", saveDirPath);
ob.Disconnect();

I found the problem. Apparently I just have to flush the server replies with ftpClient.completePendingCommand() after reading the version from the text file. I still do not get why happens just with one of both processes though, I would like to understand.
Thanks!:)

Related

File upload/download using nfs-client package not working after n iteration

I am using nfs-client package to realize the NFS protocol to connect to the linux server for file upload/download. Basically the purpose is NFS protocol testing .
This is my sample codebase
import com.emc.ecs.nfsclient.nfs.io.Nfs3File;
import com.emc.ecs.nfsclient.nfs.io.NfsFileInputStream;
import com.emc.ecs.nfsclient.nfs.io.NfsFileOutputStream;
import com.emc.ecs.nfsclient.nfs.nfs3.Nfs3;
import com.emc.ecs.nfsclient.rpc.CredentialUnix;
int thread_count = ctx.getThreadNum();
String lrandom_name = service_name + "_" + replica_id + "_" + thread_count;
public static void uploadFileToNfs() {
String localDir = "F:\\look\\"+lrandom_name;
InputStream inputStream = null;
OutputStream outputStream = null;
try {
//Create a local file object
File localFile = new File(localDir);
//Get the file name of the local file, this name is used to create a file with the same name in the specified directory on the remote Nfs server
String localFileName = localFile.getName();
Nfs3 nfs3 = new Nfs3(NFS_IP, NFS_DIR, new CredentialUnix(0, 0, null), 3);
//Create Nfs file object on remote server
Nfs3File NfsFile = new Nfs3File(nfs3, "/" + localFileName);
//Open a file input stream
inputStream = new BufferedInputStream(new FileInputStream(localFile));
//Open a remote Nfs file output stream and copy the file to the destination
outputStream = new BufferedOutputStream(new NfsFileOutputStream(NfsFile));
//Buffer memory
byte[] buffer = new byte[1024];
while ((inputStream.read(buffer)) != -1) {
outputStream.write(buffer);
}
System.out.println("File upload complete!");
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
I am running the script from Jmeter , but after n number of iteration (threaded manner) I am getting below error. Similar script for File download from NFS is working fine in threaded manner parallelly .
com.emc.ecs.nfsclient.nfs.NfsException: rpc error, server: 192.168.0.101, RPC error: RPC call is ACCEPTED, but the status is not success, acceptStat=4
at com.emc.ecs.nfsclient.rpc.RpcWrapper.handleRpcException(RpcWrapper.java:311)
at com.emc.ecs.nfsclient.rpc.RpcWrapper.callRpcWrapped(RpcWrapper.java:159)
at com.emc.ecs.nfsclient.nfs.nfs3.Nfs3.wrapped_sendWrite(Nfs3.java:756)
at com.emc.ecs.nfsclient.nfs.nfs3.Nfs3.wrapped_sendWrite(Nfs3.java:90)
at com.emc.ecs.nfsclient.nfs.io.NfsFileBase.write(NfsFileBase.java:866)
at com.emc.ecs.nfsclient.nfs.io.NfsFileOutputStream.writeBufferToFile(NfsFileOutputStream.java:296)
at com.emc.ecs.nfsclient.nfs.io.NfsFileOutputStream.write(NfsFileOutputStream.java:262)
at java.base/java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:81)
at java.base/java.io.BufferedOutputStream.write(BufferedOutputStream.java:127)
at java.base/java.io.FilterOutputStream.write(FilterOutputStream.java:108)
at java_io_FilterOutputStream$write.call(Unknown Source)
at com.emc.ecs.nfsclient.nfs.io.Script1.run(Script1.groovy:77)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317)
at org.codehaus.groovy.jsr223.GroovyCompiledScript.eval(GroovyCompiledScript.java:71)
at java.scripting/javax.script.CompiledScript.eval(CompiledScript.java:89)
at org.apache.jmeter.util.JSR223TestElement.processFileOrScript(JSR223TestElement.java:217)
at org.apache.jmeter.protocol.java.sampler.JSR223Sampler.sample(JSR223Sampler.java:72)
at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:635)
at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:558)
at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:489)
at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:256)
at java.base/java.lang.Thread.run(Thread.java:834)
Update
Based on feedback instead of one static file for all Threads(Users) , now I have unique file for each Threads(Users) to upload/download
Update
Tried below one more use case randomly picking file from one NFS share folder to other ,same issue coming. Suspecting something wrong with NFS configuration only
package com.emc.ecs.nfsclient.nfs.io;
import org.junit.Test;
import com.emc.ecs.nfsclient.nfs.NfsSetAttributes;
import com.emc.ecs.nfsclient.nfs.io.Nfs3File;
import com.emc.ecs.nfsclient.nfs.io.NfsFileInputStream;
import com.emc.ecs.nfsclient.nfs.io.NfsFileOutputStream;
import com.emc.ecs.nfsclient.nfs.nfs3.Nfs3;
import com.emc.ecs.nfsclient.rpc.CredentialUnix;
import java.util.Random;
import java.io.IOException;
import org.junit.Test;
String nfs_folder = vars.get("nfs_folder");
String files_folder = vars.get("files_folder");
String NFS_IP = vars.get("server_ip");
String NFS_DIR = "/"+nfs_folder+"/"+files_folder;
String NFS_DIR_ROOT = "/"+nfs_folder;
int thread_count = ctx.getThreadNum();
String service_name = vars.get("Service_Name");
String replica_id = vars.get("Replica_ID");
String lrandom_name = service_name + "_" + replica_id + "_" + thread_count;
int transfer_size = Integer.parseInt(vars.get("transfer_size"))
InputStream inputStream = null;
OutputStream outputStream = null;
try {
Nfs3 nfs3 = new Nfs3(NFS_IP, NFS_DIR, new CredentialUnix(0, 0, null), 3);
Nfs3 nfs4 = new Nfs3(NFS_IP, NFS_DIR_ROOT, new CredentialUnix(0, 0, null), 3);
nfsFiles = new Nfs3File(nfs3,"/");
List<String> children = nfsFiles.list();
Random randomGenerator = new Random();
int fileIndex = randomGenerator.nextInt(children.size());
OUT.println(fileIndex)
String NfsFileDir = "/"+children.get(fileIndex);
OUT.println(children.size())
OUT.println(children.get(fileIndex))
//Create Nfs file object on remote server
Nfs3File nfsFile = new Nfs3File(nfs3, NfsFileDir);
Nfs3File NfsFile = new Nfs3File(nfs4, "/" + lrandom_name);
//String localFileName = localDir + lrandom_name;
//Create a local file object
//File localFile = new File(localFileName);
//Open a file input stream
inputStream = new BufferedInputStream(new NfsFileInputStream(nfsFile));
//Open a remote Nfs file output stream and copy the file to the destination
outputStream = new BufferedOutputStream(new NfsFileOutputStream(NfsFile));
//Buffer memory
byte[] buffer = new byte[transfer_size];
while (inputStream.read(buffer) != -1) {
outputStream.write(buffer);
}
System.out.println("File copy complete!");
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
I don't think your use case is valid as uploading the same file into the same destination is not something what real NFS users would be doing, so maybe it worth considering using i.e. Directory Listing Config plugin so each virtual user will have its own file(s) to upload/download
Looking at NFS RPC RFC the status you're getting is:
GARBAGE_ARGS = 4, /* procedure can't decode params */
so double check that your JMeter instance has enough headroom to operate in terms of JVM setup, CPU, RAM, etc. as it looks like that your NFS server cannot properly parse the request so it might be the case JMeter sends some trash. Ensure to follow JMeter Best Practices
Basically the same as point 2 but for NFS server side, it might be the case it is overloaded hence cannot properly parse the incoming valid requests, check NFS logs and your operating system logs for any suspicious entries.

Given an existing jar file and source code, how do I alter the code?

I am working with some java code downloaded from SourceForge. I have the sourcecode, but users are directed to run the code via jar files:
java -jar jarfile.jar -i inputfile.txt -o $outputdirectory
Naturally, I can see what's inside the jarfile.jar with the following command,
jar tf jarfile.jar
Here is my question: There are a few things I would like to change in the sourcecode, and then run. How do I do this? Do users usually
(1) change the compiled java code in jarfile.jar? That sounds like a mess
(2) wrap the source code into the jar file again?
How would I take the source code (with changes I've made) and create a new .jar file?
Found this code here to extract the JAR contents:
import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.zip.ZipException;
public class jara {
public static void main (String args[])throws IOException,ZipException
{
JarFile jarFile = new JarFile("jarfile.jar");
Enumeration en = jarFile.entries();
while (en.hasMoreElements()) {
String ent=proc(en.nextElement());
if(ent.indexOf("/")>0)
{
String fil=ent.substring(0,ent.indexOf("/"));
System.out.println(fil);
File local=new File(fil);
if(!local.exists())
local.mkdirs();
}
if(ent.indexOf(".")>0)
{
int n=ent.length();
String fil1=ent.substring(ent.lastIndexOf("/")+1,n);
System.out.println(fil1);
extract(jarFile.getName(),ent);
}
}
}
public static String proc(Object obj)
{
JarEntry entry = (JarEntry)obj;
String name = entry.getName();
System.out.println("\nEntry Name: "+name);
return(name);
}
public static void extract(String jarName,String entryName)throws IOException,ZipException
{
JarFile jar = new JarFile(jarName);
System.out.println(jarName + " opened.");
try {
// Get the entry and its input stream.
JarEntry entry = jar.getJarEntry(entryName);
// If the entry is not null, extract it. Otherwise, print a
// message.
if (entry != null) {
// Get an input stream for the entry.
InputStream entryStream = jar.getInputStream(entry);
try {
// Create the output file (clobbering the file if it exists).
FileOutputStream file = new FileOutputStream(entry.getName());
try {
// Allocate a buffer for reading the entry data.
byte[] buffer = new byte[1024];
int bytesRead;
// Read the entry data and write it to the output file.
while ((bytesRead = entryStream.read(buffer)) != -1) {
file.write(buffer, 0, bytesRead);
}
System.out.println(entry.getName() + " extracted.");
}
finally {
file.close();
}
}
finally {
entryStream.close();
}
}
else {
System.out.println(entryName + " not found.");
} // end if
}
finally {
jar.close();
System.out.println(jarName + " closed.");
}
}
}
Then, just read the Java source files as if they were text files, modify them, then write them to new files.
Then to package the jar, you can use this tutorial, which uses the Java Compiler API.

Query on java I/O byte stream class

Below is the program which created separate byte streams for reading from and writing into same file.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopyNoBuffer{
public static void main(String[] args){
FileInputStream in = null;
FileOutputStream out = null;
Long startTime, elapsedTime;
//String inFileStr = "C:\\project\\books\\java-Preparation\\main_docs\\practice.jpg";
//String outFileStr = "C:\\project\\books\\java-Preparation\\main_docs\\practice_out.jpg";
String fileStr = "C:\\project\\books\\java-Preparation\\main_docs\\practice.jpg";
File file = new File(fileStr);
System.out.println("File size before - r/w is: " + file.length() + " bytes");
try{
in = new FileInputStream(fileStr);
out = new FileOutputStream(fileStr);
startTime = System.nanoTime();
int byteRead;
while((byteRead = in.read()) != -1){
out.write(byteRead);
}
elapsedTime = System.nanoTime() - startTime;
System.out.println("Elapsed Time is: " + (elapsedTime/1000000.0) + " msec");
System.out.println("File size after - r/w is: " + file.length() + " bytes");
}catch(IOException ex){
ex.printStackTrace();
}finally{
try{
if(in != null){
in.close();
}
if(out != null){
out.close();
}
}catch(IOException ex){
ex.printStackTrace();
}
}
}
}
Below is the observation:
File size before - r/w is: 1115512 bytes
Elapsed Time is: 0.040711 msec
File size after - r/w is: 0 bytes
I know that FileInputStream and FileOutputStream are non-buffer byte stream I/O classes.
I expect that the file size to remain same after writing to same file.
What could be under-hood reason that file size goes to zero?
Note : am learning java 1.6 I/O
[...]for reading from and writing into same file.
NEVER DO THAT. EVER.
The results are unpredictable.
If you want to modify the contents of a file, write the new contents into a new file and then atomically rename to the old -- after you have ensured that the new content was successfully written.
Also, this is 2014, so unless you REALLY have to use Java 6, use java.nio.file instead, especially if you have to rename, since File will leave you stranded more often than not.
Sample code with java.nio.file:
final Path toChange = Paths.get("pathtoyourfilehere");
final Path dir = toChange.getParent();
final Path tmpfile = Files.createTempFile(dir, "foo", "bar");
try (
final InputStream in = Files.newInputStream(toChange);
final InputStream out = Files.newOutputStream(tmpfile);
) {
// Work with in and out
}
// Then move!
try {
Files.move(tmpfile, toChange, StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.ATOMIC_MOVE);
} catch (AtomicMoveNotSupportedException ignored) {
Files.move(tmpfile, toChange, StandardCopyOption.REPLACE_EXISTING);
}
You are using default constructor for FileOutputStream
Try using this:
out = new FileOutputStream(fileStr,true);
So that now your data will be appended instead of being overwritten.You can go through this:
doc

Write to a windows network share from unix

The following code works like a charm in eclipse under windows:
public static void main(String[] args)
{
try
{
String filePath = "\\\\myserver\\dir";
String fileName = "myFile.txt";
FileWriter myFileWriter = new FileWriter(filePath + File.separator + fileName);
BufferedWriter myBufferedWriter = new BufferedWriter(myFileWriter);
myBufferedWriter.write("test");
myBufferedWriter.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
Now I want to run this code from a unix machine in the same network. The program runs, but does not write my file or throws an exception. Any ides ?
Cheers
If that destination unix machine has Samba installed you might want to try the following library:
http://jcifs.samba.org/
You would need a username and password though.
try {
String filePath = "myserver/dir";
String fileName = "myFile.txt";
String user = "username";
String password = "password";
// URL: smb://user:passwd#host/share/filname
SmbFileOutputStream out = new SmbFileOutputStream("smb://" + user + ":" + password + "#" + filePath
+ File.separator + fileName);
out.write("test".getBytes());
out.close();
} catch (Exception e) {
e.printStackTrace();
}
This would also work with a windows machine as the destination if the server is configured as an SMB server.
Because in Unix/Linux this is not the right path
String filePath = "\\\\myserver\\dir";
I suggest to check such path exist, and 99% chances you will not have permission to create them. It would be more or less
String filePath = "/usr/xx/";
Creating folder:
File temp = new File("temp");
boolean test = temp.mkDir();

connect to an IP address true a file sharing program

I have this file sharing program, where i can get mye files from a local location JFileChooser chooser = new JFileChooser("C://Users"), but i want to get files from a Server using an IP address. I have trying String hostname = "192.168.1.1"; but its not working. When i open the file chooser i get to my own folder. some tips?
public void download(String username) throws RemoteException, NullPointerException{
JFileChooser chooser = new JFileChooser("//" + hostname + "/C://");
chooser.setFileView(new FileView() {
#Override
public Boolean isTraversable(File f) {
return (f.isDirectory() && f.getName().equals("C://"));
}
});
int returnVal = chooser.showOpenDialog(parent);
if (returnVal == JFileChooser.APPROVE_OPTION) {
System.out.println("You chose to open this file: " + chooser.getSelectedFile().getName());
} try {
String fileName = chooser.getSelectedFile().getName();
File selectedFile = chooser.getSelectedFile();
//String name = "//" + hostname + "/chatter";
System.out.println(fileName);
//ChatFront cf = (ChatFront) Naming.lookup(name);
String ClientDirectory = getProperty + "/desktop/";
byte[] filedata = cf.downloadFile(selectedFile);
File file = new File(fileName);
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(ClientDirectory + file.getName()));
output.write(filedata, 0, filedata.length);
notifySelf(getUsername(), "You have now downloaded: " + file.getName() + " from the server");
output.flush();
output.close();
} catch (Exception e) {
System.err.println("FileServer exception: " + e.getMessage());
e.printStackTrace();
}
}
Thanks in Advance :)
You're using "//" + hostname + "/C://" as the path for your JFileChooser. That's not a valid path. If you're trying to access files in a shared folder on a LAN, the path for that looks like \\hostname\sharename.
Even if no shared folders have been defined on the remote machine, may be an "administrative share" of the C: drive called C$, so you could use \\hostname\C$. But you have to authenticate as a valid user on that system to have permission to access the share. (I'm not sure how that'll work when you try to acccess the path from a Java program — Windows might pop up a login box for the remote system, or it might just fail.)

Categories

Resources