Spring 4 Email With Attachment Tutorial

Spring provides org.springframework.mail.javamail.MimeMessageHelper class which act as a Helper class for populating a javax.mail.internet.MimeMessage. It offers support for HTML text content, inline elements such as images, and typical mail attachments. In other words, it helps preparing a Multi-part MimeMessage’s.


As we saw in previous tutorial, Spring’s org.springframework.mail.javamail.MimeMessagePreparator Callback interface comes handy for the preparation of JavaMail MIME messages. We can use both MimeMessagePreparator & MimeMessageHelper to finally create a MimeMessage with attachment or inline content.

Preparing MimeMessage with Attachement

Below method returns a MimeMessagePreparator consisting an MimeMessage which in addition to text, contains an image as an attachment.

	private MimeMessagePreparator getContentWtihAttachementMessagePreparator(final ProductOrder order) {

		MimeMessagePreparator preparator = new MimeMessagePreparator() {

			public void prepare(MimeMessage mimeMessage) throws Exception {
				MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);

				helper.setSubject("Your order on Demoapp with attachement");
				helper.setFrom("customerserivces@yourshop.com");
				helper.setTo(order.getCustomerInfo().getEmail());
				String content = "Dear " + order.getCustomerInfo().getName()
						+ ", thank you for placing order. Your order id is " + order.getOrderId() + ".";

				helper.setText(content);

				// Add a resource as an attachment
				helper.addAttachment("cutie.png", new ClassPathResource("linux-icon.png"));

			}
		};
		return preparator;
	}

Preparing MimeMessage with inline content

Below method returns a MimeMessagePreparator consisting an MimeMessage which includes inline resources. Inline resources can be images or a stylesheet included in your message, not as an attachment.

	private MimeMessagePreparator getContentAsInlineResourceMessagePreparator(final ProductOrder order) {

		MimeMessagePreparator preparator = new MimeMessagePreparator() {

			public void prepare(MimeMessage mimeMessage) throws Exception {
				MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");

				helper.setSubject("Your order on Demoapp with Inline resource");
				helper.setFrom("customerserivces@yourshop.com");
				helper.setTo(order.getCustomerInfo().getEmail());

				String content = "Dear " + order.getCustomerInfo().getName()
						+ ", thank you for placing order. Your order id is " + order.getOrderId() + ".";

				// Add an inline resource.
				// use the true flag to indicate you need a multipart message
				helper.setText("<html><body><p>" + content + "</p><img src='cid:company-logo'></body></html>", true);
				helper.addInline("company-logo", new ClassPathResource("linux-icon.png"));
			}
		};
		return preparator;
	}

Take special note that inline resources are specified using Content-ID [cid:].

In above snippet for inline content, you can see that we are preparing HTML right in helper. Spring provides a better option to handle HTML content using Template libraries [Velocity, Freemaker,..], thereby seperating the domain logic from view[Email template in this case]. You can create a specific Email template for your application, and include that in all your messages. Post Spring 4 Email Tutorial using Velocity/Freemarker Templates shows that in action.

Once you got the MimeMessagePreparator, message can be send using JavaMailSender.

	@Autowired
	JavaMailSender mailSender;

	mailSender.send(preparator);

Shown below is complete example:


Project structure

SpringEmailAttachement_img1

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.websystique.spring</groupId>
	<artifactId>SpringEmailWithAttachmentsExample</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>SpringEmailWithAttachmentsExample</name>

	<properties>
		<springframework.version>4.3.0.RELEASE</springframework.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>${springframework.version}</version>
		</dependency>
		<dependency>
    		<groupId>javax.mail</groupId>
    		<artifactId>javax.mail-api</artifactId>
    		<version>1.5.5</version>
		</dependency>
		<dependency>
    		<groupId>javax.mail</groupId>
    		<artifactId>mail</artifactId>
    		<version>1.4.7</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.2</version>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Configuration Class

package com.websystique.spring.configuration;

import java.util.Properties;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;

@Configuration
@ComponentScan(basePackages = "com.websystique.spring")
public class AppConfig {
	
	//Put Other Application configuration here.
	
	@Bean
	public JavaMailSender getMailSender(){
		JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
		//Using gmail		
		mailSender.setHost("smtp.gmail.com");
		mailSender.setPort(587);
		mailSender.setUsername("Your-gmail-id");
		mailSender.setPassword("Your-gmail-password");
		
		Properties javaMailProperties = new Properties();
		javaMailProperties.put("mail.smtp.starttls.enable", "true");
		javaMailProperties.put("mail.smtp.auth", "true");
		javaMailProperties.put("mail.transport.protocol", "smtp");
		javaMailProperties.put("mail.debug", "true");
		
		
		mailSender.setJavaMailProperties(javaMailProperties);
		return mailSender;
	}
}

