Let's create a RESTful email sending service + client

xmax.png  Merry Christmas! It is the last day of "Java Advent Calendar 2020". I changed the title to make it more fun (?) Like Christmas. I thought it was just an introduction to the library, but it's Christmas and I'm going to use the library to create a useful web service. It explains RESTful web services from the basics, so it is recommended for anyone who wants to know this field.

The details of the library are summarized at the end of the post, so if you are interested, please have a look there first.

1. 1. Creating a mail service --- Sender class

A service that changes the address and sends the same email all at once

In other words,

Mr. ○○ Hello,···.

It is a service that sends an email to all the people on the list at once, saying that the address and title are attached and the contents are the same (the point is that the address and title are attached). Sending an email uses a library, so it only takes a few lines, and the sending service should be easy to write using the Jakarta REST mechanism. By the way, the development / execution environment of Jakarta REST can be prepared in 5 minutes using JET, so even if you are new to Jakarta REST, please try it.

(** You can download JET from here **. There are also instructions and videos on how to use it.)

On the other hand, the client program that uses the service simply creates a web screen and sends the data by POST, so it can be created in any language such as PHP and Javascript. This is one of the strengths of web services. However, Java also has a ** client API **, so there is no inconvenience even if you make it in Java.

(Please ** Download the project from here ** of the mail sending service and client program explained below. Also, the ** Important.txt ** file included in the project is SMTP. There is a note about the server settings. Be sure to read it before using it.)

It became like this

Let's look at the visible results first. This is the screen of the client that uses the service.

client004-S.png

The upper row is the area for writing the contents of the email, and the lower row is the list screen. The list uploads an Excel file. Since it is an automatic upload, just select the file and it will be uploaded and the contents will be displayed. The check box on the far left is a selection field for whether to use it as a destination. Initially, everyone is targeted, but you can uncheck unnecessary destinations.

First of all, what is RESTful web service?

RESTful web services do the job for you when you access a specific URI (almost the same as the URL) with GET or POST. There is no API-like method call, and the corresponding processing is executed by simple HTTP access. The combination of "modes" such as GET, POST, PUT, and DELETE and the URI to be accessed distinguishes what kind of work is done. At the same time as accessing, you can pass the necessary data using URL parameters and POST data.

URI is http: // server/project/service/path / Configure as follows.

For example Server: localhost: 8080 Project name: mailservice Overall service name: sendmail One service name (here, send a broadcast email): cc

If you name it, the URI looks like this:

    http://localhost:8080/mailservice/sendmail/cc/

There are text mail and HTML mail in the mail, so you can cut the subpath further and do as follows.

    http://localhost:8080/mailservice/sendmail/cc/text

This is the URI for broadcasting text mail. HTML mail

    http://localhost:8080/mailservice/sendmail/cc/html

Anyway, you should do it.

How to write to construct a URI --- Jakarta REST

This is easy because it uses Jakarta REST (= JAX-RS). First of all, there is no problem because the project name is the name of the created project. Other than that, all you have to do is add certain annotations to the classes and methods in your project. First, for the service (sendmail) part, create an empty class (any name) and add @ApplicationPath ("sendmail") as follows. Only this.

ServerConfig.java


@ApplicationPath("sendmail")
    public class ServiceConfig extends Application {	
}   

Next, for the path representing the individual service, create another class (any name is Sender class) and add @Path ("cc"). Then create a concrete service feature within this Sender class.

Sender.java


@Path("cc")
public class Sender {
    
}  

Finally, the subpath text, which adds @Path ("text") to the method (send method) that is actually in charge of processing in the Sender class. Also, since it is a method that is accessed by POST, add the @POST annotation.

Sender.java


@Path("cc")
public class Sender {
    @Path("text")
    @POST
    public Response send(DataSet data){
        
    }   
}  

With this alone, the URI corresponding to POST access,    http://localhost:8080/mailservice/sendmail/cc/text Was able to be configured. On the client side, POST access to this URI invokes the send method. All you have to do now is create the contents of the send method. How is it easy!

Data transfer

Oh yeah, I forgot to explain the arguments and return types of the send method.

