Java Android FTP Upload Issue & "Async" - java

I am attempting to upload via ftp a file, named "advancedsettings.xml" located in path "/storage/emulated/0/advancedsettings.xml" from my Android device. It doesn't seem to be working; the file does not upload and the following exception is thrown:
01-06 17:56:17.498 28084-28084/com.name.example.appname E/SmsReceiverīš• android.os.NetworkOnMainThreadException
I discovered that basically, an application cannot attempt to perform a networking operation "on its main thread".
I am new at Java but I understand, following from this, I must implement "ASync"; I haven't understood how to implement it. Could somebody help describe this to me and how I might implement it in respect of the below code?
My code is as follows:
public class FtpUpload {
// use this method to upload the file using file path global var and ftp code,
//then return the link string.
//TO DO: UID file name to prevent file already exists overwrite on server?
public void total() {
FTPClient con = null;
String dest_fname = "advancedsettings.xml"; // Added to create a destination file with a dynamically created name (same as the file name in /sdcard/ftp/)
try
{
con = new FTPClient();
con.connect("ftp.domain.co.uk");
// Check your USERNAME e.g myuser#mywebspace.com and check your PASSWORD to ensure they are OK.
if (con.login("username", "password"))
{
con.enterLocalPassiveMode(); // important!
con.setFileType(FTP.BINARY_FILE_TYPE);
String data = "/storage/emulated/0/advancedsettings.xml";
FileInputStream in = new FileInputStream(data);
boolean result = con.storeFile(dest_fname, in);
in.close();
if (result) Log.v("upload result", "succeeded");
con.logout();
con.disconnect();
} else { // This Error Log was created
// Create error log as a file
File log_file = new File("/storage/emulated/0/error.txt");
try {
FileWriter lfw = new FileWriter(log_file);
BufferedWriter lout = new BufferedWriter(lfw);
// Continue
lout.write("Upload Connection Failed!");
lout.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
Log.e("SmsReceiver", e1.toString());
}
}
}
catch (Exception e)
{
Log.e("SmsReceiver", e.toString());
}
}
Thank you in advance.
K

Shamefully I didn't initially come across this documentation on Async Tasks, which has proven invaluable.
With a bit of improv though, I got it working. I just modified my class as such:
private class FtpUpload extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... params) {
//code here
}
And used the following to call the above Async method:
new FtpUpload().execute();
Of course, you won't get very far with FTP networking without declaring the following user permissions in your manifest file (outside of the "application" tags):
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

Related

How to use URLClassloader in an auto-update jar launcher?

