domingo, 18 de agosto de 2019

Enviar correos con GMAIL(2). Simple Java Mail (Recomendable)

0.Introducción

Justo después de realizar la entrada anterior, me encuentro con Simple Java Mail que es muy simple de utilizar, así que vamos a utilizar esta librería.

1. Dependencia maven


Es la siguiente;


1
2
3
4
5
6
    <!-- https://mvnrepository.com/artifact/org.simplejavamail/simple-java-mail -->
    <dependency>
      <groupId>org.simplejavamail</groupId>
      <artifactId>simple-java-mail</artifactId>
      <version>5.3.0</version>
    </dependency>

2. Abrir la seguridad Google a aplicaciones no seguras

Hay que ir a este enlace: https://myaccount.google.com/lesssecureapps y desactivar la opción de seguridad. Tal como se vió en la entrada anterior

3. Verificar que el certificado del smtp.google.com está en el cacert

Esto se vió en la entrada anterior y se basa en  My Tech Notes


4. Ver esta simple clase de Java



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package u.requests;

import org.simplejavamail.email.Email;
import org.simplejavamail.email.EmailBuilder;
import org.simplejavamail.mailer.MailerBuilder;
import org.simplejavamail.mailer.config.TransportStrategy;

public class SendMail {

 public static void main(String[] args) {
  Email email = EmailBuilder.startingBlank()
   .from("Ximo Dante", "ximodante@gmail.com")
   .to("edu"  , "eduxxxxx@gmail.com")
   .to("kevin", "kevinxxx@gmail.com")
   .withSubject("Using Simple Java Mail 01")
   .withPlainText("Edu, Kevin. I am using Simple Java Mail API...!!!")
   .buildEmail();

  MailerBuilder
   .withSMTPServer("smtp.gmail.com", 587, "ximodante@gmail.com", "mypassword")   .withTransportStrategy(TransportStrategy.SMTP_TLS)
   // or
   //.withSMTPServer("smtp.gmail.com", 465, "your user", "your password")
   //.withTransportStrategy(TransportStrategy.SMTPS);
   .buildMailer()
   .sendMail(email);
 }

}

Y a funcionar!!!!!

Enviar correos con GMAIL(1). Java Mail (No recomendable)

0. Introducción.


Aunque parece sencillo enviar e-mails mediante Java y Gmail, hay algunos problemas tontos que solucionar.

El primero es seleccionar las librerías de "javamail". Parece ser que "javamail" ahora cambian de nombre a "jakarta.mail".

El segundo es el tema de la configuración del protocolo  "smtp" de gmail, que se puede utilizar mediante protocolos TLS y SSL, mediante puertos diferentes (587 y 465 respectivamente), y cada cual requiere unas propiedades de configuración.

El tercero es que hay que ir a este enlace: https://myaccount.google.com/lesssecureapps y desactivar la opción de seguridad.

El cuarto es que el almacén de certificados del java que estemos utilizando debe de tener el certificado del smtp de google para confiar en él.

1. Definir las dependencias en el fichero pom de maven

Hay que tener en cuenta que en mi caso estoy utilizando un jdk 10, por tanto es prudente adjuntar las librerías que a partir del jdk 9 dejan de ser incluidas.

