jersey-freemarker - jersey

I'm developing a small tool based on jersey and freemarker, which will enable designers to test there freemarker templates, locally, using some mok-objects.
I'm sorry to write here, but I cant find any documentation about it except some code and javadocs.
To do that I did the following:
1 Dependencies:
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-freemarker</artifactId>
<version>1.9</version>
</dependency>
2 Starting grizzly, telling where to find freemarker templates:
protected static HttpServer startServer() throws IOException {
System.out.println("Starting grizzly...");
Map<String, Object> params = new HashMap<String, Object>();
params.put("com.sun.jersey.freemarker.templateBasePath", "/");
ResourceConfig rc = new PackagesResourceConfig("resource.package");
rc.setPropertiesAndFeatures(params);
HttpServer server = GrizzlyServerFactory.createHttpServer(BASE_URI, rc);
server.getServerConfiguration().addHttpHandler(
new StaticHttpHandler("/libs"), "/libs");
return server;
}
3 Creates the root resource and binds freemarker files:
#Context ResourceConfig resourceConfig;
#Path("{path: ([^\\s]+(\\.(?i)(ftl))$)}")
public Viewable renderFtl (#PathParam("path") String path) throws IOException {
Viewable view = new Viewable("/"+path);
return view;
}
Everything works fine, except that freemarker files are not rendered. I have an empty white page, but file exists and debugger enter inside renderFtl method right.
Do you know how can I do that?
I read a lot of articles here and around the web, but old posts only or articles talking about spring integration and I don't want to integrate it because I don't need it.
I really like Jersey, I think is one of the most complete and power framework on java world, but anytime I try to find documentation on specific features or contribs libraries, I'm lost... There no escape from groups forums :)
Where can I find a complete documentation about it?
Tanks a lot David
Updates:
Trying to solve I understood I cannot use built-in jersey support, because it needs to use files placed in resources tree. So What I did is to build freemarker configuration, in test for now, directly #runtime and returns a StreamingOutput object:
#Path("{path: ([^\\s]+(\\.(?i)(ftl))$)}")
public StreamingOutput renderFtl (#PathParam("path") String path) throws Exception {
Configuration cfg = new Configuration();
// Specify the data source where the template files come from.
// Here I set a file directory for it:
cfg.setDirectoryForTemplateLoading(new File("."));
// Create the root hash
Map<String, Object> root = new HashMap<String, Object>();
Template temp = cfg.getTemplate(path);
return new FTLOutput(root, temp);
}
FTLOutput is here:
This is not a good code, but is for test only...
class FTLOutput implements StreamingOutput {
private Object root;
private Template t;
public FTLOutput(Object root, Template t) {
this.root = root;
this.t = t;
}
#Override
public void write(OutputStream output) throws IOException {
Writer writer = new OutputStreamWriter(output);
try {
t.process(root, writer);
writer.flush();
} catch (TemplateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I have no errors evidence on debug and freemarker tells me that template is found and rendered, but jersey still no give me a result...
I really don't know why!

Why are you using Jersey 1.9? 1.11 is already out, you should update if you can
Have you seen "freemarker" sample from Jersey? It demonstrates simple usecase of using freemarker with jersey.
Where are your resources?
Templates are being found by calling [LastMatchedResourceClass].getResources(...), so if your templates are not accessible as resources, they can't be rendered correctly. you can checkout Jersey source and place some breakpoints into FreemarkerViewProcessor, it should tell you where exactly the problem is..

Related

How to enable Spring Boot to display a list of files under a directory

I have a folder structure /data/reports on a file system, which contains all reports.
How can I configure a SpringBoot application to serve the contents of this file sytem.
Currently I have tried few options, but none working
#Configuration
#EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
#Value(value = "${spring.resources.static-locations:#{null}}")
private String fileSystem;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/data/reports/**")
.addResourceLocations(fileSystem)
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
}
and in application.properties I have defined
spring.resources.static-locations=file:///data/reports
server.servlet.jsp.init-parameters.listings=true
But in both cases, when I try
http://host:port/application/data/reports
I'm getting 404
What am I missing ?
Based on the suggestions given, I realized that one mistake I'm doing is to access the reports via
http://host:port/application/data/reports
instead of
http://host:port/data/reports
if I use application in the request, those calls will go through RequestDispatcher and will try to find for a matching RequestMapping, which does not exist. I think I'm convinced so far.
But the problem I'm seeing now is, I'm getting SocketTimeoutException while trying to read from the resource listed in the URL. I had put some breakpoints in Spring source "ResourceHttpMessageConverter.java"
protected void writeContent(Resource resource, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
try {
InputStream in = resource.getInputStream(); //It is timing out here
try {
StreamUtils.copy(in, outputMessage.getBody());
}
catch (NullPointerException ex) {
// ignore, see SPR-13620
}
The resource is a small text file with 1 line "Hello World". Yet it is timing out.
The resource in the above class is a FileUrlResource opened on file:///c:/data/reports/sample.txt
On the other hand, I tried to read that resource as
File file = new File("c:/data/reports/sample.txt");
System.out.println(file.exists());
URL url = file.toURI().toURL();
URLConnection con = url.openConnection();
InputStream is = con.getInputStream(); //This works
Thanks

Spring SOAP Service accepts username/password: how to require that info via WSDL?

I have a SOAP service implemented using Spring. The service accepts the username/password via the <UsernameToken> element in the SOAP header. That all works fine.
However, the client consuming this SOAP service requests that I include in the WSDL file that the username/password is required via a <Policy> in the <wsdl:binding> element.
I have a method in my code like:
#EnableWs
#Configuration
public class SoapWebServiceConfig extends WsConfigurerAdapter {
#Bean
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema s) {
var wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("MyPort");
wsdl11Definition.setLocationUri("/soap");
wsdl11Definition.setTargetNamespace("http://myservice.com/");
wsdl11Definition.setSchema(s);
return wsdl11Definition;
}
...
}
This produces a WSDL file, but without information in it that the <UsernameToken> is required.
How can I persuade Spring to include the necessary <Policy> information in the WSDL file?
I have looked at the Spring documentation but was unable to determine the incantation necessary. I have looked through the Spring source code but was also not able to see an obvious hook to add the extra information. What I am looking for is something similar to https://stackoverflow.com/a/19726325/220627 but for Spring.
What I do now is a bit of a hack, but I'll put it here in case it's helpful for someone. I'll leave the question open in the hope that someone comes along with a better solution!
private Source addWsdlUsernameTokenPolicy(#Source xml) {
try {
var originalDocumentDOMResult = new DOMResult();
var transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(xml, originalDocumentDOMResult);
var doc = (Document) originalDocumentDOMResult.getNode();
// ... alter the document using DOM methods as necessary ...
return new DOMSource(doc);
}
catch (TransformerConfigurationException | TransformerException e) {
throw new RuntimeException(e);
}
}
#Bean
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema s) {
var wsdl11Definition = new DefaultWsdl11Definition() {
// This is the trick. The getSource() returns the XML of the WSDL.
// You can convert this "Source" (of XML) into a DOM XML structure
// then alter the XML as you like with normal DOM operations.
#Override public Source getSource() {
return addWsdlUsernameTokenPolicy(super.getSource());
}
};
...
return wsdl11Definition;
}

Logging with XQuery

I'm using XQuery 3.0 to transform an incoming message to fit my system.
The XQuery is called from an Apache Camel Route via the transform EIP.
Example:
transform().xquery("resource:classpath:xquery/myxquery.xquery",String.class)
While the transformation works without problems it would be nice, since it's partly very complex, to be able to log some informations directly during the transformation process.
So I wanted to ask if it is possible to log "into" logback directly from XQuery?
I already searched stackoverflow and of course https://www.w3.org/TR/xquery-30-use-cases/ and other sources, but I just couldn't find any information about how to log in Xquery.
My project structure is:
Spring-Boot 2 application
Apache-Camel as Routing framework
Logback as Logging framework
Update: For the integration of XQuery in the Apache-Camel Framework I use the org.apache.camel:camel-saxon-starter:2.22.2.
Update: Because the use of fn:trace was kind of ugly I searched further and now I use the extension mechanism from Saxon to provide different logging functions which can be accessed via xquery:
For more information see the documentation: http://www.saxonica.com/documentation/#!extensibility/integratedfunctions/ext-full-J
Here is what I did for logging (tested with Saxon-HE, Camel is not mandatory, I just use it by coincidence):
First step:
Extend the class net.sf.saxon.lib.ExtensionFunctionDefinition
public class XQueryInfoLogFunctionDefinition extends ExtensionFunctionDefinition{
private static final Logger log = LoggerFactory.getLogger(XQueryInfoLogFunctionDefinition.class);
private final XQueryInfoExtensionFunctionCall functionCall = new XQueryInfoExtensionFunctionCall();
private static final String PREFIX = "log";
#Override
public StructuredQName getFunctionQName() {
return new StructuredQName(PREFIX, "http://thehandofnod.com/saxon-extension", "info");
}
#Override
public SequenceType[] getArgumentTypes() {
return new SequenceType[] { SequenceType.SINGLE_STRING };
}
#Override
public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
return SequenceType.VOID;
}
#Override
public ExtensionFunctionCall makeCallExpression() {
return functionCall;
}
}
Second step:
Implement the FunctionCall class
public class XQueryInfoExtensionFunctionCall extends ExtensionFunctionCall {
private static final Logger log = LoggerFactory.getLogger(XQueryInfoLogFunctionDefinition.class);
#Override
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
if (arguments != null && arguments.length > 0) {
log.info(((StringValue) arguments[0]).getStringValue());
} else
throw new IllegalArgumentException("We need a message");
return EmptySequence.getInstance();
}
}
Third step:
Configure the SaxonConfiguration and bind it into the camel context:
public static void main(String... args) throws Exception {
Main main = new Main();
Configuration saxonConfig = Configuration.newConfiguration();
saxonConfig.registerExtensionFunction(new XQueryInfoLogFunctionDefinition());
main.bind("saxonConfig", saxonConfig);
main.addRouteBuilder(new MyRouteBuilder());
main.run(args);
}
Fourth step:
Define the SaxonConfig in your XQueryEndpoint:
.to("xquery:test.xquery?configuration=#saxonConfig");
Fifth step:
Call it in your xquery:
declare namespace log="http://thehandofnod.com/saxon-extension";
log:info("Das ist ein INFO test")
Original post a.k.a How to overwrite the fn:trace Funktion:
Thanks to Martin Honnen I tried the fn:trace function. Problem was that by default it logs into the System.err Printstream and that's not what I wanted, because I wanted to combine the fn:trace function with the Logback Logging-Framework.
So I debugged the net.sf.saxon.functions.Trace methods and came to the following solution for my project setup.
Write a custom TraceListener which extends from net.sf.saxon.trace.XQueryTraceListener and implement the methods enter and leave in a way that the InstructionInfo with constructType == 2041 (for user-trace) is forwarded to the SLF4J-API. Example (for only logging the message):
#Override
public void enter(InstructionInfo info, XPathContext context) {
// no call to super to keep it simple.
String nachricht = (String) info.getProperty("label");
if (info.getConstructType() == 2041 && StringUtils.hasText(nachricht)) {
getLogger().info(nachricht);
}
}
#Override
public void leave(InstructionInfo info) {
// no call to super to keep it simple.
}
set the custom trace listener into your net.sf.saxon.Configuration Bean via setTraceListener
Call your xquery file from camel via the XQueryEndpoint because only there it is possible to overwrite the Configuration with an option: .to("xquery:/xquery/myxquery.xquery?configuration=#saxonConf"). Unfortunately the transform().xquery(...) uses it's own objects without the possibility to configure them.
call {fn:trace($element/text(),"Das ist ein Tracing Test")} in your xquery and see the message in your log.

How to live reload without restart on additional static resources in spring boot?

I'm attempting to set a new resource location on a spring boot project that is enabled for live reloads but not for application restarts. I'm able to add additional resources, but any change made in the new directory causes the application to restart.
The documentation on this seems light. I must be misinterpreting something.
My layout looks like this:
- src
- main
- java
- resources
- static
- web
- dist
And my application class looks like this:
#Bean
WebMvcConfigurer configurer () {
return new WebMvcConfigurerAdapter() {
#Override
public void addResourceHandlers (ResourceHandlerRegistry registry) {
registry.addResourceHandler("/dist/**")
.addResourceLocations(""file:web/dist/"")
.setCachePeriod(0);
}
};
}
public static void main(String[] args)
{
SpringApplication app = new SpringApplication(Application.class);
app.addListeners(new PropertyLogger());
Properties properties = new Properties();
properties.setProperty("spring.devtools.restart.additional-paths", "web/dist/");
properties.setProperty("spring.devtools.restart.additional-exclude", "/dist/**");
app.setDefaultProperties(properties);
app.run(args);
}
I've read through several similar questions and this seems to be the best I can do. Is it possible to enable live reload on dist without a full application restart?
By the way, my IDE is IntelliJ. I'm beginning to wonder if IntelliJ needs to exclude the dist directory. I'll followup if that's the case.
I've beaten this to death and have finally found a working solution.
Properties properties = new Properties();
properties.setProperty("spring.devtools.restart.additional-paths", "web/");
properties.setProperty("spring.devtools.restart.additional-exclude", "dist/**");
app.setDefaultProperties(properties);
Defining the web directory as an additional path combined with the pattern used for additional exclude does the trick.
I won't mark this as accepted unless there are upvotes to back my conclusion.

Specifying relative path in application.properties in Spring

Is there a way we can lookup file resources using relative path in application.properties file in Spring boot application as specified below
spring.datasource.url=jdbc:hsqldb:file:${project.basedir}/db/init
I'm using spring boot to build a upload sample, and meet the same problem, I only want to get the project root path. (e.g. /sring-boot-upload)
I find out that below code works:
upload.dir.location=${user.dir}\\uploadFolder
#membersound answer is just breaking up the hardcoded path in 2 parts, not dynamically resolving the property. I can tell you how to achieve what you're looking for, but you need to understand is that there is NO project.basedir when you're running the application as a jar or war. Outside the local workspace, the source code structure doesn't exist.
If you still want to do this for testing, that's feasible and what you need is to manipulate the PropertySources. Your simplest option is as follows:
Define an ApplicationContextInitializer, and set the property there. Something like the following:
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext appCtx) {
try {
// should be /<path-to-projectBasedir>/build/classes/main/
File pwd = new File(getClass().getResource("/").toURI());
String projectDir = pwd.getParentFile().getParentFile().getParent();
String conf = new File(projectDir, "db/init").getAbsolutePath();
Map<String, Object> props = new HashMap<>();
props.put("spring.datasource.url", conf);
MapPropertySource mapPropertySource = new MapPropertySource("db-props", props);
appCtx.getEnvironment().getPropertySources().addFirst(mapPropertySource);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}}
Looks like you're using Boot, so you can just declare context.initializer.classes=com.example.MyApplicationContextInitializer in your application.properties and Boot will run this class at startup.
Words of caution again:
This will not work outside the local workspace as it depends on the source code structure.
I've assumed a Gradle project structure here /build/classes/main. If necessary, adjust according to your build tool.
If MyApplicationContextInitializer is in the src/test/java, pwd will be <projectBasedir>/build/classes/test/, not <projectBasedir>/build/classes/main/.
your.basedir=${project.basedir}/db/init
spring.datasource.url=jdbc:hsqldb:file:${your.basedir}
#Value("${your.basedir}")
private String file;
new ClassPathResource(file).getURI().toString()

Resources