Please do not forget to update above configuration with your [email] credentials in order to run this example.

Mail Sender service

package com.websystique.spring.service;

import javax.mail.internet.MimeMessage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.stereotype.Service;

import com.websystique.spring.model.ProductOrder;

@Service("mailService")
public class MailServiceImpl implements MailService {

	@Autowired
	JavaMailSender mailSender;

	@Override
	public void sendEmail(Object object) {

		ProductOrder order = (ProductOrder) object;

		MimeMessagePreparator preparator = getContentWtihAttachementMessagePreparator(order);

		try {
			mailSender.send(preparator);
			System.out.println("Message With Attachement has been sent.............................");
			preparator = getContentAsInlineResourceMessagePreparator(order);
			mailSender.send(preparator);
			System.out.println("Message With Inline Resource has been sent.........................");
		} catch (MailException ex) {
			System.err.println(ex.getMessage());
		}
	}

	private MimeMessagePreparator getContentWtihAttachementMessagePreparator(final ProductOrder order) {

		MimeMessagePreparator preparator = new MimeMessagePreparator() {

			public void prepare(MimeMessage mimeMessage) throws Exception {
				MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);

				helper.setSubject("Your order on Demoapp with attachement");
				helper.setFrom("customerserivces@yourshop.com");
				helper.setTo(order.getCustomerInfo().getEmail());
				String content = "Dear " + order.getCustomerInfo().getName()
						+ ", thank you for placing order. Your order id is " + order.getOrderId() + ".";

				helper.setText(content);

				// Add a resource as an attachment
				helper.addAttachment("cutie.png", new ClassPathResource("linux-icon.png"));

			}
		};
		return preparator;
	}

	private MimeMessagePreparator getContentAsInlineResourceMessagePreparator(final ProductOrder order) {

		MimeMessagePreparator preparator = new MimeMessagePreparator() {

			public void prepare(MimeMessage mimeMessage) throws Exception {
				MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");

				helper.setSubject("Your order on Demoapp with Inline resource");
				helper.setFrom("customerserivces@yourshop.com");
				helper.setTo(order.getCustomerInfo().getEmail());

				String content = "Dear " + order.getCustomerInfo().getName()
						+ ", thank you for placing order. Your order id is " + order.getOrderId() + ".";

				// Add an inline resource.
				// use the true flag to indicate you need a multipart message
				helper.setText("<html><body><p>" + content + "</p><img src='cid:company-logo'></body></html>", true);
				helper.addInline("company-logo", new ClassPathResource("linux-icon.png"));
			}
		};
		return preparator;
	}

}

Main class to run this example

package com.websystique.spring.configuration;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

import com.websystique.spring.model.CustomerInfo;
import com.websystique.spring.model.ProductOrder;
import com.websystique.spring.service.OrderService;

public class SampleEmailApplication {

	public static void main(String[] args) {
		AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

		OrderService orderService = (OrderService) context.getBean("orderService");
		orderService.sendOrderConfirmation(getDummyOrder());
     	((AbstractApplicationContext) context).close();
	}
	
	public static ProductOrder getDummyOrder(){
		ProductOrder order = new ProductOrder();
		order.setOrderId("1111");
		order.setProductName("Thinkpad T510");
		order.setStatus("confirmed");
		
		CustomerInfo customerInfo = new CustomerInfo();
		customerInfo.setName("Websystique Admin");
		customerInfo.setAddress("WallStreet");
		customerInfo.setEmail("websystique@gmail.com");
		order.setCustomerInfo(customerInfo);
		return order;
	}

}
Important: In case you are getting problems while connecting to Gmail, check first if you have two step security enabled in your gmail. Additionally, check if you have received an email from provider[gmail] saying “Sign-in attempt prevented”. If yes, there will be instruction in that email on how to allow/disallow less secured apps to access your mail provider [Turn on/off access to less secured apps].

filler classes

package com.websystique.spring.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.websystique.spring.model.ProductOrder;

@Service("orderService")
public class OrderServiceImpl implements OrderService{

	@Autowired
	MailService mailService;

	@Override
	public void sendOrderConfirmation(ProductOrder productOrder) {
		mailService.sendEmail(productOrder);
	}
	
}


package com.websystique.spring.service;

import com.websystique.spring.model.ProductOrder;

public interface OrderService {

	public void sendOrderConfirmation(ProductOrder productOrder);
	
}

package com.websystique.spring.service;

public interface MailService {