(1) Argument

First, you can pass any type of object as an argument. Here, we are passing the DataSet type. The DataSet type is the class created this time, and the content includes all the data necessary for sending, such as the email title, body, and recipient list. When the client side sends this object with POST access, the server side can receive it in the argument of the send method, DataSet data. The field variables of the DataSet class are:

DataSet.java


public class DataSet {
    private String sender;              //Sender email address
    private String title;               //Email title
    private String message;             //Body or HTML text
    private String type;                // "CC" or "BCC" or "TO"
    private List<Recipient> dlist;      //List of destinations

Of the field variables, List <Recipient> dlist is the destination List. The Recipient class looks like this:

Recipient.java


public class Recipient {
    private boolean flag;   //For recording the status (use is arbitrary, such as whether to send)
    private String id;      //Number, ID, etc.
    private String name;    //name
    private String email;   //mail address
    private String mr;      //Honorific titles (sama, lord, teacher, etc.)

These objects are actually automatically converted to JSON or XML format data and passed. Therefore, clients other than the Java language can pass the same thing using JSON or XML. In other words, there are no programming language restrictions.

If you do not specify the data format to be passed, it will be decided appropriately (usually JSON), but you can specify the format to exchange with annotations. Next is the specification to use JSON or XML. @Produces specifies the format of the data returned by the send method, and @Consumes specifies the format of the data received by the send method as an argument. If you specify multiple formats like this (roughly speaking), you can handle one of them on the client side.

Sender.java


@Path("cc")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public class Sender {
    @Path("text")
    @POST
    public Response send(DataSet data){
        
    }   
}  

Oh yeah, I was inadvertent. Annotate the class with @XmlRootElement for objects that may interact in XML format. That's all it takes to automatically convert to XML (nothing needs to be done with JSON).

DataSet.java


@XmlRootElement
public class DataSet {

(2) Return type

The method usually returns an object of type Response. The Response class is a convenient all-in-one object that can contain various response headers and their values, a status that represents the execution result, an object to return to the caller, and so on. Sent according to HTTP conventions, client programs can receive them in a standard way specific to their respective languages. I'll explain how to do it in the Java language later.

Let's send an email!

The process of the send method is as simple as:

Sender.java


    @Path("/text")
    @POST
    public Response send(DataSet data) {

        EmailStatus result = semdMails(data);  //Email sending process
        reply(data,result);                    //Notify the client of the transmission result by email

        return Response
                .ok()
                .build();
    }

sendMails (data) is the part that actually sends the mail, and reply (data, result) is the part that notifies the client of the sending result (the content will be explained later). However, there is a slight problem as it is. That's because if you have dozens of emails to send, you can expect it to take tens of seconds to send. During that time, blocking will occur and you will not be able to return a response to the client. The client has to wait for a while, which makes me feel very uneasy.

This is where multithreading comes in. For the time being, while executing the transmission process asynchronously in another thread, return a message like "I will send it now" to the client with return Response .....

The sendMails method returns the execution result as a return value, so use this to notify the client of the result with the reply method. So the sendMails and reply methods are a series of inseparable processes. Therefore, there is no choice but to execute the two together in one thread ...

(1) How to write stylish asynchronous processing

By the way, do you know the ** ComputableFutuer ** class? This class was introduced from Java 8 to handle the above problems in a cool way. It's Christmas, so let's write it cool here.

Sender.java


    @Path("/text")
    @POST
    public Response send(DataSet data) {
        CompletableFuture.supplyAsync(()->sendMails(data))
                         .thenAccept(result->reply(data,result));
        return Response
                .ok("Start sending. After this, you will receive an email with the result of sending.")
                .build();
    }

How about it, I was able to write it pretty smartly. ComputableFuture first executes the sendMails method asynchronously with the ** supplyAsync ** method, and when it finishes and returns a return value (result in the program), it uses it to reply with the ** thenAcceput ** method. Executes the method asynchronously. The thenAcceput method is a method that can receive the result of the asynchronous process of the previous stage and use it to execute the next process asynchronously. The whole is stylishly written in the method chain. Computable Future is convenient because you can do all this in one sentence. Note that supplyAsync and thenAccept take the method to be executed as an argument, so the argument is to be written as a ** lambda expression **.    Those who say, "But the lambda expression is ..." are okay. You just need to understand what you're doing here. For lambda expressions, please refer to " Easy-to-understand Java object-oriented thorough explanation "(^ _ ^ ;. Also, in the same book, Computable Future is also explained in detail.

(2) How to return the response to the client

Next, I will explain how to write a return statement in the send method. The return in the send method is the content that is sent as a response to the client as it is. The return is an object of the Respose class. Create it using the class method of the Response class as follows:

Sender.java


