Tapestry how to serve a dynamically generated XML file? - file

I am having problems related to Tapestry on my final year project (Maven + Hibernate + Spring + Tapestry). I hope somebody could help on it.
I generate an XML file (its content is my MySql DB data in a custom format I created) on my service layer (I tried it and it is propperly generated: it is working). I tested it from my Junit tests. The problem is that I am unable to get it working from view layer, using Tapestry.
I tried this but unsuccessfully
I think that it is because file does not exist already: it is dinamically generated when user clicks on "Download XML" link.
Here you are my source code (user clicks on a link which points to this page).
POJO for the page (xmlService.exportXml is the method from my service layer which creates the XML file):
public class DownloadAll {
#Component
private Form xmlDownloadForm;
#Property
private File xmlFile;
#Property
#SessionState(create=false)
private UserSession userSession;
#Inject
private XmlService xmlService;
public StreamResponse onSubmit() {
xmlFile = xmlService.exportXml(userSession.getUserProfileId());
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime());
InputStream input = DownloadAll.class.getResourceAsStream("exportedData-"
+ userSession.getLoginName() + timeStamp + ".xml");
return new XMLAttachment(input);
}
}
And this is the page template:
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
t:type="Layout" t:pageTitle="title"
xmlns:p="tapestry:parameter"
t:menuExplanation="menuExplanation">
<form t:type="Form" t:id="xmlDownloadForm">
<input type="submit" value="${message:download}"/>
</form>
</html>
Does anybody know how to make it working? Thanks and regards.
Edit: File is generated (I can see it in the folder) when I submit the form but file is not served. I get this error instead:
org.apache.tapestry5.runtime.ComponentEventException Class
es.udc.decompras.web.pages.xml.util.XMLAttachment has been transformed
and may not be directly instantiated.
XMLAttachment is the same than JPEGAttachment.java from this link Here you are the source code:
public class XMLAttachment extends AttachmentStreamResponse {
public XMLAttachment(InputStream is, String args) {
super(is, args);
this.contentType = "application/xml";
this.extension = "xml";
}
public XMLAttachment(InputStream is) {
super(is);
this.contentType = "application/xml";
this.extension = "xml";
}
}

Only pages can be in your "pages" package. Move XMLAttachment class to any package not managed by tapestry (eg NOT base, components or pages).
Tapestry performs byte code magic on the managed packages and uses a special classloader to load them which is not compatible for utility classes etc.

Related

Load Freemarker Template from External Url

