minify html and js in jersey Interceptor - java

used jersey mvc and jsp, all requests to html or js files did through #Template or Viewable.
example;
#GET
#Path(JS_URL + "{type}")
#Template(name = "grid")
#Produces("application/javascript")
public Response buildJSGrid(#DefaultValue("") #PathParam("type") String type) {
Grid grid = new Grid(type);
....
return Response.ok(grid).build();
}
where grid is grid.jsp file with pure javascript inside
<%# page contentType="application/javascript;charset=UTF-8" language="java" %>
.....
also possible other variant with html and js, example;
#GET
#Path(FORM_URL + "{type}")
#Template(name = "form")
#Produces(MediaType.TEXT_HTML)
public Response buildAccountForm(#DefaultValue("") #PathParam("type") String type) {
Form form = new Form(type);
....
return Response.ok(form).build();
}
where form is form.jsp with html and js inside <script>..</script>
<%# page contentType="text/html;charset=UTF-8" language="java" %>
...
i need to minify result js and html/js before send to client, i try to use https://code.google.com/archive/p/htmlcompressor/ lib, but there need to pass String to htmlCompressor.compress(input);
tried use WriterInterceptor
public class MinifyJsInterceptor implements WriterInterceptor {
#Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
final OutputStream outputStream = context.getOutputStream();
// here need to convert outputStream to InputStream and after to String ?
// result string to htmlCompressor.compress(resultString);
// after that convert result minify string back to resultOutputStream and set to context ?
context.setOutputStream(new GZIPOutputStream(resultOutputStream));
is it correct way ? and i can`t converts that outputstream to string
thanks
--update
answer to questions;
html + js mean that in some jsp are html markup and js code
<div id="form" style="width: 500px; display: none">
<div class="w2ui-page page-0">
<div class="w2ui-field">
</div>....
<script type="text/javascript">
var uiElement = (function () {
var config = {
onOpen: function (event) {
event.onComplete = function () {
$('#formContainer').w2render('form');
}
...
}());
</script>
on client that file requested by
$('#tempContainer').load('that file name - also dynamic', function (data, status, xhr) {
uiElement.init();
w2ui[layout].content(layout_main, w2ui[uiElement.name]);
});
And do you really return js-files in you resource methods?
some js and html + js files are dynamic build, example;
grid.jsp contains inside
<%# page contentType="application/javascript;charset=UTF-8" language="java" %>
var uiElement = (function () {
var config = {
grid: {
name: ${it.name},
listUrl:'${it.entityListUrl}',
formUrl:'${it.entityFormUrl}',
columns: ${it.columns},
records: ${it.records},
}}
there are ${it..} values from el expression and setting in resource method
#GET
#Path(JS_URL + "{type}")
#Template(name = "grid")
#Produces("application/javascript")
public Response buildJSGrid(#DefaultValue("") #PathParam("type") String type) {
Grid grid = new Grid(type);
....
return Response.ok(grid).build();
}}
and from client that js 'file' called by
$.getScript('dynamic js file name' - it is dynamic too).done(function (script, status, xhr) {
//console.log(xhr.responseText);
uiElement.init();
w2ui[layout].content(layout_main, w2ui[uiElement.name]);
});
also some html blocks build dynamic
{
<c:if test="${it.recid != 0}">
<div class="w2ui-field">
<label>active:</label>
<div>
<input name="active" type="checkbox"/>
</div>
</div>
</c:if>
}
-- update description,
grid builder;
one resource and one template for build any grid,
#GET
#Path(GRID + "{type}")
#Template(name = W2UI_VIEW_PREFIX + "grid/grid")
#Produces(MEDIA_TYPE_APPLICATION_JAVASCRIPT)
public Response buildGrid(#DefaultValue("") #PathParam("type") String type) {
for (W2UI ui : W2UI.values()) {
if (type.equals(ui.getName())) {
W2UIElement grid = ui.getUI();
return Response.ok(grid).build();
}
}
return Response.noContent().build();
}
also possible different templates(jsp files) through Viewable(template, model)
somewhere in menu builder for menu.jsp template
List<MenuItem> items..
MenuItem item1 = new MenuItem(W2UI.TASK_GRID, W2UIService.GRID);
items.add(item1);
where
W2UIService.GRID is string url for client js request and for server method resource #Path() anno.
and
public enum W2UI {
TASK_GRID("task_grid", "tasks", Type.SCRIPT){
#Override
public W2UIElement getUI() {
return new TaskGrid(getName());
}
},
.....
}
TaskGrid is filled model for grid.jsp template with js code, so easy to add any type of grid with different sets of data and buttons.
type of component(Type.SCRIPT) processing on the client by $.getScript(), Type.HTML by $('#tempContainer').load()
---update factory and providers;
#Provider
#Priority(200)
#HtmlMinify
public class HtmlMinifyInterceptor implements WriterInterceptor {
#Inject private HtmlCompressor compressor;
...
public class HtmlMinifierFactory implements Factory<HtmlCompressor> {
private HtmlCompressor compressor;
#Override
public HtmlCompressor provide() {
if (null == compressor) compressor = new HtmlCompressor();
ClosureJavaScriptCompressor jsCompressor = new ClosureJavaScriptCompressor();
jsCompressor.setCompilationLevel(CompilationLevel.SIMPLE_OPTIMIZATIONS);
..
#ApplicationPath("/")
public class MainRsConfig extends ResourceConfig {
public MainRsConfig() {
..
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(HtmlMinifierFactory.class).to(HtmlCompressor.class).in(Singleton.class);
}
});
..

You can use a custom implementation of a ByteArrayOutputStream as a wrapper to the OutputStream of the WriterInterceptorContext:
import com.googlecode.htmlcompressor.compressor.Compressor;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class HtmlMinifyOutputStream extends ByteArrayOutputStream {
private OutputStream origOut;
private Compressor compressor;
public HtmlMinifyOutputStream(OutputStream origOut, Compressor compressor) {
this.origOut = origOut;
this.compressor = compressor;
}
public void close() throws IOException {
super.close();
String compressedBody = compressor.compress(new String(this.buf));
this.origOut.write(compressedBody.getBytes());
this.origOut.close();
}
}
The HtmlMinifyOutputStream can be used in the WriterInterceptor implementation. The HtmlCompressor instance is injected:
import com.googlecode.htmlcompressor.compressor.Compressor;
import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import java.io.*;
#Provider
#HtmlMinify
public class MinifyHtmlInterceptor implements WriterInterceptor {
#Inject
private Compressor compressor;
#Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
final OutputStream outputStream = context.getOutputStream();
context.setOutputStream(new HtmlMinifyOutputStream(outputStream, compressor));
context.proceed();
}
}
#HtmlMinify is a NameBinding annotation, used to activate the MinifyHtmlInterceptor on specific resource methods. (see https://jersey.java.net/documentation/latest/filters-and-interceptors.html#d0e9988):
import javax.ws.rs.NameBinding;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#NameBinding
#Retention(value = RetentionPolicy.RUNTIME)
public #interface HtmlMinify {}
The HtmlCompressor can be created only once per application and used concurrently, because:
HtmlCompressor and XmlCompressor classes are considered thread safe* and can be used in multi-thread environment (https://code.google.com/archive/p/htmlcompressor/)
Here is a HK2 factory (see: Implementing Custom Injection Provider) which creates the compressor instance and enables inline css and javascript compression:
import com.googlecode.htmlcompressor.compressor.Compressor;
import com.googlecode.htmlcompressor.compressor.HtmlCompressor;
import org.glassfish.hk2.api.Factory;
public class HtmlCompressorFactory implements Factory<Compressor> {
private HtmlCompressor compressor;
#Override
public Compressor provide() {
if(compressor == null) {
compressor = new HtmlCompressor();
}
compressor.setCompressJavaScript(true);
compressor.setCompressCss(true);
return compressor;
}
#Override
public void dispose(Compressor compressor) {}
}
The factory is registered with an AbstractBinder:
final ResourceConfig rc = new ResourceConfig().packages("com.example");
rc.register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(HtmlCompressorFactory.class).to(Compressor.class).in(Singleton.class);
}
});
If inline javascript or inline css compression is enabled:
HTML compressor with default settings doesn't require any dependencies. Inline CSS compression requires YUI compressor library.Inline JavaScript compression requires either YUI compressor library (by default) or Google Closure Compiler library. (https://code.google.com/archive/p/htmlcompressor/)
I use maven, so I added this dependency to my pom.xml:
<dependency>
<groupId>com.yahoo.platform.yui</groupId>
<artifactId>yuicompressor</artifactId>
<version>2.4.8</version>
</dependency>
If you want to use the Google Closure Compiler use this dependency:
<dependency>
<groupId>com.google.javascript</groupId>
<artifactId>closure-compiler</artifactId>
<version>r2388</version>
</dependency>
and activate it:
compressor.setJavaScriptCompressor(new ClosureJavaScriptCompressor());
compressor.setCompressJavaScript(true);
compressor.setCssCompressor(new YuiCssCompressor());
compressor.setCompressCss(true);
return compressor;
If you want to compress pure JavaScript or CSS files, you cannot use the htmlcompressor. This library supports only HTML files with inline CSS/JS. But you could implement a MinifyJsInterceptor or MinifyCssInterceptor analog to the MinifyHtmlInterceptor, which uses the YUI-Compressor and/or Google Closure libraries directly.
For gzip compression you should implement another interceptor. So it is possible to configure the minification and compression separately. If you activate multiple interceptors, use javax.annotation.Priority to controll the order of execution. (see: https://jersey.java.net/documentation/latest/filters-and-interceptors.html#d0e9927)

Related

How to create simple web service that takes input as JSONObject in java

Hello I am new to web services.
I am able to create simple web service which accept input string and return another string using eclipse.
But when it comes to JSONObject i am facing problems,while invoking web service
public class HelloWorld {
private int rowNumber;
public byte[] readJSON(JSONObject jsonObject ) throws Exception
{
rowNumber=0;
File excelFile = new File("Test2.xlsx");
OutputStream outStream = new FileOutputStream(excelFile);
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("TestSheet");
XSSFRow row ;
XSSFCell cell;
JSONArray msg = (JSONArray) jsonObject.get("messages");
Iterator<String> iterator = msg.iterator();
while (iterator.hasNext()) {
row = sheet.createRow(rowNumber);
cell= row.createCell(0);
cell.setCellValue(iterator.next());
rowNumber=rowNumber+1;
}
workbook.write(outStream);
outStream.close();
Path path = Paths.get("Test2.xlsx");
byte[] data = Files.readAllBytes(path);
return data;
}
public float addValue(float value) {
return (value + 10);
}
}
so help me to consume the web service.
SimpleDeserializer encountered a child element, which is NOT expected, in something it was trying to deserialize. this error i am getting when i try to invoke client. and another thing input parameter as JSONObject is allowed?
You can use the package Package javax.ws.rs.
import javax.ws.rs.*;
Here would be a short of example of the library in action:
Here is the HTML:
<div>
Welcome and happy <span id="today"></span>.
What's your name?
<input id="name" type="text" autofocus />
<button id="submit" onclick="greet()">Submit</button>
</div>
<div id="greet">
<!-- greeting goes here -->
</div>
<script>
// fills in <span id="today">...</span> with today's day of the week
// returned from /rest/today server endpoint
function today() {
$.get("/rest/today", function(theday) {
$("#today").text(theday);
});
};
// fills in <div id="greeting">...</div> with the greeting
// returned from calling the /rest/hello?name=... server endpoint
// with the name from the input text box
function greet() {
var thename = $("#name").val();
$.get("/rest/hello", { name: thename }, function(thehello) {
$("#greet").text(thehello);
})
.fail(function(jqXHR, textStatus, errorThrown) {
// displays server error message, e.g. if called with empty name
$("#greet").text(textStatus + ": " + errorThrown);
});
};
$(today); // execute today() after DOM is ready, see https://api.jquery.com/ready/
</script>
</body>
</html>
With corresponding java code:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
/**
* REST service that greets requests.
*
* This is a "root resource class" as explained in
* https://jersey.java.net/documentation/latest/jaxrs-resources.html
*/
#Path("/")
public class HelloService {
#GET
#Path("/today")
public String today() {
return DayOfWeek.today();
}
#GET
#Path("/hello")
public Response hello(#QueryParam("name") String name) {
if (name == null || name.isEmpty()) {
return Response.status(Response.Status.BAD_REQUEST).build();
} else {
return Response.ok("hello " + name).build();
}
}
}
In order to work with JSON objects, you'll need to use Gson.toJson(). Do something along the lines of this:
String json = new Gson().toJson(some_object);
return Response.ok(json, MediaType.APPLICATION_JSON).build();
I hope this was helpful!
You can try to use Jackson: very good library in which you define you Java object model class that you can convert to JSON or parse from JSON.
You will find a lot of examples

UI4J - How Do I Add Listeners on All Page Loads for All Elements Specified?

How do I propagate the listeners on the next loaded page using UI4J?
https://github.com/ui4j/ui4j
For example, The below will navigate to Google and attach to all input tags a click listener, but I would like to have the binds reattached after searching and on every page loaded on the "input" tags.
import com.ui4j.api.browser.BrowserEngine;
import com.ui4j.api.browser.BrowserFactory;
import com.ui4j.api.browser.Page;
import com.ui4j.api.dom.Element;
public class AutoWeb {
public static void main(String[] args) {
// get the instance of the webkit
BrowserEngine browser = BrowserFactory.getWebKit();
// navigate to Google
Page page = browser.navigate("http://www.google.com");
// show the browser page
page.show();
// bind the "input" tags
// This should apply for all "input" tags on any page.
for (Element element: page.getWindow().getDocument().queryAll("input")) {
element.bindClick((handler) -> {
System.out.println("Clicked!");
});
}
}
}
If I use an Interceptor, how do I access the Page Document from the afterLoad() method in order to re-apply the bindings?
Interceptor interceptor = new Interceptor() {
#Override
public void beforeLoad(Request request) {
}
#Override
public void afterLoad(Response response) {
// Apply the bindings on after load.
}
};
PageConfiguration pageConfiguration = new PageConfiguration(interceptor);
Page page = browser.navigate("http://www.google.com", pageConfiguration);

Javascript failing to see java function

I'm embedding an applet in a web page and trying to call a function in the applet but get the error "Object doesn't support property or method 'setDestination' when I click the button and the "doit()" function is called. The applet is loaded and on the screen.
Java code (compiled and put into a signed jar named webcam.jar):
import javax.swing.JApplet;
public class MyAppletLauncher extends JApplet {
private JarClassLoader jcl;
public void setDestination()
{
System.out.println("MyAppletLauncher: setdestination!");
System.out.println(url);
}
#Override
public void init() {
jcl = new JarClassLoader();
try {
jcl.initApplet("webcam", this);
} catch (Throwable e) {
e.printStackTrace();
}
}
#Override
public void start() {
jcl.startApplet();
}
#Override
public void stop() {
jcl.stopApplet();
}
#Override
public void destroy() {
jcl.destroyApplet();
}
} // class MyAppletLauncher
Here's the HTML:
<HTML><BODY>
<applet id=cameraapplet name="camerax" code="MyAppletLauncher.class" height="100%" width="100%" archive="webcam.jar">
</applet>
<SCRIPT type="text/javascript">
function doit() {
alert(1);
document.camerax.setDestination(); // Dies on this line
alert(2);
}
</Script>
<input type=button onclick='doit();'>
</BODY></HTML>
Use the applet id to get a reference to the applet object:
document.getElementById("cameraapplet").setDestination();
Note however that the <applet> tag is not supported by HTML5. Read this article on how to use
<embed> or <object> for Java applets.

Can't display StreamedContent in Primefaces media in JSF [duplicate]

I use the <p:media> to display static PDF content.
<p:media value="/resource/test.pdf"
width="100%" height="300px" player="pdf">
</p:media>
How can I change it to display dynamic content?
Like as in <p:graphicImage>, the value attribute can point to a bean property returning StreamedContent. This only requires a special getter method for the reasons which is explained in detail in the following answer on using <p:graphicImage> with a dynamic resource from a database: Display dynamic image from database with p:graphicImage and StreamedContent.
In your particular example, it would look like this:
<p:media value="#{mediaManager.stream}" width="100%" height="300px" player="pdf">
<f:param name="id" value="#{bean.mediaId}" />
</p:media>
With
#ManagedBean
#ApplicationScoped
public class MediaManager {
#EJB
private MediaService service;
public StreamedContent getStream() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
// So, we're rendering the HTML. Return a stub StreamedContent so that it will generate right URL.
return new DefaultStreamedContent();
} else {
// So, browser is requesting the media. Return a real StreamedContent with the media bytes.
String id = context.getExternalContext().getRequestParameterMap().get("id");
Media media = service.find(Long.valueOf(id));
return new DefaultStreamedContent(new ByteArrayInputStream(media.getBytes()));
}
}
}

JSP Custom Taglib: Nested Evaluation

Say I have my custom taglib:
<%# taglib uri="http://foo.bar/mytaglib" prefix="mytaglib"%>
<%# taglib uri="http://java.sun.com/jstl/core" prefix="c"%>
<mytaglib:doSomething>
Test
</mytaglib:doSomething>
Inside the taglib class I need to process a template and tell the JSP to re-evaluate its output, so for example if I have this:
public class MyTaglib extends SimpleTagSupport {
#Override public void doTag() throws JspException, IOException {
getJspContext().getOut().println("<c:out value=\"My enclosed tag\"/>");
getJspBody().invoke(null);
}
}
The output I have is:
<c:out value="My enclosed tag"/>
Test
When I actually need to output this:
My enclosed tag
Test
Is this feasible? How?
Thanks.
Tiago, I do not know how to solve your exact problem but you can interpret the JSP code from a file. Just create a RequestDispatcher and include the JSP:
public int doStartTag() throws JspException {
ServletRequest request = pageContext.getRequest();
ServletResponse response = pageContext.getResponse();
RequestDispatcher disp = request.getRequestDispatcher("/test.jsp");
try {
disp.include(request, response);
} catch (ServletException e) {
throw new JspException(e);
} catch (IOException e) {
throw new JspException(e);
}
return super.doStartTag();
}
I tested this code in a Liferay portlet, but I believe it should work in other contexts anyway. If it don't, I would like to know :)
HTH
what you really need to have is this:
<mytaglib:doSomething>
<c:out value="My enclosed tag"/>
Test
</mytaglib:doSomething>
and change your doTag to something like this
#Override public void doTag() throws JspException, IOException {
try {
BodyContent bc = getBodyContent();
String body = bc.getString();
// do something to the body here.
JspWriter out = bc.getEnclosingWriter();
if(body != null) {
out.print(buff.toString());
}
} catch(IOException ioe) {
throw new JspException("Error: "+ioe.getMessage());
}
}
make sure the jsp body content is set to jsp in the tld:
<bodycontent>JSP</bodycontent>
Why do you write a JSTL tag inside your doTag method?
The println is directly going into the compiled JSP (read: servlet) When this gets rendered in the browser it will be printed as it is since teh browser doesn't understand JSTL tags.
public class MyTaglib extends SimpleTagSupport {
#Override public void doTag() throws JspException, IOException {
getJspContext().getOut().println("My enclosed tag");
getJspBody().invoke(null);
}
}
You can optionally add HTML tags to the string.

Categories