SERVEI DE FTP















Introducció

Propòsit

El servei de FTP de canigo permet enviar i rebre: arxius, directoris sencers o Streams, entre el servidor on s'executa l'aplicació a altres servidors.

El servei està basat en Jakarta Commons Net, es tracta d'un projecte open source englobat dins del projecte Jakarta que proporciona les funcionalitats clàssiques associades al protocol FTP, però com a característica destacada la possibilitat de transferir els fitxers o els streams en paral- lel.

Context i Escenaris d'Ús

El servei de FTP es troba dins dels serveis d'integració de canigo.

El seu ús és necessari en cas de voler intercanviar fitxers entre servidors utilitzant el protocol FTP.

Versions i Dependències

Les dependències descrites a la següent url son requerides per tal de compilar i fer funcionar el projecte:
Dependències Servei de FTP

A qui va dirigit

Aquest document va dirigit als següents perfils:

  1. Programador. Per conèixer l'ús del servei.
  2. Arquitecte. Per conèixer quins són els components i la configuració del servei.
  3. Administrador. Per conèixer com configurar el servei en cadascun dels entorns en cas de necessitat.

Documents i Fonts de Referència

[1] Jakarta Commons Net http://jakarta.apache.org/commons/index.html

Glossari

FTP
File Transfer Protocol (Protocol de Transferència d'Arxius), es tracta d'un protocol de transferència de fitxers entre computadors.

Stream
La traducció de l'anglès significa: corrent, fluxe, fluir... S'asocia el terme a un fluxe de dades sense suport físic (fitxer).

Download
Rebre arxius des d'un servidor.

Upload
Enviar arxius a un servidor.

Socket
Port del servidor habilitat per realitzar la transferència de fitxers/streams mitjançant el protocol FTP.

Descripció Detallada

Arquitectura i Components

canigo ofereix una arquitectura d'ús del protocol FTP totalment deslligada de qualsevol implementació.

Els components podem classificar-los en:

  1. Interfícies i Components Genèrics. Interfícies del servei i components d'ús general amb independència de la implementació escollida.
  2. Implementació de les interfícies basada en Jakarta Commons Net.
    Es pot trobar tota la documentació JavaDoc y el codi font referent aquests components a les següents urls:

JavaDoc: http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-ftp/apidocs/index.html
Codi Font:  http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-ftp/xref/index.html

Instal- lació i Configuració

Instal- lació

La instal- lació del servei requereix de la utilització de la llibreria 'canigo-services-ftp' i les dependències indicades a l'apartat 'Introducció - Versions i Dependències'.

Per la instal- lació del component Jakarta Commons Net, no es requereix res a part del JAR indicat en la dependència "Jakarta Commons Net".

Configuració

La configuració del Servei de FTP implica:

  1. Definir el servei.

Definició del Servei



La definició del servei requereix configurar un bean amb un identificador (es recomana usar 'ftpService' ) i els següents atributs:

Atributs:

Atribut Requerit Descripció
class Implementació concreta del servei a utilitzar.

Opcions:
# net.gencat.ctti.canigo.services.ftp.impl.FtpClient

També es poden configurar les següents propietats:

Propietat Requerit Descripció
logService No Referència a la definició del servei de traces.

Exemple:

...
<!- FTP service ->
<bean id="ftpService" class="net.gencat.ctti.canigo.services.ftp.impl.FtpClient">
<property name="logService">
<ref local="loggingService"/>
</property>
</bean>

...
<bean id="loggingService" class="net.gencat.ctti.canigo.services.logging.log4j.
Log4JServiceImpl" init-method="init">
...
</bean>
...











Utilització del Servei

Realitzar connexió

Tal i com s'ha comentat a l'apartat 'Arquitectura i Components' les classes principals per realitzar transferències de fitxers/streams es troben al package 'net.gencat.ctti.canigo.services.ftp'.

Per realitzar una connexió seguirem un patró com el mostrat en el següent exemple:

if (this.ftpClient != null ) {
    ftpClient.login(userName, password, host);
}








Realitzarem doncs els següents passos:


  1. En primer lloc comprovarem que s'ha realitzat correctament la injecció a la nostra classe del servei de FTP.
  2. Seguidament cridem al mètode login() per realitzar la connexió i l'autentificació al servidor remot mitjançant el protocol FTP.

Es crida el mètode login() que amb els següents paràmetres:

ordre Requerit Tipus Descripció
1 Si String Nom de l'usuari de connexió mitjançant el protocol FTP.
2 Si String Contrasenya de l'usuari de connexió mitjançant el protocol FTP.
3 String Nom del servidor remot (o IP) on es vol obrir una connexió mitjançant el protocol FTP.

El mètode retorna un valor booleà (TRUE/FALSE) indicant si la connexió s'ha realitzat correctament.

Realitzar desconnexió del servidor FTP

Per realitzar la desconnexió del servidor FTP seguirem un patró com el mostrat en el següent exemple:

if (this.ftpClient != null ) {
    if (ftpClient.isConnected()) {
        ftpClient.disconnect();
    }
}








Realitzarem doncs els següents passos:


  1. En primer lloc comprovarem que s'ha realitzat correctament la injecció a la nostra classe del servei de FTP.
  2. Realitzar la desconnexió del servidor FTP cridant al mètode disconnect().

Realitzar download d'un fitxer

Per realitzar un download d'un fitxer seguirem un patró com el mostrat en el següent exemple:

if (this.ftpClient != null ) {
    ftpClient.login(userName, password, host);
    if (ftpClient.isConnected()) {
        InputStream stream = ftpClient.retrieveFileStream(serverFile);
        ftpClient.disconnect();
    }
}








Realitzarem doncs els següents passos:


  1. En primer lloc comprovarem que s'ha realitzat correctament la injecció a la nostra classe del servei de FTP.
  2. Seguidament cridem al mètode login() per realitzar la connexió i l'autentificació al servidor remot mitjançant el protocol FTP.
  3. En cas de que la connexió al servidor remot s'hagi realitzat correctament es crida al mètode retrieveFileStream() per realitzar el download del fitxer del servidor remot.
  4. Realitzar la desconnexió del servidor FTP.

Es crida el mètode retrieveFileStream() que amb els següents paràmetres:

ordre Requerit Tipus Descripció
1 Si String Nom del fitxer del servidor remot que es vol realitzar el download.

I retorna:

ordre Requerit Tipus Descripció
1 Si InpuStream Variable InputStream amb les dades del fitxer remot

Realitzar upload d'un fitxer

Per realitzar un upload d'un fitxer seguirem un patró com el mostrat en el següent exemple:

if (this.ftpClient != null ) {
    ftpClient.login(userName, password, host);
    if (ftpClient.isConnected()) {
        ftpClient.storeFile(serverFileUploaded, input);
        ftpClient.disconnect();
    }
}








Realitzarem doncs els següents passos:


  1. En primer lloc comprovarem que s'ha realitzat correctament la injecció a la nostra classe del servei de FTP.
  2. Seguidament cridem al mètode login() per realitzar la connexió i l'autentificació al servidor remot mitjançant el protocol FTP.
  3. En cas de que la connexió al servidor remot s'hagi realitzat correctament es crida al mètode storeFile() per realitzar el upload del fitxer del servidor local al servidor remot.
  4. Realitzar la desconnexió del servidor FTP.

Es crida el mètode storeFile() que amb els següents paràmetres:

ordre Requerit Tipus Descripció
1 Si String Nom del fitxer del servidor remot on es vol deixar el fitxer.
2 Si InputStream Variable amb les dades a pujar al servidor FTP

Obtenir la llista de noms dels fitxers del servidor remot

Per obtenir la llista de fitxers en una carpeta d'un servidor remot seguirem un patró com el mostrat en el següent exemple:

if (this.ftpClient != null ) {
    ftpClient.login(userName, password, host);
    if (ftpClient.isConnected()) {
        String[] listFileNames = ftpClient.listNames(serverFolder)
    }
}








Realitzarem doncs els següents passos:


  1. En primer lloc comprovarem que s'ha realitzat correctament la injecció a la nostra classe del servei de FTP.
  2. Seguidament cridem al mètode login() per realitzar la connexió i l'autentificació al servidor remot mitjançant el protocol FTP.
  3. En cas de que la connexió al servidor remot s'hagi realitzat correctament es crida al mètode listNames() per obtenir una llista de fitxers en una carpeta d'un servidor remot.

Es crida el mètode listNames() que amb els següents paràmetres:

ordre Requerit Tipus Descripció
1 String Nom de la carpeta del servidor remot d'on es vol obtenir la llista de fitxers.

El mètode retorna un array de Strings amb els noms dels fitxers de la carpeta del servidor remot.

Executar una comanda FTP

Per executar una comanda FTP seguirem un patró com el mostrat en el següent exemple:

if (this.ftpClient != null ) {
    ftpClient.login(userName, password, host);
    if (ftpClient.isConnected()) {
        String line = ftpClient.sendCommand(command);
    }
}








Realitzarem doncs els següents passos:


  1. En primer lloc comprovarem que s'ha realitzat correctament la injecció a la nostra classe del servei de FTP.
  2. Seguidament cridem al mètode login() per realitzar la connexió i l'autentificació al servidor remot mitjançant el protocol FTP.
  3. En cas de que la connexió al servidor remot s'hagi realitzat correctament es crida al mètode sendCommand() per executar una comanda FTP en el servidor remot..

Es crida el mètode sendCommand() que amb els següents paràmetres:

ordre Requerit Tipus *Descripció*
1 String Comanda FTP a executar.

El mètode retorna un String el resultat de l'execució de la comanda FTP en el servidor remot.

Exemples

Tests Unitaris

Un exemple d'utilització del servei de FTP són els tests unitaris, a on s'obté el bean del servei a partir del fitxer de definició (applicationContext.xml) i s'envia un log amb les sortides especificades.

S'ha de tenir en compte que s'ha de disposar d'accés a un servidor remot mitjançant el protocol FTP per poder realitzar els tests.

package net.gencat.ctti.canigo.services.ftp.test;

...

La utilització del servei es independent de la configuració del mateix. Així, un possible fitxer de configuració del servei seria:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- FTP service  -->
    <bean id="ftpService" class="net.gencat.ctti.canigo.services.ftp.impl.FtpClient">
        <property name="logService">
            <ref bean="loggingService" />
        </property>
    </bean>


    <!-- LOGGING service  -->
    <bean id="loggingService" class="net.gencat.ctti.canigo.services.logging.log4j.
    Log4JServiceImpl" init-method="init">
        <property name="configurator">
            <ref local="loggingConfigurator"/>
        </property>
    </bean>

    <!-- configurator bean -->
    <bean id="loggingConfigurator" class="net.gencat.ctti.canigo.services.logging.log4j.xml.
    HostDOMConfigurator">
        <property name="configFileName">
            <value>classpath:log4j-test.xml</value>
        </property>
    </bean>

</beans>






...

    public void testConnectAndLogin() throws Exception {
        FtpClientIF ftpClient = getFtpService();

        assertTrue(ftpClient.login(userName, password, host));

        if (ftpClient.isConnected()) {
            ftpClient.disconnect();
        }

        if (getLogService() != null) {
            getLogService().getLog(this.getClass().getName()).debug("End
            testConnectAndLogin()");
        }
    }


    public void testConnectFailed() throws Exception {
        password    = "incorrectPasword";

        FtpClientIF ftpClient = getFtpService();

        assertFalse(ftpClient.login(userName, password, host));

        if (ftpClient.isConnected()) {
            ftpClient.disconnect();
        }

        if (getLogService() != null) {
            getLogService().getLog(this.getClass().getName()).debug("End
            testConnectFailed()");
        }
    }


    public void testSendRawCommand() throws Exception {
        FtpClientIF ftpClient = getFtpService();
        ftpClient.login(userName, password, host);

        int returnCodeOk     = 257;
        int returnCode         = ftpClient.sendCommand("PWD");

        if (ftpClient.isConnected()) {
            ftpClient.disconnect();
        }

        assertEquals(returnCodeOk, returnCode);
        if (getLogService() != null) {
            getLogService().getLog(this.getClass().getName()).debug("End
            testSendRawCommand()");
        }
    }


    public void testListFileNames() throws Exception {
        FtpClientIF ftpClient = getFtpService();
        ftpClient.login(userName, password, host);

        String[] listFileNames = ftpClient.listNames(serverFolder);

        if (ftpClient.isConnected()) {
            ftpClient.disconnect();
        }

        assertTrue(0<listFileNames.length);
        if (getLogService() != null) {
            getLogService().getLog(this.getClass().getName()).debug("End
            testListFileNames()");
        }
    }


    public void testUploadFile() throws Exception {
        FtpClientIF ftpClient = getFtpService();
        ftpClient.login(userName, password, host);

        InputStream input;
        input = new FileInputStream(localFolder+localFile);

        String serverFileUploaded = localFile;
        ftpClient.storeFile(serverFileUploaded, input);

        input.close();

        // validate file at server
        boolean existsAtServer = false;
        String[] listFileNames = ftpClient.listNames(serverFolder);

        if (listFileNames != null) {
            for (int i=0; i<listFileNames.length; i++) {
                String fileName = (String)listFileNames[i];
                if (localFile.equals(fileName)) {
                    existsAtServer = true;
                    break;
                }
            }
        }

        if (ftpClient.isConnected()) {
            ftpClient.disconnect();
        }

        assertTrue(existsAtServer);

        if (getLogService() != null) {
            getLogService().getLog(this.getClass().getName()).debug("End
            testUploadFile()");
        }
    }


    public void testDownloadFile() throws Exception {
        FtpClientIF ftpClient = getFtpService();
        ftpClient.login(userName, password, host);

        InputStream inputStream = ftpClient.retrieveFileStream(serverFile);
        byte[] buf = new byte[4096];
        FileOutputStream fileDownload = new FileOutputStream(localFolder+serverFile);
        for (int len=-1;(len=inputStream.read(buf))!=-1;) {
            fileDownload.write(buf,0,len);
        }
        fileDownload.flush();


        if (ftpClient.isConnected()) {
            ftpClient.disconnect();
        }

        File file = new File(localFolder+serverFile);
        assertTrue(file.exists());
        if (getLogService() != null) {
            getLogService().getLog(this.getClass().getName()).debug("End
            testDownloadFile()");
        }
    }


    private FtpClientIF getFtpService() {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        FtpClientIF ftpClient = (FtpClientIF)beanFactory.getBean("ftpService");
        logService = ftpClient.getLogService();

        assertNotNull(ftpClient);
        return ftpClient;
    }
...