tomcat-8.0.20 - Javamail don't send emails with attachments - maven

Hi I just wanted to ask for advice I am really thankful to those who can suggest any. Thanks in advance. I am trying to send an email with attachment it is working if I run it in desktop application netbeans maven however if it I run it in tomcat it will send with no error but if I check the email there is no attachment or the attachment is not sending correctly? if I run in netbeans main class it will send with attachment
I didnt get any error in tomcat or netbeans
I dont receive attachment in email when run in tomcat I only recieve message below
------=_Part_0_1437359590.1537185365793--
but if I run in netbeans main class I will receive correct attachment
I dont know why
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6.SEC03</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>2.5.6.SEC03</version>
<scope>test</scope>
</dependency>
<!-- Change plugin specific dependencies here -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.3</version>
</dependency>
<!-- End change plugin specific dependencies here -->
<dependency>
<groupId>net.sourceforge.jexcelapi</groupId>
<artifactId>jxl</artifactId>
<version>2.6.12</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.5.5</version>
<type>jar</type>
</dependency>
</dependencies>
code
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
import jxl.Workbook;
import jxl.format.Alignment;
import jxl.format.Border;
import jxl.format.BorderLineStyle;
import jxl.format.Colour;
import jxl.format.VerticalAlignment;
import jxl.write.Label;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
public class Stack {
public static void main(String[] args) throws IOException {
try {
// *** for Database Connected ***//
Connection connect = null;
Statement s = null;
Class.forName("com.mysql.jdbc.Driver");
connect = DriverManager.getConnection("jdbc:mysql://localhost/mydatabase?user=root&password=root");
s = connect.createStatement();
String sql = "SELECT * FROM customer ORDER BY CustomerID ASC";
ResultSet rec = s.executeQuery(sql);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
WritableWorkbook workbook = Workbook.createWorkbook(baos);
// *** Create Font ***//
WritableFont fontBlue = new WritableFont(WritableFont.TIMES, 10);
fontBlue.setColour(Colour.BLUE);
WritableFont fontRed = new WritableFont(WritableFont.TIMES, 10);
fontRed.setColour(Colour.RED);
// *** Sheet 1 ***//
WritableSheet ws1 = workbook.createSheet("mySheet1", 0);
// *** Header ***//
WritableCellFormat cellFormat1 = new WritableCellFormat(fontRed);
// cellFormat2.setBackground(Colour.ORANGE);
cellFormat1.setAlignment(Alignment.CENTRE);
cellFormat1.setVerticalAlignment(VerticalAlignment.CENTRE);
cellFormat1.setBorder(Border.ALL, BorderLineStyle.THIN);
// *** Data ***//
WritableCellFormat cellFormat2 = new WritableCellFormat(fontBlue);
// cellFormat2.setWrap(true);
cellFormat2.setAlignment(jxl.format.Alignment.CENTRE);
cellFormat2.setVerticalAlignment(VerticalAlignment.CENTRE);
cellFormat2.setWrap(true);
cellFormat2.setBorder(jxl.format.Border.ALL, jxl.format.BorderLineStyle.HAIR, jxl.format.Colour.BLACK);
ws1.mergeCells(0, 0, 5, 0);
Label lable = new Label(0, 0, "Customer Report", cellFormat1);
ws1.addCell(lable);
// *** Header ***//
ws1.setColumnView(0, 10); // Column CustomerID
ws1.addCell(new Label(0, 1, "CustomerID", cellFormat1));
ws1.setColumnView(1, 15); // Column Name
ws1.addCell(new Label(1, 1, "Name", cellFormat1));
ws1.setColumnView(2, 25); // Column Email
ws1.addCell(new Label(2, 1, "Email", cellFormat1));
ws1.setColumnView(3, 12); // Column CountryCode
ws1.addCell(new Label(3, 1, "CountryCode", cellFormat1));
ws1.setColumnView(4, 10); // Column Budget
ws1.addCell(new Label(4, 1, "Budget", cellFormat1));
ws1.setColumnView(5, 10); // Column Used
ws1.addCell(new Label(5, 1, "Used", cellFormat1));
int iRows = 2;
while((rec!=null) && (rec.next())) {
ws1.addCell(new Label(0, iRows, rec.getString("CustomerID"), cellFormat2));
ws1.addCell(new Label(1, iRows, rec.getString("Name"), cellFormat2));
ws1.addCell(new Label(2, iRows, rec.getString("Email"), cellFormat2));
ws1.addCell(new Label(3, iRows, rec.getString("CountryCode"), cellFormat2));
ws1.addCell(new Label(4, iRows, rec.getString("Budget"), cellFormat2));
ws1.addCell(new Label(5, iRows, rec.getString("Used"), cellFormat2));
++iRows;
}
workbook.write();
workbook.close();
System.out.println("Excel file created.");
// Close
try {
if (connect != null) {
s.close();
connect.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sendMail(baos);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void sendMail(ByteArrayOutputStream baos) throws AddressException, MessagingException {
final String username = "your.mail.id#gmail.com";
final String password = "your.password";
Properties props = new Properties();
props.put("mail.smtp.auth", true);
props.put("mail.smtp.starttls.enable", true);
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.port", "587");
props.put("protocol", "smtp");
Session session = Session.getInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("from.mail.id#g_mail.com"));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("to.your.mail#g_mail.com"));
message.setSubject("Testing Subject");
message.setText("PFA");
MimeBodyPart messageBodyPart = new MimeBodyPart();
Multipart multipart = new MimeMultipart();
messageBodyPart = new MimeBodyPart();
String fileName = "attachmentName.xls";
DataSource aAttachment = new ByteArrayDataSource(baos.toByteArray(), "application/octet-stream");
messageBodyPart.setDataHandler(new DataHandler(aAttachment));
messageBodyPart.setFileName(fileName);
multipart.addBodyPart(messageBodyPart);
message.setContent(multipart);
System.out.println("Sending");
Transport.send(message);
System.out.println("Done");
}
}

Related

Spring Boot + Azure SDK, extra characters at the end of the file while copying to Azure Storage account

Some extra characters are added at the end of the file after uploading the file into storage account. And there is no issue with 1.33gb file, observed the size difference for 2.22gb file. Below is the code snippet and pom.xml details.
how to resolve it? let me know any details are needed.
Code:
private boolean upload(final MultipartFile file) throws IOException {
BlobClientBuilder blobClientBuilder = new BlobClientBuilder();
blobClientBuilder.endpoint(STORAGE_URL).connectionString(storageConnectionString); blobClientBuilder.containerName(CONTAINER_NAME);
BlobClient blobClient = blobClientBuilder.blobName(file.getOriginalFilename()).buildClient();
blobClient.upload(file.getInputStream(), file.getSize());
boolean uploadStatus = blobClient.exists();
pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core</artifactId>
<version>1.18.0</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.12.0</version>
<exclusions>
<exclusion>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/io.projectreactor/reactor-core -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.8</version>
<!--$NO-MVN-MAN-VER$ -->
<!-- Please don't remove/degrade the version, possible for compatibility
issues -->
</dependency>
<!-- https://mvnrepository.com/artifact/io.projectreactor.netty/reactor-netty -->
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty</artifactId>
<version>1.0.9</version>
<!--$NO-MVN-MAN-VER$ -->
<!-- Please don't remove/degrade the version, possible for compatibility
issues -->
</dependency>
1.33gb file uploaded correctly but 2.22gb shows some extra characters which leads to increase the size of the file in bytes
Instead of uploading large file directly upload them in zip file or chucks
Try with this code
public static void uploadFilesByChunk() {
String connString = "<conn str>";
String containerName = "<container name>";
String blobName = "UploadOne.zip";
String filePath = "D:/temp/" + blobName;
BlobServiceClient client = new BlobServiceClientBuilder().connectionString(connString).buildClient();
BlobClient blobClient = client.getBlobContainerClient(containerName).getBlobClient(blobName);
long blockSize = 2 * 1024 * 1024; //2MB
ParallelTransferOptions parallelTransferOptions = new ParallelTransferOptions()
.setBlockSizeLong(blockSize).setMaxConcurrency(2)
.setProgressReceiver(new ProgressReceiver() {
#Override
public void reportProgress(long bytesTransferred) {
System.out.println("uploaded:" + bytesTransferred);
}
});
BlobHttpHeaders headers = new BlobHttpHeaders().setContentLanguage("en-US").setContentType("binary");
blobClient.uploadFromFile(filePath, parallelTransferOptions, headers, null, AccessTier.HOT,
new BlobRequestConditions(), Duration.ofMinutes(30));
}
For more details refer this SO Thread
Thanks #ShrutiJoshi-MT for your code snippet.
I am not sure why it is working with 'uploadFromFile' method and having issue with 'upload' method of BlobClient. Below is the final code I am using, it is working for different file extensions. Anyone finds bug or having suggestions for below code please let me know, it helps me a lot.
First copying Multipartfile to local file and then providing the path.
public boolean uploadWithFile(final MultipartFile multipartFile) throws Exception {
logger.info("uploadWithFile started");
File file = null;
try {
String fileName = multipartFile.getOriginalFilename();
file = new File(fileName);
logger.info("uploadWithFile fileName: {}", fileName);
Path path = Paths.get(fileName);
logger.debug("Copying from MultipartFile to file");
try (InputStream inputStream = multipartFile.getInputStream()) {
Files.copy(inputStream, path, StandardCopyOption.REPLACE_EXISTING);
}
logger.debug("Copied from MultipartFile to file");
String filePath = file.getPath();
logger.debug("Copied file name: {}", file.getName());
logger.debug("Copied file Path: {}", filePath);
logger.debug("Copied file length: {}", file.length());
String containerName = "temp";
String storageConnectionString = "<primarykey> or <secondarykey>";
BlobClientBuilder blobClientBuilder = new BlobClientBuilder();
blobClientBuilder.endpoint(STORAGE_URL).connectionString(storageConnectionString);
blobClientBuilder.containerName(containerName);
BlobClient blobClient = blobClientBuilder.blobName(fileName).buildClient();
logger.debug("uploading to storage account");
blobClient.uploadFromFile(filePath);
logger.debug("uploaded to storage account");
boolean uploadStatus = blobClient.exists();
logger.debug("uploaded status : {}", uploadStatus);
logger.info("uploadWithFile ended");
return uploadStatus;
} catch (Exception exception) {
logger.error("uploadWithFile upload failed: {}", exception);
throw exception;
} finally {
if (Objects.nonNull(file) && file.exists()) {
logger.debug("delete file: {}", file.getName());
file.delete();
logger.debug("deleted file: {}", file.getName());
}
}
}```

Upload data file as byte array with Feign

How can I send file in Feign as byte array?
#RequestLine("POST /api/files/{num}/push")
#Headers({"Content-Type: application/zip"})
void pushFile(#Param("num") String num, #Param("file") byte[] file);
This is not working and passing the data in form of json with top element named file.
What can I do to properly receive array of bytes on the other side using this controller method parameter annotation?
#RequestBody byte[] file
You can try OpenFeign/feign-form, simple example:
pom.xml dependencies
<dependencies>
<!--feign dependencies-->
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>10.1.0</version>
</dependency>
<!--jetty to dependencies to check feign request-->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.3.v20170317</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>9.4.3.v20170317</version>
</dependency>
</dependencies>
FeignUploadFileExample.java:
import feign.*;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Type;
import java.util.Map;
import static java.nio.charset.StandardCharsets.UTF_8;
public class FeignUploadFileExample {
public static void main(String[] args) throws Exception {
// start jetty server to check feign request
startSimpleJettyServer();
SimpleUploadFileApi simpleUploadFileApi = Feign.builder()
.encoder(new SimpleFileEncoder())
.target(SimpleUploadFileApi.class, "http://localhost:8080/upload");
// upload file bytes (simple string bytes)
byte[] fileBytes = "Hello World".getBytes();
String response = simpleUploadFileApi.uploadFile(fileBytes);
System.out.println(response);
}
public static final String FILE_PARAM = "file";
// encode #Param("file") to request body bytes
public static class SimpleFileEncoder implements Encoder {
public void encode(Object object, Type type, RequestTemplate template)
throws EncodeException {
template.body(Request.Body.encoded(
(byte[]) ((Map) object).get(FILE_PARAM), UTF_8));
}
}
// feign interface to upload file
public interface SimpleUploadFileApi {
#RequestLine("POST /upload")
#Headers("Content-Type: application/zip")
String uploadFile(#Param(FILE_PARAM) byte[] file);
}
// embedded jetty server
public static void startSimpleJettyServer() throws Exception {
Server server = new Server(8080);
ServletContextHandler handler = new ServletContextHandler(server, "/upload");
handler.addServlet(SimpleBlockingServlet.class, "/");
server.start();
}
// simple servlet get request and return info of received data
public static class SimpleBlockingServlet extends HttpServlet {
protected void doPost(
HttpServletRequest request,
HttpServletResponse response) throws IOException {
String data = new BufferedReader(
new InputStreamReader(request.getInputStream())).readLine();
response.setStatus(HttpStatus.OK_200);
response.getWriter().println("Request header 'Content-Type': " +
request.getHeaders("Content-Type").nextElement());
response.getWriter().println("Request downloaded file data: " + data);
}
}
}
response output:
Request header 'Content-Type': application/zip
Request downloaded file data: Hello World
also #RequestBody it's annotation for REST json body, for files:
#RequestParam("file") MultipartFile file
take a look at Spring Boot Uploading Files
You can use FormData from feign-form to upload file and specify Content-type and name:
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.8.0</version>
</dependency>
#RequestLine("POST /api/files/{num}/push")
void pushFile(#Param("num") String num, #Param("file") FormData file);
Usage example:
byte[] bytes = getFileContent();
FormData file = new FormData("application/zip", "example.zip", bytes);
client.pushFile(num, file);
Or in case you are using spring-cloud-starter-openfeign:
#PostMapping("/api/files/{num}/push")
void pushFile(#PathVariable String num, #RequestPart FormData file);
Tested this for spring-cloud-starter-openfeign but I assume it works without spring considering FormData class lives in form-data dependency.

Issue getting MessageAttributes in spring-cloud SNS/SQS listener

I am using spring-boot-1.5.10 and spring-cloud and using spring-cloud-starter-aws-messaging. I am able to send and receive the message but couldn't get the SNS message attributes. Any help would be really appreciable. Please find the code below,
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.19.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aws.sample</groupId>
<artifactId>aws</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>aws</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Edgware.SR5</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-aws</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-aws-messaging</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
controller.java
#RestController
#RequestMapping(value = "/sns")
#AllArgsConstructor
public class SimpleSnsController {
private NotificationMessagingTemplate notificationMessagingTemplate;
#PostMapping("/saveEmployee")
public String save(#RequestBody Employee employee){
Map<String,Object> headers = new HashMap<>();
headers.put("subject", "send employee details to sqs");
headers.put("name","murugan");
headers.put("traceId","sample");
//notificationMessagingTemplate.sendNotification("sample-sns", employee, "send employee details to sqs");
notificationMessagingTemplate.convertAndSend("sample-sns", employee, headers);
return "success";
}
//#SqsListener(value = "sample-queue")
#SqsListener(value = "${sqs.consumer.name}")
public void receiveSnsSqs(String message, #NotificationMessage Employee employee) {
System.out.println("SNS Consumer received the message::"+message);
System.out.println("SNS Consumer received the notificationMessage::"+employee);
//Here i would like to get the message attribute
}
}
output message received:
{
"Type" : "Notification",
"MessageId" : "ba9dab52-aae8-5940-a3e2-ff8c8458ef52",
"TopicArn" : "arn:aws:sns:XXX",
"Message" : "{\"name\":\"David\",\"age\":\"31\",\"designation\":\"developer\"}",
"Timestamp" : "2019-02-13T14:40:48.501Z",
"SignatureVersion" : "1",
"Signature" : "XXX",
"SigningCertURL" : "XXX",
"UnsubscribeURL" : "XXX",
"MessageAttributes" : {
"traceId" : {"Type":"String","Value":"sample"},
"subject" : {"Type":"String","Value":"send employee details to sqs"},
"name" : {"Type":"String","Value":"murugan"},
"id" : {"Type":"String","Value":"68bf17f2-0f88-4cc5-0609-0ccd42b19ce4"},
"SenderId" : {"Type":"String","Value":"David"},
"contentType" : {"Type":"String","Value":"application/json;charset=UTF-8"},
"timestamp" : {"Type":"Number.java.lang.Long","Value":"1550068848349"}
}
}
I would like to fetch the messageAttribute like name,traceId in consumer which I set in SNS producer. I have browsed a lot but couldn't find any solution. Any help would be really appreciable.
Try enabling Raw Message Delivery.
It will not wrap the original SNS message, and allow you to get the message and headers through the normal annotations #Header, #Headers
https://docs.aws.amazon.com/sns/latest/dg/sns-large-payload-raw-message-delivery.html
If you can't use Raw Message Delivery, I made a new annotation to assist in retrieving a Notification Header
#NotificationHeader
import org.springframework.core.annotation.AliasFor;
import org.springframework.messaging.handler.annotation.ValueConstants;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface NotificationHeader {
/**
* Alias for {#link #name}.
*/
#AliasFor("name")
String value() default "";
/**
* The name of the request header to bind to.
*/
#AliasFor("value")
String name() default "";
/**
* Whether the header is required.
* <p>Default is {#code true}, leading to an exception if the header is
* missing. Switch this to {#code false} if you prefer a {#code null}
* value in case of a header missing.
* #see #defaultValue
*/
boolean required() default true;
/**
* The default value to use as a fallback.
* <p>Supplying a default value implicitly sets {#link #required} to {#code false}.
*/
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
*NotificationHeaderArgumentResolver
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.cloud.aws.messaging.support.NotificationMessageArgumentResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.handler.annotation.support.HeaderMethodArgumentResolver;
import org.springframework.util.Assert;
public class NotificationHeaderArgumentResolver extends HeaderMethodArgumentResolver {
private NotificationMessageArgumentResolver notificationArgumentResolver;
public NotificationHeaderArgumentResolver(ConversionService cs, ConfigurableBeanFactory beanFactory) {
super(cs, beanFactory);
notificationArgumentResolver = new NotificationMessageArgumentResolver(new NoOptMessageConverter());
}
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(NotificationHeader.class);
}
#Override
#Nullable
protected Object resolveArgumentInternal(MethodParameter parameter, Message<?> message, String name)
throws Exception {
Message notificationMessage = (Message) notificationArgumentResolver.resolveArgument(parameter, message);
return super.resolveArgumentInternal(parameter, notificationMessage, name);
}
#Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
NotificationHeader annotation = parameter.getParameterAnnotation(NotificationHeader.class);
Assert.state(annotation != null, "No Header annotation");
return new HeaderNamedValueInfo(annotation);
}
private static class HeaderNamedValueInfo extends NamedValueInfo {
private HeaderNamedValueInfo(NotificationHeader annotation) {
super(annotation.name(), annotation.required(), annotation.defaultValue());
}
}
public static class NoOptMessageConverter implements MessageConverter {
#Override
public Message<?> toMessage(Object payload, #Nullable MessageHeaders headers) {
return null;
}
#Override
public Object fromMessage(Message<?> message, Class<?> targetClass) {
return message;
}
}
}
*NotificationHeaderConfiguration
#Bean
public QueueMessageHandlerFactory queueMessageHandlerFactory() {
QueueMessageHandlerFactory queueMessageHandlerFactory = new QueueMessageHandlerFactory();
queueMessageHandlerFactory.setArgumentResolvers(Collections.singletonList(new NotificationHeaderArgumentResolver(null, null)));
return queueMessageHandlerFactory;
}

How to use XPATH functions(concat, string-join) in JDOM2?

Here I have a simple project.
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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jeecourse.tutorial</groupId>
<artifactId>JDOM2Demo</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>JDOM2Demo</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
</dependencies>
</project>
hr.xml put in the root folder of project:
<HolidayRequest xmlns="http://mycompany.com/hr/schemas">
<Holiday>
<StartDate>2006-07-03</StartDate>
<EndDate>2006-07-07</EndDate>
</Holiday>
<Employee>
<Number>42</Number>
<FirstName>Arjen</FirstName>
<LastName>Poutsma</LastName>
</Employee>
</HolidayRequest>
Source Code to decode:
package com.jeecourse.tutorial;
import java.io.IOException;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.Namespace;
import org.jdom2.filter.Filters;
import org.jdom2.input.SAXBuilder;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;
public class XPathDecode {
private static final String NAMESPACE_URI = "http://mycompany.com/hr/schemas";
public static void main(String args[]) {
XPathExpression<Element> startDateExpression;
XPathExpression<Element> endDateExpression;
XPathExpression<Element> nameExpression;
XPathExpression<Element> nameExpression2;
XPathExpression<Element> fnameExpression;
XPathExpression<Element> lnameExpression;
XPathFactory xFactory;
SAXBuilder sax = new SAXBuilder();
Document holidayRequest = null;
try {
holidayRequest = sax.build("hr.xml");
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Namespace namespace = Namespace.getNamespace("hr", NAMESPACE_URI);
// use the default implementation
xFactory = XPathFactory.instance();
startDateExpression = xFactory.compile("//hr:StartDate", Filters.element(), null, namespace);
endDateExpression = xFactory.compile("//hr:EndDate", Filters.element(), null, namespace);
fnameExpression = xFactory.compile("//hr:FirstName", Filters.element(), null, namespace);
lnameExpression = xFactory.compile("//hr:LastName", Filters.element(), null, namespace);
nameExpression = xFactory.compile("concat(//hr:FirstName,'#',//hr:LastName)", Filters.element(), null, namespace);
//nameExpression2 = xFactory.compile("string-join((//hr:FirstName, //hr:LastName), '#')", Filters.element(), null, namespace);
Element startDate = startDateExpression.evaluateFirst(holidayRequest);
System.out.println(startDate.getValue());
Element endDate = endDateExpression.evaluateFirst(holidayRequest);
System.out.println(endDate.getValue());
Element fname = fnameExpression.evaluateFirst(holidayRequest);
System.out.println(fname.getValue());
Element lname = lnameExpression.evaluateFirst(holidayRequest);
System.out.println(lname.getValue());
Element name = nameExpression.evaluateFirst(holidayRequest);
System.out.println(name.getValue());
//Element name2 = nameExpression2.evaluateFirst(holidayRequest);
System.out.println(name2.getValue());
}
}
But both the nameExpression and nameExpression2 don't work. The output result is:
2006-07-03
2006-07-07
Arjen
Poutsma
Exception in thread "main" java.lang.NullPointerException
at com.jeecourse.tutorial.XPathDecode.main(XPathDecode.java:62)
nameExpression2 causes compilation error. Could you please help. Thanks.
For nameExpression the expression you're using will return a result of type string rather than a node set, so Filters.element() doesn't match and therefore nameExpression.evaluateFirst will return null. You should declare it as an XPathExpression<String> instead, and use an appropriate filter.
XPathExpression<String> nameExpression;
nameExpression = xFactory.compile("concat(//hr:FirstName,'#',//hr:LastName)",
Filters.fstring(), null, namespace);
As for nameExpression2, the string-join function is an XPath 2.0 feature but Jaxen only supports XPath 1.0 (and even if you had XPath 2.0 support you'd hit the same return type problem as with nameExpression as string-join returns a string rather than an element).

MRUnit with Avro NullPointerException in Serialization

I'm trying to test a Hadoop .mapreduce Avro job using MRUnit. I am receiving a NullPointerException as seen below. I've attached a portion of the pom and source code. Any assistance would be appreciated.
Thanks
The error I'm getting is :
java.lang.NullPointerException
at org.apache.hadoop.mrunit.internal.io.Serialization.copy(Serialization.java:73)
at org.apache.hadoop.mrunit.internal.io.Serialization.copy(Serialization.java:91)
at org.apache.hadoop.mrunit.internal.io.Serialization.copyWithConf(Serialization.java:104)
at org.apache.hadoop.mrunit.TestDriver.copy(TestDriver.java:608)
at org.apache.hadoop.mrunit.MapDriverBase.setInputKey(MapDriverBase.java:64)
at org.apache.hadoop.mrunit.MapDriverBase.setInput(MapDriverBase.java:104)
at org.apache.hadoop.mrunit.MapDriverBase.withInput(MapDriverBase.java:218)
at org.lab41.project.mapreduce.ParseMetadataAsTextIntoAvroTest.testMap(ParseMetadataAsTextIntoAvroTest.java:115)
.....
pom snippet:
<dependency>
<groupId>org.apache.mrunit</groupId>
<artifactId>mrunit</artifactId>
<version>0.9.0-incubating</version>
<classifier>hadoop2</classifier>
<scope>test</scope>
</dependency>
<avro.version>1.7.4</avro.version>
<hadoop.version>2.0.0-mr1-cdh4.1.3</hadoop.version>
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro</artifactId>
<version>${avro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-core</artifactId>
<version>${hadoop.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro-mapred</artifactId>
<version>${avro.version}</version>
<classifier>hadoop2</classifier>
</dependency>
Here is an excerpt of the test :
import static org.junit.Assert.*;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.apache.avro.mapred.AvroKey;
import org.apache.avro.hadoop.io.AvroSerialization;
import org.apache.avro.mapred.AvroValue;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mrunit.mapreduce.MapDriver;
import org.apache.hadoop.mrunit.types.Pair;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.lab41.project.domain.DataRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ParseMetadataAsTextIntoAvroTest {
Logger logger = LoggerFactory
.getLogger(ParseMetadataAsTextIntoAvroTest.class);
private MapDriver<LongWritable, Text, AvroKey<Long>, AvroValue<DataRecord>> mapDriver;
#BeforeClass
public static void setUpClass() {
}
#AfterClass
public static void tearDownClass() {
}
#Before
public void setUp() throws IOException {
ParseMetadataAsTextIntoAvroMapper mapper = new ParseMetadataAsTextIntoAvroMapper();
mapDriver = new MapDriver<LongWritable, Text, AvroKey<Long>, AvroValue<DataRecord>>();
mapDriver.setMapper(mapper);
mapDriver.getConfiguration().setStrings("io.serializations", new String[]{
AvroSerialization.class.getName()
});
}
#Test
public void testMap() throws ParseException, IOException {
Text testInputText = new Text(test0);
DataRecord record = new DataRecord();
….
AvroKey<Long> expectedPivot = new AvroKey<Long>(1L);
AvroValue<DataRecord> expectedRecord = new AvroValue<DataRecord>(record);
mapDriver.withInput(new Pair<LongWritable, Text>(new LongWritable(1), testInputText));
mapDriver.withOutput(new Pair<AvroKey<Long>, AvroValue<DataRecord>>(expectedPivot, expectedRecord));
mapDriver.runTest();
}
}
In order to get this to work you have add the AvroSerializatio to the default serailizations. You also have to configure AvroSerializationn.
#Before
public void setUp() throws IOException {
ParseMetadataAsTextIntoAvroMapper mapper = new ParseMetadataAsTextIntoAvroMapper();
mapDriver = new MapDriver<LongWritable, Text, AvroKey<Long>, AvroValue<NetworkRecord>>();
mapDriver.setMapper(mapper);
//Copy over the default io.serializations. If you don't do this then you will
//not be able to deserialize the inputs to the mapper
String[] strings = mapDriver.getConfiguration().getStrings("io.serializations");
String[] newStrings = new String[strings.length +1];
System.arraycopy( strings, 0, newStrings, 0, strings.length );
newStrings[newStrings.length-1] = AvroSerialization.class.getName();
//Now you have to configure AvroSerialization by sepecifying the key
//writer Schema and the value writer schema.
mapDriver.getConfiguration().setStrings("io.serializations", newStrings);
mapDriver.getConfiguration().setStrings("avro.serialization.key.writer.schema", Schema.create(Schema.Type.LONG).toString(true));
mapDriver.getConfiguration().setStrings("avro.serialization.value.writer.schema", NetworkRecord.SCHEMA$.toString(true));
}
This also solve the problem, with merits of shorter and more clear code.
MapDriver driver = MapDriver.newMapDriver(your mapper);
Configuration conf = driver.getConfiguration();
AvroSerialization.addToConfiguration(conf);
AvroSerialization.setKeyWriterSchema(conf, your schema);
AvroSerialization.setKeyReaderSchema(conf, your schema);
Job job = new Job(conf);
job.set... your job settings;
AvroJob.set... your avro job settings;
It may be bug of mrunit, that don't set the io.serializations right
Instead it should have been set by job.setInputFormatClass(AvroKeyInputFormat.class) I think.
You have to add AvroSerialization to the default serializations and configure AvroSerialization.
#Before
public void setUp() throws IOException {
ParseMetadataAsTextIntoAvroMapper mapper = new ParseMetadataAsTextIntoAvroMapper();
mapDriver = new MapDriver<LongWritable, Text, AvroKey<Long>, AvroValue<NetworkRecord>>();
mapDriver.setMapper(mapper);
Configuration configuration = mapDriver.getConfiguration();
// Add AvroSerialization to the configuration
// (copy over the default serializations for deserializing the mapper inputs)
String[] serializations = configuration.getStrings(CommonConfigurationKeysPublic.IO_SERIALIZATIONS_KEY);
String[] newSerializations = Arrays.copyOf(serializations, serializations.length + 1);
newSerializations[serializations.length] = AvroSerialization.class.getName();
configuration.setStrings(CommonConfigurationKeysPublic.IO_SERIALIZATIONS_KEY, newSerializations);
//Configure AvroSerialization by specifying the key writer and value writer schemas
AvroSerialization.setKeyWriterSchema(configuration, Schema.create(Schema.Type.LONG));
AvroSerialization.setValueWriterSchema(configuration, NetworkRecord.SCHEMA$)
}
Answered here: https://issues.apache.org/jira/browse/MRUNIT-181 specifically: https://cwiki.apache.org/confluence/display/MRUNIT/MRUnit+with+Avro

Resources