        return Response
                .ok("Start sending. After this, you will receive an email with the result of sending.")
                .build();

You can write in a method chain like this because the Response class has the following class methods.

Return type Method name Function
ResponseBuilder ok() status"OK"Is set in the response
ResponseBuilder ok(obj) status"OK"And the object obj in the response
ResponseBuilder status(statusValue) Set any status

The return type of these methods is ResponseBuilder type, but since this class has the following methods, it is a mechanism to finally return it as Response type with the build () method while including the object and status. is.

Return type Method name Function
ResponseBuilder entity(object) Include any object in the response
ResponseBuilder header(name, value) Set any header and its value in the response
Response build() Create a Response instance

Here are some writing patterns:

How to write Taste
Response.ok().build() status"OK"return it
Response.ok("Done").build() status"OK"と「Done」という文字列を返す
Response.status(Status.BAD_REQUEST).build() status"BAD_REQUEST"return it
Response.status(Status.BAD_REQUEST).entity("Parameter error").build() "BAD_REQUEST"And return a message
Response.ok(obj).build() status"OK"And return the object obj

You can create flexible responses by combining methods. Regarding the result status, if only the main ones are shown, there are the following. The status value can be an Enum or an integer.

status Taste
Status.OK 200 OK
Status.CREATED 201 Created
Status.ACCEPTED 202 Approved
Status.NO_CONTENT 204 No content
Status.BAD_REQUEST 400 Request is incorrect
Status.FORBIDDEN 403 forbidden
Status.FOUND 302 found
Status.NOT_FOUND 403 not found
Status.REQUEST_TIMEOUT 408 Request timed out
Status.CONFLICT 409 Conflicting

Creating the sendMails method

Now let's create a sendMails method that actually sends the email. The SendMails method is easy to create using the SendMail library. The library also has a method for batch sending by BCC or CC, but since each email is given an address and title, it is necessary to send one individually here.

In order to handle such cases, the ** EmailSender ** class of the library has methods (** connect () **, ** xsend () that connect, send, and disconnect to the mail server individually. Since there are **, ** disconnect () **), use these and write as follows.

Sender.java


    @Inject  EmailSender es;    //Get an instance of EmailSender
    ....    

    //Connect to mail server
    es.connect();
    //Send email to all destinations
    List<Recipient> rs = data.getDlist(); //Retrieving the destination list
    for(Recipient r : rs){
        //Processing to add address and title to email body
        .....
        //Transmission process
        es.xsend(destination address,title,Text);
    }
    //Disconnect from mail server
    es.disconnect(); 

Variable declarations, annotations attached to EmailSender es;, and ** @ Inject ** are used when receiving from the system without creating an instance of the object with new. If you annotate the variable declaration with @Inject, the system will instantiate the EmailSender class and put it in the variable es. This is called "** (context and) dependency injection **". In the world of enterprise Java, not just Jakarta EE, it is common to get an instance by "dependency injection". Actually, it creates an instance using a constructor with no arguments, but it is quite convenient because you do not have to write new.

The rest of the story is about using EmailSender's methods to connect to a mail server, send as many emails as you need, and finally disconnect. It's easy. However, this is a "synopsis", so we need to consider the content a little more.

(1) I want to add an address and title to the text

There is nothing wrong with it. The text will be rewritten before sending as follows.

Sender.java


