Emailsender
Author: D | 2025-04-23
In my Startup.cs I have. services.AddTransient Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, EmailSender (i = new EmailSender( Configuration[ EmailSender @Component( asdasd_MyEmailerImpl ) public class MyEmailerImpl extends EmailerImpl { public void setEmailSender(EmailSender emailSender) { this.emailSender = emailSender; } } there is only two places where EmailSender used
EMailSender/EMailSender.cpp at master xreef/EMailSender
This tutorial will show you how to send a email via Spring framework’s email support. The Spring Framework provides a helpful utility library for sending email that shields the user from the specifics of the underlying mailing system and is responsible for low level resource handling on behalf of the client.The org.springframework.mail.javamail.JavaMailSender interface adds specialized JavaMail features such as MIME message support to the MailSender interface (from which it inherits).JavaMailSender also provides a callback interface for preparation of JavaMail MIME messages, called org.springframework.mail.javamail.MimeMessagePreparator.PrerequisitesEclipse 2019-12, Java at least 1.8, Gradle 6.4.1, Maven 3.6.3, Spring Boot Mail Starter 2.3.1, Java mail API 1.6.2Project SetupCreate either gradle or maven based project in Eclipse. The name of the project is spring-email-javamailsender-and-mimemessagepreparator.If you are creating gradle based project then you can use below build.gradle script:buildscript { ext { springBootVersion = '2.3.1.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") }}plugins { id 'java-library' id 'org.springframework.boot' version "${springBootVersion}"}sourceCompatibility = 12targetCompatibility = 12repositories { mavenCentral()}dependencies { implementation("org.springframework.boot:spring-boot-starter-mail:${springBootVersion}") implementation('javax.mail:javax.mail-api:1.6.2')}If you are creating maven based project then you can use below pom.xml file: 4.0.0 com.roytuts spring-email-javamailsender-and-mimemessagepreparator 0.0.1-SNAPSHOT org.springframework.boot spring-boot-starter-parent 2.3.1.RELEASE UTF-8 org.springframework.boot spring-boot-starter-mail javax.mail javax.mail-api 1.6.2 org.apache.maven.plugins maven-compiler-plugin 3.8.1 at least 8 at least 8 Email ConfigurationWe will create an email configuration class where we will configure SMTP (Simple Mail Transfer Protocol) server details, from email address.package com.roytuts.spring.email.javamailsender.and.mimemessagepreparator;import java.util.Properties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.mail.javamail.JavaMailSender;import org.springframework.mail.javamail.JavaMailSenderImpl;@Configurationpublic class EmailConfig { @Bean public JavaMailSender mailSender() { JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); mailSender.setHost("smtp.gmail.com"); mailSender.setPort(587); mailSender.setUsername("[email protected]"); mailSender.setPassword("gmail password"); Properties javaMailProperties = new Properties(); javaMailProperties.put("mail.smtp.auth", true); javaMailProperties.put("mail.smtp.starttls.enable", true); mailSender.setJavaMailProperties(javaMailProperties); return mailSender; }}We need to issue STARTTLS command otherwise you will get the error message similar to the following:#smtplib.SMTPSenderRefused: (530, b'5.7.0 Must issue a STARTTLS command first. p7sm24605501pfn.14 - gsmtp', '[email protected]')You will get below error if your security level is high:#smtplib.SMTPAuthenticationError: (535, b'5.7.8 Username and Password not accepted. Learn more at\n5.7.8 x10sm26098036pfn.36 - gsmtp')Therefore you need to lower your Gmail’s security settings.Email SenderEmail sender class just does the right job for sending the email to the intended recipient.package com.roytuts.spring.email.javamailsender.and.mimemessagepreparator;import javax.mail.Message;import javax.mail.internet.InternetAddress;import javax.mail.internet.MimeMessage;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.mail.javamail.JavaMailSender;import org.springframework.mail.javamail.MimeMessagePreparator;import org.springframework.stereotype.Component;@Componentpublic class EmailSender { @Autowired private JavaMailSender mailSender; public void sendEmail(final String subject, final String message, final String fromEmailAddress, final String toEmailAddresses) { MimeMessagePreparator preparator = new MimeMessagePreparator() { public void prepare(MimeMessage mimeMessage) throws Exception { mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(toEmailAddresses)); mimeMessage.setFrom(new InternetAddress(fromEmailAddress)); mimeMessage.setSubject(subject); mimeMessage.setText(message); } }; try { mailSender.send(preparator); System.out.println("Email sending complete."); } catch (Exception e) { e.printStackTrace(); } }}Main ClassA class is having main method with @SpringBootApplication annotation is enough to start up the Spring Boot application.In this class we specify the subject, message and the recipient to send the email.package com.roytuts.spring.email.javamailsender.and.mimemessagepreparator;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.CommandLineRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class MimeMessagePreparatorApp implements CommandLineRunner { @Autowired private EmailSender emailSender; public static void main(String[] args) { SpringApplication.run(MimeMessagePreparatorApp.class, args);
EMailSender/README.md at master xreef/EMailSender - GitHub
Public void setTo(String to) { this.to = to; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } @Override public String toString() { return "Mail{" + "from='" + from + ''' + ", to='" + to + ''' + ", subject='" + subject + ''' + ", content='" + content + ''' + '}'; }}Creating Email Service SenderNow lets create a service that’ll be responsible for sending the emails out. We have created a method called sendSimpleMessage() which takes a Mail argument. First we create a SimpleMailMessage and assign the properties of the Mail object to it. Next, we use the JavaMailSender which Spring Boot automatically Initialized with the properties found in the above configuration files.package com.memorynotfound.mail;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.mail.SimpleMailMessage;import org.springframework.mail.javamail.JavaMailSender;import org.springframework.stereotype.Service;@Servicepublic class EmailService { @Autowired private JavaMailSender emailSender; public void sendSimpleMessage(final Mail mail){ SimpleMailMessage message = new SimpleMailMessage(); message.setSubject(mail.getSubject()); message.setText(mail.getContent()); message.setTo(mail.getTo()); message.setFrom(mail.getFrom()); emailSender.send(message); }}Spring Mail – Sending Simple Email with JavaMailSender ExampleWe are using Spring Boot to bootstrap our application. When the application is invoked we simply create a new Mail object and send it using our previously created EmailServicepackage com.memorynotfound.mail;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.ApplicationArguments;import org.springframework.boot.ApplicationRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class Application implements ApplicationRunner { private static Logger log = LoggerFactory.getLogger(Application.class); @Autowired private EmailService emailService; public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); } @Override public void run(ApplicationArguments applicationArguments) throws Exception { log.info("Spring Mail - Sending SimpleEMailSender/EMailSender.h at master xreef/EMailSender - GitHub
. In my Startup.cs I have. services.AddTransient Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, EmailSender (i = new EmailSender( Configuration[ EmailSenderEmailSender/README.md at master A10ha/EmailSender - GitHub
Blazor Server Identity Provider with ASP.NET Core Identity using OpenIddictBased on official microsoft documentation for ASP.NET Core Identity.In this sample we will be using:Blazor ServerASP.NET Core IdentitySQLiteOpenIddict CoreOpenIddict SamplesSteps to run through this sample/tutorialCreate the solution filedotnet new sln -n BlazorIdentityCreate and add a clean blazor server projectdotnet new blazorserver -n BlazorIdentity.Serverdotnet sln add BlazorIdentity.ServerAdd dependencies to the projectInstall the tools used in this sample:dotnet tool install -g dotnet-efdotnet tool install -g dotnet-aspnet-codegeneratorAdd the packages:dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCoredotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCoredotnet add package Microsoft.AspNetCore.Identity.UIdotnet add package Microsoft.EntityFrameworkCore.Designdotnet add package Microsoft.EntityFrameworkCore.Sqlitedotnet add package Microsoft.EntityFrameworkCore.Toolsdotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Designdotnet add package OpenIddict.AspNetCore -v 3.0.0-beta5.20503.76dotnet add package OpenIddict.EntityFrameworkCore -v 3.0.0-beta5.20503.76Create the Database contextCreate an ApplicationUser class in /Models:public class ApplicationUser : IdentityUser{}Create the DbContext class in /Data:{ public AppDbContext(DbContextOptions options) : base(options) { }}">public class AppDbContext : IdentityDbContextApplicationUser>{ public AppDbContext(DbContextOptionsAppDbContext> options) : base(options) { }}Setup other required classesCreate /Services/EmailSender.cs:public class EmailSender : IEmailSender{ public Task SendEmailAsync(string email, string subject, string htmlMessage) { return Task.CompletedTask; }}Add to Startup.cs, ConfigureServices method:(options =>{ // Configure the context to use SQLite. options.UseSqlite(Configuration.GetConnectionString("AppDbContextConnection")); // Register the entity sets needed by OpenIddict. // Note: use the generic overload if you need // to replace the default OpenIddict entities. options.UseOpenIddict();});services.AddIdentity(options =>{ options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; // Add more password requirements ...}) .AddEntityFrameworkStores() .AddDefaultTokenProviders();// Configure Identity to use the same JWT claims as OpenIddict instead// of the legacy WS-Federation claims it uses by default (ClaimTypes),// which saves you from doing the mapping in your authorization controller.services.Configure(options =>{ options.ClaimsIdentity.UserNameClaimType = Claims.Name; options.ClaimsIdentity.UserIdClaimType = Claims.Subject; options.ClaimsIdentity.RoleClaimType = Claims.Role;});services.AddOpenIddict() // Register the OpenIddict core components. .AddCore(options => { // Configure OpenIddict to use the Entity Framework Core stores and models. // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. options.UseEntityFrameworkCore() .UseDbContext(); }) // Register the OpenIddict server components. .AddServer(options => { //how to set EMailSender emailSend(email_login, email_password
Need to support implicit, password or client credentials. options.AllowAuthorizationCodeFlow() .AllowRefreshTokenFlow(); // Register the signing and encryption credentials. options.AddDevelopmentEncryptionCertificate() .AddDevelopmentSigningCertificate(); // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. options.UseAspNetCore() .EnableAuthorizationEndpointPassthrough() .EnableLogoutEndpointPassthrough() .EnableStatusCodePagesIntegration() .EnableTokenEndpointPassthrough(); }) // Register the OpenIddict validation components. .AddValidation(options => { // Import the configuration from the local OpenIddict server instance. options.UseLocalServer(); // Register the ASP.NET Core host. options.UseAspNetCore(); });// Add our custom Email Senderservices.AddSingletonIEmailSender, EmailSender>();Add to Startup.cs, Configure method:{ endpoints.MapRazorPages(); endpoints.MapControllers(); endpoints.MapBlazorHub(); // Enable For Blazor Server only endpoints.MapFallbackToPage("/_Host"); // Enable For Blazor Server only});">app.UseAuthentication();app.UseAuthorization();app.UseEndpoints(endpoints =>{ endpoints.MapRazorPages(); endpoints.MapControllers(); endpoints.MapBlazorHub(); // Enable For Blazor Server only endpoints.MapFallbackToPage("/_Host"); // Enable For Blazor Server only});Set up the database (Sqlite)Create an initial migration and run it:dotnet ef migrations add InitialSchema -o "Data/Migrations"dotnet ef database updateScaffold all the Identity filesUse for only these pages:dotnet aspnet-codegenerator identity -dc BlazorIdentity.Server.Data.AppDbContext -sqlite --files "Account.Register;Account.Login;Account.Logout;Account.ResetPassword"Use to scaffold ALL Identity pages:dotnet aspnet-codegenerator identity -dc BlazorIdentity.Server.Data.AppDbContext -sqliteApply --force to regenerate.First Test !Open the url to check your OpenID specification: continue!Create a redirect componentCreate component /Shared/RedirectToLogin.cs:public class RedirectToLogin : ComponentBase{ [Inject] NavigationManager NavigationManager { get; set; } protected override void OnInitialized() { NavigationManager.NavigateTo($"Identity/Account/Login?returnUrl={Uri.EscapeDataString(NavigationManager.Uri)}", true); }}Create a login display component in /Shared/LoginDisplay.razor: Hello, @context.User.Identity.Name! Logout Login ">@using Microsoft.AspNetCore.Components.Authorization @inject NavigationManagerNavigationAuthorizeView> Authorized> a href="Identity/Account/Manage/Index"> Hello, @context.User.Identity.Name! a> form action="/Identity/Account/Logout?returnUrl=%2F" method="post"> button class="nav-link btn btn-link" type="submit">Logoutbutton> form> Authorized> NotAuthorized> a href="Identity/Account/Login">Logina> NotAuthorized>AuthorizeView>Add the Login Display to MainLayout.razor: About">div class="top-row px-4"> LoginDisplay /> a href=" target="_blank">Abouta>div>EMailSender and SendGrid – Renzo Mischianti
EMailSender/EMailSenderKey.h at master - GitHub
メリークリスマス! 「Javaアドベントカレンダー2020」の最終日です。 クリスマスらしく、楽しい(?)内容になるようにタイトルを変えました。ライブラリの紹介だけと思っていましたが、クリスマスだし、ライブラリを使って役に立つウェブサービスを作ってみよう、というわけです。 RESTfulウェブサービスについて基本から説明しているので、この分野を知りたい人にもおススメですよ。 なお、ライブラリの詳細は、投稿の最後にまとめましたので、気になる方は、先に、そちらを見てください。#1.メールサービスの作成 --- Senderクラス##宛名を変えて、同じメールを一斉に送ってくれるサービス つまりですね、 ○○ 様 こんにちは、・・・。 のような、宛名と敬称付きで、後の内容は同じ、というメールを、名簿にある人達に一斉に送ってくれるサービスです(宛名と敬称がつくところがポイントです)。メール送信は、ライブラリを使うので数行ですし、送信サービスもJakarta RESTの仕組みを使うと簡単に書けるはずです。ちなみに、Jakarta RESTの開発・実行環境はJETを使うと5分で準備できますので、初めての方も是非トライしてみてください。 (ここからJETをダウンロードできます。使い方の解説やビデオもあります。) 一方、サービスを利用するクライアントプログラムは、ウェブ画面を作ってデータをPOSTで送るだけなので、PHPやJavascriptなどあらゆる言語で作成できます。ウェブサービスの強みのひとつです。ただ、JavaにもクライアントAPIがあるので、Javaで作っても何の不都合もありません。 (これから解説するメール送信サービスとクライアントプログラムのプロジェクトはここからダウンロードしてください。また、プロジェクトに同梱している重要.txtファイルには、SMTPサーバーの設定について、注意が書いてあります。必ず、読んだ上で利用してください。) ##こんな形になりました 先に目に見える結果を見ておきましょう。サービスを利用するクライアントの画面です。 上段は、メールの内容を書く領域で、下段は名簿画面です。名簿はExcelファイルをアップロードします。自動アップロードなので、ファイルを選択するだけでアップロードされ、内容が表示されます。左端のチェックボックスは、送信先として使うかどうかの選択欄です。初期は全員が対象になっていますが、不要な宛先はチェックを外したりできます。 ##まず、RESTfulウェブサービスとは GETやPOSTなどで特定のURI(URLとほぼ同じ)にアクセスすると、決められた仕事をやってくれるのがRESTfulウェブサービスです。API的なメソッド呼び出しは一切なく、単なるHTTPアクセスで対応する処理を実行します。GET、POST、PUT、DELETEなどの"モード"と、アクセスするURIの組み合わせで、どんな仕事をするのか切り分けます。アクセスと同時に、URLパラメータやPOSTデータを使って、必要なデータも渡せます。 URIは、 のように構成します。 例えば、 サーバー:localhost:8080 プロジェクト名:mailservice 全体のサービス名:sendmail ひとつのサービス名(ここでは同報メール送信):ccという名前にする場合、URIは次のようになります。 なお、メールにはテキストメールとHTMLメールがあるので、さらにサブパスを切って、次のようにできます。 これはテキストメールの同報送信を行うURIです。HTMLメールなら --- Jakarta REST Jakarta REST(=JAX-RS)を使うので、これはもう簡単です。 まず、プロジェクト名は作ったプロジェクトの名前ですから問題ありませんね。 それ以外の部分は、何と、プロジェクト内のクラスやメソッドに、決められたアノテーションを付けるだけでいいのです。 最初に、サービス(sendmail)の部分ですが、次のように中身のないクラス(名前は任意)を作って、@ApplicationPath("sendmail")を付けます。これだけです。ServerConfig.java@ApplicationPath("sendmail") public class ServiceConfig extends Application { } 次に個別のサービスを表すパスは、別にクラス(名前は任意。Senderクラス)を作って、@Path("cc")を付ければOKです。それから、このSenderクラス内に具体的なサービス機能を作成します。Sender.java@Path("cc")public class Sender { } 最後に、サブパスtextですが、これは、Senderクラス内で実際に処理を担当するメソッド(sendメソッド)に、@Path("text")を付けます。また、POSTでアクセスするメソッドなので、@POSTアノテーションも付けておきます。Sender.java@Path("cc")public class Sender { @Path("text") @POST public Response send(DataSet data){ } } これだけで、POSTアクセスに対応するURI、 そうそう、sendメソッドの引数と戻り値型についての説明を忘れていました。###(1)引数 まず、引数にはどんな型のオブジェクトでも渡せます。ここではDataSet型を渡すことにしています。DataSet型は、今回、作成したクラスで、内容は、メールタイトル、本文、宛先リストなど、送信に必要なすべてのデータを含みます。クライアント側が、POSTアクセスでこのオブジェクトを送信すると、サーバー側はそれをsendメソッドの引数、DataSet dataに受け取ることができます。DataSetクラスのフィールド変数は次のようです。DataSet.javapublic class DataSet { private String sender; // 送信者メールアドレス private String title; // メールタイトル private String message; // 本文 or HTML文 private String type; // "CC" or "BCC" or "TO" private ListRecipient> dlist; // 宛先のリスト フィールド変数のうち、List dlistは宛先のListです。Recipientクラスは次のようにしました。Recipient.javapublic class Recipient { private boolean flag; // 状態の記録用(送信対象とするかなど、用途は任意) private String id; // 番号やIDなど private String name; // 名前 private String email; // メールアドレス private String mr; // 敬称(様、殿、先生など) これらのオブジェクトは、実際には、JSON形式やXML形式のデータに自動変換されて受け渡されます。したがって、Java言語以外のクライアントでも、JSONやXMLを使って、同じものを受け渡しできます。つまり、プログラミング言語の制限はない、ということです。 受け渡すデータ形式を指定しなければ、適当に(といっても大抵はJSON)決められますが、やり取りする形式をアノテーションで指定しておくことができます。次は、JSONかXMLを使うというという指定です。 @Producesで、sendメソッドが返すデータの形式を指定し、@Consumesで、sendメソッドが引数に受け取るデータの形式を指定します。(ざっくりした言い方ですが)このように複数の形式を指定しておくと、クライアント側でどれかに対応できます。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){ } } そうそう、うっかりするところでした。XML形式でやり取りする可能性があるオブジェクトには、クラスに@XmlRootElementというアノテーションを付けます。これだけで、XMLへの変換が自動的に行われるようになります(JSONについては何もする必要はありません)。DataSet.java@XmlRootElementpublic class DataSet {###(2)戻り値型 メソッドはResponse型のオブジェクトを返すのが普通です。Responseクラスは、オールインワンの便利なオブジェクトで、その中には、いろいろな応答ヘッダとその値、実行結果を表すステータス、呼び出し側に返すオブジェクトなどを含めることができます。HTTPの規約に従って送信されるので、クライアントプログラムは、それぞれの言語固有の標準的な方法でそれらを受け取ることができます。Java言語でのやり方は、後ほど説明します。##では、メールを送信しよう! sendメソッドの処理は次のように簡単です。Sender.java @Path("/text") @POST public Response send(DataSet data) { EmailStatus result = semdMails(data); // メール送信処理 reply(data,result); // 送信結果をクライアントにメールで通知する return Response .ok() .build(); } sendMails(data)が、実際にメール送信を行う部分で、reply(data, result)が、送信結果をクライアントに通知する部分です(内容は後で説明)。 ただ、このままでは、ちょっと問題があります。 というのも、送信するメールの件数が何十もあると、送信に数十秒かかることが予想されます。その間、ブロッキングが発生して、クライアントに応答を返すことができなくなります。クライアントはボーッと待たされるので、かなり不安な気持ちになりますね。 ここはマルチスレッドの出番です。ひとまず別のスレッドで非同期に送信処理を実行しつつ、クライアントには、return Response.・・・で「いまから送ります」的なメッセージを返しておきます。 sendMailsメソッドは実行結果を戻り値として返すので、これを使って、replyメソッドで結果をクライアントに知らせます。なので、sendMailsとreplyメソッドは一連の不可分な処理です。そこで、2つをまとめて1つのスレッドで実行するしかありません・・・。###(1)スタイリッシュな非同期処理の書き方 ところで、ComputableFutuerクラスって知っていますか? 上記のような問題をカッコよく処理するためにJava8から導入されたクラスです。クリスマスですからね、ここはカッコよく書いてみましょう。Sender.java @Path("/text") @POST public Response send(DataSet data) { CompletableFuture.supplyAsync(()->sendMails(data)) .thenAccept(result->reply(data,result)); return Response .ok("送信を開始します。この後、送信結果のメールが届きます。") .build(); } どうでしょう、かなりスマートに書けました。 ComputableFutureは、最初に、supplyAsyncメソッドでsendMailsメソッドを非同期に実行し、それが終了して戻り値(プログラムではresult)を返したら、それを使って、thenAcceputメソッドでreplyメソッドを非同期に実行します。thenAcceputメソッドは、前段の非同期処理の結果を受け取り、それを使って、次の処理を非同期に実行することができるメソッドです。 全体は、メソッドチェーンでスタイリッシュな書き方をします。これだけのことを1文でかけるのですから、ComputableFutureって便利ですね。なお、supplyAsyncやthenAcceptは実行するメソッドを引数に取るので、引数はラムダ式で書くことになっています。 「でも、ラムダ式は・・・」という方も大丈夫です。ここは、やっていることの意味がわかればいいだけですから。ラムダ式については、「わかりやすいJava オブジェクト指向徹底解説」で平易に解説していますのでご覧ください (^_^;。また、同書では、ComputableFutuerについても詳しく解説しています。###(2)クライアントへの応答の返し方 次に、sendメソッドでのreturn文の書き方について説明します。sendメソッドでのreturnは、そのままクライアントへ応答として送信される内容になります。returnで返すのは、Resposeクラスのオブジェクトです。次のように、Responseクラスのクラスメソッドを使って作成します。Sender.java return Response .ok("送信を開始します。この後、送信結果のメールが届きます。") .build(); このようにメソッドチェーンで書けるのは、Responseクラスに次のようなクラスメソッドがあるからです。戻り値型メソッド名機 能ResponseBuilderok()ステータス"OK"を応答にセットするResponseBuilderok(obj)ステータス"OK"とオブジェクトobjを応答にセットするResponseBuilderstatus(statusValue)任意のステータスをセットするこれらメソッドの戻り値型は、ResponseBuilder型ですが、このクラスには次のようなメソッドがあるので、オブジェクトやステータスを含めつつ、最終的にbuild()メソッドで、Response型にして返す、という仕組みです。戻り値型メソッド名機 能ResponseBuilderentity(object)応答に任意のオブジェクトを含めるResponseBuilderheader(name, value)応答に任意のヘッダとその値をセットするResponsebuild()Responseインスタンスを作成する いくつか、書き方のパターンを示すと、次のようなやり方があります。書き方意 味Response.ok().build()ステータス"OK"を返すResponse.ok("完了").build()ステータス"OK"と「完了」という文字列を返すResponse.status(Status.BAD_REQUEST).build()ステータス"BAD_REQUEST"を返すResponse.status(Status.BAD_REQUEST).entity("パラメータの誤り").build()"BAD_REQUEST"とメッセージを返すResponse.ok(obj).build()ステータス"OK"とオブジェクトobjを返す メソッドを組み合わせるといろいろ出来るので、柔軟な応答を作成できます。 なお、結果ステータスですが、主なものだけ示すと、次のようなものがあります。ステータスの値としては、Enum型か整数を使うことができます。ステータス意 味Status.OK200 OK Status.CREATED201 作成 Status.ACCEPTED202 承認済 Status.NO_CONTENT204 コンテンツなし Status.BAD_REQUEST400 リクエストが正しくない Status.FORBIDDEN403 禁止されている Status.FOUND302 見つかった Status.NOT_FOUND403 見つからない Status.REQUEST_TIMEOUT408 リクエストがタイムアウトしたStatus.CONFLICT409 競合している ##sendMailsメソッドの作成 では、いよいよ、実際にメールを送信するsendMailsメソッドを作成しましょう。 SendMailsメソッドは、メール送信ライブラリを使うと簡単に作成できます。ライブラリには、BCCやCCで一括送信するメソッドもありますが、各メールごとに宛名と敬称を付けるので、ここでは、個別に1通ずつ送信する必要があります。 そのようなケースに対応するために、ライブラリのEmailSenderクラスには、メールサーバーとの接続、送信、切断をそれぞれ個別に行うメソッド(connect()、xsend()、disconnect())があるので、これらを使って、次のように書きます。Sender.java @Inject EmailSender es; // EmailSenderのインスタンスを得る .... // メールサーバーに接続 es.connect(); // すべての送信先にメール送信 ListRecipient> rs = data.getDlist(); // 宛先リストを取り出す for(Recipient r : rs){ // メール本文に宛名と敬称を付加する処理 ..... // 送信処理 es.xsend(宛先アドレス, 表題, 本文); } // メールサーバーから切断 es.disconnect(); 変数宣言、EmailSender es; に付けたアノテーション、@Injectは、オブジェクトのインスタンスをnewで作らず、システムから受け取る時に使います。変数宣言に、@Injectアノテーションを付けると、システムがEmailSenderクラスのインスタンスを作成し、変数esに入れてくれるのです。 これを「(コンテキストと)依存性注入」といいます。Jakarta EEに限らず、エンタープライズJavaの世界では、「依存性注入」によりインスタンスを取得するのが普通です。実際には、引数のないコンストラクタを使って、インスタンスを生成してくれるのですが、new を書かなくてよいので、なかなか便利です。 後は、EmailSenderのメソッドを使ってメールサーバーに接続し、必要なだけメールを送信し、最後に切断する、というストーリーです。簡単ですね。ただ、これは「あらすじ」ですから、あと少し、内容を検討する必要があります。###(1)本文に宛名と敬称を追加したい どうということもありません。次のようにして送信前に、本文を書き換えてしまいます。Sender.java // 送信 // すべての送信先にメール送信 ListRecipient> rs = data.getDlist(); // 宛先リストを取り出す for(Recipient r : rs){ // メール本文に宛名と敬称を付加する処理 StringBuilder sb = new StringBuilder(); sb.append(r.getName()).append(" ") // 宛名 .append(r.getMr()).append("\n") // 敬称 .append(data.getMessage()); // メール本文 // 送信処理 es.xsend(r.getEmail(), data.getTitle(), sb.toString()); } for文を使って、宛先リストの数だけメールを送信しますが、その際、本文の前に、宛名と敬称(どちらもRecipientクラスのフィールド変数に含まれています)を追加してしまうわけです。###(2)接続、送信、切断の実行結果を返すようにしたい connect()、send()、disconnect()の各メソッドは、実行結果のステータスをEmailStatus型の値(列挙型)で返します。ステータス意 味EmailStatus.DONE正常終了 EmailStatus.INIT_ERROR接続パラメータの誤りEmailStatus.CONNECT_ERROR接続できないEmailStatus.SEND_ERRORメール送信に失敗したEmailStatus.DISCONNECT_ERROR切断時のエラー そこで、接続フェーズでエラーが起こった時は、処理を中止してステータスを返します。 また、メール送信中にエラーが起こった時(メールアドレスの形式不正が原因)は、その宛先(Recipientオブジェクト)にfalseを書き込んでおいて、処理を継続します。つまり、記録を残すわけです。 こうしておくと、後で、reply()メソッドが実行結果をメールで返す時、未送信の宛先を報告できます。 なお、切断フェースは、最後の処理なので単にステータスを返すだけで構いません。 以上から、完成したsendMailsメソッドは次のようになります。Sender.java// 平文・複数メールの送信private EmailStatus sendMails(DataSet data){ // メールサーバーに接続 EmailStatus sts = es.connect(); if(sts!=EmailStatus.DONE) return sts; // エラーなら終了 // すべての送信先にメール送信 ListRecipient> rs = data.getDlist(); // 宛先リストを取り出す for(Recipient r : rs){ StringBuilder sb = new StringBuilder(); // 本文を書き換える sb.append(r.getName()).append(" ") // 宛名 .append(r.getMr()).append("\n") // 敬称 .append(data.getMessage()); // メール本文 sts = es.xsend(r.getEmail(),data.getSender(), data.getTitle(), sb.toString()); if(sts!=EmailStatus.DONE) r.setFlag(false); // 送信エラーの宛先にはfalseを付ける } // メールサーバーから切断 sts = es.disconnect(); // ステータスを返す return sts;} ところで、メールアドレスの形式が不正だと、送信エラーとして捕捉できますが、形式が正しければ、間違いのメールアドレスでも、そのまま送信されてしまいます。後でリターンメールが返ってくるかもしれませんが、それをどうするかは、別の問題です。 一般に、メールアドレスを登録してもらう時は、本人に確認メールを送り、リターンがあれば登録という手順を踏みます。この手順に従えば、メールアドレスの誤りは発生しません。ただ、登録者がメールアドレスを廃棄・変更した場合にはどうにもなりませんので、時々、確認メールを送って調べる必要があります。 もちろん、このシステムにそのようなサービスも追加できますが、やってみませんか?##応答を返すreplyメソッド 最後は、送信終了後に、結果をメールで通知するreply()メソッドを作成します。 reply()メソッドの引数は、送信データ(DataSet)と結果のステータス(EmailStatus)です。Sender.java // 送信結果通知メールの送信 private void reply(DataSet data, EmailStatus status){ .... es.send(data.getSender(), "送信結果のお知らせ", 本文); } メール送信はJ-mailライブラリのsend()メソッドを使うだけです。 xが付かないsend()メソッドは、 send(宛先, タイトル, 本文)の形式で使う、コンビニエンスメソッドで、メールサーバーへの接続、切断も一緒にやってくれます。###(1)通知に必要なデータを集める 送信は簡単ですが、手間なのは本文を作らなければいけないことです。 本文には、送信結果の通知、メールの件数や、送信できなかったメールアドレス、そして、送信した本文のコピーを付けておきます。そこで、最初に、次のようにして、それらのデータを取得します。Sender.java // 送信結果通知メールの送信 private void reply(DataSet data, EmailStatus status){ String result = returnMessage(status); // ①メッセージに変換 int all = data.getDlist().size(); // ②全件数 int done = doneMails(data); // ③送信件数 int errs = all - done; // ④エラー件数 String errors = errMails(data); // ⑤エラーアドレス(CSV) .... es.send(data.getSender(), "送信結果のお知らせ", 本文); } では、実行内容を、順に確認しましょう。####①String result = returnMessage(status); // メッセージに変換 returnMessageメソッドで、実行結果のステータス(status)を、メッセージ文字列に変換して返します。returnMessageメソッドは、次のように、簡単なswitch文で変換しています。Sender.java // 終了コードをメッセージに変換して返す private String returnMessage(EmailStatus status){ switch(status){ case INIT_ERROR: return "ユーザー名・パスワードが設定されていません"; case CONNECT_ERROR: return "SMTPサーバーに接続できないため送信できませんでした"; case SEND_ERROR: return "送信中に予期しないエラーが発生したため送信できませんでした"; case DISCONNECT_ERROR: return "切断時のエラーのため送信できませんでした"; default: return "送信完了しました"; } }####②int all = data.getDlist().size(); // 全件数 data.getDlist()は、メンバのdlist、つまり宛先オブジェクト(Recipient)のリストです。. In my Startup.cs I have. services.AddTransient Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, EmailSender (i = new EmailSender( Configuration[ EmailSender
EmailSender with Next.js and Resend - GitHub
Size()でその要素数を求めると送信要求されたメール件数が得られます。####③int done = doneMails(data); // 送信件数 doneMailsメソッドで、実際に送信したメールの件数を求めます。 先のsendMailsメソッドで、送信できなかった宛先オブジェクト(Recipient)について、そのflagメンバをfalseにセットしておいたので、doneMailsメソッドは、それを利用します。つまり、dataから宛先オブジェクト(Recipient)のリスト(dlist)を取り出し、ストリーム処理で、flagがtrueになっているRecipientオブジェクトだけを抽出して、最後にその件数を数えます。Sender.java // 送信済件数を返す public int doneMails(DataSet data){ int n = (int)data.getDlist().stream() .filter(Recipient::isFlag) // flagがtrueのものだけ .count(); // 件数を得る return n; } filterメソッドは、条件に合うものだけを抽出するメソッドです。条件は、Recipient::isFlagですが、これは、メソッド参照という書き方です。r->r.isFlag()というラムダ式と同じで、意味は、r.isFlag()==trueです。つまり、Flagフィールドの値がtrueなら、ということです。 ストリーム処理やラムダ式を使わずに、普通の構文でも同じことをかけますが、ストリーム処理は、簡潔に書けるのが利点です。####④int errs = all - done; // エラー件数 全件数から送信済件数を引いて、エラー件数を求めます。####⑤String errors = errMails(data); // エラーアドレス(CSV) errMailsメソッドで、送信エラーになったメールアドレスをコンマ区切りの文字列として取得します。 ③と同じように、ストリーム処理を使います。Sender.java // 送信エラーのアドレスをCSV文字列にして返す public String errMails(DataSet data){ String errors = data.getDlist().stream() .filter(r->!r.isFlag()) // flagがfalseのものだけ .map(Recipient::getEmail) // メールアドレスだけにする .collect(Collectors.joining(",")); // コンマで連結する return errors; } filterメソッドで、今度はflagメンバがfalseであるものだけを抽出します。続くmapメソッドは変換メソッドです。ここでは、Recipientオブジェクトのストリームを、emailのストリームに変換します。Recipient::getEmailは、r->r.getEmail()というラムダ式と同じです。つまり、Recipientオブジェクトrから、emailを取り出してストリームに流すので、Recipientのストリームが、文字列であるemailのストリームに変わります。 最後のcollectメソッドは、高次の集約、変換を行うメソッドで、ここでは、文字列ストリームの要素を、すべてコンマで連結して、ひとつの文字列にする、という処理をおこないます。 以上のことを普通の構文で書くと倍以上のコードを書くことになるでしょう。ストリーム処理は、本当に強力ですね!(「わかりやすいJava オブジェクト指向徹底解説」には、ストリーム処理の詳しい解説があります)###(2)通知メールの本文を作る データがそろったら、あとは簡単です。StringBuilderを使って、次のように文字列を作成します。 Sender.java StringBuilder sb = new StringBuilder(); sb.append(result).append("\n\n") .append("\t受付件数 ").append(all).append("件\n") .append("\t送信件数 ").append(done).append("件\n"); if(errs!=0){ sb.append("\n送信不能 ").append(errs).append("件\n") .append("\t次の宛先は無効です\n\t").append(errors); } es.send(data.getSender(), "送信結果のお知らせ", sb.toString()); 特に説明がいらないくらい単純ですね。これで、次のようなメール本文が作成できます。###(3)完成したreplyメソッド 以上から、完成したreplyメソッドは、次のようです。Sender.java // 送信結果通知メールの送信 private void reply(DataSet data, EmailStatus status){ String result = returnMessage(status); // メッセージに変換 int all = data.getDlist().size(); // 全件数 int done = doneMails(data); // 送信件数 int errs = all - done; // エラー件数 String errors = errMails(data); // エラーアドレス(CSV) StringBuilder sb = new StringBuilder(); sb.append(result).append("\n\n") .append("\t受付件数 ").append(all).append("件\n") .append("\t送信件数 ").append(done).append("件\n"); if(errs!=0){ sb.append("\n送信不能 ").append(errs).append("件\n") .append("\t次の宛先は無効です\n\t").append(errors); } es.send(data.getSender(), "送信結果のお知らせ", sb.toString()); }#2.サービスクライアントの作成 --- SenderClientクラス 今度は、クライアント側の作成です。 Java言語で作る時は、Jakarta REST Client APIを利用します。このクライアントAPIには、URIを指定してGET、POSTなどでアクセスするためのメソッドが用意されています。また、サービスからの戻り値として、Responseクラスのオブジェクトを取得できます。###(1)クライアントクラスの自動生成 IDEとして、NetBeansを使うと、サービスクライアントは、自動生成できます。新しくmailserviceClietnというプロジェクトを作り、何か適当な名前のクラス(ここではMultimailクラス)を作成して、次の手順を実行します。①クラス定義の外側にカーソルを置く ②[ソース]⇒[コードを挿入]と選択する③ダイアログが開くので[RESTクライアントを生成]を選択する④[元のサービス]が選択されていることを確認し、[参照]を押す⑤現在開いているプロジェクトが表示される④RESTサービスプロジェクト(ここではmailservice)を展開し[Sender]を選択して[OK]を押す ⑤元のダイアログに戻るので、クラス名にSenderClientと入力して[OK]を押す 以上で、現在のクラスの中に、内部クラスとしてSenderClientクラスが生成されます。###(2)クライアントクラスの修正 生成されるクラスは次のようです。一部、修正する必要がありますので、説明は後にして、修正箇所を直してしまいましょう。Multimail.javapublic class Multimail { static class SenderClient { private WebTarget webTarget; private Client client; private static final String BASE_URI = " 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(); } } } 修正箇所は、次の通りです。 ①send()メソッドに引数として、DataSet dataを指定する。 ②postメソッドの引数を、post(Entity.entity(ds, MediaType.APPLICATION_JSON)) と書き換える ③closeメソッドに、@preDestroy というアノテーションを付ける 修正すると、次のようになります。Multimail.javapublic class Multimail { static class SenderClient { private WebTarget webTarget; private Client client; private static final String BASE_URI = " 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(); } } } では、順に説明していきましょう。###(3)クライアントクラスの説明####どうして内部クラス? まず、クラスが、内部クラス(静的内部クラス)として生成されたことです。これは、必ず内部クラスにまとめる必要があるわけではありません。実際には外側のMultimailクラス(外部クラスといいます)の中に、これらの変数やメソッドをちりばめて書いても問題ありません。ただ、内部クラスにまとめておくと、外部クラスと守備範囲をはっきり分けることができる、という利点があります。 外部クラスであるMultimailクラスの中身はこれから作成しますが、ユーザーインタフェースを担当するだけのクラスです。つまり、冒頭に見たようなウェブ画面を生成し、利用者に入力してもらいます。そして、入力されたデータは、SenderClientクラスのメソッドを使って、サービスに送信する、という役割分担です。####クライアントクラスの機能#####1.フィールド変数SenderClient.java private WebTarget webTarget; // URIを指定してサービスにアクセスする機能を持つオブジェクト private Client client; // WebTargetを生成するためのクラス // サービス全体を表すURI(@ApplicationPath("sendmail")で指定したURI) private static final String BASE_URI = " WebTargetクラスを使って、サービスにアクセスしますが、WebTargerクラスのインスタンスは、Clientクラスを使って作成します。そこで、フィールド変数として、それぞれの変数を宣言しておきます。また、BASE_URIは、対象とするメール送信サービスのURIで、全体のサービス名までのURIです。#####2.コンストラクタSenderClient.java public SenderClient() { // Clientのインスタンスを生成する client = javax.ws.rs.client.ClientBuilder.newClient(); // (@Path("cc")で指定した)Senderクラスのサービスを表すURIで、 // WebTargetのインスタンスを生成しておく webTarget = client.target(BASE_URI).path("cc"); } コンストラクタでは、ClientクラスとWebTargetクラスのインスタンスを作成し、アクセスの準備をしておきます。Clientクラスのインスタンス生成は、いつもこのように書く決まりきったやり方です。コストのかかる処理なので、コンストラクタでやっておくのが普通です。 WebTargetのインスタンは、SenderクラスのURIを指すようにして生成します。"cc"に続く後の部分のパスは、実際にアクセスする時、pathメソッドで指定します。######3.send()メソッドSenderClient.java public Response send(DataSet data) throws ClientErrorException { return webTarget .path("text") // パスを追加 .request() // アクセスする .post(Entity.entity(data, MediaType.APPLICATION_JSON)); // POSTアクセスでdsを送信する } これは、自動生成なので、ウェブサービス側と同じ名前になります。おかげで対応が分かりやすくなる効果があります。 結局これは、 に、POSTでアクセスするためのメソッドです。そのため、引数をDataSet型のdataと修正したのでした。dataは、POSTで送信するオブジェクトです。 POSTアクセスの手順は、①path("text")によって、末尾に"text"を追加する、②request()でアクセスし、③postでdataを送信する、という順序です。この一連のメソッド呼び出しは、自動生成されました。ただ、postメソッドの引数は、DateSet型のオブジェクトdataを送信するように、手直ししました。 サービスにオブジェクトを送る時は、Entityクラスのentityメソッドを使って、変数とMediaTypeを指定します。MediaTypsとしはこのように、APPLICATION_JSONを指定するといいでしょう。 post(Entity.entity(data, MediaType.APPLICATION_JSON)) これは、オブジェクトを送信する時の一般的な書き方です。#####4.closeメソッドSenderClient.java @PreDestroy public void close() { client.close(); } 処理が終わったら、リソースを開放するために、Clientクラスをcloseしなければいけません。これはそのためのメソッドですが、実行し忘れても大丈夫なように、@PreDestroyアノテーションを付けておきます。このアノテーションを付けておくと、クラスのインスタンスが消去される直前に、close()メソッドが自動実行されるので安心です。 ###(4)ユーザーインタフェース --- JSF ここでは、ユーザーインタフェースはJSF(Jakarta Faces)で作成しています。つまり、MultimailクラスはJSFのクラスです。JSFのコントローラはバッキングビーンというので、Multimailクラスはバッキングビーンです。必要なアノテーションなどを付けた形は次のようです。また、作成する主な機能もコトバで書き込んでいます。SenderClient.java@ViewScoped@Namedpublic class Multimail implements Serializable { // ユーザーインタフェースにかかるフィールド変数の宣言 // SenderClientのインスタンスを生成 // アップロードファイルの受け取り処理(宛先リストのExcelファイルを受け取る) // Excelファイルを読んでRecipientオブジェクトのListを作成する処理 // 入力完了後、SenderClientのpostメソッドを使って、データを送信する処理 static class SenderClient { ... }} ソースコードは、プロジェクトファイルの中にあります(100行くらいです)が、長くなりすぎるので、これ以降の解説は、今回は割愛します。それでも、ここまでで、RESTfulウェブサービスとクライアントの説明は、ひとまずできたように思いますが、どうでしょう。 RESTfulウェブサービスの、さらに詳細な情報は、書籍「 わかりやすいJakarta EE 」にありますので、よろしければご覧ください。 なお、このユーザーインタフェースにかかる部分については、ファイルアップロード処理や、Excelファイルの読み取りなど、面白い内容なので、Qiitaに、別途、投稿する予定です。年明けには投稿できると思いますので、興味のある方は続けて、ご覧ください。##3.メール送信ライブラリ --- J-mail メール送信に使ったのは、J-mailというライブラリです。すでに10年以上に渡って改訂を繰り返していて、今年は、Jakarta EEで使えるように改訂しました。ライブラリのソースコードは、ここからダウンロードできます。また、今回作成したmailserviceプロジェクトにも含まれています。jmailというパッケージがそれです。###◆ソースコードファイルの内容 ライブラリのファイルは、EmailSender.javaと、JmSender.javaの2つが主なものです。EmailSenderはJmSenderを利用するクラスで、公開するAPIを定義しています。JmSenderはJakarta Mail(=Java Mail)を直接操作するクラスです。また、SenderProperties.javaは、インタフェースで、SMTPサーバーアクセスに必要なデータをプロパティファイルで取得するgetProperties()メソッドだけを定義しています。 このインタフェースは、プロパティのデータを、xmlファイルからロードするか、microprofile-config.propatiesファイルを使うかなど、柔軟に選択できるようにするためです。ライブラリでは、2つの実装(ConfigToProperties.java、XmlToProperties.java)を提供しています。CDIビーンなので、beans.xmlファイルで、どちらを利用するか記述します。デフォルトではXmlToPropertiesです。###◆J-mailライブラリのAPI EmailSenderの公開APIです。平文メール、HTMLメールを送信できます。また、どちらのメールも、ファイル添付やCC/BCC送信ができます。さらに、今回の投稿のように、こまかな操作をしたい場合は、接続、メール送信(平文orHTML)、切断の操作を分けて実行するメソッドもあります。####(1)戻り値型 すべてのメソッドは、実行結果を示すステータス(EmailStatus型)を返します。ただし、簡略化のためAPIの表では戻り値の記載を省略しています。戻り値は次のような値です。ステータス意 味EmailStatus.DONE正常終了 EmailStatus.INIT_ERROR接続パラメータの誤りEmailStatus.CONNECT_ERROR接続できないEmailStatus.SEND_ERRORメール送信に失敗したEmailStatus.DISCONNECT_ERROR切断時のエラー####(2)メソッドの引数引数は次のような意味です。引数名意 味String to宛先メールアドレスString subjectメールタイトルString bodyメール本文String htmlメール本文になるHTMLデータString type送信区分:"TO" "CC" "BCC" "TO"は「宛先は1人」の意味String fileDirファイルのあるディレクトリ名List flist添付ファイル名のリスト・htmlは、HTML文書全体を文字列にしたものです・fileDirは、送信ライブラリのあるコンピュータ上の場所でなければいけません####(3)接続、切断をマニュアルで行うAPI・戻り値はすべてEmailStatus型です・引数はすべてString型ですAPI機 能connect()SMTPサーバーに接続するdisconnect()SMTPサーバーから切断するxsend(to, subject, body)1通のテキストメールを送信するxsendHtml(to, subject, body)1通のHTMLメールを送信する・多数のメールを送信するのに適したAPIです・繰り返し送信ができるので、CC/BCC指定はしません・ファイル添付機能はありません。通常はファイルのあるURLをメール本文に含めます。####(4)コンビニエンスAPI(接続・切断処理が不要)・1~数通のメールを手軽に送るためのAPIです・戻り値はすべてEmailStatus型です・引数はList型以外はすべてString型ですAPI機 能send(to, subject, body)メールを送信するsend(to, subject, body, type)CC/BCCを指定してメールを送信するsend(to, subject, body, fileDir, List flist)ファイル添付メールを送信するsend(to, subject, body, fileDir, List flist, String type)CC/BCC指定を指定してファイル添付メールを送信するsendHtml(to, subject, html)HTMLメールを送信するsendHtml(to, subject, html, type)CC/BCCを指定してHTMLメールを送信するsendHtml(to, subject, html, fileDir, List flist)ファイル添付HTMLメールを送信するsendHtml(to, subject, html, fileDir, List flist, type)CC/BCC指定、ファイル添付HTMLメールを送信するEmailSender 1.0.1 - NuGet Gallery
. In my Startup.cs I have. services.AddTransient Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, EmailSender (i = new EmailSender( Configuration[ EmailSender @Component( asdasd_MyEmailerImpl ) public class MyEmailerImpl extends EmailerImpl { public void setEmailSender(EmailSender emailSender) { this.emailSender = emailSender; } } there is only two places where EmailSender usedEMailSender/examples/DistributionList/DistributionList.ino at
Comments
This tutorial will show you how to send a email via Spring framework’s email support. The Spring Framework provides a helpful utility library for sending email that shields the user from the specifics of the underlying mailing system and is responsible for low level resource handling on behalf of the client.The org.springframework.mail.javamail.JavaMailSender interface adds specialized JavaMail features such as MIME message support to the MailSender interface (from which it inherits).JavaMailSender also provides a callback interface for preparation of JavaMail MIME messages, called org.springframework.mail.javamail.MimeMessagePreparator.PrerequisitesEclipse 2019-12, Java at least 1.8, Gradle 6.4.1, Maven 3.6.3, Spring Boot Mail Starter 2.3.1, Java mail API 1.6.2Project SetupCreate either gradle or maven based project in Eclipse. The name of the project is spring-email-javamailsender-and-mimemessagepreparator.If you are creating gradle based project then you can use below build.gradle script:buildscript { ext { springBootVersion = '2.3.1.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") }}plugins { id 'java-library' id 'org.springframework.boot' version "${springBootVersion}"}sourceCompatibility = 12targetCompatibility = 12repositories { mavenCentral()}dependencies { implementation("org.springframework.boot:spring-boot-starter-mail:${springBootVersion}") implementation('javax.mail:javax.mail-api:1.6.2')}If you are creating maven based project then you can use below pom.xml file: 4.0.0 com.roytuts spring-email-javamailsender-and-mimemessagepreparator 0.0.1-SNAPSHOT org.springframework.boot spring-boot-starter-parent 2.3.1.RELEASE UTF-8 org.springframework.boot spring-boot-starter-mail javax.mail javax.mail-api 1.6.2 org.apache.maven.plugins maven-compiler-plugin 3.8.1 at least 8 at least 8 Email ConfigurationWe will create an email configuration class where we will configure SMTP (Simple Mail Transfer Protocol) server details, from email address.package com.roytuts.spring.email.javamailsender.and.mimemessagepreparator;import java.util.Properties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.mail.javamail.JavaMailSender;import org.springframework.mail.javamail.JavaMailSenderImpl;@Configurationpublic class EmailConfig { @Bean public JavaMailSender mailSender() { JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); mailSender.setHost("smtp.gmail.com"); mailSender.setPort(587); mailSender.setUsername("[email protected]"); mailSender.setPassword("gmail password"); Properties javaMailProperties = new Properties(); javaMailProperties.put("mail.smtp.auth", true); javaMailProperties.put("mail.smtp.starttls.enable", true); mailSender.setJavaMailProperties(javaMailProperties); return mailSender; }}We need to issue STARTTLS command otherwise you will get the error message similar to the following:#smtplib.SMTPSenderRefused: (530, b'5.7.0 Must issue a STARTTLS command first. p7sm24605501pfn.14 - gsmtp', '[email protected]')You will get below error if your security level is high:#smtplib.SMTPAuthenticationError: (535, b'5.7.8 Username and Password not accepted. Learn more at\n5.7.8 x10sm26098036pfn.36 - gsmtp')Therefore you need to lower your Gmail’s security settings.Email SenderEmail sender class just does the right job for sending the email to the intended recipient.package com.roytuts.spring.email.javamailsender.and.mimemessagepreparator;import javax.mail.Message;import javax.mail.internet.InternetAddress;import javax.mail.internet.MimeMessage;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.mail.javamail.JavaMailSender;import org.springframework.mail.javamail.MimeMessagePreparator;import org.springframework.stereotype.Component;@Componentpublic class EmailSender { @Autowired private JavaMailSender mailSender; public void sendEmail(final String subject, final String message, final String fromEmailAddress, final String toEmailAddresses) { MimeMessagePreparator preparator = new MimeMessagePreparator() { public void prepare(MimeMessage mimeMessage) throws Exception { mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(toEmailAddresses)); mimeMessage.setFrom(new InternetAddress(fromEmailAddress)); mimeMessage.setSubject(subject); mimeMessage.setText(message); } }; try { mailSender.send(preparator); System.out.println("Email sending complete."); } catch (Exception e) { e.printStackTrace(); } }}Main ClassA class is having main method with @SpringBootApplication annotation is enough to start up the Spring Boot application.In this class we specify the subject, message and the recipient to send the email.package com.roytuts.spring.email.javamailsender.and.mimemessagepreparator;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.CommandLineRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class MimeMessagePreparatorApp implements CommandLineRunner { @Autowired private EmailSender emailSender; public static void main(String[] args) { SpringApplication.run(MimeMessagePreparatorApp.class, args);
2025-04-01Public void setTo(String to) { this.to = to; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } @Override public String toString() { return "Mail{" + "from='" + from + ''' + ", to='" + to + ''' + ", subject='" + subject + ''' + ", content='" + content + ''' + '}'; }}Creating Email Service SenderNow lets create a service that’ll be responsible for sending the emails out. We have created a method called sendSimpleMessage() which takes a Mail argument. First we create a SimpleMailMessage and assign the properties of the Mail object to it. Next, we use the JavaMailSender which Spring Boot automatically Initialized with the properties found in the above configuration files.package com.memorynotfound.mail;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.mail.SimpleMailMessage;import org.springframework.mail.javamail.JavaMailSender;import org.springframework.stereotype.Service;@Servicepublic class EmailService { @Autowired private JavaMailSender emailSender; public void sendSimpleMessage(final Mail mail){ SimpleMailMessage message = new SimpleMailMessage(); message.setSubject(mail.getSubject()); message.setText(mail.getContent()); message.setTo(mail.getTo()); message.setFrom(mail.getFrom()); emailSender.send(message); }}Spring Mail – Sending Simple Email with JavaMailSender ExampleWe are using Spring Boot to bootstrap our application. When the application is invoked we simply create a new Mail object and send it using our previously created EmailServicepackage com.memorynotfound.mail;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.ApplicationArguments;import org.springframework.boot.ApplicationRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class Application implements ApplicationRunner { private static Logger log = LoggerFactory.getLogger(Application.class); @Autowired private EmailService emailService; public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); } @Override public void run(ApplicationArguments applicationArguments) throws Exception { log.info("Spring Mail - Sending Simple
2025-03-27Blazor Server Identity Provider with ASP.NET Core Identity using OpenIddictBased on official microsoft documentation for ASP.NET Core Identity.In this sample we will be using:Blazor ServerASP.NET Core IdentitySQLiteOpenIddict CoreOpenIddict SamplesSteps to run through this sample/tutorialCreate the solution filedotnet new sln -n BlazorIdentityCreate and add a clean blazor server projectdotnet new blazorserver -n BlazorIdentity.Serverdotnet sln add BlazorIdentity.ServerAdd dependencies to the projectInstall the tools used in this sample:dotnet tool install -g dotnet-efdotnet tool install -g dotnet-aspnet-codegeneratorAdd the packages:dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCoredotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCoredotnet add package Microsoft.AspNetCore.Identity.UIdotnet add package Microsoft.EntityFrameworkCore.Designdotnet add package Microsoft.EntityFrameworkCore.Sqlitedotnet add package Microsoft.EntityFrameworkCore.Toolsdotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Designdotnet add package OpenIddict.AspNetCore -v 3.0.0-beta5.20503.76dotnet add package OpenIddict.EntityFrameworkCore -v 3.0.0-beta5.20503.76Create the Database contextCreate an ApplicationUser class in /Models:public class ApplicationUser : IdentityUser{}Create the DbContext class in /Data:{ public AppDbContext(DbContextOptions options) : base(options) { }}">public class AppDbContext : IdentityDbContextApplicationUser>{ public AppDbContext(DbContextOptionsAppDbContext> options) : base(options) { }}Setup other required classesCreate /Services/EmailSender.cs:public class EmailSender : IEmailSender{ public Task SendEmailAsync(string email, string subject, string htmlMessage) { return Task.CompletedTask; }}Add to Startup.cs, ConfigureServices method:(options =>{ // Configure the context to use SQLite. options.UseSqlite(Configuration.GetConnectionString("AppDbContextConnection")); // Register the entity sets needed by OpenIddict. // Note: use the generic overload if you need // to replace the default OpenIddict entities. options.UseOpenIddict();});services.AddIdentity(options =>{ options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; // Add more password requirements ...}) .AddEntityFrameworkStores() .AddDefaultTokenProviders();// Configure Identity to use the same JWT claims as OpenIddict instead// of the legacy WS-Federation claims it uses by default (ClaimTypes),// which saves you from doing the mapping in your authorization controller.services.Configure(options =>{ options.ClaimsIdentity.UserNameClaimType = Claims.Name; options.ClaimsIdentity.UserIdClaimType = Claims.Subject; options.ClaimsIdentity.RoleClaimType = Claims.Role;});services.AddOpenIddict() // Register the OpenIddict core components. .AddCore(options => { // Configure OpenIddict to use the Entity Framework Core stores and models. // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. options.UseEntityFrameworkCore() .UseDbContext(); }) // Register the OpenIddict server components. .AddServer(options => { //
2025-04-10Need to support implicit, password or client credentials. options.AllowAuthorizationCodeFlow() .AllowRefreshTokenFlow(); // Register the signing and encryption credentials. options.AddDevelopmentEncryptionCertificate() .AddDevelopmentSigningCertificate(); // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. options.UseAspNetCore() .EnableAuthorizationEndpointPassthrough() .EnableLogoutEndpointPassthrough() .EnableStatusCodePagesIntegration() .EnableTokenEndpointPassthrough(); }) // Register the OpenIddict validation components. .AddValidation(options => { // Import the configuration from the local OpenIddict server instance. options.UseLocalServer(); // Register the ASP.NET Core host. options.UseAspNetCore(); });// Add our custom Email Senderservices.AddSingletonIEmailSender, EmailSender>();Add to Startup.cs, Configure method:{ endpoints.MapRazorPages(); endpoints.MapControllers(); endpoints.MapBlazorHub(); // Enable For Blazor Server only endpoints.MapFallbackToPage("/_Host"); // Enable For Blazor Server only});">app.UseAuthentication();app.UseAuthorization();app.UseEndpoints(endpoints =>{ endpoints.MapRazorPages(); endpoints.MapControllers(); endpoints.MapBlazorHub(); // Enable For Blazor Server only endpoints.MapFallbackToPage("/_Host"); // Enable For Blazor Server only});Set up the database (Sqlite)Create an initial migration and run it:dotnet ef migrations add InitialSchema -o "Data/Migrations"dotnet ef database updateScaffold all the Identity filesUse for only these pages:dotnet aspnet-codegenerator identity -dc BlazorIdentity.Server.Data.AppDbContext -sqlite --files "Account.Register;Account.Login;Account.Logout;Account.ResetPassword"Use to scaffold ALL Identity pages:dotnet aspnet-codegenerator identity -dc BlazorIdentity.Server.Data.AppDbContext -sqliteApply --force to regenerate.First Test !Open the url to check your OpenID specification: continue!Create a redirect componentCreate component /Shared/RedirectToLogin.cs:public class RedirectToLogin : ComponentBase{ [Inject] NavigationManager NavigationManager { get; set; } protected override void OnInitialized() { NavigationManager.NavigateTo($"Identity/Account/Login?returnUrl={Uri.EscapeDataString(NavigationManager.Uri)}", true); }}Create a login display component in /Shared/LoginDisplay.razor: Hello, @context.User.Identity.Name! Logout Login ">@using Microsoft.AspNetCore.Components.Authorization @inject NavigationManagerNavigationAuthorizeView> Authorized> a href="Identity/Account/Manage/Index"> Hello, @context.User.Identity.Name! a> form action="/Identity/Account/Logout?returnUrl=%2F" method="post"> button class="nav-link btn btn-link" type="submit">Logoutbutton> form> Authorized> NotAuthorized> a href="Identity/Account/Login">Logina> NotAuthorized>AuthorizeView>Add the Login Display to MainLayout.razor: About">div class="top-row px-4"> LoginDisplay /> a href=" target="_blank">Abouta>div>
2025-04-20メリークリスマス! 「Javaアドベントカレンダー2020」の最終日です。 クリスマスらしく、楽しい(?)内容になるようにタイトルを変えました。ライブラリの紹介だけと思っていましたが、クリスマスだし、ライブラリを使って役に立つウェブサービスを作ってみよう、というわけです。 RESTfulウェブサービスについて基本から説明しているので、この分野を知りたい人にもおススメですよ。 なお、ライブラリの詳細は、投稿の最後にまとめましたので、気になる方は、先に、そちらを見てください。#1.メールサービスの作成 --- Senderクラス##宛名を変えて、同じメールを一斉に送ってくれるサービス つまりですね、 ○○ 様 こんにちは、・・・。 のような、宛名と敬称付きで、後の内容は同じ、というメールを、名簿にある人達に一斉に送ってくれるサービスです(宛名と敬称がつくところがポイントです)。メール送信は、ライブラリを使うので数行ですし、送信サービスもJakarta RESTの仕組みを使うと簡単に書けるはずです。ちなみに、Jakarta RESTの開発・実行環境はJETを使うと5分で準備できますので、初めての方も是非トライしてみてください。 (ここからJETをダウンロードできます。使い方の解説やビデオもあります。) 一方、サービスを利用するクライアントプログラムは、ウェブ画面を作ってデータをPOSTで送るだけなので、PHPやJavascriptなどあらゆる言語で作成できます。ウェブサービスの強みのひとつです。ただ、JavaにもクライアントAPIがあるので、Javaで作っても何の不都合もありません。 (これから解説するメール送信サービスとクライアントプログラムのプロジェクトはここからダウンロードしてください。また、プロジェクトに同梱している重要.txtファイルには、SMTPサーバーの設定について、注意が書いてあります。必ず、読んだ上で利用してください。) ##こんな形になりました 先に目に見える結果を見ておきましょう。サービスを利用するクライアントの画面です。 上段は、メールの内容を書く領域で、下段は名簿画面です。名簿はExcelファイルをアップロードします。自動アップロードなので、ファイルを選択するだけでアップロードされ、内容が表示されます。左端のチェックボックスは、送信先として使うかどうかの選択欄です。初期は全員が対象になっていますが、不要な宛先はチェックを外したりできます。 ##まず、RESTfulウェブサービスとは GETやPOSTなどで特定のURI(URLとほぼ同じ)にアクセスすると、決められた仕事をやってくれるのがRESTfulウェブサービスです。API的なメソッド呼び出しは一切なく、単なるHTTPアクセスで対応する処理を実行します。GET、POST、PUT、DELETEなどの"モード"と、アクセスするURIの組み合わせで、どんな仕事をするのか切り分けます。アクセスと同時に、URLパラメータやPOSTデータを使って、必要なデータも渡せます。 URIは、 のように構成します。 例えば、 サーバー:localhost:8080 プロジェクト名:mailservice 全体のサービス名:sendmail ひとつのサービス名(ここでは同報メール送信):ccという名前にする場合、URIは次のようになります。 なお、メールにはテキストメールとHTMLメールがあるので、さらにサブパスを切って、次のようにできます。 これはテキストメールの同報送信を行うURIです。HTMLメールなら --- Jakarta REST Jakarta REST(=JAX-RS)を使うので、これはもう簡単です。 まず、プロジェクト名は作ったプロジェクトの名前ですから問題ありませんね。 それ以外の部分は、何と、プロジェクト内のクラスやメソッドに、決められたアノテーションを付けるだけでいいのです。 最初に、サービス(sendmail)の部分ですが、次のように中身のないクラス(名前は任意)を作って、@ApplicationPath("sendmail")を付けます。これだけです。ServerConfig.java@ApplicationPath("sendmail") public class ServiceConfig extends Application { } 次に個別のサービスを表すパスは、別にクラス(名前は任意。Senderクラス)を作って、@Path("cc")を付ければOKです。それから、このSenderクラス内に具体的なサービス機能を作成します。Sender.java@Path("cc")public class Sender { } 最後に、サブパスtextですが、これは、Senderクラス内で実際に処理を担当するメソッド(sendメソッド)に、@Path("text")を付けます。また、POSTでアクセスするメソッドなので、@POSTアノテーションも付けておきます。Sender.java@Path("cc")public class Sender { @Path("text") @POST public Response send(DataSet data){ } } これだけで、POSTアクセスに対応するURI、 そうそう、sendメソッドの引数と戻り値型についての説明を忘れていました。###(1)引数 まず、引数にはどんな型のオブジェクトでも渡せます。ここではDataSet型を渡すことにしています。DataSet型は、今回、作成したクラスで、内容は、メールタイトル、本文、宛先リストなど、送信に必要なすべてのデータを含みます。クライアント側が、POSTアクセスでこのオブジェクトを送信すると、サーバー側はそれをsendメソッドの引数、DataSet dataに受け取ることができます。DataSetクラスのフィールド変数は次のようです。DataSet.javapublic class DataSet { private String sender; // 送信者メールアドレス private String title; // メールタイトル private String message; // 本文 or HTML文 private String type; // "CC" or "BCC" or "TO" private ListRecipient> dlist; // 宛先のリスト フィールド変数のうち、List dlistは宛先のListです。Recipientクラスは次のようにしました。Recipient.javapublic class Recipient { private boolean flag; // 状態の記録用(送信対象とするかなど、用途は任意) private String id; // 番号やIDなど private String name; // 名前 private String email; // メールアドレス private String mr; // 敬称(様、殿、先生など) これらのオブジェクトは、実際には、JSON形式やXML形式のデータに自動変換されて受け渡されます。したがって、Java言語以外のクライアントでも、JSONやXMLを使って、同じものを受け渡しできます。つまり、プログラミング言語の制限はない、ということです。 受け渡すデータ形式を指定しなければ、適当に(といっても大抵はJSON)決められますが、やり取りする形式をアノテーションで指定しておくことができます。次は、JSONかXMLを使うというという指定です。 @Producesで、sendメソッドが返すデータの形式を指定し、@Consumesで、sendメソッドが引数に受け取るデータの形式を指定します。(ざっくりした言い方ですが)このように複数の形式を指定しておくと、クライアント側でどれかに対応できます。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){ } } そうそう、うっかりするところでした。XML形式でやり取りする可能性があるオブジェクトには、クラスに@XmlRootElementというアノテーションを付けます。これだけで、XMLへの変換が自動的に行われるようになります(JSONについては何もする必要はありません)。DataSet.java@XmlRootElementpublic class DataSet {###(2)戻り値型 メソッドはResponse型のオブジェクトを返すのが普通です。Responseクラスは、オールインワンの便利なオブジェクトで、その中には、いろいろな応答ヘッダとその値、実行結果を表すステータス、呼び出し側に返すオブジェクトなどを含めることができます。HTTPの規約に従って送信されるので、クライアントプログラムは、それぞれの言語固有の標準的な方法でそれらを受け取ることができます。Java言語でのやり方は、後ほど説明します。##では、メールを送信しよう! sendメソッドの処理は次のように簡単です。Sender.java @Path("/text") @POST public Response send(DataSet data) { EmailStatus result = semdMails(data); // メール送信処理 reply(data,result); // 送信結果をクライアントにメールで通知する return Response .ok() .build(); } sendMails(data)が、実際にメール送信を行う部分で、reply(data, result)が、送信結果をクライアントに通知する部分です(内容は後で説明)。 ただ、このままでは、ちょっと問題があります。 というのも、送信するメールの件数が何十もあると、送信に数十秒かかることが予想されます。その間、ブロッキングが発生して、クライアントに応答を返すことができなくなります。クライアントはボーッと待たされるので、かなり不安な気持ちになりますね。 ここはマルチスレッドの出番です。ひとまず別のスレッドで非同期に送信処理を実行しつつ、クライアントには、return Response.・・・で「いまから送ります」的なメッセージを返しておきます。 sendMailsメソッドは実行結果を戻り値として返すので、これを使って、replyメソッドで結果をクライアントに知らせます。なので、sendMailsとreplyメソッドは一連の不可分な処理です。そこで、2つをまとめて1つのスレッドで実行するしかありません・・・。###(1)スタイリッシュな非同期処理の書き方 ところで、ComputableFutuerクラスって知っていますか? 上記のような問題をカッコよく処理するためにJava8から導入されたクラスです。クリスマスですからね、ここはカッコよく書いてみましょう。Sender.java @Path("/text") @POST public Response send(DataSet data) { CompletableFuture.supplyAsync(()->sendMails(data)) .thenAccept(result->reply(data,result)); return Response .ok("送信を開始します。この後、送信結果のメールが届きます。") .build(); } どうでしょう、かなりスマートに書けました。 ComputableFutureは、最初に、supplyAsyncメソッドでsendMailsメソッドを非同期に実行し、それが終了して戻り値(プログラムではresult)を返したら、それを使って、thenAcceputメソッドでreplyメソッドを非同期に実行します。thenAcceputメソッドは、前段の非同期処理の結果を受け取り、それを使って、次の処理を非同期に実行することができるメソッドです。 全体は、メソッドチェーンでスタイリッシュな書き方をします。これだけのことを1文でかけるのですから、ComputableFutureって便利ですね。なお、supplyAsyncやthenAcceptは実行するメソッドを引数に取るので、引数はラムダ式で書くことになっています。 「でも、ラムダ式は・・・」という方も大丈夫です。ここは、やっていることの意味がわかればいいだけですから。ラムダ式については、「わかりやすいJava オブジェクト指向徹底解説」で平易に解説していますのでご覧ください (^_^;。また、同書では、ComputableFutuerについても詳しく解説しています。###(2)クライアントへの応答の返し方 次に、sendメソッドでのreturn文の書き方について説明します。sendメソッドでのreturnは、そのままクライアントへ応答として送信される内容になります。returnで返すのは、Resposeクラスのオブジェクトです。次のように、Responseクラスのクラスメソッドを使って作成します。Sender.java return Response .ok("送信を開始します。この後、送信結果のメールが届きます。") .build(); このようにメソッドチェーンで書けるのは、Responseクラスに次のようなクラスメソッドがあるからです。戻り値型メソッド名機 能ResponseBuilderok()ステータス"OK"を応答にセットするResponseBuilderok(obj)ステータス"OK"とオブジェクトobjを応答にセットするResponseBuilderstatus(statusValue)任意のステータスをセットするこれらメソッドの戻り値型は、ResponseBuilder型ですが、このクラスには次のようなメソッドがあるので、オブジェクトやステータスを含めつつ、最終的にbuild()メソッドで、Response型にして返す、という仕組みです。戻り値型メソッド名機 能ResponseBuilderentity(object)応答に任意のオブジェクトを含めるResponseBuilderheader(name, value)応答に任意のヘッダとその値をセットするResponsebuild()Responseインスタンスを作成する いくつか、書き方のパターンを示すと、次のようなやり方があります。書き方意 味Response.ok().build()ステータス"OK"を返すResponse.ok("完了").build()ステータス"OK"と「完了」という文字列を返すResponse.status(Status.BAD_REQUEST).build()ステータス"BAD_REQUEST"を返すResponse.status(Status.BAD_REQUEST).entity("パラメータの誤り").build()"BAD_REQUEST"とメッセージを返すResponse.ok(obj).build()ステータス"OK"とオブジェクトobjを返す メソッドを組み合わせるといろいろ出来るので、柔軟な応答を作成できます。 なお、結果ステータスですが、主なものだけ示すと、次のようなものがあります。ステータスの値としては、Enum型か整数を使うことができます。ステータス意 味Status.OK200 OK Status.CREATED201 作成 Status.ACCEPTED202 承認済 Status.NO_CONTENT204 コンテンツなし Status.BAD_REQUEST400 リクエストが正しくない Status.FORBIDDEN403 禁止されている Status.FOUND302 見つかった Status.NOT_FOUND403 見つからない Status.REQUEST_TIMEOUT408 リクエストがタイムアウトしたStatus.CONFLICT409 競合している ##sendMailsメソッドの作成 では、いよいよ、実際にメールを送信するsendMailsメソッドを作成しましょう。 SendMailsメソッドは、メール送信ライブラリを使うと簡単に作成できます。ライブラリには、BCCやCCで一括送信するメソッドもありますが、各メールごとに宛名と敬称を付けるので、ここでは、個別に1通ずつ送信する必要があります。 そのようなケースに対応するために、ライブラリのEmailSenderクラスには、メールサーバーとの接続、送信、切断をそれぞれ個別に行うメソッド(connect()、xsend()、disconnect())があるので、これらを使って、次のように書きます。Sender.java @Inject EmailSender es; // EmailSenderのインスタンスを得る .... // メールサーバーに接続 es.connect(); // すべての送信先にメール送信 ListRecipient> rs = data.getDlist(); // 宛先リストを取り出す for(Recipient r : rs){ // メール本文に宛名と敬称を付加する処理 ..... // 送信処理 es.xsend(宛先アドレス, 表題, 本文); } // メールサーバーから切断 es.disconnect(); 変数宣言、EmailSender es; に付けたアノテーション、@Injectは、オブジェクトのインスタンスをnewで作らず、システムから受け取る時に使います。変数宣言に、@Injectアノテーションを付けると、システムがEmailSenderクラスのインスタンスを作成し、変数esに入れてくれるのです。 これを「(コンテキストと)依存性注入」といいます。Jakarta EEに限らず、エンタープライズJavaの世界では、「依存性注入」によりインスタンスを取得するのが普通です。実際には、引数のないコンストラクタを使って、インスタンスを生成してくれるのですが、new を書かなくてよいので、なかなか便利です。 後は、EmailSenderのメソッドを使ってメールサーバーに接続し、必要なだけメールを送信し、最後に切断する、というストーリーです。簡単ですね。ただ、これは「あらすじ」ですから、あと少し、内容を検討する必要があります。###(1)本文に宛名と敬称を追加したい どうということもありません。次のようにして送信前に、本文を書き換えてしまいます。Sender.java // 送信 // すべての送信先にメール送信 ListRecipient> rs = data.getDlist(); // 宛先リストを取り出す for(Recipient r : rs){ // メール本文に宛名と敬称を付加する処理 StringBuilder sb = new StringBuilder(); sb.append(r.getName()).append(" ") // 宛名 .append(r.getMr()).append("\n") // 敬称 .append(data.getMessage()); // メール本文 // 送信処理 es.xsend(r.getEmail(), data.getTitle(), sb.toString()); } for文を使って、宛先リストの数だけメールを送信しますが、その際、本文の前に、宛名と敬称(どちらもRecipientクラスのフィールド変数に含まれています)を追加してしまうわけです。###(2)接続、送信、切断の実行結果を返すようにしたい connect()、send()、disconnect()の各メソッドは、実行結果のステータスをEmailStatus型の値(列挙型)で返します。ステータス意 味EmailStatus.DONE正常終了 EmailStatus.INIT_ERROR接続パラメータの誤りEmailStatus.CONNECT_ERROR接続できないEmailStatus.SEND_ERRORメール送信に失敗したEmailStatus.DISCONNECT_ERROR切断時のエラー そこで、接続フェーズでエラーが起こった時は、処理を中止してステータスを返します。 また、メール送信中にエラーが起こった時(メールアドレスの形式不正が原因)は、その宛先(Recipientオブジェクト)にfalseを書き込んでおいて、処理を継続します。つまり、記録を残すわけです。 こうしておくと、後で、reply()メソッドが実行結果をメールで返す時、未送信の宛先を報告できます。 なお、切断フェースは、最後の処理なので単にステータスを返すだけで構いません。 以上から、完成したsendMailsメソッドは次のようになります。Sender.java// 平文・複数メールの送信private EmailStatus sendMails(DataSet data){ // メールサーバーに接続 EmailStatus sts = es.connect(); if(sts!=EmailStatus.DONE) return sts; // エラーなら終了 // すべての送信先にメール送信 ListRecipient> rs = data.getDlist(); // 宛先リストを取り出す for(Recipient r : rs){ StringBuilder sb = new StringBuilder(); // 本文を書き換える sb.append(r.getName()).append(" ") // 宛名 .append(r.getMr()).append("\n") // 敬称 .append(data.getMessage()); // メール本文 sts = es.xsend(r.getEmail(),data.getSender(), data.getTitle(), sb.toString()); if(sts!=EmailStatus.DONE) r.setFlag(false); // 送信エラーの宛先にはfalseを付ける } // メールサーバーから切断 sts = es.disconnect(); // ステータスを返す return sts;} ところで、メールアドレスの形式が不正だと、送信エラーとして捕捉できますが、形式が正しければ、間違いのメールアドレスでも、そのまま送信されてしまいます。後でリターンメールが返ってくるかもしれませんが、それをどうするかは、別の問題です。 一般に、メールアドレスを登録してもらう時は、本人に確認メールを送り、リターンがあれば登録という手順を踏みます。この手順に従えば、メールアドレスの誤りは発生しません。ただ、登録者がメールアドレスを廃棄・変更した場合にはどうにもなりませんので、時々、確認メールを送って調べる必要があります。 もちろん、このシステムにそのようなサービスも追加できますが、やってみませんか?##応答を返すreplyメソッド 最後は、送信終了後に、結果をメールで通知するreply()メソッドを作成します。 reply()メソッドの引数は、送信データ(DataSet)と結果のステータス(EmailStatus)です。Sender.java // 送信結果通知メールの送信 private void reply(DataSet data, EmailStatus status){ .... es.send(data.getSender(), "送信結果のお知らせ", 本文); } メール送信はJ-mailライブラリのsend()メソッドを使うだけです。 xが付かないsend()メソッドは、 send(宛先, タイトル, 本文)の形式で使う、コンビニエンスメソッドで、メールサーバーへの接続、切断も一緒にやってくれます。###(1)通知に必要なデータを集める 送信は簡単ですが、手間なのは本文を作らなければいけないことです。 本文には、送信結果の通知、メールの件数や、送信できなかったメールアドレス、そして、送信した本文のコピーを付けておきます。そこで、最初に、次のようにして、それらのデータを取得します。Sender.java // 送信結果通知メールの送信 private void reply(DataSet data, EmailStatus status){ String result = returnMessage(status); // ①メッセージに変換 int all = data.getDlist().size(); // ②全件数 int done = doneMails(data); // ③送信件数 int errs = all - done; // ④エラー件数 String errors = errMails(data); // ⑤エラーアドレス(CSV) .... es.send(data.getSender(), "送信結果のお知らせ", 本文); } では、実行内容を、順に確認しましょう。####①String result = returnMessage(status); // メッセージに変換 returnMessageメソッドで、実行結果のステータス(status)を、メッセージ文字列に変換して返します。returnMessageメソッドは、次のように、簡単なswitch文で変換しています。Sender.java // 終了コードをメッセージに変換して返す private String returnMessage(EmailStatus status){ switch(status){ case INIT_ERROR: return "ユーザー名・パスワードが設定されていません"; case CONNECT_ERROR: return "SMTPサーバーに接続できないため送信できませんでした"; case SEND_ERROR: return "送信中に予期しないエラーが発生したため送信できませんでした"; case DISCONNECT_ERROR: return "切断時のエラーのため送信できませんでした"; default: return "送信完了しました"; } }####②int all = data.getDlist().size(); // 全件数 data.getDlist()は、メンバのdlist、つまり宛先オブジェクト(Recipient)のリストです。
2025-03-25Size()でその要素数を求めると送信要求されたメール件数が得られます。####③int done = doneMails(data); // 送信件数 doneMailsメソッドで、実際に送信したメールの件数を求めます。 先のsendMailsメソッドで、送信できなかった宛先オブジェクト(Recipient)について、そのflagメンバをfalseにセットしておいたので、doneMailsメソッドは、それを利用します。つまり、dataから宛先オブジェクト(Recipient)のリスト(dlist)を取り出し、ストリーム処理で、flagがtrueになっているRecipientオブジェクトだけを抽出して、最後にその件数を数えます。Sender.java // 送信済件数を返す public int doneMails(DataSet data){ int n = (int)data.getDlist().stream() .filter(Recipient::isFlag) // flagがtrueのものだけ .count(); // 件数を得る return n; } filterメソッドは、条件に合うものだけを抽出するメソッドです。条件は、Recipient::isFlagですが、これは、メソッド参照という書き方です。r->r.isFlag()というラムダ式と同じで、意味は、r.isFlag()==trueです。つまり、Flagフィールドの値がtrueなら、ということです。 ストリーム処理やラムダ式を使わずに、普通の構文でも同じことをかけますが、ストリーム処理は、簡潔に書けるのが利点です。####④int errs = all - done; // エラー件数 全件数から送信済件数を引いて、エラー件数を求めます。####⑤String errors = errMails(data); // エラーアドレス(CSV) errMailsメソッドで、送信エラーになったメールアドレスをコンマ区切りの文字列として取得します。 ③と同じように、ストリーム処理を使います。Sender.java // 送信エラーのアドレスをCSV文字列にして返す public String errMails(DataSet data){ String errors = data.getDlist().stream() .filter(r->!r.isFlag()) // flagがfalseのものだけ .map(Recipient::getEmail) // メールアドレスだけにする .collect(Collectors.joining(",")); // コンマで連結する return errors; } filterメソッドで、今度はflagメンバがfalseであるものだけを抽出します。続くmapメソッドは変換メソッドです。ここでは、Recipientオブジェクトのストリームを、emailのストリームに変換します。Recipient::getEmailは、r->r.getEmail()というラムダ式と同じです。つまり、Recipientオブジェクトrから、emailを取り出してストリームに流すので、Recipientのストリームが、文字列であるemailのストリームに変わります。 最後のcollectメソッドは、高次の集約、変換を行うメソッドで、ここでは、文字列ストリームの要素を、すべてコンマで連結して、ひとつの文字列にする、という処理をおこないます。 以上のことを普通の構文で書くと倍以上のコードを書くことになるでしょう。ストリーム処理は、本当に強力ですね!(「わかりやすいJava オブジェクト指向徹底解説」には、ストリーム処理の詳しい解説があります)###(2)通知メールの本文を作る データがそろったら、あとは簡単です。StringBuilderを使って、次のように文字列を作成します。 Sender.java StringBuilder sb = new StringBuilder(); sb.append(result).append("\n\n") .append("\t受付件数 ").append(all).append("件\n") .append("\t送信件数 ").append(done).append("件\n"); if(errs!=0){ sb.append("\n送信不能 ").append(errs).append("件\n") .append("\t次の宛先は無効です\n\t").append(errors); } es.send(data.getSender(), "送信結果のお知らせ", sb.toString()); 特に説明がいらないくらい単純ですね。これで、次のようなメール本文が作成できます。###(3)完成したreplyメソッド 以上から、完成したreplyメソッドは、次のようです。Sender.java // 送信結果通知メールの送信 private void reply(DataSet data, EmailStatus status){ String result = returnMessage(status); // メッセージに変換 int all = data.getDlist().size(); // 全件数 int done = doneMails(data); // 送信件数 int errs = all - done; // エラー件数 String errors = errMails(data); // エラーアドレス(CSV) StringBuilder sb = new StringBuilder(); sb.append(result).append("\n\n") .append("\t受付件数 ").append(all).append("件\n") .append("\t送信件数 ").append(done).append("件\n"); if(errs!=0){ sb.append("\n送信不能 ").append(errs).append("件\n") .append("\t次の宛先は無効です\n\t").append(errors); } es.send(data.getSender(), "送信結果のお知らせ", sb.toString()); }#2.サービスクライアントの作成 --- SenderClientクラス 今度は、クライアント側の作成です。 Java言語で作る時は、Jakarta REST Client APIを利用します。このクライアントAPIには、URIを指定してGET、POSTなどでアクセスするためのメソッドが用意されています。また、サービスからの戻り値として、Responseクラスのオブジェクトを取得できます。###(1)クライアントクラスの自動生成 IDEとして、NetBeansを使うと、サービスクライアントは、自動生成できます。新しくmailserviceClietnというプロジェクトを作り、何か適当な名前のクラス(ここではMultimailクラス)を作成して、次の手順を実行します。①クラス定義の外側にカーソルを置く ②[ソース]⇒[コードを挿入]と選択する③ダイアログが開くので[RESTクライアントを生成]を選択する④[元のサービス]が選択されていることを確認し、[参照]を押す⑤現在開いているプロジェクトが表示される④RESTサービスプロジェクト(ここではmailservice)を展開し[Sender]を選択して[OK]を押す ⑤元のダイアログに戻るので、クラス名にSenderClientと入力して[OK]を押す 以上で、現在のクラスの中に、内部クラスとしてSenderClientクラスが生成されます。###(2)クライアントクラスの修正 生成されるクラスは次のようです。一部、修正する必要がありますので、説明は後にして、修正箇所を直してしまいましょう。Multimail.javapublic class Multimail { static class SenderClient { private WebTarget webTarget; private Client client; private static final String BASE_URI = " 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(); } } } 修正箇所は、次の通りです。 ①send()メソッドに引数として、DataSet dataを指定する。 ②postメソッドの引数を、post(Entity.entity(ds, MediaType.APPLICATION_JSON)) と書き換える ③closeメソッドに、@preDestroy というアノテーションを付ける 修正すると、次のようになります。Multimail.javapublic class Multimail { static class SenderClient { private WebTarget webTarget; private Client client; private static final String BASE_URI = " 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(); } } } では、順に説明していきましょう。###(3)クライアントクラスの説明####どうして内部クラス? まず、クラスが、内部クラス(静的内部クラス)として生成されたことです。これは、必ず内部クラスにまとめる必要があるわけではありません。実際には外側のMultimailクラス(外部クラスといいます)の中に、これらの変数やメソッドをちりばめて書いても問題ありません。ただ、内部クラスにまとめておくと、外部クラスと守備範囲をはっきり分けることができる、という利点があります。 外部クラスであるMultimailクラスの中身はこれから作成しますが、ユーザーインタフェースを担当するだけのクラスです。つまり、冒頭に見たようなウェブ画面を生成し、利用者に入力してもらいます。そして、入力されたデータは、SenderClientクラスのメソッドを使って、サービスに送信する、という役割分担です。####クライアントクラスの機能#####1.フィールド変数SenderClient.java private WebTarget webTarget; // URIを指定してサービスにアクセスする機能を持つオブジェクト private Client client; // WebTargetを生成するためのクラス // サービス全体を表すURI(@ApplicationPath("sendmail")で指定したURI) private static final String BASE_URI = " WebTargetクラスを使って、サービスにアクセスしますが、WebTargerクラスのインスタンスは、Clientクラスを使って作成します。そこで、フィールド変数として、それぞれの変数を宣言しておきます。また、BASE_URIは、対象とするメール送信サービスのURIで、全体のサービス名までのURIです。#####2.コンストラクタSenderClient.java public SenderClient() { // Clientのインスタンスを生成する client = javax.ws.rs.client.ClientBuilder.newClient(); // (@Path("cc")で指定した)Senderクラスのサービスを表すURIで、 // WebTargetのインスタンスを生成しておく webTarget = client.target(BASE_URI).path("cc"); } コンストラクタでは、ClientクラスとWebTargetクラスのインスタンスを作成し、アクセスの準備をしておきます。Clientクラスのインスタンス生成は、いつもこのように書く決まりきったやり方です。コストのかかる処理なので、コンストラクタでやっておくのが普通です。 WebTargetのインスタンは、SenderクラスのURIを指すようにして生成します。"cc"に続く後の部分のパスは、実際にアクセスする時、pathメソッドで指定します。######3.send()メソッドSenderClient.java public Response send(DataSet data) throws ClientErrorException { return webTarget .path("text") // パスを追加 .request() // アクセスする .post(Entity.entity(data, MediaType.APPLICATION_JSON)); // POSTアクセスでdsを送信する } これは、自動生成なので、ウェブサービス側と同じ名前になります。おかげで対応が分かりやすくなる効果があります。 結局これは、 に、POSTでアクセスするためのメソッドです。そのため、引数をDataSet型のdataと修正したのでした。dataは、POSTで送信するオブジェクトです。 POSTアクセスの手順は、①path("text")によって、末尾に"text"を追加する、②request()でアクセスし、③postでdataを送信する、という順序です。この一連のメソッド呼び出しは、自動生成されました。ただ、postメソッドの引数は、DateSet型のオブジェクトdataを送信するように、手直ししました。 サービスにオブジェクトを送る時は、Entityクラスのentityメソッドを使って、変数とMediaTypeを指定します。MediaTypsとしはこのように、APPLICATION_JSONを指定するといいでしょう。 post(Entity.entity(data, MediaType.APPLICATION_JSON)) これは、オブジェクトを送信する時の一般的な書き方です。#####4.closeメソッドSenderClient.java @PreDestroy public void close() { client.close(); } 処理が終わったら、リソースを開放するために、Clientクラスをcloseしなければいけません。これはそのためのメソッドですが、実行し忘れても大丈夫なように、@PreDestroyアノテーションを付けておきます。このアノテーションを付けておくと、クラスのインスタンスが消去される直前に、close()メソッドが自動実行されるので安心です。 ###(4)ユーザーインタフェース --- JSF ここでは、ユーザーインタフェースはJSF(Jakarta Faces)で作成しています。つまり、MultimailクラスはJSFのクラスです。JSFのコントローラはバッキングビーンというので、Multimailクラスはバッキングビーンです。必要なアノテーションなどを付けた形は次のようです。また、作成する主な機能もコトバで書き込んでいます。SenderClient.java@ViewScoped@Namedpublic class Multimail implements Serializable { // ユーザーインタフェースにかかるフィールド変数の宣言 // SenderClientのインスタンスを生成 // アップロードファイルの受け取り処理(宛先リストのExcelファイルを受け取る) // Excelファイルを読んでRecipientオブジェクトのListを作成する処理 // 入力完了後、SenderClientのpostメソッドを使って、データを送信する処理 static class SenderClient { ... }} ソースコードは、プロジェクトファイルの中にあります(100行くらいです)が、長くなりすぎるので、これ以降の解説は、今回は割愛します。それでも、ここまでで、RESTfulウェブサービスとクライアントの説明は、ひとまずできたように思いますが、どうでしょう。 RESTfulウェブサービスの、さらに詳細な情報は、書籍「 わかりやすいJakarta EE 」にありますので、よろしければご覧ください。 なお、このユーザーインタフェースにかかる部分については、ファイルアップロード処理や、Excelファイルの読み取りなど、面白い内容なので、Qiitaに、別途、投稿する予定です。年明けには投稿できると思いますので、興味のある方は続けて、ご覧ください。##3.メール送信ライブラリ --- J-mail メール送信に使ったのは、J-mailというライブラリです。すでに10年以上に渡って改訂を繰り返していて、今年は、Jakarta EEで使えるように改訂しました。ライブラリのソースコードは、ここからダウンロードできます。また、今回作成したmailserviceプロジェクトにも含まれています。jmailというパッケージがそれです。###◆ソースコードファイルの内容 ライブラリのファイルは、EmailSender.javaと、JmSender.javaの2つが主なものです。EmailSenderはJmSenderを利用するクラスで、公開するAPIを定義しています。JmSenderはJakarta Mail(=Java Mail)を直接操作するクラスです。また、SenderProperties.javaは、インタフェースで、SMTPサーバーアクセスに必要なデータをプロパティファイルで取得するgetProperties()メソッドだけを定義しています。 このインタフェースは、プロパティのデータを、xmlファイルからロードするか、microprofile-config.propatiesファイルを使うかなど、柔軟に選択できるようにするためです。ライブラリでは、2つの実装(ConfigToProperties.java、XmlToProperties.java)を提供しています。CDIビーンなので、beans.xmlファイルで、どちらを利用するか記述します。デフォルトではXmlToPropertiesです。###◆J-mailライブラリのAPI EmailSenderの公開APIです。平文メール、HTMLメールを送信できます。また、どちらのメールも、ファイル添付やCC/BCC送信ができます。さらに、今回の投稿のように、こまかな操作をしたい場合は、接続、メール送信(平文orHTML)、切断の操作を分けて実行するメソッドもあります。####(1)戻り値型 すべてのメソッドは、実行結果を示すステータス(EmailStatus型)を返します。ただし、簡略化のためAPIの表では戻り値の記載を省略しています。戻り値は次のような値です。ステータス意 味EmailStatus.DONE正常終了 EmailStatus.INIT_ERROR接続パラメータの誤りEmailStatus.CONNECT_ERROR接続できないEmailStatus.SEND_ERRORメール送信に失敗したEmailStatus.DISCONNECT_ERROR切断時のエラー####(2)メソッドの引数引数は次のような意味です。引数名意 味String to宛先メールアドレスString subjectメールタイトルString bodyメール本文String htmlメール本文になるHTMLデータString type送信区分:"TO" "CC" "BCC" "TO"は「宛先は1人」の意味String fileDirファイルのあるディレクトリ名List flist添付ファイル名のリスト・htmlは、HTML文書全体を文字列にしたものです・fileDirは、送信ライブラリのあるコンピュータ上の場所でなければいけません####(3)接続、切断をマニュアルで行うAPI・戻り値はすべてEmailStatus型です・引数はすべてString型ですAPI機 能connect()SMTPサーバーに接続するdisconnect()SMTPサーバーから切断するxsend(to, subject, body)1通のテキストメールを送信するxsendHtml(to, subject, body)1通のHTMLメールを送信する・多数のメールを送信するのに適したAPIです・繰り返し送信ができるので、CC/BCC指定はしません・ファイル添付機能はありません。通常はファイルのあるURLをメール本文に含めます。####(4)コンビニエンスAPI(接続・切断処理が不要)・1~数通のメールを手軽に送るためのAPIです・戻り値はすべてEmailStatus型です・引数はList型以外はすべてString型ですAPI機 能send(to, subject, body)メールを送信するsend(to, subject, body, type)CC/BCCを指定してメールを送信するsend(to, subject, body, fileDir, List flist)ファイル添付メールを送信するsend(to, subject, body, fileDir, List flist, String type)CC/BCC指定を指定してファイル添付メールを送信するsendHtml(to, subject, html)HTMLメールを送信するsendHtml(to, subject, html, type)CC/BCCを指定してHTMLメールを送信するsendHtml(to, subject, html, fileDir, List flist)ファイル添付HTMLメールを送信するsendHtml(to, subject, html, fileDir, List flist, type)CC/BCC指定、ファイル添付HTMLメールを送信する
2025-04-11