Send Template Email Using Struts2 View - struts2

I am using Struts2, and I want to send the html output of one of my actions as an email.
In other words, instead of displaying the output html in the browser, I want to send it as an email.
I am using Apache Tiles to create my pages.
EDIT:
In general, can we get the the html result of struts view as a stream and pass it to another action?

First of all, I need to mention that using JSPs as email templates are not a good idea. But, if you need to do this, or you need to log the exact HTML that user sees (or any other reason), you can do this:
Create an interceptor for your action
Get the response from ServletActionContext
Wrap the response
Invoke the action
get (or even change) the response.
return the result.
This is part of my code:
import java.io.CharArrayWriter;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
//STEP 1
public class EmailInterceptor implements Interceptor {
#Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
// STEP 2
HttpServletResponse response = ServletActionContext.getResponse();
PrintWriter out = response.getWriter();
// STEP 3
CharResponseWrapper responseWrapper = new CharResponseWrapper((HttpServletResponse) response);
ServletActionContext.setResponse(responseWrapper);
// STEP 4
String result = actionInvocation.invoke();
// STEP 5
String servletResponse = new String(responseWrapper.toString());
out.write(servletResponse + "whatever");
// STEP 6
return result;
}
public class CharResponseWrapper extends HttpServletResponseWrapper {
private CharArrayWriter output;
public String toString() {
return output.toString();
}
public CharResponseWrapper(HttpServletResponse response) {
super(response);
output = new CharArrayWriter();
}
public PrintWriter getWriter() {
return new PrintWriter(output);
}
}
}

Related

Making business domain objects available to Jersey Servlet Context in embedded Jetty server