    //Send
    //Send email to all destinations
    List<Recipient> rs = data.getDlist(); //Retrieving the destination list
    for(Recipient r : rs){
        //Processing to add address and title to email body
        StringBuilder sb = new StringBuilder();
        sb.append(r.getName()).append(" ")     //address
          .append(r.getMr()).append("\n")      //Honorific title
          .append(data.getMessage());          //the content of the email
        //Transmission process
        es.xsend(r.getEmail(), data.getTitle(), sb.toString());
    }

I use the for statement to send as many emails as there are recipient lists, but at that time I add the address and title (both are included in the field variable of the Recipient class) before the body. ..

(2) I want to return the execution result of connection, transmission, and disconnection.

The connect (), send (), and disconnect () methods return the status of the execution result as an EmailStatus type value (enumeration type).

status Taste
EmailStatus.DONE Successful completion
EmailStatus.INIT_ERROR Incorrect connection parameter
EmailStatus.CONNECT_ERROR can not connect
EmailStatus.SEND_ERROR Failed to send email
EmailStatus.DISCONNECT_ERROR Error when disconnecting

Therefore, when an error occurs in the connection phase, the process is stopped and the status is returned. Also, if an error occurs while sending an email (due to incorrect email address format), write false to the destination (Recipient object) and continue processing. In other words, it keeps a record. This will allow you to report unsent destinations later when the reply () method emails the execution result. Note that the cut face is the last process, so you can simply return the status.

From the above, the completed sendMails method looks like this:

Sender.java


//Send plaintext / multiple emails
private EmailStatus sendMails(DataSet data){
    //Connect to mail server
    EmailStatus sts = es.connect();                                                
    if(sts!=EmailStatus.DONE)   return  sts; //Exit if error
    //Send email to all destinations
    List<Recipient> rs = data.getDlist(); //Retrieving the destination list
    for(Recipient r : rs){                                      
        StringBuilder sb = new StringBuilder(); //Rewrite the text
        sb.append(r.getName()).append(" ")  //address
          .append(r.getMr()).append("\n")   //Honorific title
          .append(data.getMessage());       //the content of the email

        sts = es.xsend(r.getEmail(),data.getSender(), data.getTitle(), sb.toString());
        if(sts!=EmailStatus.DONE) r.setFlag(false); //Add false to the destination of the transmission error
    }
    //Disconnect from mail server
    sts = es.disconnect();  //Returns status
    return sts;
}

By the way, if the format of the email address is incorrect, it can be caught as a transmission error, but if the format is correct, even the wrong email address will be sent as it is. You may get a return email later, but what to do with it is another matter.

Generally, when you want to register your e-mail address, send a confirmation e-mail to the person and if there is a return, follow the procedure of registration. If you follow this procedure, your email address will not be incorrect. However, if the registrant discards or changes the email address, nothing will happen, so it is necessary to send a confirmation email from time to time to check it. Of course, you can add such a service to this system, but why not try it?

Reply method that returns a response

Finally, create a reply () method to notify the result by email after the transmission is completed. The arguments of the reply () method are the sent data (DataSet) and the result status (EmailStatus).

Sender.java


    //Sending result notification email
    private void reply(DataSet data, EmailStatus status){
        ....
        es.send(data.getSender(), "Notification of transmission result",Text);
    }

To send an email, just use the send () method of the J-mail library. The send () method without x is send (destination, title, body) It is a convenience method used in the form of, and it also connects and disconnects to the mail server.

(1) Collect the data required for notification

Sending is easy, but the hassle is that you have to create the text. In the body, add a notification of the transmission result, the number of emails, the email address that could not be sent, and a copy of the body sent. So, first, get those data as follows:

Sender.java


    //Sending result notification email
    private void reply(DataSet data, EmailStatus status){

        String result = returnMessage(status); //① Convert to message
        int all = data.getDlist().size(); //② Total number
        int done = doneMails(data);       //③ Number of transmissions
        int errs = all - done;            //④ Number of errors
        String errors = errMails(data);   //⑤ Error address (CSV)
        ....
        es.send(data.getSender(), "Notification of transmission result",Text);
    }

Now, let's check the execution contents in order.

String result = returnMessage (status); // Convert to message

In the returnMessage method, the status of the execution result is converted to a message string and returned. The returnMessage method is converted with a simple switch statement as follows.

Sender.java


