How to Override or Add a new protocol to the java.net.URL and changing FTP Client
If you use java.net.URL to load url and in some cases you want to add a new protocol or if you want to use another API to handle certain protocols it is possible to do it. In this example I'm overriding the FTP protocol so that it is handled by the org.apache.commons.net.ftp.FTPClient API
The following features are covered.
- Loading an FTP file with an URL
- Implementing a URLStreamHandlerFactory
- Loading an FTP file with an URL with the Apache org.apache.commons.net.ftp.FTPClient
Code Snippet 1
This code shows loads the same file twice. The first time being handled by the deafult FTP api and the second by FTPClient by apache.
package net.custom.streamhandler.apacheftp; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URI; import java.net.URL; public class FTPClass { private static final String FTP_URL = "ftp://ftp.adobe.com/lbtest.txt"; public static void main(String[] args) { loadFTP(); URL.setURLStreamHandlerFactory(new ApacheURLStreamHandlerFactory()); loadFTP(); } private static void loadFTP(){ try { URI uri = new URI(FTP_URL); URL ftpFile = uri.toURL(); BufferedReader in = new BufferedReader(new InputStreamReader( ftpFile.openStream())); String inputLine; while ((inputLine = in.readLine()) != null) System.out.println(inputLine); in.close(); } catch (Exception e) { e.printStackTrace(); } } }
Change your FTP_URL accordingly. In this case it is the annonymous user to the adobe ftp. ftp://
user:
password@
host:
port/
path e.g. ftp://john:mypassword@mydomain.com:21/logs/mylog.log
Code Snippet 2
This code shows how to create a custom URLStreamHanlderFactory the objects involved and how it is implemented. The CustomURLConnection extends URLConnection and uses the Apache FTPClient to connect to the FTP.
package net.custom.streamhandler.apacheftp; import java.io.IOException; import java.io.InputStream; import java.net.SocketException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; public class ApacheURLStreamHandlerFactory implements URLStreamHandlerFactory { public URLStreamHandler createURLStreamHandler(String protocol) { //this will only override the chosen protocol if ( protocol.equalsIgnoreCase("ftp") ) return new CustomHandler(); else return null; } } class CustomHandler extends URLStreamHandler { protected URLConnection openConnection(URL url) throws IOException { return new CustomURLConnection(url); } } class CustomURLConnection extends URLConnection { int reply; FTPClient ftp = new FTPClient(); InputStream in; static int defaultPort = 21; static String defaultPath = "/"; CustomURLConnection ( URL url) throws IOException { super( url ); } synchronized public void connect() throws IOException { try { int port; if ((port = url.getPort()) == -1 ) port = defaultPort; ftp.connect(url.getHost(), port); String login = "anonymous"; String password = ""; if(url.getAuthority().indexOf(':')>-1 && url.getAuthority().indexOf('@')>-1){ String []auxArray = url.getAuthority().replaceAll("@", ":").split(":"); login = auxArray[0]; password = auxArray[1]; } ftp.login(login, password); reply = ftp.getReplyCode(); if (FTPReply.isPositiveCompletion(reply)) { System.out.println("*********Connected Apache Success*********"); } else { System.out.println("*********Connection Apache Failed*********"); ftp.disconnect(); } in = ftp.retrieveFileStream(url.getFile()); } catch (SocketException ex) { ex.printStackTrace(); } catch (IOException ex) { ex.printStackTrace(); } connected = true; } synchronized public InputStream getInputStream() throws IOException { if (!connected) connect(); return ( in ); } }
Import the libraries.
In this example I'm using the Apache Commons 2.0. Go to the apache site or use maven to download the libraries required.
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>2.0</version>
</dependency>
Run the code
You should receive a *********Connected Apache Success********* between both connections. This means that from now on is the apache library the one that is handling new ftp requests
Create your custom protocol
Change the code Snippet 1 and 2 to ...
public class FTPClass { private static final String FTP_URL = "ftpapache://ftp.adobe.com/lbtest.txt"; public static void main(String[] args) { URL.setURLStreamHandlerFactory(new ApacheURLStreamHandlerFactory()); loadFTP(); }
....
public class ApacheURLStreamHandlerFactory implements URLStreamHandlerFactory { public URLStreamHandler createURLStreamHandler(String protocol) { //this will only override the chosen protocol if ( protocol.equalsIgnoreCase("ftpapache") ) return new CustomHandler(); else return null; }
Now every time you use the custom "protocol?" ftpapache it will be handled by Apache FTP Client