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

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).

Related

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;
}

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

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");
}
}

.jar created from maven shade plugin throws error when accessing resources under src/main/resources, but running main from exploded .jar works?

Updated Exec Summary of Solution
Following up from the answer provided by Victor, I implemented a Java class that lists the contents of a folder resource in the classpath. Most critical for me was that this had to work when the class path resource is discovered when executing from the IDE, from an exploded uberjar, or from within an unexploded uberjar (which I typically create with the maven shade plugin.) Class and associated unit test available here.
Original Question
I am seeing strange behavior with the maven-shade-plugin and class path resources when I run very simple
java Test program that access a directory structure in a standard maven project like this:
src/main
Test.java
resources/
resource-directory
spark
junk1
zeppelin
junk2
When run from the IDE or the exploded maven shaded .jar (please see below)
it works correctly, which means it prints this:.
result of directory contents as classpath resource:[spark, zeppelin]
The source is as follows:
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
public class Tester {
public void test(String resourceName) throws IOException {
InputStream in = this.getClass().getClassLoader().getResourceAsStream(resourceName);
System.out.println("input stream: " + in);
Object result = IOUtils.readLines(in);
System.out.println("result of directory contents as classpath resource:" + result);
}
public static void main(String[] args) throws IOException {
new Tester().test("resource-directory");
}
}
Now, if I run mvn clean install in my project and run the
maven shaded .jar under ${project.dir}target, I see the following exception:
> java -jar target/sample.jar
Exception in thread "main" java.lang.NullPointerException
at java.io.FilterInputStream.read(FilterInputStream.java:133)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at org.apache.commons.io.IOUtils.readLines(IOUtils.java:1030)
at org.apache.commons.io.IOUtils.readLines(IOUtils.java:987)
at org.apache.commons.io.IOUtils.readLines(IOUtils.java:968)
at Tester.test(Tester.java:16)
at Tester.main(Tester.java:24)
Running with Exploded .jar
> mkdir explode/
> cd explode/
> jar xvf ../sample.jar
......
inflated: META-INF/MANIFEST.MF
created: META-INF/
etc etc.
> ls # look at contents of exploded .jar:
logback.xml META-INF org resource-directory Tester.class
#
# now run class with CLASSPATH="."
(master) /tmp/maven-shade-non-working-example/target/explode > java Tester
input stream: java.io.ByteArrayInputStream#70dea4e
result of directory contents as classpath resource:[spark, zeppelin] # <<<- works !
I have the whole project here: https://github.com/buildlackey/maven-shade-non-working-example
but for convenience, here is the pom.xml(below), with two maven shade configs that I tried.
Note: I don't think the IncludeResourceTransformer would be of any use because my resources are appearing
at the appropriate levels in the .jar file.
<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.foo.core</groupId>
<artifactId>sample</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>sample</name>
<url>http://maven.apache.org</url>
<properties>
<jdk.version>1.8</jdk.version>
<junit.version>4.11</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency><!-- commons-io: Easy conversion from stream to string list, etc.-->
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
<build>
<finalName>sample</finalName>
<plugins>
<!-- Set a compiler level -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
<!-- Maven Shade Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<!-- Run shade goal on package phase -->
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<!-- add Main-Class to manifest file -->
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>Tester</mainClass>
</transformer>
<!-- tried with the stanza below enabled, and also disabled: in both cases, got exceptions from runs -->
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>src/main/resources/</resource>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
anyway, thanks in advance for any help you can provide ~
chris
UPDATE
This didn't work for me in Spring when I tried it (but I'd be interested if anyone has success with a Spring approach). I have a working alternative which I will post shortly. But if you care to comment on how to fix this broken Spring attempt, I'd be very interested.
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.io.IOException;
public class Tester {
public void test(String resourceName) throws IOException {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resourceResolver.getResources("resource-directory/*");
for (Resource resource : resources) {
System.out.println("resource: " + resource.getDescription());
}
}
public static void main(String[] args) throws IOException {
new Tester().test("resource-directory/*");
}
}
The problem is that getResourceAsStream can read only files as a stream, not folders, from a jar file.
To read folder contents from a jar file you might need to use the approach, like described in the accepted answer to this question:
How can I get a resource "Folder" from inside my jar File?
To supplement the answer from my good friend Victor, here is a full code solution. below. The full project is available here
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* List entries of a subfolder of an entry in the class path, which may consist of file system folders and .jars.
*/
public class ClassPathResourceFolderLister {
private static final Logger LOGGER = LoggerFactory.getLogger(ClassPathResourceFolderLister.class);
/**
* For each entry in the classpath, verify that (a) "folder" exists, and (b) "folder" has child content, and if
* these conditions hold, return the child entries (be they files, or folders). If neither (a) nor (b) are true for
* a particular class path entry, move on to the next entry and try again.
*
* #param folder the folder to match within the class path entry
*
* #return the subfolder items of the first matching class path entry, with a no duplicates guarantee
*/
public static Collection<String> getFolderListing(final String folder) {
final String classPath = System.getProperty("java.class.path", ".");
final String[] classPathElements = classPath.split(System.getProperty("path.separator"));
List<String> classPathElementsList = new ArrayList<String> ( Arrays.asList(classPathElements));
return getFolderListingForFirstMatchInClassPath(folder, classPathElementsList);
}
private static Collection<String>
getFolderListingForFirstMatchInClassPath(final String folder, List<String> classPathElementsList) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("getFolderListing for " + folder + " with classpath elements " + classPathElementsList);
}
Collection<String> retval = new HashSet<String>();
String cleanedFolder = stripTrailingAndLeadingSlashes(folder);
for (final String element : classPathElementsList) {
System.out.println("class path element:" + element);
retval = getFolderListing(element, cleanedFolder);
if (retval.size() > 0) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("found matching folder in class path list. returning: " + retval);
}
return retval;
}
}
return retval;
}
private static String stripTrailingAndLeadingSlashes(final String folder) {
String stripped = folder;
if (stripped.equals("/")) { // handle degenerate case:
return "";
} else { // handle cases for strings starting or ending with "/", confident that we have at least two characters
if (stripped.endsWith("/")) {
stripped = stripped.substring(0, stripped.length()-1);
}
if (stripped.startsWith("/")) {
stripped = stripped.substring(1, stripped.length());
}
if (stripped.startsWith("/") || stripped.endsWith("/")) {
throw new IllegalArgumentException("too many consecutive slashes in folder specification: " + stripped);
}
}
return stripped;
}
private static Collection<String> getFolderListing( final String element, final String folderName) {
final File file = new File(element);
if (file.isDirectory()) {
return getFolderContentsListingFromSubfolder(file, folderName);
} else {
return getResourcesFromJarFile(file, folderName);
}
}
private static Collection<String> getResourcesFromJarFile(final File file, final String folderName) {
final String leadingPathOfZipEntry = folderName + "/";
final HashSet<String> retval = new HashSet<String>();
ZipFile zf = null;
try {
zf = new ZipFile(file);
final Enumeration e = zf.entries();
while (e.hasMoreElements()) {
final ZipEntry ze = (ZipEntry) e.nextElement();
final String fileName = ze.getName();
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("zip entry fileName:" + fileName);
}
if (fileName.startsWith(leadingPathOfZipEntry)) {
final String justLeafPartOfEntry = fileName.replaceFirst(leadingPathOfZipEntry,"");
final String initSegmentOfPath = justLeafPartOfEntry.replaceFirst("/.*", "");
if (initSegmentOfPath.length() > 0) {
LOGGER.trace(initSegmentOfPath);
retval.add(initSegmentOfPath);
}
}
}
} catch (Exception e) {
throw new RuntimeException("getResourcesFromJarFile failed. file=" + file + " folder=" + folderName, e);
} finally {
if (zf != null) {
try {
zf.close();
} catch (IOException e) {
LOGGER.error("getResourcesFromJarFile close failed. file=" + file + " folder=" + folderName, e);
}
}
}
return retval;
}
private static Collection<String> getFolderContentsListingFromSubfolder(final File directory, String folderName) {
final HashSet<String> retval = new HashSet<String>();
try {
final String fullPath = directory.getCanonicalPath() + "/" + folderName;
final File subFolder = new File(fullPath);
System.out.println("fullPath:" + fullPath);
if (subFolder.isDirectory()) {
final File[] fileList = subFolder.listFiles();
for (final File file : fileList) {
retval .add(file.getName());
}
}
} catch (final IOException e) {
throw new Error(e);
}
return retval;
}
}