    //Convert exit code to message and return
    private String returnMessage(EmailStatus status){
        switch(status){
            case INIT_ERROR:        return "User name / password is not set";
            case CONNECT_ERROR:     return "Could not send because it cannot connect to the SMTP server";
            case SEND_ERROR:        return "Could not send due to an unexpected error during sending";
            case DISCONNECT_ERROR:  return "Could not send due to an error when disconnecting";
            default:                return "Transmission completed";
        }
    }

int all = data.getDlist (). size (); // total number

data.getDlist () is a member dlist, a list of destination objects (Recipients). If you use size () to find the number of elements, you can get the number of emails requested to be sent.

int done = doneMails (data); // Number of transmissions

Use the doneMails method to find the number of emails actually sent. In the sendMails method above, the flag member of the recipient object (Recipient) that could not be sent was set to false, so the doneMails method uses it. In other words, the list (dlist) of destination objects (Recipient) is extracted from data, only the Recipient objects whose flag is true are extracted by stream processing, and the number is counted at the end.

Sender.java


    //Returns the number sent
    public int doneMails(DataSet data){
        int n = (int)data.getDlist().stream()
                        .filter(Recipient::isFlag)  //Only those with true flag
                        .count();                   //Get the number
        return n;
    }

The filter method is a method that extracts only those that meet the conditions. The condition is Recipient :: isFlag, which is written as a method reference. Same as the lambda expression r-> r.isFlag (), meaning r.isFlag () == true. That is, if the value of the Flag field is true. You can do the same with normal syntax without using stream processing or lambda expressions, but stream processing has the advantage of being concise.

int errs = all --done; // Number of errors

Calculate the number of errors by subtracting the number of sent items from the total number of items.

String errors = errMails (data); // Error address (CSV)

Use the errMails method to get the email address that caused the transmission error as a comma-separated string. Use stream processing in the same way as ③.

Sender.java


    //Returns the address of the transmission error as a CSV character string
    public String errMails(DataSet data){
        String errors = data.getDlist().stream()
                            .filter(r->!r.isFlag())   //Only those with false flag
                            .map(Recipient::getEmail) //Only email address
                            .collect(Collectors.joining(",")); //Connect with commas
        return errors;
    }

The filter method, in turn, extracts only those whose flag member is false. The following map method is a conversion method. Here we will convert the stream of the Recipient object to the stream of the email. Recipient :: getEmail is the same as the lambda expressionr-> r.getEmail (). In other words, since the email is fetched from the Recipient object r and sent to the stream, the Recipient stream changes to the email stream which is a string.

The last collect method is a method that performs high-order aggregation and conversion. Here, all the elements of the string stream are concatenated with commas to form a single string.

Writing the above in normal syntax would result in more than double the code. Stream processing is really powerful! ("Easy-to-understand Java object-oriented thorough explanation`" has a detailed explanation of stream processing.)

(2) Create the body of the notification email

Once you have the data, the rest is easy. Use StringBuilder to create a string like this:

Sender.java


    StringBuilder sb = new StringBuilder();
    sb.append(result).append("\n\n")
      .append("\t Number of receptions").append(all).append("Case\n")
      .append("\t Number of transmissions").append(done).append("Case\n");
    if(errs!=0){
        sb.append("\n Unable to send").append(errs).append("Case\n")
          .append("\t The following destination is invalid\n\t").append(errors);
    }

    es.send(data.getSender(), "Notification of transmission result", sb.toString());

It's so simple that no explanation is needed. You can now create the following email body: client006.png

(3) Completed reply method

From the above, the completed reply method is as follows.

Sender.java