	public void sendEmail(final Object object);
}

package com.websystique.spring.model;


public class ProductOrder {
	
	private String orderId;
	
	private String productName;

	private String status;
	
	private CustomerInfo customerInfo;
	
	public String getOrderId() {
		return orderId;
	}

	public void setOrderId(String orderId) {
		this.orderId = orderId;
	}

	public String getProductName() {
		return productName;
	}

	public void setProductName(String productName) {
		this.productName = productName;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public CustomerInfo getCustomerInfo() {
		return customerInfo;
	}

	public void setCustomerInfo(CustomerInfo customerInfo) {
		this.customerInfo = customerInfo;
	}

	@Override
	public String toString() {
		return "ProductOrder [orderId=" + orderId + ", productName=" + productName + ", status=" + status
				+ ", customerInfo=" + customerInfo + "]";
	}

	
}
package com.websystique.spring.model;

public class CustomerInfo {

	private String name;
	
	private String address;
	
	private String email;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	@Override
	public String toString() {
		return "CustomerInfo [name=" + name + ", address=" + address + ", email=" + email + "]";
	}
	
}

Build & Execute

Run the mail program, and check your [customer] mailbox. You should see two new messages.

SpringEmailAttachement_img2

One with Attachment.

SpringEmailAttachement_img3

Other with Inline content.

SpringEmailAttachement_img4

And finally, the logs.

Jul 06, 2016 12:44:17 AM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@64964f8e: startup date [Wed Jul 06 00:44:17 CEST 2016]; root of context hierarchy
DEBUG: JavaMail version 1.5.5
DEBUG: successfully loaded resource: /META-INF/javamail.default.providers
DEBUG: Tables of loaded providers
DEBUG: Providers Listed By Class Name: {com.sun.mail.smtp.SMTPSSLTransport=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], com.sun.mail.smtp.SMTPTransport=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle], com.sun.mail.imap.IMAPSSLStore=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], com.sun.mail.pop3.POP3SSLStore=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle], com.sun.mail.imap.IMAPStore=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], com.sun.mail.pop3.POP3Store=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle]}
DEBUG: Providers Listed By Protocol: {imaps=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], imap=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], smtps=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], pop3=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle], pop3s=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle], smtp=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]}
DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "smtp.gmail.com", port 587, isSSL false
220 smtp.gmail.com ESMTP p126sm453929wmp.13 - gsmtp
DEBUG SMTP: connected to host "smtp.gmail.com", port: 587

EHLO 192.168.1.2
250-smtp.gmail.com at your service, [81.243.87.175]
250-SIZE 35882577
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250 SMTPUTF8
DEBUG SMTP: Found extension "SIZE", arg "35882577"
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "STARTTLS", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "SMTPUTF8", arg ""
STARTTLS
220 2.0.0 Ready to start TLS
EHLO 192.168.1.2
250-smtp.gmail.com at your service, [81.243.87.175]
250-SIZE 35882577
250-8BITMIME
250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
250-ENHANCEDSTATUSCODES
250-PIPELINING
250 SMTPUTF8
DEBUG SMTP: Found extension "SIZE", arg "35882577"
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "AUTH", arg "LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH"
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "SMTPUTF8", arg ""
DEBUG SMTP: Attempt to authenticate using mechanisms: LOGIN PLAIN DIGEST-MD5 NTLM 
DEBUG SMTP: AUTH LOGIN command trace suppressed
DEBUG SMTP: AUTH LOGIN succeeded
DEBUG SMTP: use8bit false
MAIL FROM:<customerserivces@yourshop.com>
250 2.1.0 OK p126sm453929wmp.13 - gsmtp
RCPT TO:<websystique@gmail.com>
250 2.1.5 OK p126sm453929wmp.13 - gsmtp
DEBUG SMTP: Verified Addresses
DEBUG SMTP:   websystique@gmail.com
DATA
354  Go ahead p126sm453929wmp.13 - gsmtp
Date: Wed, 6 Jul 2016 00:44:22 +0200 (CEST)
From: customerserivces@yourshop.com
To: websystique@gmail.com
Message-ID: <1541786836.2.1467758662936@dragon>
Subject: Your order on Demoapp with attachement
MIME-Version: 1.0
Content-Type: multipart/mixed; 
	boundary="----=_Part_0_592641362.1467758658216"

------=_Part_0_592641362.1467758658216
Content-Type: multipart/related; 
	boundary="----=_Part_1_736374497.1467758658232"

------=_Part_1_736374497.1467758658232
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Dear Websystique Admin, thank you for placing order. Your order id is 1111.
------=_Part_1_736374497.1467758658232--