Programmatically resolve Thymeleaf templates

I'm trying to render XML/JSON using Thymeleaf templates. I don't want to render a view using the template name, just want to resolve the template as shown below. Trouble is all I get back is the template name, not it's content.
Set up:
#Bean
SpringResourceTemplateResolver xmlTemplateResolver(ApplicationContext appCtx) {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(appCtx);
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".xml");
templateResolver.setTemplateMode(XML);
templateResolver.setCharacterEncoding(UTF_8.name());
templateResolver.setCacheable(false);
return templateResolver;
}
#Bean
SpringTemplateEngine templateEngine(ApplicationContext appCtx) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(xmlTemplateResolver(appCtx));
return templateEngine;
}
Template (src/main/resources/templates/breakfast-menu.xml):
<?xml version="1.0" encoding="UTF-8"?>
<breakfast_menu>
<food>
<name>${item['name']}</name>
<price>${item['price']}</price>
<description>${item['description']}</description>
<calories>${item['calories']}</calories>
</food>
</breakfast_menu>
Usage:
#Autowired
SpringTemplateEngine templateEngine;
someMethod() {
Context context = new Context();
context.setVariable("item", item);
item.put("name", "Waffle");
String content = templateEngine.process("breakfast-menu", context);
// content == "breakfast-menu". WTH?
}
Using Thymeleaf 3.0.0.BETA01.
I solved this issue with help from the Thymeleaf user forum. For reasons unbeknownst to me, templateEngine.addTemplateResolver doesn't work but templateEngine.setTemplateResolver does. The templates for XML and JSON output are shown below:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<breakfast_menu>
<food>
<name th:text="${item['name']}"></name>
<price th:text="${item['price']}"></price>
<description th:text="${item['description']}"></description>
<calories th:text="${item['calories']}"></calories>
</food>
</breakfast_menu>
JSON:
{
"food": {
"name": "[[${item['name']}]]",
"price": "[[${item['price']}]]",
"description": "[[${item['description']}]]",
"calories": "[[${item['calories']}]]"
}
}
Just for the sake of completeness is here some code to resolve a Thymeleaf template with plain-Thymeleaf without Spring.
This is for HTML but you can adapt it to some other template mode by setting another parameter for the setTemplateMode call:
Maven dependency:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
Java class:
import java.util.Map;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.StringTemplateResolver;
public class ThymeleafResolver {
public static String resolveHTMLWithThymeleaf(String html, Map<String, Object> variables) {
TemplateEngine templateEngine = new TemplateEngine();
StringTemplateResolver templateResolver = new StringTemplateResolver();
templateResolver.setTemplateMode(TemplateMode.HTML);
templateEngine.setTemplateResolver(templateResolver);
Context context = new Context();
context.setVariables(variables);
return templateEngine.process(html, context);
}
}
Testcase Java code:
Map<String, Object> variables = new HashMap<>();
variables.put("mytext", "Hello World");
String result = ThymeleafInterpreter.interpretHTMLWithThymeleaf("<html><body><span th:text=\"${mytext}\"></span></body></html>", variables);
Assert.assertEquals("<html><body><span>Hello World</span></body></html>", result);

