jsch find specific file over SFTP - java

I have Java application connecting to linux over SFTP. I'm using jsch as a framework. Now, let's say I need to rename a file.
public boolean rename(String name) {
boolean result = false;
channelSftp = (ChannelSftp)channel;
LsEntry currentFile = //here I have LsEntry object, pointing to specific record;
logger.info("Renaming CRC file " + currentFile.getFilename() + " to " + name);
try {
//For the first parameter I get current position in the directory, then I append the filename of the currently processed file.
//For the first parameter I get current position in the directory, then I append the new name.
channel.rename(channel.pwd() + currentFile .getFilename(), channel.pwd() + name);
result = true;
} catch (Exception e) {
logger.error("Error renaming crc file to " + name, e);
result = false;
}
return result;
}
Now after renaming the file on the filesystem, I also need to rename the file in the current LsEntry object I'm working with. The problem is that LsEntry doesn't provide such method, so I have to load it again. Now how do I look for it? I need to find specific file so I can use it as updated LsEntry object for later use. Is that possible?
EDIT1:
The LsEntry object, which represents entry on the filesystem has to be created somehow, I do that by casting vector object into it. Like so:
System.out.println("searching for files in following directory" + directory);
channelSftp.cd(directory);
Vector foundFiles = channelSftp.ls(directory);
for(int i=2; i<foundFiles.size();i++){
LsEntry files = (LsEntry) foundFiles.get(i);
System.out.println("found file: " + files.getFilename());
System.out.println("Found file with details : " + files.getLongname());
System.out.println("Found file on path: " + channelSftp.pwd());
channelSftp.rename(channelSftp.pwd() + files.getFilename(), channelSftp.pwd() + "picovina");
//LsEntry has now old name.

public class SftpClient {
private final JSch jSch;
private Session session;
private ChannelSftp channelSftp;
private boolean connected;
public SftpClient() { this.jSch = new JSch(); }
public void connect(final ConnectionDetails details) throws ConnectionException {
try {
if (details.usesDefaultPort()) {
session = jSch.getSession(details.getUserName(), details.getHost());
} else {
session = jSch.getSession(details.getUserName(), details.getHost(), details.getPort());
}
channelSftp = createSftp(session, details.getPassword());
channelSftp.connect();
connected = session.isConnected();
} catch (JSchException e) {
throw new ConnectionException(e.getMessage());
}
}
public void disconnect() {
if (connected) {
channelSftp.disconnect();
session.disconnect();
}
}
public void cd(final String path) throws FileActionException {
try {
channelSftp.cd(path);
} catch (SftpException e) {
throw new FileActionException(e.getMessage());
}
}
public List<FileWrapper> list() throws FileActionException {
try {
return collectToWrapperList(channelSftp.ls("*"));
} catch (SftpException e) {
throw new FileActionException(e.getMessage());
}
}
public String pwd() throws FileActionException {
try {
return channelSftp.pwd();
} catch (SftpException e) {
throw new FileActionException(e.getMessage());
}
}
public boolean rename(final FileWrapper wrapper, final String newFileName) throws FileActionException {
try {
String currentPath = channelSftp.pwd();
channelSftp.rename(currentPath + wrapper.getFileName(), currentPath + newFileName);
} catch (SftpException e) {
throw new FileActionException(e.getMessage());
}
return true;
}
private List<FileWrapper> collectToWrapperList(Vector<ChannelSftp.LsEntry> entries) {
return entries.stream()
.filter(entry -> !entry.getAttrs().isDir())
.map(entry -> FileWrapper.from(entry.getAttrs().getMtimeString(), entry.getFilename(), entry.getAttrs().getSize()))
.collect(Collectors.toList());
}
private ChannelSftp createSftp(final Session session, final String password) throws JSchException {
session.setPassword(password);
Properties properties = new Properties();
properties.setProperty("StrictHostKeyChecking", "no");
session.setConfig(properties);
session.connect();
return (ChannelSftp) session.openChannel("sftp");
}
}
Note here that the list method effectively returns a list of FileWrapper objects instead of LsEntry objects.
public class FileWrapper {
private static final String TIME_FORMAT = "EEE MMM dd HH:mm:ss zzz yyyy";
private Date timeStamp;
public Date getTimeStamp() { return timeStamp; }
private String fileName;
public String getFileName() { return fileName; }
private Long fileSize;
public Long getFileSize() { return fileSize; }
private FileWrapper(String timeStamp, String fileName, Long fileSize) throws ParseException {
this.timeStamp = new SimpleDateFormat(TIME_FORMAT).parse(timeStamp);
this.fileName = fileName;
this.fileSize = fileSize;
}
public static FileWrapper from(final String timeStamp, final String fileName, final Long fileSize) {
try {
return new FileWrapper(timeStamp, fileName, fileSize);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
With this, you can easily list the remote directory and get all the files' attributes.
With that on hand you can simply invoke SftpClient#rename and rename the file you want.
I know that you want to avoid refactoring, but given the very tight nature or LsEntry as well as the fact that the library still uses Vector and such, I suppose that this is the best way to go (you'll avoid headaches in the future).
I know that this may not be 100% the answer you expect, but I think it's going to be helpful for you.

Related

How to use an interface/something similar to clean up duplicate code in Java

I have output functions that output data from different objects to their corresponding files. Most of these functions follow the same pattern, the only differences are the objects being used and by extension, the object's inherent methods being used.
I pass in 'obj' to all the write functions, and in each individual write function we call different 'obj.get...' functions to get different objects to grab data from to output.
My output functions are called like so:
for (Object obj : objects) {
writer.writeSubOject1(obj, dir, "subObject1.csv", true);
writer.writeSubOject2(obj, dir, subObject2.csv, true);
....
}
Code for the write functions:
public class Writer{
public void writeSubOject1(Object obj, File dir, String filename, Boolean append) {
ArrayList<SubObject1> subObject1 = obj.getSubObject1();
try {
log.info("writing " + filename + "...");
ArrayList<String[]> so1Data = SubObject1.getData(subObject1);
final File out = CreateFileObject(dir, filename);
writeCsv(so1Data, out, append);
} catch (Exception e) {
log.info("Error in writeSubOject1");
log.error(e);
e.printStackTrace();
}
}
public void writeSubOject2(Object obj, File dir, String filename, Boolean append) {
ArrayList<SubObject2> subObject2 = obj.getSubObject2();
try {
log.info("writing " + filename + "...");
ArrayList<String[]> so2Data = SubObject2.getData(subObject2);
final File out = CreateFileObject(dir, filename);
writeCsv(so2Data, out, append);
} catch (Exception e) {
log.info("Error in writeSubOject2");
log.error(e);
e.printStackTrace();
}
}
}
You can see that the only differences between the two methods is the obj.getSubObjectX() call, and the getData() method which has a unique implementation in SubObject1 and 2.
Is there a better way to do this to get rid of duplicate code?
make a private method
writeSubOject(ArrayList<String[]> soData, File dir, String filename, Boolean append) {
try {
log.info("writing " + filename + "...");
final File out = CreateFileObject(dir, filename);
writeCsv(soData, out, append);
} catch (Exception e) {
log.info("Error in writeSubOject");
log.error(e);
e.printStackTrace();
}
}
then in your public methods
public void writeSubOject1(Object obj, File dir, String filename, Boolean append) {
ArrayList<SubObject1> subObject1 = obj.getSubObject1();
ArrayList<String[]> so1Data = SubObject1.getData(subObject1);
writeSubOject(so1Data,dir,filename,append);
}
public void writeSubOject2(Object obj, File dir, String filename, Boolean append) {
ArrayList<SubObject2> subObject2 = obj.getSubObject2();
ArrayList<String[]> so2Data = SubObject2.getData(subObject2);
writeSubOject(so2Data,dir,filename,append);
}
Your question is not well put, but I will try to answer as best as I can with the provided information.
First, I would write an abstract class.
public abstract class WritableObject {
List<WritableObject> children = new ArrayList<>();
public abstract List<String> getDataAsStringList();
List<WritableObject> getChildren() {
return this.children;
}
public void addChild(WritableObject wo) {
children.add(wo);
}
}
Now we can implement this interface in as many other classes as needed. Let's assume you have two for now.
public class WritableObjectOne extends WritableObject {
#Override
public List<String> getDataAsStringList() {
return Arrays.asList(""); // here the object returns it's String representation
}
}
public class WritableObjectTwo extends WritableObject {
#Override
public List<String> getDataAsStringList() {
return Arrays.asList(""); // here the object returns it's String representation
}
}
Now, the best part is that you can combine however you want. You can have children of any WritableObject and can have children for the child and so on.
Now, in your writer, you can just have one method:
public class Writer {
public void writeSubOject(WritableObject obj, File dir, String filename, Boolean append) {
List<WritableObject> children = obj.getChildren();
try {
log.info("writing " + filename + "...");
List<String> data = new ArrayList<>();
for (WritableObject child:children) {
data.addAll(child.getDataAsStringList());
}
final File out = CreateFileObject(dir, filename);
writeCsv(data, out, append);
} catch (Exception e) {
log.info("Error in writeSubOject1");
log.error(e);
e.printStackTrace();
}
}
}
Again, maybe this is not exactly how you want it (I find it weird how a list of strings is returned), but it should at least help you get to the right solution.

How to make ObjectInputStream read all objects from file in java?

I have a file which is storing objects and I have a *getAll() method which needs to return the List<Secretary>. But, I only see single object being printed in console.
I searched for the problem and tried 3 ways but it did not work.
The insert method for inserting object in file is:
#Override
public Secretary insert(Secretary t) {
try {
System.out.println("insert called");
FileOutputStream file = new FileOutputStream
(filename,true);
ObjectOutputStream out = new ObjectOutputStream
(file);
Method for serialization of object
out.writeObject(t);
out.close();
file.close();
return t;
}
catch (IOException ex) {
ex.printStackTrace();
}
return null;
}
I have declared append mode as true as, my file was being replaced by new object when saving.
So,i need to fetch all object from file and need to assign to a list.I tried:
public class SecretaryDaoImpl implements SecretaryDAO{
private String filename = "secretary.txt";
private Secretary sec=null;
#Override
public List<Secretary> getAll() {
//Method 1
try {
Reading the object from a file
FileInputStream file = new FileInputStream
(filename);
ObjectInputStream in = new ObjectInputStream
(file);
List<Secretary> secList=new ArrayList<>();
Method for deserialization of object
secList.add((Secretary)in.readObject());
in.close();
file.close();
System.out.println("Object has been deserialized\n"
+ "Data after Deserialization.");
System.out.println("secList is" +secList);
return secList;
}
catch (IOException ex) {
System.out.println("Secreatary file not found");
return null;
}
catch (ClassNotFoundException ex) {
System.out.println("ClassNotFoundException" +
" is caught");
}
return null;
//Method 2
List<Secretary> secList=new ArrayList<>();
ObjectInputStream objectinputstream = null;
try {
FileInputStream streamIn = new FileInputStream(filename);
objectinputstream = new ObjectInputStream(streamIn);
List<Secretary> readCase = (List<Secretary>) objectinputstream.readObject();
for(Secretary s:readCase){
secList.add(s);
}
System.out.println("seclist is" + secList);
return secList;
} catch (Exception e) {
e.printStackTrace();
} finally {
if(objectinputstream != null){
try {
objectinputstream.close();
} catch (IOException ex) {
Logger.getLogger(SecretaryDaoImpl.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
//Method 3
try{
File file = new File(filename);
List<Secretary> list = new ArrayList<>();
if (file.exists()) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
list.add((Secretary) ois.readObject());
}
}
System.out.println("getall is"+list);
}
catch(Exception e){
}
return null;
}
}
I have commented out my code but here while posting in stackoverflow I have uncommented all the codes.
My Secretary.java is :
package com.npsc.entity;
import java.io.Serializable;
/**
*
* #author Ashwin
*/
public class Secretary implements Serializable {
private static final long serialVersionUID = 6529685098267757690L;
private int id;
private String userName;
private String password;
private Branch branch;
public String getUserName() {
return userName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Secretary(String userName, String password) {
this.userName = userName;
this.password = password;
}
public Branch getBranch() {
return branch;
}
public void setBranch(Branch branch) {
this.branch = branch;
}
#Override
public String toString() {
return "Secretary{" + "id=" + id + ", userName=" + userName + ", password=" + password + ", branch=" + branch + '}';
}
}
While performing insert operation,my txt file saving objects is:
But,I am unable to read all the object and add in list.Where I am facing the problem?
You will need to store in the file, the number of Secretary objects to read back. You can then determine how many entities to read, and thus repopulate your list.
Something like:
List<Secretary> list;
private void persistList(ObjectOutputStream out) {
out.writeInt(list.size());
for (Secretary sec : list) {
out.writeObject(sec);
}
}
And then to read:
private List<Secretary> readFromStream(ObjectInputStream in) {
int numObjects = in.readInt();
List<Secretary> result = new ArrayList<>(numObjects);
for (int i = 0; i < numObjects; i++) {
result.add((Secretary)in.readObject());
}
return result;
}
This is just a sketch of the technique (and ignores error handling, stream opening/closing etc.); the main thing is to integrate the idea of recording the size of the list, then reading that many Secretaries into your existing code.
Write a List<Secretary> to file and read same back, then you will have all.
write (Secretary s) {
read List<Secretary> currentList ;
currentList.add(s)
write List<Secretary>
}

How do I load and save an arrayList<String> into a text class in java?

I am trying to implement a save and load function in my program that saves an arrayList to a textfile and then can later load all of the past lists I have saved and print them out. I am currently using these two methods:
public static void save(Serializable data, String fileName) throws Exception {
try (ObjectOutputStream oos = new ObjectOutputStream((Files.newOutputStream(Paths.get(fileName))))) {
oos.writeObject(data);
}
}
public static Object load(String fileName) throws Exception {
try (ObjectInputStream oos = new ObjectInputStream((Files.newInputStream(Paths.get(fileName))))) {
return oos.readObject();
}
}
As well as a class that represents a list as serializable data.
The problem with this is that it won't save the data after I terminate the program, and when it loads data it prints it with a great deal of extra text besides the list I want it to return. Is there a better or easier way of doing this?
I once had a same problem. So I will show you how to do it ...
Be the below Level class is what is to be saved:
public class Level implements Serializable {
private int level = 1;
private int star;
private int point;
// Constructor
public Level() {
}
public void setLevel(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
public void setStar(int stars) {
this.star = stars;
}
public int getStar() {
return star;
}
public void setPoint(int points) {
this.point = points;
}
public int getPoint() {
return point;
}
#Override
public String toString() {
return "Level-" + level + " " +
(star > 1 ? star + " stars": star + " star") + " " +
(point > 1 ? point + " points" : point + " point") + "\n";
}
}
We will save the list into this file:
private static final String FILENAME = "data.level";
This is the List of our objects:
List<Level> mLevels;
Call this method to save the list into the file:
private void save() {
if(mLevels.isEmpty()) {
return;
}
try
{
ObjectOutputStream oos = new ObjectOutputStream(mContext.openFileOutput(FILENAME, Context.MODE_PRIVATE));
oos.writeObject(mLevels);
}
catch (IOException e)
{
//Toast.makeText(mContext,e.getMessage(),Toast.LENGTH_SHORT).show();
}
}
Use this method to load the list from saved file. Notice, we cast the list of our object with this (List<Level>) ois.readObject(); in the method:
private void load() {
try
{
ObjectInputStream ois = new ObjectInputStream(mContext.openFileInput(FILENAME));
mLevels = (List<Level>) ois.readObject();
}
catch (IOException e)
{
//Toast.makeText(mContext,e.getMessage(),Toast.LENGTH_SHORT).show();
}
catch (ClassNotFoundException e)
{
//Toast.makeText(mContext,e.getMessage(),Toast.LENGTH_SHORT).show();
}
if(mLevels == null) {
mLevels = new ArrayList<>();
//Toast.makeText(mContext,"List created",Toast.LENGTH_SHORT).show();
}
}
By now, you should get the idea of how to save your own list.

FTPClient download corrupted file

When download file from ftp server via FTPClient class files comes corrupted check for initial file (img1) and downloaded file (img2)
Source:
public class FtpClient extends FTPClient {
private String host;
private int port;
private String username;
private String password;
private boolean connected;
public FtpClient(String host, int port, String username, String password) {
this.host = host;
this.port = port;
this.username = username;
this.password = password;
connected = connect();
}
private void verifyConnection() throws ConnectionException {
if(!connected){
ConnectionException ex = new ConnectionException();
log.error(ex.getMessage());
throw ex;
}
}
private boolean connect(){
try {
//try to connect
connect(host, port);
if(!login(username, password)){
logout();
return false;
}
int reply = getReplyCode();
if(!FTPReply.isPositiveCompletion(reply)){
disconnect();
return false;
}
enterRemotePassiveMode();
setFileTransferMode(BINARY_FILE_TYPE);
setFileType(BINARY_FILE_TYPE);
log.debug("Remote system is " + getSystemType());
log.debug("Current directory is " + printWorkingDirectory());
return true;
} catch (IOException e) {
log.error(e.getMessage());
e.printStackTrace();
return false;
}
}
public void get(String batchName, String fileName) throws IOException, ConnectionException {
verifyConnection();
File file = new File("/tmp/" + batchName);
if (!file.exists()){
boolean mkdir = file.mkdir();
log.info("Create batch directory '{}' result: {}",batchName, mkdir);
}
OutputStream outputStream = new FileOutputStream("/tmp/" + batchName + "/" + fileName);
boolean result = retrieveFile(batchName + "/" + fileName, outputStream);
log.debug("Retrieve file {}, result: {}", batchName + "/" + fileName, result);
outputStream.close();
}
}
img1
img2
What is going wrong?
You are using an incorrect CONSTANT for FTPClient.setFileTransferMode.
setFileTransferMode(BINARY_FILE_TYPE);
Like the doc says :
mode - The new transfer mode to use (one of the FTP class _TRANSFER_MODE constants).
You can use one of those :
FTP.BLOCK_TRANSFER_MODE
FTP.COMPRESSED_TRANSFER_MODE
FTP.STREAM_TRANSFER_MODE
This will probably correct your problem of transfer, since your are using a value that could be anything ...
You are passing a value of 2 where it is expecting one of [10|11|12]. So It probably keep the default STREAM_TRANSFER_MODE value.

How to make a variable/constant available all the time once initialized?

I've a swing application that has to connect to database for some resources, for this i used .properties file to store database properties and that can be read at runtime.
For this i am using the following code
public void readPropertiesFile(){
try{
InputStream is = ReadValues.class.getResourceAsStream(PROP_FILE);
Properties prop = new Properties();
prop.load(is);
String URL = prop.getProperty("DB_URL");
String user = prop.getProperty("DB_USER");
String pwd = prop.getProperty("DB_PWD");
is.close();
/* code to use values read from the file*/
}catch(Exception e){
System.out.println("Failed to read from " + PROP_FILE + " file.");
}
}
but i've to call this method whenever i want to connect to the database (for Connection object).
I know the thing that now processing is fast enough to run these lines in micro seconds, but it's for my own knowledge that suggest me the ways through which i can store these DB values when application starts or the first time user try to connect to DB for any operation in such objects or variables or constants that will be usable until the application restarts and can be called directly without reading the file.
P.S. : I know that the DB values will not change oftentimes, and if it happens than i'll be happy to restart my application :)
by making these static fields in a separate class, they will not be loaded until the first time you access URL,USER, or PASSWORD.
public class DbProps {
public static final String URL;
public static final String USER;
public static final String PASSWORD;
static {
try{
InputStream is = ReadValues.class.getResourceAsStream(PROP_FILE);
try {
Properties prop = new Properties();
prop.load(is);
URL = prop.getProperty("DB_URL");
USER = prop.getProperty("DB_USER");
PASSWORD = prop.getProperty("DB_PWD");
} finally {
is.close();
}
}catch(Exception e){
throw new RuntimeException("Failed to read from " + PROP_FILE + " file.", e);
}
}
}
You can nake a check condition which will check if it is first time then set the value other wise use the existing value
public static boolean isFirstTime = true;
public static String URL = true;
public static String user = true;
public static String pwd = true;
public void readPropertiesFile(){
if(isFirstTime){
try{
InputStream is = ReadValues.class.getResourceAsStream(PROP_FILE);
Properties prop = new Properties();
prop.load(is);
URL = prop.getProperty("DB_URL");
user = prop.getProperty("DB_USER");
pwd = prop.getProperty("DB_PWD");
isFirstTime = false;
is.close();
/* code to use values read from the file*/
}catch(Exception e){
System.out.println("Failed to read from " + PROP_FILE + " file.");
}
}
}
//use this URL user and pwd in your application
Here's a generic environment class for you. You can get your DB props like Environment.getEnvironment().getProperty("DB_URL"), etc.
public class Environment {
private static final String PROP_FILE = "somefilename";
private static final Environment singleton = new Environment();
public static Environment getEnvironment() {
return singleton;
}
private Properties properties = new Properties();
protected Environment() {
super();
loadProperties();
}
public Properties getProperties() {
return properties;
}
public String getProperty(String propertyName) {
return getProperty(propertyName, System.getProperty(propertyName));
}
public String getProperty(String propertyName, String defaultValue) {
return getProperties().getProperty(propertyName, defaultValue);
}
public void loadProperties() {
URL resourceURL = null;
try {
resourceURL = Thread.currentThread().getContextClassLoader()
.getResource(PROP_FILE);
getProperties().load(resourceURL.openStream());
System.out.println("Loaded properties from "
+ resourceURL.toExternalForm());
} catch (IOException ioe) {
System.err.println("Failed to load properties from "
+ resourceURL.toExternalForm());
}
}
}

Categories

Resources