Se ha marcado con fondo amarillo las dependencias críticas que son las de jakarta.mail y las no incluidas en los JDKs posteriores a 1.8.


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>ximodante</groupId>
  <artifactId>DecretsScheduler01</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>DecretsScheduler01</name>
  <description>Scheduler for updating SOFTPROP decret number and GEXFLOW doc description</description>
  
  <properties>
    <lombok.version>1.18.8</lombok.version>
    <h2.version>1.4.196</h2.version>
    <hibernate.version>5.2.16.Final</hibernate.version>
    <jaxb.version>2.3.0</jaxb.version>
    <activation.version>1.2.0</activation.version>
    
        
    <failOnMissingWebXml>false</failOnMissingWebXml>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!-- Java version-->
    <java.version>10</java.version>
  </properties>  
    
  <dependencies>
   <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok.version}</version>
      <scope>provided</scope>
    </dependency>
    
    <!--  Apache commons string utils .. -->
    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.7</version>
    </dependency>
    
    <!-- Apache commons bean utils-->
    <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
    <dependency>
      <groupId>commons-beanutils</groupId>
      <artifactId>commons-beanutils</artifactId>
      <version>1.9.3</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>42.2.6</version>
    </dependency>  
     
       
    
    <!-- JPA 2.1 Provider -->
    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
    
    <!-- Envers for auditing the database -->
    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-envers -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-envers</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
    
    <!-- Hibernate Testing !!! -->
    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-testing -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-testing</artifactId>
      <version>${hibernate.version}</version>
      <scope>test</scope>
    </dependency>
    
    <!-- 2018-05 Hibernate Bean Validator Optional Doesn't follow the same versioning !!-->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.10.Final</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/com.sun.mail/jakarta.mail -->
    <dependency>
      <groupId>com.sun.mail</groupId>
      <artifactId>jakarta.mail</artifactId>
      <version>1.6.3</version>
    </dependency>

 
    <!--BEGIN Java 9 references to JEE  not included in JDK9-->
    <!-- 2017-07 JAXB API -->
    <!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
    <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <version>${jaxb.version}</version>
    </dependency>
    
    <!-- 2017-08 JAXB Implementation -->
    <!-- https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-impl -->
   
    <dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-impl</artifactId>
      <version>${jaxb.version}</version>
    </dependency>
        
    <!-- 2017-08 Old JAXB Core -->
    <!-- https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-core -->
    <dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-core</artifactId>
      <version>${jaxb.version}</version>
    </dependency>
    
    <!-- 2017-09-06 Activation API -->
    <!-- https://mvnrepository.com/artifact/javax.activation/javax.activation-api -->
    <dependency>
      <groupId>javax.activation</groupId>
      <artifactId>javax.activation-api</artifactId>
      <version>${activation.version}</version>
    </dependency>

    <!-- 2017-09-06 Activation Implementation -->
    <!-- https://mvnrepository.com/artifact/com.sun.activation/javax.activation -->
    <dependency>
      <groupId>com.sun.activation</groupId>
      <artifactId>javax.activation</artifactId>
      <version>${activation.version}</version>
    </dependency>

    <!-- END Java 9 references to JEE  not included in JDK9-->
    
    
    
  </dependencies>
  
</project>


2. Definir las propiedades para utilizar el servidor de GMAIL

Como hemos dicho se pueden definir el acceso por TLS y SSL , para ello hemos creado un fichero de propiedades en

src/mail/rsources/properties/mail.properties

donde se muestran las 2 configuraciones. Basta con comentar la que no se desee. En este caso está activada la opción 1.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#For Google Mail GMail
username=myuser@gmail.com
password=mypassword

#OPTION 01
#--BEGIN If use TLS
mail.smtp.host=smtp.gmail.com
mail.smtp.port=587
mail.smtp.auth=true
mail.smtp.starttls.enable=true
#--END If use TLS

#OPTION 02
#--BEGIN If use SSL
#mail.smtp.ssl.enable=true
#mail.smtp.host=smtp.gmail.com
#mail.smtp.ssl=true
#mail.smtp.port=465
#mail.smtp.auth=true
#mail.smtp.socketFactory.port=465;
#mail.smtps.quitwait=false
#mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory;
#--END If use SSL

3. Abrir la seguridad Google a aplicaciones no seguras

Hay que ir a este enlace: https://myaccount.google.com/lesssecureapps y desactivar la opción de seguridad.

4. Agregar el certificado de smtp.gmail.com al almacén de certificados del java a utilizar