EclipseLink Moxy unmarshall and getValueByXPath gives null

I use the below code to get unmarshall and query the unmarshelled object by Xpath.
I am able to get the object after unmarshalling, but while querying by XPath, the value is coming as null.
Do I need to specify any NameSpaceResolver?
Please let me know if you are looking for any further information.
My code:
JAXBContext jaxbContext = (JAXBContext) JAXBContextFactory.createContext(new Class[] {Transaction.class}, null);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StreamSource streamSource= new StreamSource(new StringReader(transactionXML));
transaction = unmarshaller.unmarshal(streamSource, Transaction.class).getValue();
String displayValue = jaxbContext.getValueByXPath(transaction, xPath, null, String.class);
My XML:
<Transaction xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<SendingCustomer firstName="test">
</SendingCustomer>
</Transaction>
Since there are no namespaces in your example you do not need to worry about leveraging a NamespaceResolver. You didn't provide the XPath that you were having trouble with, so I have just picked one in the example below.
JAVA MODEL
Transaction
import javax.xml.bind.annotation.*;
#XmlRootElement(name="Transaction")
public class Transaction {
#XmlElement(name="SendingCustomer")
private Customer sendingCustomer;
}
Customer
import javax.xml.bind.annotation.XmlAttribute;
public class Customer {
#XmlAttribute
private String firstName;
#XmlAttribute
private String lastNameDecrypted;
#XmlAttribute(name="OnWUTrustList")
private boolean onWUTrustList;
#XmlAttribute(name="WUTrustListType")
private String wuTrustListType;
}
DEMO CODE
input.xml
<Transaction xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SendingCustomer firstName="test" lastNameDecrypted="SMITH"
OnWUTrustList="false" WUTrustListType="NONE">
</SendingCustomer>
</Transaction>
Demo
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContext;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jaxbContext = (JAXBContext) JAXBContextFactory.createContext(new Class[] {Transaction.class}, null);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StreamSource streamSource= new StreamSource("src/forum17687460/input.xml");
Transaction transaction = unmarshaller.unmarshal(streamSource, Transaction.class).getValue();
String displayValue = jaxbContext.getValueByXPath(transaction, "SendingCustomer/#firstName", null, String.class);
System.out.println(displayValue);
}
}
Output
test

Resources