    //Sending result notification email
    private void reply(DataSet data, EmailStatus status){
        
        String result = returnMessage(status); //Convert to message
        int all = data.getDlist().size(); //Total number
        int done = doneMails(data);       //Number of transmissions
        int errs = all - done;            //Number of errors
        String errors = errMails(data);   //Error address (CSV)
        
        StringBuilder sb = new StringBuilder();
        sb.append(result).append("\n\n")
          .append("\t Number of receptions").append(all).append("Case\n")
          .append("\t Number of transmissions").append(done).append("Case\n");
        if(errs!=0){
            sb.append("\n Unable to send").append(errs).append("Case\n")
              .append("\t The following destination is invalid\n\t").append(errors);
        }

        es.send(data.getSender(), "Notification of transmission result", sb.toString());
    }

2. 2. Creating a service client --- SenderClient class

Now it's client-side creation. When writing in Java language, use ** Jakarta REST Client API **. This client API provides methods for accessing by GET, POST, etc. by specifying the URI. You can also get an object of class Response as a return value from the service.

(1) Automatic generation of client class

Using NetBeans as an IDE, service clients can be automatically generated. Create a new project called mailserviceClietn, create a class with a suitable name (here Multimail class), and perform the following steps.

① Place the cursor outside the class definition   client001.png ② Select [Source] ⇒ [Insert Code] (3) A dialog opens, so select [Generate REST Client]. ④ Make sure that [Original Service] is selected, and press [Browse]. ⑤ The currently open project is displayed. ④ Expand the REST service project (mailservice in this case), select [Sender], and press [OK].  client002.png ⑤ The original dialog will be displayed. Enter SenderClient as the class name and press [OK].  client003.png

With the above, SenderClient class is generated as an inner class in the current class.

(2) Modification of client class

The generated class looks like this: Some parts need to be corrected, so let's fix the corrected parts later.

Multimail.java


public class Multimail {

    static class SenderClient {
        private WebTarget webTarget;
        private Client client;
        private static final String BASE_URI = "http://localhost:8080/mailservice/sendmail";
        public SenderClient() {
            client = javax.ws.rs.client.ClientBuilder.newClient();
            webTarget = client.target(BASE_URI).path("cc");
        }
        public Response send() throws ClientErrorException {
            return webTarget.path("text").request()
                .post(null, Response.class);
        }
        public void close() {
            client.close();
        }
    }
   
}

The corrections are as follows. (1) Specify ** DataSet data ** as an argument to the send () method. (2) Set the argument of the post method to post (** Entity.entity (ds, MediaType.APPLICATION_JSON) **) Rewrite as ③ Annotate the close method with ** @ preDestroy **

With the fix, it looks like this:

Multimail.java


public class Multimail {

    static class SenderClient {
        private WebTarget webTarget;
        private Client client;
        private static final String BASE_URI = "http://localhost:8080/mailservice/sendmail";
        public SenderClient() {
            client = javax.ws.rs.client.ClientBuilder.newClient();
            webTarget = client.target(BASE_URI).path("cc");
        }
        public Response send(DateSet data) throws ClientErrorException {
            return webTarget.path("text").request()
                .post(Entity.entity(ds, MediaType.APPLICATION_JSON));
        }
        @PreDestroy
        public void close() {
            client.close();
        }
    }
   
}

Let's explain in order.

(3) Explanation of client class

Why inner class?

First, the class was created as an inner class (static inner class). This does not necessarily have to be grouped into an inner class. Actually, it's okay to sprinkle these variables and methods inside the outer Multimail class (called the outer class). However, putting them together in the inner class has the advantage that the defensive range can be clearly separated from the outer class.

The contents of the external class Multimail class will be created from now on, but it is only responsible for the user interface. In other words, generate a web screen like the one you saw at the beginning and ask the user to input it. Then, the input data is sent to the service using the method of the SenderClient class.

Client class features

1. 1. Field variables

SenderClient.java


    private WebTarget webTarget;  //An object that has the ability to access a service by specifying a URI
    private Client client;        //Class for generating WebTarget
    //URI that represents the entire service@ApplicationPath("sendmail")URI specified in)
    private static final String BASE_URI = "http://localhost:8080/mailservice/sendmail"; 

Use the WebTarget class to access the service, but create an instance of the WebTarger class using the Client class. Therefore, declare each variable as a field variable. In addition, BASE_URI is the URI of the target mail sending service, and is the URI up to the entire service name.

2. 2. constructor

SenderClient.java