Hay que tener en cuenta que si se cambia el Java a ejecutar el programa, se tendrá que volver a instalar el certificado.

Hay quien dice que una vez instalado el certificado en el alamcén de certificados, se puede copiar este fichero "cacerts" al nuevo java.

Veamos los pasos a seguier según My Tech Notes:

1. Capturar el certificado de smtp.gmail.com


1
2
3
4
5
6
7
8
#For Linuix and Mac
openssl s_client -connect smtp.gmail.com:465


#For Windows
#Install openssl first
#Run command:
s_client -connect smtp.gmail.com:465


2. Copiar en un fichero (por ejemplo gmail.cert) el contenido entre "-----BEGIN CERTIFICATE-----" y "-----END CERTIFICATE-----" inclusive


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
-----BEGIN CERTIFICATE-----
MIIFiTCCBHGgAwIBAgIQAiKavL3Byn4CAAAAAD7OozANBgkqhkiG9w0BAQsFADBC
MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZpY2VzMRMw
EQYDVQQDEwpHVFMgQ0EgMU8xMB4XDTE5MDcyOTE3MzA0NVoXDTE5MTAyNzE3MzA0
NVowaDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcT
DU1vdW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBMTEMxFzAVBgNVBAMTDnNt
dHAuZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvarO
2yqbcJ3AJRFMqP5PXmWbSTQz3E+MwgUKxeK0HH6JM9AB/WzkRxNiRy87tZyJXHRw
J2LBed5ZbLHCf8XC+A4ZZFjyz58/Vd63ix1g7+j7xwYNFynUcmDUYKbTsTYY1dSS
IP4tnLFDQUgcm0QogGu29DKWydTQ13d0PV71ggYPwALffoFjzg4sJ+F5aLGklChs
H6UDDkUbCe6bL+Xd52NX7I9j+jABkDsBAnMRnqWToZ8oGLWP6WPR5qi6BR31X//q
QwJwtV5kYL+Ga5/NDhy2vr4FNp9j479scYoaqAHakfaerwQecDGSgNvhWMgVHvzP
fmUH/Wbw19XrKj+1XwIDAQABo4ICUzCCAk8wDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFIjG4Xk2Ljbc
/tW/gDBpSAspoVomMB8GA1UdIwQYMBaAFJjR+G4Q68+b7GCfGJAboOt9Cf0rMGQG
CCsGAQUFBwEBBFgwVjAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AucGtpLmdvb2cv
Z3RzMW8xMCsGCCsGAQUFBzAChh9odHRwOi8vcGtpLmdvb2cvZ3NyMi9HVFMxTzEu
Y3J0MBkGA1UdEQQSMBCCDnNtdHAuZ21haWwuY29tMCEGA1UdIAQaMBgwCAYGZ4EM
AQICMAwGCisGAQQB1nkCBQMwLwYDVR0fBCgwJjAkoCKgIIYeaHR0cDovL2NybC5w
a2kuZ29vZy9HVFMxTzEuY3JsMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHUAY/Lb
zeg7zCzPC3KEJ1drM6SNYXePvXWmOLHHaFRL2I0AAAFsPv/GPwAABAMARjBEAiBV
Sx+FOZIHrNFGeFn+FzijD+cAxQGNs4ZBtse46eqYhwIgF5ik74oyQtJcHDIiUNNH
k3KO/uf8ybdf+w4dqwxFN0kAdgB0ftqDMa0zEJEhnM4lT0Jwwr/9XkIgCMY3NXnm
EHvMVgAAAWw+/8YbAAAEAwBHMEUCICbDyQ2QAmzHNU9UvMfQcrKx0zNKod1rrD4l
asqF9KwxAiEAh5xlln8LCxVgrCrzSUJ53OhVOPSFpwtSckOCj4zSul8wDQYJKoZI
hvcNAQELBQADggEBAMRgqtLuioioRY90BaDyofKqPuH//hv6Au6R1f5FMeQIfZbC
ADpeFenRqBAR/a522YrawKfrI2SLQQquVUPNZWQWrmZCPpkJWhBg2FuxZlezsRO7
KEdOAuf2RmHISJuXj8SAxCpAsUw3w8ReyNdLa7DFRE/uhMDXgyGynWp7yJcUdFoa
itQfEo2Uf3dbTfVuqvHMi2FCrY/+Tn9+l2tj/XhERnGQ9akB/gmHruOrpgMcWZH/
XdRl7bJqxsQPItOB8l1AH/y7+dn4hFbf7AO/tf4tZQmzD0kBUPCixt/5GA5qSMU6
cRY2a+xRS1gs3twpkqscIFyoY1ttEyEQIHDjoLM=
-----END CERTIFICATE-----