I've come across many posts about these two topics: Auto-Updating and URLClassloaders. I'll start with the auto updating goal. I found this post here that talks about a 2 jar system. One jar that launches the main app jar: From Stephen C:
The launcher could be a Java application that creates a classloader for the new JAR, loads an entrypoint class and calls some method on it. If you do it this way, you have to watch for classloader storage leaks, but that's not difficult. (You just need to make sure that no objects with classes loaded from the JAR are reachable after you relaunch.)
This is the approach I'm taking, but I'm open to other ideas if they prove easier and/or more reliable. The Coordinator has posted some pretty cool launcher code to which I plan on incorporating some of this reload type code in my launcher, but first I need to get it to work.
My issue is that my main app jar has many other dependencies, and I cannot get some of those classes to load despite the fact that all the jars have been added to the URL's array. This brings up the second topic URLClassloader.
Side Note for future readers: When passing a URL to the URLClassloader that is a directory, a helpful note that would have saved me (an embarrassingly large) amount of time is that the contents of the directory must be .class files! I was originally pointing to my dependent jar directory, no good.
Context for the code below, my launcher jar resides in the same directory as my app jar, which is why I'm using user.dir. I will probably change this, but for now the code works and gets far enough into my app's code to request a connection to a sqlite database before failing.
Launcher:
public class Launcher {
public static void main(String[] args) {
try {
String userdir = System.getProperty("user.dir");
File parentDir = new File(userdir);
ArrayList<URL> urls = getJarURLs(parentDir);
URL[] jarURLs = new URL[urls.size()];
int index = 0;
for (URL u : urls) {
System.out.println(u.toString());
jarURLs[index] = u;
index ++;
}
URLClassLoader urlCL = new URLClassLoader(jarURLs);
Class<?> c = urlCL.loadClass("main.AppStart");
Object [] args2 = new Object[] {new String[] {}};
c.getMethod("main", String[].class).invoke(null, args2);
urlCL.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
public static ArrayList<URL> getJarURLs(File parentDir) throws MalformedURLException {
ArrayList<URL> list = new ArrayList<>();
for (File f : parentDir.listFiles()) {
if (f.isDirectory()) {
list.addAll(getJarURLs(f));
} else {
String name = f.getName();
if (name.endsWith(".jar")) {
list.add(f.toURI().toURL());
}
}
}
return list;
}
}
Here's an example of the URL output added to the array:
file:/C:/my/path/to/dependent/jars/sqlite-jdbc-3.32.3.2.jar
file:/C:/my/path/to/main/app.jar
file: ... [10 more]
The URLClassloader seems to work well enough to load my main method in app.jar. The main executes a some startup type stuff, before attempting to load a login screen. When the request is made to get the user info database, my message screen loads and displays (<-this is important for later)
the stacktrace containing:
java.sql.SQLException: No suitable driver found for jdbc:sqlite:C:\...\users.db
I understand that this is because that jar is not on the class path, but it's loaded via the class loader, so why can't it find the classes from the jar? From this post JamesB suggested adding Class.forName("org.sqlite.JDBC"); before the connection request. I rebuilt the app jar with this line of code and it worked!
The weird thing that happened next, is that my message screen class can no longer be found even though earlier it loaded and displayed correctly. The message screen is a class inside my main app.jar and not in a dependent jar, which is why I'm baffled. Am I going to have to add Class.forName before every instance of any of my classes? That seems rude..
So what could I be doing wrong with the class loader? Why does it load some classes and not others despite that fact that all the jars have been added to the URL array?
Some other relative info: My app works perfectly as intended when launched from windows command line when the classpath is specified: java -cp "main-app.jar;my/dependent/jar/directory/*" main.AppStart. It's only when I try launching the app via this classloader that I have these issues.
By the way, is this java command universal? Will it work on all operating systems with java installed? If so, could I not just scrap this launcher, and use a process builder to execute the above command? Bonus points for someone who can tell me how to execute the command from a jre packaged with my app, as that's what I plan on doing so the user does not have to download Java.
EDIT
I figured out one of the answers to one of the questions below. Turns out, I didn't need to do any of the code below. My main method loads a login screen but after it's loaded it returns back to the AppLauncher code, thus closing the URLClassLoader! Of course, at that point any requested class will not be found as the loader has been closed! What an oof! Hopefully I will save someone a headache in the future...
Original
Well, after more time, effort, research, and effective use of Eclipse's debugging tool, I was able to figure out what I needed to do to resolve my issues.
So the first issue was my JDBC driver was never registered when passing the jars to the URLClassloader. This is the part I sorta don't understand, so advisement would be welcomed, but there is a static block in the JDBC class that registers the driver so it can be used by DriverManager see code below. Loading the class is what executes that static block, hence why calling Class.forName works.
static {
try {
DriverManager.registerDriver(new JDBC());
} catch (SQLException e) {
e.printStackTrace();
}
}
What I don't understand, is how class loading works if jars are specified via the class path. The URLClassLoader doesn't load any of those classes until they are called, and I never directly work with the JDBC class, thus no suitable driver exception, but are all the classes specified via the classpath loaded initially? Seems that way for static blocks to execute.
Anyhow, to resolve my other issue with some of my app's classes not being found I had to implement my own classloader. I get what I did and how it works well, but still don't understand why I had to do it. All of my jars were loaded to the original URLClassloader so if I could find them and the files within, why couldn't it do it?
Basically, I had to override the findClass and findResource methods to return jarEntry information that I had to store. I hope this code helps someone!
public class SBURLClassLoader extends URLClassLoader {
private HashMap<String, Storage> map;
public SBURLClassLoader(URL[] urls) {
super(urls);
map = new HashMap<>();
try {
storeClasses(urls);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private void storeClasses(URL[] urls) throws ClassNotFoundException {
for (URL u : urls) {
try {
JarFile jarFile = new JarFile(new File(u.getFile()));
Enumeration<JarEntry> e = jarFile.entries();
while (e.hasMoreElements()) {
JarEntry jar = e.nextElement();
String entryName = jar.getName();
if (jar.isDirectory()) continue;
if (!entryName.endsWith(".class")) {
//still need to store these non-class files as resources
//let code continue to store entry un-altered
} else {
entryName = entryName.replace(".class", "");
entryName = entryName.replace("/", ".");
}
map.put(entryName, new Storage(jarFile, jar));
System.out.println(entryName);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c = null;
try {
c = super.findClass(name);
} catch (ClassNotFoundException e) {
Storage s = map.get(name);
try {
InputStream in = s.jf.getInputStream(s.je);
int len = in.available();
c = defineClass(name, in.readAllBytes(), 0, len);
resolveClass(c);
} catch (IOException e1) {
e1.printStackTrace();
}
if (c == null) throw e;
}
return c;
}
#Override
public URL findResource(String name) {
URL url = super.findResource(name);
if (url == null) {
Storage s = map.get(name);
if (s != null) {
try {
url = new URL("jar:"+s.base.toString() + "!/" + name);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return url;
}
private class Storage {
public JarFile jf;
public JarEntry je;
public URL base;
public Storage(JarFile jf, JarEntry je) {
this.jf = jf;
this.je = je;
try {
base = Path.of(jf.getName()).toUri().toURL();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
}

Unable to copy a database on Android

I have permissions for read and writing on AndroidManifest:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
... because I want to copy one file. I'm performing this process in two steps:
1. Launching an Intent so the user creates the file where he wants:
This code is mostly the example of the Android developers site.
private void createFile() {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/vnd.sqlite3");
intent.putExtra(Intent.EXTRA_TITLE, "test.db");
startActivityForResult(intent, CREATE_FILE);
}
2. Copy the file.
The intent returns a Uri, so inside the onActivityResult:
try {
destinationOutputStream = getContentResolver().openOutputStream(data.getData());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
(where data is the Intent), I'm able to get the output stream.
Finally, and according to the documentation, I should be able to copy the file:
try {
Long totalBytes = Files.copy(originalPath, destinationOutputStream);
} catch (IOException e) {
e.printStackTrace();
}
(where originalPath is the path (of type Path) where the original file is stored).
But, on runtime, I'm getting the following error:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

Java Properties Class

using java 8, tomcat 8
Hi, i am loading a file using properties, but i have a check before loading which returns the same properties object if its already been loaded (not null). which is a normal case scenario but i want to know if there is any way that if any change occur in target file, and some trigger should be called and refreshes all the properties objects. here is my code.
public static String loadConnectionFile(String keyname) {
String message = "";
getMessageFromConnectionFile();
if (propertiesForConnection.containsKey(keyname))
message = propertiesForConnection.getProperty(keyname);
return message;
}
public static synchronized void getMessageFromConnectionFile() {
if (propertiesForConnection == null) {
FileInputStream fileInput = null;
try {
File file = new File(Constants.GET_CONNECTION_FILE_PATH);
fileInput = new FileInputStream(file);
Reader reader = new InputStreamReader(fileInput, "UTF-8");
propertiesForConnection = new Properties();
propertiesForConnection.load(reader);
} catch (Exception e) {
Utilities.printErrorLog(Utilities.convertStackTraceToString(e), logger);
} finally {
try {
fileInput.close();
} catch (Exception e) {
Utilities.printErrorLog(Utilities.convertStackTraceToString(e), logger);
}
}
}
}
the loadConnectionFile method executes first and calls getMessageFromConnectionFile which has check implemented for "null", now if we remove that check it will definitely load updated file every time but it will slower the performance. i want an alternate way.
hope i explained my question.
thanks in advance.
Java has a file watcher service. It is an API. You can "listen" for changes in files and directories. So you can listen for changes to your properties file, or the directory in which your properties file is located. The Java Tutorials on Oracle's OTN Web site has a section on the watcher service.
Good Luck,
Avi.

Java.security.Access.ControlException: access denied("java.io.FilePermission" "[object file]" "read")

I have the following problem:
I have a Java program that receives via Applet a binary file.
I received that file with getParameter(file) and read that file with java.io.FileInputStream(file).
I put this file on web server and call the java program via javascript
First, when I was running the program, was occuring the message error:
Java.security.Access.ControlException: access denied("java.io.FilePermission" "[object file]" "read")
I created a key via keytool and signed the jar file with jarsigner.
But, even executing the command jarsigner, when I run the Java program again, the error message continues occuring:
Java.security.Access.ControlException: access denied("java.io.FilePermission" "[object file]" "read").
Therefore, the error persists even after signing.
I really do not know what to do.
Can anyone help me?
Below the java code:
public class InJava extends Applet{
String parametro;
public void sayHello() {
parametro = getParameter("parametro");
java.io.FileInputStream fis = null;
try {
fis = new java.io.FileInputStream(parametro);
}
catch (Exception e) {
String retorno_exc = e.toString();
return ;
}
}
You need to wrap your code in AccessController.doPrivileged, like:
public class InJava extends Applet{
public void sayHello() {
final String parametro = getParameter("parametro");
FileInputStream fis = AccessController.doPrivileged(new PrivilegedAction<FileInputStream>() {
public FileInputStream run() {
try {
retrun new FileInputStream(parametro);
} catch (IOException e) {
// handle exception
}
}
});
}
Make sure that your applet jar(s) are signed, and that you understand all other consequences of running an applet.

Upload Xml file to internet via FTP

I'm developing a game in Android. The game has many levels and a Level Editor. So when a user make a level, the data are saving as Xml file. So I want to upload this Xml file to internet to share the other users. I searhed and tried these below codes. But It didn't work. The whole code like this:
String FTP_HOST= "185.27.134.11";
String FTP_USER = "fees0_14042425";
String FTP_PASS ="kadi1sd22";
File f = new File(Environment.getExternalStorageDirectory()+"/kadirGameLevels1/a.png");
FTPClient client = new FTPClient();
try {
client.connect(FTP_HOST,21);
client.login(FTP_USER, FTP_PASS);
client.setType(FTPClient.TYPE_BINARY);
client.changeDirectory("/levels/");
client.upload(f, new MyTransferListener());
} catch (Exception e) {
e.printStackTrace();
try {
client.disconnect(true);
} catch (Exception e2) {
e2.printStackTrace();
}
}
But even if I only use this single line, it still stop running. Did I something wrong with is integration or anything else?
FTPClient client = new FTPClient();
Make sure you have the INTERNET permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
This makes sure your app has the right permission to access the internet.
Also don't put any networking code in the main UI thread or you will likely get a NetworkOnMainThreadException.
Instead put all your FTP-connecting/accessing code into an AsyncTask: https://stackoverflow.com/a/6343299/833647

Categories