    public SenderClient() {
        //Create an instance of Client
        client = javax.ws.rs.client.ClientBuilder.newClient();
        // (@Path("cc")A URI that represents the service of the Sender class (specified in)
        //Create an instance of WebTarget
        webTarget = client.target(BASE_URI).path("cc"); 
    }

In the constructor, create an instance of the Client class and WebTarget class and prepare for access. Instantiating the Client class is always the usual way to write it this way. It's a costly process, so it's usually done in the constructor. The WebTarget instant is generated by pointing to the URI of the Sender class. The path after "cc" is specified by the path method when actually accessing.

3. 3. send () method

SenderClient.java


    public Response send(DataSet data) throws ClientErrorException {
        return webTarget
                 .path("text")   //Add path
                 .request()      //to access
                 .post(Entity.entity(data, MediaType.APPLICATION_JSON));  //Send ds with POST access
    }

Since this is an automatic generation, it will have the same name as the web service side. Thanks to this, it has the effect of making the correspondence easier to understand. After all, this is the method for POST access to http: // localhost: 8080/mailservice/sendmail/cc/text. Therefore, I modified the argument to be DataSet type data. data is an object sent by POST.

The procedure for POST access is as follows: (1) add "text" to the end by path ("text"), (2) access by request (), and (3) send data by post. This series of method calls was automatically generated. However, the argument of the post method has been modified so that it sends the DateSet type object data.

When sending an object to the service, use the entity method of the Entity class to specify the variable and MediaType. For MediaTyps, it's a good idea to specify APPLICATION_JSON like this.

post(Entity.entity(data, MediaType.APPLICATION_JSON))

This is a common way to write an object.

4. close method

SenderClient.java


    @PreDestroy
    public void close() {
        client.close();
    }

When you're done, you must close the Client class to free up resources. This is the method for that, but I'll add the @PreDestroy annotation so that you don't have to forget to run it. With this annotation, you can rest assured that the close () method will be executed automatically just before the class instance is deleted.

(4) User interface --- JSF

Here, the user interface is created with JSF (Jakarta Faces). In other words, the Multimail class is a JSF class. The JSF controller is called a backing bean, so the Multimail class is a backing bean. The form with necessary annotations is as follows. In addition, the main functions to be created are also written in Kotoba.

SenderClient.java


@ViewScoped
@Named
public class Multimail implements Serializable {

    //Declaration of field variables related to user interface
    
    //Create an instance of SenderClient

    //Upload file reception process (Receive Excel file of destination list)

    //The process of reading an Excel file and creating a List of Recipient objects

    //After the input is completed, the process of sending data using the post method of SenderClient