3. Averiguar la ruta del java utilizado. Puede ser la misma del $JAVA_HOME. Incluir este certificado en el cacert


1
2
3
4
5
#Si el JDK es el mismo del $JAVA_HOME
sudo keytool -import -alias smtp.gmail.com -keystore $JAVA_HOME/lib/security/cacerts -file /ruta/al/fichero/gmail.cert

#Si el JDK no es el mismo del $JAVA_HOME
sudo keytool -import -alias smtp.gmail.com -keystore /ruta/al/jdk/lib/security/cacerts -file /ruta/al/fichero/gmail.cert

Nos preguntará el password del cacert que suele ser "changeit"
y contestar a Trust this certificate? [no]:  yes

5. Programa fuente para ejecutar

Hay muchas opciones mas simples como la de Dileep Guntanadugu, que son mas fáciles de entender. Pero ahí va mi versión.

Aquí está una simple propuesta para enviar un correo. El problema puede aparecer al utilzar las clases que se sirven para acceder al fichero de propiedades.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package openadmin.utils;

import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class MailUtils {

 public static Properties getEmailProperties(boolean isExecutedFromJar) throws Exception {
  if (isExecutedFromJar) return PropertiesUtils.getRelativeFromJarFileProperties("mail.properties");
  else return PropertiesUtils.getRelativeProperties("properties/mail.properties");
 } 
 
 public static Session getMailSession(Properties mailProps) {
  return Session.getInstance(
   mailProps,
            new javax.mail.Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
     return new PasswordAuthentication(
      mailProps.getProperty("username"),
                        mailProps.getProperty("password"));
                }
            });
 };
 
 
 public static Message getEmailMessage(Session mailSession, String fromEmail, String toCommaEmails, String subject, String text ) throws AddressException, MessagingException {
  Message message = new MimeMessage(mailSession);
        
  message.setFrom(new InternetAddress(fromEmail));
        message.setRecipients(
                Message.RecipientType.TO,
                
                InternetAddress.parse(toCommaEmails)
        );
        
        message.setSubject(subject);
        
        message.setText(text);        
        return message;
 };
 
 
 
 
 public static void sendEmail(Message message) throws MessagingException  {
  Transport.send(message);
 }
 
  

 /******************************************************************************
  * MAIN METHOD
  ******************************************************************************/
 public static void main(String args[]) {
  boolean isExecutedFromJar=false;
  try {
   Properties mailProps=getEmailProperties(isExecutedFromJar);
   Session mailSession=getMailSession(mailProps);
   
   Message mailMessage=getEmailMessage(mailSession, "ximodante@gmail.com", "destination01@gmail.com, destination02@gmail.com", "PRUEBA N-SIMA DE email ximodante", "This is the content of the n-esimo email de Ximo Dante...");
   
   
   sendEmail(mailMessage);
   
  } catch(Exception e) {
   e.printStackTrace();
  }
 }


6. Clases para acceder al fichero de propiedades


Se adjunta la clase PropertiesUtils y FileUtils para acceder al fichero de propiedades:


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package openadmin.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;

import java.util.Properties;

public class PropertiesUtils {
 