I have implemeted spring boot app where we need to send email using freemarker.
App is going to deployed on google app engine, where file structure is not available to store the templates. So, I saved templates on google storage with public access. But not able to load in freemarker template engine.
freeMarkerConfiguration.setDirectoryForTemplateLoading(new File("/home/dnilesh/Downloads/helloworld-springboot/src/main/resources/"));
content.append(FreeMarkerTemplateUtils.processTemplateIntoString(
freeMarkerConfiguration.getTemplate("Email.html"),model));
This above configuration will work on development env. But on Google app engine I dont have directory to store template.
I tried this :
freeMarkerConfiguration.setDirectoryForTemplateLoading(new File("https://storage.googleapis.com/nixon-medical/"));
content.append(FreeMarkerTemplateUtils.processTemplateIntoString(
freeMarkerConfiguration.getTemplate("Email.html"),model));
But freemarker not loading Template from External URL. How can I load this?
For external URL,you should use URLTemplateLoader:
If your template source accesses the templates through an URL, you needn't implement a TemplateLoader from scratch; you can choose to subclass freemarker.cache.URLTemplateLoader instead and just implement the URL getURL(String templateName) method.
See code sample
Though there is an accepted answer, I did not find the integration with spring boot. So I have done this
I was trying to read Freemarker template from google cloud storage with spring boot application.
So, I have done the following and it worked for me.
Implement URLTemplateLoader and only override getURL method On
FreeMarkerConfigurer bean, set pretemplate as custom template
CloudTemplateLoader - my custom loader
public class CloudTemplateLoader extends URLTemplateLoader {
private URL root;
public CloudTemplateLoader(URL root) {
super();
this.root = root;
}
#Override
protected URL getURL(String template) {
try {
return new URL(root, "/" + template);
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
}
FreeMarkerConfigurer Bean to set my custom loader
#Bean
public FreeMarkerConfigurer freeMarkerConfigurer() throws MalformedURLException {
FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
Properties properties = new Properties();
properties.setProperty("localized_lookup", "false");
freeMarkerConfigurer.setFreemarkerSettings(properties);
freeMarkerConfigurer.setPreTemplateLoaders(new CloudTemplateLoader(new URL("https://storage.googleapis.com")));
freeMarkerConfigurer.setDefaultEncoding("UTF-8");
return freeMarkerConfigurer;
}
And my controller is following
#GetMapping
public String index() {
return "<bucket-name>/index.ftl";
}
Don't forget to upload the template on the google cloud or s3. For test purpose, I added public access on my index.ftl file.
You can use a Thymeleaf resolver to load the external files.
https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html

How to read data from csv file and store it in the database ? Spring Boot

For example I have an entity of Users consisting of username,phonenumber and address.
I want to read all these fields from a csv file and store it in the respective table in the database?
Can any one Help me by describing how to do that? Or is there any documentation on how to do that?
I assume that you want the user to upload the file from some UI. Depending on the exact way in which you build UI, you might:
Send a multipart HTTP POST request (mime type = multipart/form-data; see What should a Multipart HTTP request with multiple files look like?)
Send a simple POST request with the body directly containing the file contents.
Either of the two can be fairly easily solved using Spring.
Assuming that we have the following entity:
#Data
#Entity
public class User {
#Id
private String username;
private String phoneNumber;
private String address;
}
And we define a Spring Data repository for accessing the database:
public interface UserRepository extends JpaRepository<User, String> {
}
For the CSV deserialization, I would propose using Jackson. Spring Boot already comes with Jackson, but we need to add a data format extension for CSV in your pom:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
</dependency>
This way, we can create a simple utility method that knows to read a CSV for a given POJO class:
public class CsvUtils {
private static final CsvMapper mapper = new CsvMapper();
public static <T> List<T> read(Class<T> clazz, InputStream stream) throws IOException {
CsvSchema schema = mapper.schemaFor(clazz).withHeader().withColumnReordering(true);
ObjectReader reader = mapper.readerFor(clazz).with(schema);
return reader.<T>readValues(stream).readAll();
}
}
And then we create a simple Rest Controller for handling the upload(s):
#RestController
#RequiredArgsConstructor
public class UserController {
private final UserRepository repository;
#PostMapping(value = "/upload", consumes = "text/csv")
public void uploadSimple(#RequestBody InputStream body) {
repository.saveAll(CsvUtils.read(User.class, body));
}
#PostMapping(value = "/upload", consumes = "multipart/form-data")
public void uploadMultipart(#RequestParam("file") MultipartFile file) {
repository.saveAll(CsvUtils.read(User.class, file.getInputStream()));
}
}
In case you also need some HTML for doing the upload, the following snippet is a minimal working example:
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" id="file" />
<input type="submit" name="submit" value="Submit" />
</form>
Later edit: If you want to also validate the incoming data, first annotate your entity class attribute with javax.validation constraints. For example:
#Data
#Entity
class User {
#Id
#Email
#NotEmpty
private String username;
#Pattern(regexp = "[0-9 ()-]{4,12}")
private String phoneNumber;
private String address;
}
Then you can chose where do perform the actual validation call:
Service level. This is what I personally recommend in this case, as it is fairly easy to setup and would perform the validations early enough. For this you introduce a simple #Service class between the controller and the repository.
#Service
#Validated
#RequiredArgsConstructor
class UserService {
private final UserRepository repository;
public void saveAll(#Valid List<User> users) {
repository.saveAll(users);
}
}
You would then use this service class instead of the repository inside the controller class.
Repository level: here you don't actually need to do anything. If you annotate your entity classes with validation constraints, Hibernate would automatically call the validation in a pre-insert listener (BeanValidationEventListener).
Controller level. This is trickier to setup. Move the CSV deserialization in a custom HttpMessageConverter. You should also add this converter to the FormHttpMessageConverter (such that it can use it to deserialize a part of the multi-part request). You could then theoretically just declare the #Valid List<User> as inputs for your controller methods and Spring would automatically call the message converter based on the mime type and then call the validator. See Add JSON message converter for multipart/form-data for an example.
Lastly you can always manually call the validation whenever you want: Manually call Spring Annotation Validation.
You can achieve that easily with openCSV.
For a known POJO User, you just map the CSV columns(headers in your case) to corresponding fields in the POJO.
Just add the following to you dependency, check for the latest version for your application though.
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>4.1</version>
</dependency>
this link guides you through https://www.geeksforgeeks.org/mapping-csv-to-javabeans-using-opencsv/

Calling data from one class to another class - framework for JUnit

Please forgive me as I am still very new to the world of test automation. I have started out by using Selenium WebDriver with JUnit4, predominately on windows OS, although I have modified my scripts and ran them on Mac.
I want to be able to create a set of classes containing set data such as usernames, passwords, default url . Perhaps even calling them from an excel file, but for now Im happy to store the data in classes and then pass that data into other test classes. Im guessing this would be a framework of some sort.
Currently I am writing classes that all begin with something like:
public class ExampleSQATest{
public static Chromedriver chrome;
#BeforeClass
public static void launchBrowser(){
System.setProperty("webdriver.chrome.driver", "chromedriver/chromedriver.exe");
chrome = new ChromeDrievr();
}
#Test
public void aLogin(){
chrome.manage().window().maximize();
chrome.navigate().to("http://mydummywebsite.com");
new WebDriverWait(chrome, 10).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector
("input#UserName")));
WebElement username = chrome.findElementByCssSelector("input#UserName");
username.sendKeys("username");
WebElement password = chrome.findElementByCssSelector("input#Password");
password.sendKeys("password");
WebElement submit = chrome.findElementByCssSelector("input[type='submit']");
submit.click();
}
}
I will then proceed to write further test methods which requires entering data, but I'd like to be able to call this data from somewhere else that is already predefined.
Can anyone provide any suitable suggestions to investigate so I can learn. Something that is a guide or tutorial. Nothing too advanced, just something that helps me get started by advising me how to set a class of methods to be called by other classes and how it all links together as a framework.
Many thanks in advance.
One way to do this
public abstract class TestBase
{
private readonly INavigationManager navMgr;
private readonly IWindowNavigator windowNav;
private readonly ILoginManager loginMgr;
// All your stuff that is common for all the tests
protected TestBase()
{
this.navMgr = WebDriverManager.Get<INavigationManager>();
this.windowNav = WebDriverManager.Get<IWindowNavigator>();
this.loginMgr = WebDriverManager.Get<ILoginManager>();
}}
[TestFixture]
internal class QueriesTest : TestBase
{
private QueryTests queryTests;
[SetUp]
public void Setup()
{
this.queryTests = WebDriverManager.Get<QueryTests>();
// all the stuff you run specific before tests in this test class.
}
}
Assuming you have created test classes in webdriver-junit4, Use following two classes to call your test classes (Note-Import junit annotations)
1)Create test suite class as -
#RunWith(Suite.class)
#Suite.SuiteClasses({
YourTestClass1.class,
YourTestClass2.class,[you can add more tests you have created...]
})
public class TestSuiteJU {
}
2)Create class to call suite created above as-
public class TestExecution {
public static void main(String[] args) {
Result result = JUnitCore.runClasses(TestSuiteJU .class);
}
}