Using the following dependencies (Gradle):
org.glassfish.jersey.containers:jersey-container-servlet:2.22.2
org.eclipse.jetty:jetty-servlet:9.3.2.v20150730
I have an embedded Jetty server, with a Jersey servlet container... something like this ...
package mypkg.rest.jersey;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.servlet.ServletContainer;
import se.transmode.tnm.alarm.api.AlarmRetrieval;
import mypkg.rest.RestServer;
import mypkg.rest.jersey.serviceImpl.ModelAdapter;
public class JerseyBasedRestServer implements RestServer {
public static final int INITIALIZE_ON_USE = 0;
private Server server;
private final ServletContextHandler context;
private final ServletHolder servlet;
private final ModelAdapter modelAdapter;
public JerseyBasedRestServer(BusinessObjects businessObjects) {
this.modelAdapter = new ModelAdapter(businessObjects); //I want this instance to somehow be available for my ServletContainer to use.
context = new ServletContextHandler(ServletContextHandler.SESSIONS);
servlet = context.addServlet(ServletContainer.class, "/*");
servlet.setInitOrder(INITIALIZE_ON_USE);
servlet.setInitParameter(ServerProperties.PROVIDER_PACKAGES, "mypackage.jersey.generated.api.service");
servlet.setInitParameter(ServerProperties.MEDIA_TYPE_MAPPINGS, "json : application/json");
context.setContextPath("/");
}
private void startServlet() {
try {
servlet.start();
servlet.initialize();
} catch (Exception e) {
log.error("Failed to initialize servlet. {}", e.getMessage());
}
}
#Override
public void init(int port) {
server = new Server(port);
server.setHandler(context);
try {
server.start();
server.join();
startServlet();
} catch (Exception e) {
log.error("Failed to start jetty server for rest interface");
} finally {
server.destroy();
}
}
The Jersey Container will run server code and model generated using the Swagger code-gen tool
https://github.com/swagger-api/swagger-codegen#getting-started
which delivers the generated model, JacksonJsonProvider, and a RestApi class:
package mypackage.jersey.generated.api.service
Path("/")
public class RestApi {
private final RestApiService delegate = new RestApiServiceImpl(); //Integration point of the generated code
#GET
#Path("/list/")
#Consumes({ "application/json" })
#Produces({ "application/json" })
public Response retrieveAlarmList(#Context SecurityContext securityContext) throws NotFoundException {
return delegate.retrieveAlarmList(securityContext);
}
}
To integrate the generated code we are left to implement RestApiServiceImpl ourselves.
The ModelAdapter's job is to convert our business objects to the generated rest model.
So the question is how do I make the instance of the adapter of our business objects, in this case ModelAdapter, which lies outside the context of the Jersey servlet context, available to the RestApi class, or rather the RestApiServiceImpl?
I kind of understood from reading the past 24 hours that I need to use some sort of Context Dependency Injection either through Jetty, Jersey, or some other library (Weld seems to appear a lot), and have tried various combinations of #Inject, #Context, etc etc, but have come to the conclusion that I have no clue what I am actually doing... I'm not even sure I understand enough about the situation to phrase my question correctly.
More info can be made available on request.
Any help is appreciated.
EDIT: added a link here to https://github.com/englishbobster/JersetAndJetty
using #peeskillets suggestions, but still not working.
First thing you need to make DI work, is an AbstractBinder. This is where you will make your objects available to be injected.
class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(modelAdapter).to(ModelAdapter.class);
}
}
Then you need to register the binder with Jersey. The easiest way is to register in Jersey's ResourceConfig. In your case, you are not using one. You are configuring everything in the "web.xml". For that, you should take a look at this post.
If you want to change your configuration to use a ResourceConfig, which personally I'd rather use, you can do this
package com.some.pkg;
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("mypackage.jersey.generated.api.service");
property(ServerProperties.MEDIA_TYPE_MAPPINGS, "json : application/json");
register(new Binder());
}
}
Then to configure it with Jetty, you can do
servlet.setInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS,
"com.some.pkg.JerseyConfig");
Now you can get rid of those other two init-params, as you are configuring it inside the ResourceConfig.
Another way, without any init-params, is to do
ResourceConfig config = new JerseyConfig();
ServletHolder jerseyServlet = new ServletHolder(ServletContainer(config));
context.addServlet(jerseyServlet, "/*");
See full example of last code snippet, here.
Now you can just inject the ModelAdapter pretty much anywhere within Jersey
In a field
#Inject
private ModelAdapter adapter;
Or in a contructor
#Inject
public RestApi(ModelAdapter adapter) {
this.adapter = adapter;
}
Or method parameter
#GET
public Response get(#Context ModelAdapter adapter) {}

How to extend Jenkins job page with new links and icons

I'm developing my first Jenkins plugin and followed the tutorial at wiki.jenkins-ci.org. After adding a BuildStep and generating the results I now want to publish them to the user. I would like to do this via a new link entry on the job page and a corrsponding result view page.
Unfortunatelly I do not find the right extension points for the navigation bar at the left side, the main navigation links in the center as well as the new target page. Can somebody point me in the right direction or give me a link to a tutorial or blog post that explains this scenario?
Thanks
Root Action and Actions are different. The first one goes only to initial page (root), the second one can be attach to a Project/Job or to a Build.
To create a Root Action, just need to create a class that it's:
Annotated with #Extension (so it can be found and automatically
loaded by Jenkins)
Implements RootAction Interface
Override 3 methods: getIconFileName(), getDisplayName() and getUrlName()
For example:
#Extension
public class GoogleRootAction implements RootAction{
#Override
public String getIconFileName() {
return "clipboard.png";
}
#Override
public String getDisplayName() {
return "Google URL";
}
#Override
public String getUrlName() {
return "http://www.google.pt";
}
}
To create an Action at a Project it's more complicated, and there's more than a way, depending of what you want.
But first, the class Action itself is the easy part, since it's very similar to a class RootAction. It's not annotated with #Extension and implements Action interface instead of RootAction.
For example:
public class LatestConsoleProjectAction implements Action {
private AbstractProject<?, ?> project;
#Override
public String getIconFileName() {
return (Jenkins.RESOURCE_PATH + "/images/48x48/terminal.png").replaceFirst("^/", "");
}
#Override
public String getDisplayName() {
return Messages.Latest_Console_Project_Action();
}
#Override
public String getUrlName() {
return "lastBuild/console";
}
public LatestConsoleProjectAction(final AbstractProject<?, ?> project) {
this.project = project;
}
}
The tricky part is to inform jenkins that this class Action exists. As I said, there are different ways.
For instance, one can associate an Action to a Builder or Publisher or other by just overriding getProjectAction() method at those classes.
For example:
#Override
public Action getProjectAction(AbstractProject<?, ?> project) {
return new LatestConsoleProjectAction(project);
}
But this way, the Action link will only show on Project left menu, if the corresponding Builder or Publisher is used by the job (or selected at Job configurations).
Another way, that always shows your Action link on left menu, it's create a factory class to inform jenkins. There are many factories, but at my example I will use TransientProjectActionFactory class.
For this, one will need to create a class that:
It's annotated with #Extensions
Extends TransientProjectActionFactory class (or another Factory class)
Override createFor method to create your class Action associated with Project object
For example:
#Extension
public class LatestConsoleProjectActionFactory extends TransientProjectActionFactory {
#Override
public Collection<? extends Action> createFor(AbstractProject abstractProject) {
return Collections.singletonList(new LatestConsoleProjectAction(abstractProject));
}
}
One can still filter project object to just the projects types you want. The one you don't want, just return Collections.emptyList().
Beside this two ways, I think there are others. You can see this link to reference:
https://wiki.jenkins-ci.org/display/JENKINS/Action+and+its+family+of+subtypes
Although, they refer to addAction method and others, but I couldn't use it (I have 2.19.2 Jenkins version).
Also they refer groovy, but I didn't try it, since I want to stick with Java :)
Btw, my example will create an action link to open console page of last build. Useful to avoid selecting last build and then select his console page.
After a lot of trial and error I figured out the solution.
All in all you need two different things in your project:
1) A class that inherits from ProminentProjectAction:
import hudson.model.ProminentProjectAction;
public class MyProjectAction implements ProminentProjectAction {
#Override
public String getIconFileName() {
// return the path to the icon file
return "/images/jenkins.png";
}
#Override
public String getDisplayName() {
// return the label for your link
return "MyActionLink";
}
#Override
public String getUrlName() {
// defines the suburl, which is appended to ...jenkins/job/jobname
return "myactionpage";
}
}
2) Even more important is that you add this action somehow to your project.
In my case I wanted to show the link if and only if the related build step of my plugin is configured for the actual project. So I took my Builder class and overwrote the getProjectActionsMethod.
public class MyBuilder extends Builder {
...
#Override
public Collection<? extends Action> getProjectActions(AbstractProject<?,?> project) {
List<Action> actions = new ArrayList<>();
actions.add(new MyProjectAction());
return actions;
}
}
Maybe this is not the perfect solution yet (because I'm still trying to figure out how all the artifacts are working together), but it might give people which want to implement the same a good starting point.
The page, which is loaded after clicking the link is defined as index.jelly file under source/main/resources and an underlying package with the name of the package of your Action class appended by its class name (e.g. src/main/resources/org/example/myplugin/MyProjectAction).
As it happens, there was a plugin workshop by Steven Christou at the recent Jenkins User Conference in Boston, which covered this case. You need to add a new RootAction, as shown in the following code from the JUC session
package org.jenkinsci.plugins.JUCBeer;
import hudson.Extension;
import hudson.model.RootAction;
#Extension
public class JenkinsRootAction implements RootAction {
public String getIconFileName() {
return "/images/jenkins.png";
}
public String getDisplayName() {
return "Jenkins home page";
}
public String getUrlName() {
return "http://jenkins-ci.org";
}
}
https://github.com/jenkinsci/s3explorer-plugin is my Jenkins plugin that adds an S3 Explorer link to all Jenkins project's side-panel.
An addition to #dchang comment:
I managed to make this functionality work also on pipelines by extending TransientActionFactory<WorkflowJob>:
#Extension
public static class PipelineLatestConsoleProjectActionFactory extends TransientActionFactory<WorkflowJob> {
#Override
public Class<WorkflowJob> type() {
return WorkflowJob.class;
}
#Nonnull
#Override
public Collection<? extends Action> createFor(#Nonnull WorkflowJob job) {
return Collections.singletonList(new LatestConsoleProjectAction(job));
}
}

Struts2 Junit4 tests accumulate JSON responses with every action execution

I've written a few Junit4 tests, which looks like this :
public class TestCampaignList extends StrutsJUnit4TestCase<Object> {
public static final Logger LOG = Logger.getLogger(TestCampaignList.class.getName());
#Before
public void loginAdmin() throws ServletException, UnsupportedEncodingException {
request.setParameter("email", "nitin.cool4urchat#gmail.com");
request.setParameter("password", "22");
String response = executeAction("/login/admin");
System.out.println("Login Response : " + response);
}
#Test
public void testList() throws Exception {
request.setParameter("iDisplayStart", "0");
request.setParameter("iDisplayLength", "10");
String response = executeAction("/campaign/list");
System.out.println("Reponse : " + response);
}
}
Both actions return JSON results and executeAction javadoc says :
For this to work the configured result for the action needs to be FreeMarker, or Velocity (JSPs can be used with the Embedded JSP plugin)
Seems like it's unable to handle JSON results and hence, the second action execution shows accumulated result, such that result_for_second_action= result1 concatenate result2
Is there a solution to get the executeAction() return the actual JSON response, rather than concatenating JSON responses from all previous executions.
This is happening because you are executing action in #Before method. In that way the setUp method of StrutsJUnit4TestCase is not getting called in between your loginAdmin and test method and you previous request parameters are passed to it again. You can call setUp method by yourself in your tests method.
In your case you can actually call initServletMockObjects method to create new mock servlet objects such as request.
#Test
public void testList() throws Exception {
setUp();
// or
// initServletMockObjects();
request.setParameter("iDisplayStart", "0");
request.setParameter("iDisplayLength", "10");
String response = executeAction("/campaign/list");
System.out.println("Reponse : " + response);
}

How to get a response "stream" from an action in MVC3/Razor?

I am using MVC3, .NET4, C#.
I need to create some XHTML using a Razor View. I do this via an Action.
public ActionResult RenderDoc(int ReportId)
{
//A new document is created.
return View();
}
I then need to take the output from this and convert it to a Word Doc. I am using a 3rd party component to do this and it expects a "stream" or a "file" for the XHTML source that is read in for conversion to a DOC, like the following:
document.Open(MyXhtmlStream,FormatType.Html,XHTMLValidationType.Transitional);
My Question:
What would be a good way to call the "RenderDoc" Action and obtain the result as a stream to feed into "MyXhtmlStream".
Many thanks.
EDIT: I have had another idea !!!
1) Render the View within the action to create a String(XHTMLString). I have seen a method to do this on SO.
2) Create a MemoryStream and put this string into it.
Stream MyStream = New MemoryStream("XHTMLString and encoding method");
EDIT2: Based on Darin's answer
I need to clasyify a little further, and I hope to do this via tweaking Darin's code for my purpose.
public class XmlDocumentResult : ActionResult
{
private readonly string strXhtmlDocument;
public XmlDocumentResult(string strXhtmlDocument)
{
this.strXhtmlDocument = strXhtmlDocument;
}
public override void ExecuteResult(ControllerContext context)
{
WordDocument myWordDocument = new WordDocument();
var response = context.HttpContext.Response;
response.ContentType = "text/xml";
myWordDocument.Open(response.OutputStream, FormatType.Html, XHTMLValidationType.Transitional);
}
}
The above is closer to what I need. Note the 3rd Party WordDocument type. So there is still the issue of how I get the "strXhtmlDocument" into the "Response.OutputStream?
I would just write a custom ActionResult to handle that:
public class XmlDocumentResult : ActionResult
{
private readonly Document document;
public XmlDocumentResult(Document document)
{
this.document = document;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
response.ContentType = "text/xml";
document.Open(response.OutputStream, FormatType.Html, XHTMLValidationType.Transitional);
}
}
You could of course adjust the response Content-Type if necessary and also append a Content-Disposition header if you want.
And then simply have my controller action return this custom action result:
public ActionResult RenderDoc(int reportId)
{
Document document = repository.GetDocument(reportId);
return new XmlDocumentResult(document);
}
Now the controller action doesn't need to handle plumbing code anymore. The controller action does what a typical controller action is supposed to do:
Query the Model
Pass this model to an ActionResult
In your case the model is this Document class or whatever it is called.

Remove Default Action validation

I am using Struts validation by extending action to ActionSupport.
When I try to put special character on the UI, then default actionErrors are adding up in the collections like Illegal characters were detected in the input, please remove them and try again.
Then I need to remove that default validation coming up on UI. I have no idea to deal with it.
My code is like this:
void validate(){
if (locationBean.getReasonTypeId() == 0)
{
addActionError("Error in reason");
}
if (ValidationHelper.checkRequiredField(locationBean.getName()))
{
addActionError("Error in Location name");
}
}
My validation action class is like this:
public class ValidationErrorJSONAction extends ActionSupport
{
private JSONObject jsonObject;
#SuppressWarnings("unchecked")
public String execute() throws Exception
{
jsonObject = new JSONObject();
jsonObject.put("actionErrors", new JSONArray(getActionErrors()));
jsonObject.put("fieldErrors",new JSONObject(getFieldErrors()));
TokenHelperJSON.appendToken(jsonObject);
return SUCCESS;
}
Hope it will help you to understand the problem.

Resources