 // Valid only in web
 public static String ResourcesPath=Thread.currentThread().getContextClassLoader().getResource("").getPath();
 
 
 /**
  * The properties path is referred relatively to resources path
  * @param filePath
  * @return
  * @throws FileNotFoundException
  * @throws IOException
  */
 public static Properties getRelativeProperties(String filePath) throws FileNotFoundException, IOException {
  Properties properties = new Properties();
       
  //String path = ResourcesPath + filePath;
  //System.out.println("PROPERTIES.PATH=" + path);
  //properties.load(new FileInputStream(path));
  
  
  System.out.println(filePath);
  //URL url = ClassLoader.getSystemResource("/resources/" + filePath);
  //URL url = ClassLoader.getSystemResource("/src/main/resources/" + filePath);+
  URL url = ClassLoader.getSystemResource(filePath);
  System.out.println(url);
  properties.load(url.openStream());
  
  
  for(Object o: properties.keySet()) System.out.println(o.toString() + "-->"+ properties.getProperty(o.toString()));
  
  return properties;
 }
 
 /**
  * Leemos la propiedades de una ruta absoluta.
  * OJO: Si queremos leer un fichero de propiedades "file.properties" que se encuentra 
  *      en la misma carpeta que el proyecto (o el jar ejecutable) :
  *   
  *      final File f = new File(PropertiesUtils.class.getProtectionDomain().getCodeSource().getLocation().getPath());
  *      
  *      String suffix=File.separator +"..";
     *      //myFile=f.getAbsolutePath()+ "/../../../file.properties" ;
     *      myFile=f.getAbsolutePath()+ StringUtils.repeat(suffix, 3) + File.separator + file.properties" ;
     *      Properties myProps=PropertiesUtils.getAbsoluteProperties(myFile);
     *      
  * @param filePath
  * @return
  * @throws FileNotFoundException
  * @throws IOException
  */
 public static Properties getAbsoluteProperties(String filePath) throws FileNotFoundException, IOException {
  Properties properties = new Properties();
       
  //String path = ResourcesPath + filePath;
  System.out.println(filePath);
  properties.load(new FileInputStream(filePath));
  
  
  
  
  for(Object o: properties.keySet()) System.out.println(o.toString() + "-->"+ properties.getProperty(o.toString()));
  
  return properties;
 }
 
 /**
  * Gets the folder where resides the executable jar
  * @return
  */
 /**
 public static String getJarFolderPath() {
  
  final File f = new File(PropertiesUtils.class.getProtectionDomain().getCodeSource().getLocation().getPath());
     String suffix=File.separator +"..";
     //jarFolder=f.getAbsolutePath()+ "/../../../"file.properties" ;
     String jarFolder=f.getAbsolutePath()+ StringUtils.repeat(suffix, 3) + File.separator;
     return jarFolder;
 }
 */
 public static Properties getRelativeFromJarFileProperties(String filePath) throws Exception {
  Properties properties = new Properties();
  
  //String myPath=getJarFolderPath()+filePath;
  String myPath=FileUtils.getJarContainingFolder() + File.separator + filePath;
  System.out.println(myPath);
  //System.out.println(PropertiesUtils.class.getResource(PropertiesUtils.class.getSimpleName() + ".class").getPath());
  //System.out.println(System.getProperty("user.dir"));
  properties.load(new FileInputStream(myPath));
  
  for(Object o: properties.keySet()) System.out.println(o.toString() + "-->"+ properties.getProperty(o.toString()));
  
  return properties;
 }
 
 
 public static void saveAbsoluteProperties(Properties properties, String filePath, String comments) throws FileNotFoundException, IOException {
  //String path = ResourcesPath + filePath;
  properties.store(new FileOutputStream(filePath), comments);
 }
 