What is the best way to store user specific files in Wicket?

I am creating a file that is user specific. This file is basically a results csv that is created with the option for the user to download or not. When the user leaves the page, or ends their session I want to be able to delete this file. What is the best way to handle this?
Currently I am using the File class for Java.
Thanks!
You don't have to write a file in the first place. Create the content on the fly and stream it back to the client. Wicket has a few classes in the package org.apache.wicket.request.resource to help with that.
As a starting point, look at Wicket 6 resource management and Wicket 1.5 Mounting resources
You basically mount a resource in the WicketApplication.init():
mountResource("somePath/${param1}/${param2}", new SomeResourceReference());
Than the SomeResourceReference:
public class SomeResourceReference extends ResourceReference {
#Override
public IResource getResource() {
return new SomeResource();
}
}
And finally in SomeResource:
public class SomeResource extends AbstractResource {
#Override
public AbstractResource.ResourceResponse
newResourceResponse(Attributes attributes) {
// get the parameters
PageParameters parameters = attributes.getParameters();
final String param1 = parameters.get("param1").toStringObject();
AbstractResource.ResourceResponse response
= new AbstractResource.ResourceResponse();
response.setContentType("application/CSV");
response.setCacheDuration(Duration.NONE);
response.setCacheScope(WebResponse.CacheScope.PRIVATE);
response.setContentDisposition(ContentDisposition.INLINE);
response.setWriteCallback(new AbstractResource.WriteCallback() {
#Override
public void writeData(final Attributes attributes) throws IOException {
// create your data here
attributes.getResponse().write(dataAsString);
}
});
return response;
}
}
Wicket doesn't control destroying the session. It is the concern of the servlet container you are using.
If you want to create a file in Wicket and delete the file when the session is destroyed or user want logout, it has two parts:
User logout (in Wikcet)
Store the file path or the file reference in the WebSession (Wicket)
Override the method invalidate() of your WebSession or AutheticatedWebSession, see http://ci.apache.org/projects/wicket/apidocs/6.x/org/apache/wicket/protocol/http/WebSession.html#invalidate%28%29
Session destroyed
Store the file path or the file reference into the container session and write your listener and add it to the your servlet context (e.g. tomcat using web.xml file).
See http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionListener.html

