This is a follow-up question for Android APP connect to FTP server in Java. I am attempting to create a tiny FTP host manager with FTPHostProfile
class and FTPHostProfiles
class.
The experimental implementation
Project name: FTPHostManagement
FTPHostProfile
class implementation:package com.example.ftphostmanagement; public class FTPHostProfile { private String ProfileName = ""; private String FTPHostName = ""; private int FTPPort = 21; private String FTPUsername = ""; private String FTPPassword = ""; public FTPHostProfile(String hostnameInput, int portInput, String usernameInput, String passwordInput) { this.ProfileName = FTPHostName; this.FTPHostName = hostnameInput; this.FTPPort = portInput; this.FTPUsername = usernameInput; this.FTPPassword = passwordInput; } public FTPHostProfile(String profileNameInput, String hostnameInput, int portInput, String usernameInput, String passwordInput) { this.ProfileName = profileNameInput; this.FTPHostName = hostnameInput; this.FTPPort = portInput; this.FTPUsername = usernameInput; this.FTPPassword = passwordInput; } public String GetProfilename() { return this.ProfileName; } public String GetHostname() { return this.FTPHostName; } public int GetPort() { return this.FTPPort; } public String GetUsername() { return this.FTPUsername; } public String GetPassword() { return this.FTPPassword; } }
FTPHostProfiles
class implementation:package com.example.ftphostmanagement; import android.util.Log; import org.apache.commons.net.ftp.FTP; import java.util.ArrayList; import java.util.stream.Collectors; public class FTPHostProfiles { private ArrayList<FTPHostProfile> Collection = new ArrayList<>(); /// Empty constructor public FTPHostProfiles() { } /// Single FTPHostProfile handler public FTPHostProfiles(FTPHostProfile input) { this.Collection.add(input); } /// Multiple FTPHostProfiles handler public FTPHostProfiles(ArrayList<FTPHostProfile> input) { this.Collection = input; } public FTPHostProfiles(FTPHostProfiles input) { this.Collection.addAll(input.Collection); } public FTPHostProfiles AddProfile(FTPHostProfile input) { this.Collection.add(input); return this; } public FTPHostProfiles AddProfiles(ArrayList<FTPHostProfile> input) { this.Collection.addAll(input); return this; } public FTPHostProfiles AddProfiles(FTPHostProfiles input) { this.Collection.addAll(input.Collection); return this; } public FTPHostProfile GetProfile(String profileNameInput) { var filteredResult = this.Collection.stream().filter(element -> (element.GetProfilename().equals(profileNameInput))).collect(Collectors.toList()); if (filteredResult.stream().count() >= 1) { return filteredResult.get(0); } else { Log.d("GetProfile", "no such item in stored profiles"); throw new IllegalStateException("no such item in stored profiles"); } } }
FTPconnection
class implementation:package com.example.ftphostmanagement; import android.util.Log; import java.io.IOException; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import org.apache.commons.net.SocketClient; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; public class FTPconnection { FTPHostProfiles mFTPHostProfiles = new FTPHostProfiles(); public FTPconnection(FTPHostProfiles input) { this.mFTPHostProfiles = input; } public FTPClient connectftp(String profilename) { // Reference: https://stackoverflow.com/a/8761268/6667035 FTPClient ftp = new FTPClient(); try { var FTPHostProfile = mFTPHostProfiles.GetProfile(profilename); // Reference: https://stackoverflow.com/a/55950845/6667035 // The argument of `FTPClient.connect` method is hostname, not URL. ftp.connect(FTPHostProfile.GetHostname(), FTPHostProfile.GetPort()); boolean status = ftp.login(FTPHostProfile.GetUsername(), FTPHostProfile.GetPassword()); if (status) { ftp.enterLocalPassiveMode(); ftp.setFileType(FTP.BINARY_FILE_TYPE); ftp.sendCommand("OPTS UTF8 ON"); } System.out.println("status : " + ftp.getStatus()); } catch (UnknownHostException ex) { ex.printStackTrace(); } catch (SocketException en) { en.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return ftp; } }
User permission setting
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.ftphostmanagement"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.FTPHostManagement"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> </manifest>
build.gradle
plugins { id 'com.android.application' } android { compileSdk 30 defaultConfig { applicationId "com.example.ftphostmanagement" minSdk 26 targetSdk 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') // https://mvnrepository.com/artifact/commons-net/commons-net implementation group: 'commons-net', name: 'commons-net', version: '20030805.205232' implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' }
Full Testing Code
MainActivity.java
implementation:package com.example.ftphostmanagement; import androidx.appcompat.app.AppCompatActivity; import android.content.Context; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.widget.Toast; import org.apache.commons.net.ftp.FTPClient; public class MainActivity extends AppCompatActivity { private FTPClient ftpClient; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new FtpTask().execute(); } void ShowToast(String Text, int Duration) { Context context = getApplicationContext(); CharSequence text = Text; int duration = Duration; Toast toast = Toast.makeText(context, text, duration); toast.show(); } // AsyncTask must be subclassed to be used. The subclass will override at least one method // `doInBackground(Params...)`, and most often will override a second one `onPostExecute(Result)` // Reference: https://developer.android.com/reference/android/os/AsyncTask?authuser=4 // Reference: https://stackoverflow.com/a/12447497/6667035 private class FtpTask extends AsyncTask<Void, Void, FTPClient> { // `doInBackground` invoked on the background thread immediately after `onPreExecute()` // finishes executing. This step is used to perform background computation // that can take a long time. The parameters of the asynchronous task are // passed to this step. The result of the computation must be returned by // this step and will be passed back to the last step. This step can also use // `publishProgress(Progress...)` to publish one or more units of progress. // These values are published on the UI thread, in the `onProgressUpdate(Progress...)` // step. protected FTPClient doInBackground(Void... args) { FTPHostProfiles ftpHostProfiles = new FTPHostProfiles(); ftpHostProfiles.AddProfile(new FTPHostProfile( "Profile1", "Hostname1", 21, "Username1", "Password1")); ftpHostProfiles.AddProfile(new FTPHostProfile( "Profile2", "Hostname2", 21, "Username2", "Password2")); FTPconnection ftpConnect = new FTPconnection(ftpHostProfiles); FTPClient ftp = ftpConnect.connectftp("Profile1"); return ftp; } // `onPostExecute` invoked on the UI thread after the background computation finishes. // The result of the background computation is passed to this step as // a parameter. protected void onPostExecute(FTPClient result) { Log.v("FTPTask","FTP connection complete"); ftpClient = result; //Where ftpClient is a instance variable in the main activity Log.v("Boolean.toString(ftpClient.isConnected())", Boolean.toString(ftpClient.isConnected())); ShowToast(Boolean.toString(ftpClient.isConnected()), Toast.LENGTH_SHORT); } } }
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
What changes has been made in the code since last question?
The
FTPHostProfile
class andFTPHostProfiles
class implementation is the key part in this post.Why a new review is being asked for?
If there is any possible improvement, please let me know.