 public static void saveRelativeFromJarFileProperties(Properties properties, String filePath, String comments) throws Exception {
  String myPath=FileUtils.getJarContainingFolder() + File.separator + filePath;
  System.out.println(myPath);
  properties.store(new FileOutputStream(myPath), comments);
 }
 
 public static void main(String[] args) throws FileNotFoundException, IOException{
  //String myFile="properties/last_fac.properties";
  String myFile="properties/application.properties";
  Properties myProps=getRelativeProperties(myFile);
  myProps.setProperty("A", "9999.888");
  myProps.setProperty("J", "1111.222");
  //for(Object o: myProps.keySet()) System.out.println(o.toString() + "-->"+ myProps.getProperty(o.toString()));
  //String myFile2="/home/eduard/last_fac2.properties";
  //saveAbsoluteProperties (myProps,myFile2, "ultima actualizacion");
  //myFile="../kk.properties";
        
         /*
               ws/pr/tg/cl/file  
         myFile="../../../../kk.properties";
         Properties myProps1=getRelativeProperties(myFile);
         System.out.println(myProps1.getProperty("prop2"));
         */
  /*
      System.out.println("-------------------------");
         //myFile="kk.properties";
      myFile="";
         String prefix="../";
         for (int i=0; i<15; i++) {
          System.out.println(myFile);
          URL url = ClassLoader.getSystemResource(myFile);
          System.out.println(url);
          System.out.println("-------------------------");
          myFile=prefix+myFile;
         }
        */
  /*
        final File f = new File(PropertiesUtils.class.getProtectionDomain().getCodeSource().getLocation().getPath()); 
        System.out.println(f.getAbsolutePath());
        myFile=f.getAbsolutePath();
        String sufix="/..";
        for (int i=0; i<15; i++) {
      System.out.println(myFile);
      URL url = new URL("file:"+myFile);
      System.out.println(url);
      url=new URL("file:"+myFile+"/kk.properties");
      System.out.println(url);
      System.out.println("-------------------------");
      myFile=myFile+sufix;
     }
     */
  System.out.println("----------------------------------------------");
  final File f = new File(PropertiesUtils.class.getProtectionDomain().getCodeSource().getLocation().getPath()); 
        System.out.println(f.getAbsolutePath());
        String suffix=File.separator + "..";
        myFile=f.getAbsolutePath();
        for (int i=0; i<10; i++) {
      System.out.println(myFile);
      String propFile=myFile + File.separator + "kk.properties";
      System.out.println(propFile);
      try {
       Properties myPrp=getAbsoluteProperties(propFile);
       System.out.println(myPrp.getProperty("prop1"));
      } catch (Exception e) {
       e.printStackTrace();
      }
      System.out.println("-------------------------");
      myFile=myFile+suffix;
     }
  
 }
}




 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package openadmin.utils;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.net.URLDecoder;
import java.security.CodeSource;

public class FileUtils {

 /**
  * Folder that contains jar file or folder that contains project folder
  * @return
  * @throws Exception
  */
 public static String getJarContainingFolder() throws Exception {
  Class<?> aClass=MethodHandles.lookup().lookupClass();
  CodeSource codeSource = aClass.getProtectionDomain().getCodeSource();

  File jarFile;

  if (codeSource.getLocation() != null) {
   jarFile = new File(codeSource.getLocation().toURI());
  } else { // It is not a jar file
   String path = aClass.getResource(aClass.getSimpleName() + ".class").getPath();
      String jarFilePath = path.substring(path.indexOf(":") + 1, path.indexOf("!"));
      jarFilePath = URLDecoder.decode(jarFilePath, "UTF-8");
      jarFile = new File(jarFilePath);
  }
  String s=jarFile.getParentFile().getAbsolutePath();
  System.out.println("S------>:" + s);
  if (s.endsWith(File.separator+"target")) { // Maven target directory for compiled classes
   s=s.substring(0, s.lastIndexOf(File.separator));
   s=s.substring(0, s.lastIndexOf(File.separator));
  } 
  return s;
 }
 