------=_Part_0_592641362.1467758658216
Content-Type: image/x-png; name=cutie.png
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=cutie.png

WXMAAABIAAAASABGyWs+AAAACXZwQWcAAAB4AAAAcADdnEtTAAAp60lEQVR42u19eXiV1bnv75v2
...............
iVBORw0KGgoAAAANSUhEUgAAAH
------=_Part_0_592641362.1467758658216--
.
250 2.0.0 OK 1467758663 p126sm453929wmp.13 - gsmtp
QUIT
221 2.0.0 closing connection p126sm453929wmp.13 - gsmtp
Message With Attachement has been sent.............................
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "smtp.gmail.com", port 587, isSSL false
220 smtp.gmail.com ESMTP jt1sm1022321wjc.8 - gsmtp
DEBUG SMTP: connected to host "smtp.gmail.com", port: 587

EHLO 192.168.1.2
250-smtp.gmail.com at your service, [81.243.87.175]
250-SIZE 35882577
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250 SMTPUTF8
DEBUG SMTP: Found extension "SIZE", arg "35882577"
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "STARTTLS", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "SMTPUTF8", arg ""
STARTTLS
220 2.0.0 Ready to start TLS
EHLO 192.168.1.2
250-smtp.gmail.com at your service, [81.243.87.175]
250-SIZE 35882577
250-8BITMIME
250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
250-ENHANCEDSTATUSCODES
250-PIPELINING
250 SMTPUTF8
DEBUG SMTP: Found extension "SIZE", arg "35882577"
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "AUTH", arg "LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH"
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "SMTPUTF8", arg ""
DEBUG SMTP: Attempt to authenticate using mechanisms: LOGIN PLAIN DIGEST-MD5 NTLM 
DEBUG SMTP: AUTH LOGIN command trace suppressed
DEBUG SMTP: AUTH LOGIN succeeded
DEBUG SMTP: use8bit false
MAIL FROM:<customerserivces@yourshop.com>
250 2.1.0 OK jt1sm1022321wjc.8 - gsmtp
RCPT TO:<websystique@gmail.com>
250 2.1.5 OK jt1sm1022321wjc.8 - gsmtp
DEBUG SMTP: Verified Addresses
DEBUG SMTP:   websystique@gmail.com
DATA
354  Go ahead jt1sm1022321wjc.8 - gsmtp
Date: Wed, 6 Jul 2016 00:44:24 +0200 (CEST)
From: customerserivces@yourshop.com
To: websystique@gmail.com
Message-ID: <7787114.5.1467758664694@dragon>
Subject: Your order on Demoapp with Inline resource
MIME-Version: 1.0
Content-Type: multipart/mixed; 
	boundary="----=_Part_3_424169858.1467758664139"

------=_Part_3_424169858.1467758664139
Content-Type: multipart/related; 
	boundary="----=_Part_4_1524579013.1467758664139"

------=_Part_4_1524579013.1467758664139
Content-Type: text/html;charset=UTF-8
Content-Transfer-Encoding: 7bit

<html><body><p>Dear Websystique Admin, thank you for placing order. Your order id is 1111.</p><img src='cid:company-logo'></body></html>
------=_Part_4_1524579013.1467758664139
Content-Type: image/x-png
Content-Transfer-Encoding: base64
Content-Disposition: inline
Content-ID: <company-logo>

iVBORw0KGgoAAAANSUhEUgAAAHgAAABwCAYAAADVN7S/AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBI
.......................
mv+KjHUbAAAAAElFTkSuQmCC
------=_Part_4_1524579013.1467758664139--

------=_Part_3_424169858.1467758664139--
.
250 2.0.0 OK 1467758665 jt1sm1022321wjc.8 - gsmtp
QUIT
221 2.0.0 closing connection jt1sm1022321wjc.8 - gsmtp
Message With Inline Resource has been sent.........................
Jul 06, 2016 12:44:25 AM org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
INFO: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@64964f8e: startup date [Wed Jul 06 00:44:17 CEST 2016]; root of context hierarchy

That’s it. As we saw, it’s rather easy to use Spring’s email support in your own application. Feel free to comment, and suggest improvements.

Download Source Code



References

If you like tutorials on this site, why not take a step further and connect me on Facebook , Google Plus & Twitter as well? I would love to hear your thoughts on these articles, it will help me improve further our learning process.

If you appreciate the effort I have put in this learning site, help me improve the visibility of this site towards global audience by sharing and linking this site from within and beyond your network. You & your friends can always link my site from your site on www.websystique.com, and share the learning.

After all, we are here to learn together, aren’t we?