How can I get AngularJS working with the ServiceStack FallbackRoute attribute to support HTML5 pushstate Urls?

I am building a client/server solution, using an AngularJS Single Page App as the client component and a Self-Host ServiceStack RESTful API as the server component. A single Visual Studio Console Application Project holds HTML and JavaScript files for the AngularJS component, along with C# classes for bootstrapping the ServiceStack AppHost (I have devolved Interface and Service responsibilities to separate Visual Studio Projects).
I have set all HTML and JavaScript files to have a 'Build Action' of 'None' and a 'Copy to Output Directory' of 'Copy if newer'.
Everything is working very well as long as I am prepared to put up with having a '#' in my site URLs. I would like to eliminate this by using HTML5 pushstate URLs.
Effectively this means I need to persuade ServiceStack to serve up my default Single Page App HTML shell page whenever a non-existent route is requested. There is now a FallbackRoute attribute available in ServiceStack which appears to have been added exactly for this purpose.
However, I am unsure how to use it. I have found people asking similar questions here, here and here. But the answers given were all before the new FallbackRoute attribute arrived.
Essentially, I am looking for a simple, yet complete example of how to use the FallbackRoute attribute to ensure any requests to non-existent routes are redirected to a single static HTML page.
The RazorRockstars.Web has an implementation. I'll modify it to use a wildcard path and a default view:
[FallbackRoute("/{Path*}")]
public class Fallback
{
public string Path { get; set; }
public string PathInfo { get; set; }
}
public class RockstarsService : Service
{
[DefaultView("Index")]
public object Any(Fallback request)
{
request.PathInfo = base.Request.PathInfo;
return request;
}
// ...
}
Since this is a service it requires a View page (details here) rather than a content page.
In the RockStars example, I can't determine what view would be rendered for the FallBackResponse, but setting the view explicitly should be all you need.
The [DefaultView("Index")] attribute I added to the Any method maps the response to a Views/Index.cshtml file. The Index.cshtml file can be empty but for a template declaration, and the complete markup for your single page app can be in your template file (i.e. _Layout.cshtml)
Without Razor
Read the html into a string and return it, while setting the content type to "text/html" with an attribute, see wiki docs on service return types
public class RockstarsService : Service
{
static string readContents;
[AddHeader(ContentType = "text/html")]
public string Any(Fallback request)
{
// check timestamp for changes for production use
if (readContents == '') {
using (StreamReader streamReader = new StreamReader(pathFromConfigFile, Encoding.UTF8))
{
readContents = streamReader.ReadToEnd();
}
}
return readContents;
}
// ...
}
It turns out it is all very simple with the FallbackRoute functionality, once you work out how to use it properly:
[FallbackRoute("/{Path*}")]
public class Fallback
{
public string Path { get; set; }
}
public class FallBackService : Service
{
public object Any(Fallback request)
{
return new HttpResult(new FileInfo("index.html")) {ContentType = "text/html"};
}
}
Once this is in place, I find 'index.html' is indeed getting served up whenever I try to hit a non-existent route.
Any static files, such as JavaScript and CSS resources, get served up as normal (as long as they have a 'Copy to Output Directory' setting of 'Copy if newer', of course).
This works like a charm with the HTML5 Push-state functionality in AngularJS.

Resources