    static class SenderClient {
        ...
    }
}

The source code is in the project file (about 100 lines), but it will be too long, so I will omit the explanation after this. Still, I think I've already explained RESTful web services and clients so far, but what about? For more detailed information on RESTful web services, please refer to the book " Easy-to-understand Jakarta EE ".

The part related to this user interface is interesting, such as file upload processing and reading of Excel files, so I plan to post it separately to Qiita. ~~ I think I can post it at the beginning of the year, so if you are interested, please continue to read it. ~~

3. 3. Email sending library --- J-mail

I used a library called J-mail to send emails. It has been revised for more than 10 years, and this year it has been revised for use in Jakarta EE. The source code of the library can be ** Download from here **. It is also included in the mailservice project created this time. That is the package ** jmail **.

◆ Contents of source code file

There are two main library files, ** EmailSender.java ** and ** JmSender.java **. EmailSender is a class that uses JmSender and defines the API to be exposed. JmSender is a class that directly operates Jakarta Mail (= Java Mail). In addition, SenderProperties.java defines only the getProperties () method in the interface that retrieves the data required for SMTP server access in the properties file.

This interface gives you the flexibility to load property data from an xml file or use the microprofile-config.propaties file. The library provides two implementations (ConfigToProperties.java and XmlToProperties.java). Since it is a CDI bean, describe which one to use in the beans.xml file. The default is XmlToProperties.

◆ API of J-mail library

Email Sender's public API. You can send plain text mail and HTML mail. In addition, both emails can be attached to files and sent by CC/BCC. In addition, if you want to perform detailed operations like this post, there is also a method to execute connection, mail transmission (plaintext or HTML), and disconnection operations separately.

(1) Return type

All methods return a status (** EmailStatus ** type) indicating the execution result. However, for the sake of simplicity, the return value is omitted from the API table. The return value is as follows:

status Taste
EmailStatus.DONE Successful completion
EmailStatus.INIT_ERROR Incorrect connection parameter
EmailStatus.CONNECT_ERROR can not connect
EmailStatus.SEND_ERROR Failed to send email
EmailStatus.DISCONNECT_ERROR Error when disconnecting

(2) Method argument

The arguments have the following meanings:

Argument name Taste
String to Destination email address
String subject Email title
String body the content of the email
String html HTML data that becomes the body of the email
String type Transmission classification:"TO" "CC" "BCC" "TO"Means "one person"
String fileDir Directory name where the file is located
List<String> flist List of attachment names

・ Html is a character string of the entire HTML document. · FileDir must be on the computer where the sending library is located

(3) API to connect and disconnect manually

-All return values ​​are of Email Status type -All arguments are of type String

API Function
connect() Connect to SMTP server
disconnect() Disconnect from SMTP server
xsend​(to, subject, body) Send one text email
xsendHtml​(to, subject, body) Send one HTML email

・ It is an API suitable for sending a large number of emails. ・ CC/BCC is not specified because it can be sent repeatedly. -There is no file attachment function. Normally, the URL containing the file is included in the body of the email.

(4) Convenience API (no connection / disconnection required)

・ It is an API to easily send one to several emails. -All return values ​​are of Email Status type -All arguments are String type except List type

API Function
send​(to, subject, body) send mail
send​(to, subject, body, type) CC/Send an email with BCC
send​(to, subject, body, fileDir, List<String> flist) Send a file attachment email
send​(to, subject, body, fileDir, List<String> flist, String type) CC/Send a file attachment email with BCC specification
sendHtml​(to, subject, html) Send HTML email
sendHtml​(to, subject, html, type) CC/Send HTML email with BCC
sendHtml​(to, subject, html, fileDir, List<String> flist) Send HTML email with file attachment
sendHtml​(to, subject, html, fileDir, List<String> flist, type) CC/BCC specification, send HTML email with file attachment

Recommended Posts

Let's create a RESTful email sending service + client
Create a RESTful API service using Grape
Let's create a Java development environment (updating)
[Rails] Let's create a super simple Rails API
Let's create a REST API using WildFly Swarm.
Let's create a timed process with Java Timer! !!
Let's create a custom tab view in SwiftUI 2.0
Let's create a super-simple web framework in Java
[Java] Let's create a mod for Minecraft 1.14.4 [Introduction]
[Java] Let's create a mod for Minecraft 1.16.1 [Introduction]
[Java] Let's create a mod for Minecraft 1.14.4 [99. Mod output]
Let's create JUnit.
[Java] Let's create a mod for Minecraft 1.14.4 [0. Basic file]
[Java] Let's create a mod for Minecraft 1.14.4 [4. Add tools]
Create a weekly git GUI client [5] First desktop app
[Java] Let's create a mod for Minecraft 1.14.4 [5. Add armor]
[Java] Let's create a mod for Minecraft 1.14.4 [Extra edition]
[Java] Let's create a mod for Minecraft 1.14.4 [7. Add progress]
[Java] Let's create a mod for Minecraft 1.14.4 [6. Add recipe]
[Java] Let's create a mod for Minecraft 1.16.1 [Add item]
[Java] Let's create a mod for Minecraft 1.16.1 [Basic file]
If there is a state transition, let's create a State class
Let's create a gcloud development environment on a centos8 container
[Java] Let's create a mod for Minecraft 1.14.4 [1. Add items]
I made a Restful server and client in Spring.
Create a Service with an empty model Liferay 7.0 / DXP
[Java] Let's create a mod for Minecraft 1.14.4 [2. Add block]
[Java] Let's create a mod for Minecraft 1.16.1 [Add block]