 public static byte[] readFile(String fileName) throws IOException {
  File file = new File(fileName);//filename should be with complete path
  FileInputStream fis = new FileInputStream(file);
  byte[] b = new byte[ (int) file.length()];
  fis.read(b);
  fis.close();;
  return b;
 }
 
 public static void writeToFile(String fileName, String myString) throws IOException {
  BufferedWriter writer = new BufferedWriter( new FileWriter( fileName));
  writer.write( myString);
  // do stuff 
  writer.close();
 }
 
 /**
  * test in main class
  * @param args
  */
 public static void main(String[] args) {
  try {
   System.out.println(getJarContainingFolder());
   System.out.println(System.getProperty("java.class.path"));
   System.out.println(System.getProperty("user.dir"));
   byte[] b=readFile("/home/eduard/Audit.0.log");
   String sb=new String(b);
   System.out.println(sb);
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }

}





martes, 13 de agosto de 2019

Utilizar SAMBA con Java (I). Librería smbj

0. Introducción

Si tenemos ya montado un directorio compartido mediante SAMBA en un sistema Windows, tal como se dice un post anterior (donde se comentan las dificultades que hay),  debería ser una cosa fácil el poder acceder a un recurso remoto, dándole la ruta de montaje sobre el sistema local

/mnt/carpeta_montaje_compartida


O por contra acceder al


smb://miusuario:mipassword@192.168.xxx.xxx/micarpeta 


Pero tenemos  algunos problemas:



Si utilizamos la primera opción (la de la ruta de montaje), de momento solo he podido tener acceso a lectura pero no a escritura.


La segunda opción no he podido hacerla funcionar en Java. Por tanto recurro a la librería smbj que parece ser compatible con SMB2/SMB3.


Para ello los pasos a seguir son:

1. Crear un recurso (carpeta) compartido en un servidor windows. 
2. Conocer:
  2.1 IP del servidor Windows (por ejemplo 192.168.2.2)
  2.2 Nombre del recurso compartido (por ejemplo micarpeta)
  2.3 Nombre del dominio (por ejemplo CORPORACION)
  2.4 Usuario y contraseña ara acceder al recurso (por ejemplo miusuario y micontraseña)
  2.5 Una carpeta dentro de esa carpeta compartida (por ejemplo misubcarpeta)
3. Incluir las dependencias de Maven
4. Crear los siguientes objetos de tipo:
  4.1 SmbConfig donde se puede dar los parámetros de tiempo de conexion.
  4.2 Connection para acceder a la IP del servidor
  4.3 AuthenticationContext donde se le indica usuario, contraseña y dominio.
  4.4 Session para abrir sesión
  4.5 DiskShare para conectarse a la carpeta compartida
5. Utilizar los métodos de DiskShare para abrir ficheros ("openFile()") y otro tipo de manipulaciones de fichero o carpeta ("list()"
6. Cerrar los "Streams" utilizados y también el objeto SmbClient



1. Incluir las dependencias de Maven

Basta con añadir al pom.xml :


1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.hierynomus/smbj -->
    <dependency>
      <groupId>com.hierynomus</groupId>
      <artifactId>smbj</artifactId>
      <version>0.9.1</version>
    </dependency>

2. Ejemplo de programa simple



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
package utils;

import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
 
import com.hierynomus.msdtyp.AccessMask;
import com.hierynomus.msfscc.FileAttributes;
import com.hierynomus.msfscc.fileinformation.FileIdBothDirectoryInformation;
import com.hierynomus.mssmb2.SMB2CreateDisposition;
import com.hierynomus.mssmb2.SMB2CreateOptions;
import com.hierynomus.mssmb2.SMB2ShareAccess;
import com.hierynomus.mssmb2.SMBApiException;
import com.hierynomus.smbj.SMBClient;
import com.hierynomus.smbj.SmbConfig;
import com.hierynomus.smbj.auth.AuthenticationContext;
import com.hierynomus.smbj.connection.Connection;
import com.hierynomus.smbj.session.Session;
import com.hierynomus.smbj.share.DiskShare;
import com.hierynomus.smbj.share.File;

public class SambaUtils2 {
 
  // ONLY FOR TESTS -------------------------------------------------------
 private static final String SHARE_DOMAIN = "CORPORACION";
 private static final String SERVER_IP = "192.168.2.2";
 private static final String SHARE_USER = "miusuario";
 private static final String SHARE_PASSWORD = "micontraseña";
 private static final String SHARE_SRC_DIR = "micarpeta";
 private static final String FOLDER = "misubcarpeta";
 
 /**
  * SMB2 connection example
  *
  */

 public static void main(String[] args) {
  // Set the timeout (optional)
  SmbConfig config = SmbConfig.builder().withTimeout(120, TimeUnit.SECONDS)
   .withTimeout(120, TimeUnit.SECONDS) // Timeout sets read, write and Transact timeouts (default is 60 seconds)
   .withSoTimeout(180, TimeUnit.SECONDS) // Socket timeout (default is 0 seconds)
         .build();
  
  // If you do not set the timeout period SMBClient client = new SMBClient();
  SMBClient client = new SMBClient(config);
 
  try {
   Connection connection = client.connect(SERVER_IP); 
   AuthenticationContext ac = new AuthenticationContext(SHARE_USER, SHARE_PASSWORD.toCharArray(), SHARE_DOMAIN);
   Session session = connection.authenticate(ac);
 
   // Connect to a shared folder
   DiskShare share = (DiskShare) session.connectShare(SHARE_SRC_DIR);
   
   // List the pdf files of 
   for (FileIdBothDirectoryInformation f : share.list(FOLDER, "*.pdf")) {
                           System.out.println("File : " + f.getFileName());
                        }
   
   // Write file. Verify if exists or not
   boolean isFileAlreadyExist = false;
   File f = null;
   try {
    f = share.openFile(
     FOLDER+"/Ximo.txt", 
     new HashSet<AccessMask>(Arrays.asList(new AccessMask[]{AccessMask.MAXIMUM_ALLOWED})), 
     (Set<FileAttributes>)null, 
     SMB2ShareAccess.ALL, 
     SMB2CreateDisposition.FILE_CREATE, 
     (Set<SMB2CreateOptions>)null);
   } catch (SMBApiException var12) {
    isFileAlreadyExist = true;
    //var12.printStackTrace();
   }

   if(isFileAlreadyExist) {
    try {
     f = share.openFile(
      FOLDER+"/Ximo.txt", 
      new HashSet<AccessMask>(Arrays.asList(new AccessMask[]{AccessMask.MAXIMUM_ALLOWED})), 
      (Set<FileAttributes>)null, 
      SMB2ShareAccess.ALL, 
      SMB2CreateDisposition.FILE_OVERWRITE, 
      (Set<SMB2CreateOptions>)null);
    } catch (SMBApiException var11) {
     throw new Exception("Could not open file for write.");
    }
   }

   OutputStream outputStream = null;
   String str = "Hello World Champions";
   if(f != null) {
   outputStream = f.getOutputStream();
   outputStream .write(str.getBytes());
   outputStream .flush();
   outputStream .close();
   }
   
   
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   if (client != null) {
    client.close();
   }
  }
 }
}


Cosas a tener en cuenta:

1. Es importante cerrar los recursos abiertos.
2. Para poder escribir en un fichero abierto hay que darle la opción "SMB2ShareAccess.ALL".
3. Hay que detectar si el fichero es nuevo o si existía previamente.
4. Completar los parámetros de las máscaras de acceso.
5 ......

Hay muchos pequeños detalles que puede que te compliquen la vida. Pero de momento a mi este programita me funciona. Espero que os funcione también a vosotros.


En fin, que os sea leve!