Dynamic URLs for a Liferay Portlet - liferay

I am looking for a solution for developing a dynamic content Liferay Portlet with different URLs. I am not going create separate pages in Liferay.All the information are stored in separate database and all pages are generated by using a Liferay Portlet.My current Liferay version is 6.2 CE.
Sample URLs are ,
https://localhost/travel/hotel/Lanzarote/Costa Teguise/Hotel Beatriz Costa & Spa
https://localhost/travel/hotel/Lanzarote/Costa Teguise/Club Siroco Apartments
https://localhost/travel/hotel/Lanzarote/Costa Teguise/El Guarapo Apartments
How do I implement different URLs with out creating separate pages in Liferay? If I need to use Liferay API for generate dynamic URLs , what are the API components do I need to use?

You can get very similar urls with Liferay friendly url mapping:
https://localhost:8080/{page}/-/hotel/Lanzarote/Costa Teguise/Hotel Beatriz Costa
https://localhost:8080/{page}/-/hotel/Lanzarote/Costa Teguise/Club Siroco Apartments
https://localhost:8080/{page}/-/hotel/Lanzarote/Costa Teguise/El Guarapo Apartments
To make it work, you need to configure the mapping in liferay-portlet.xml:
<portlet>
<portlet-name>my-hotel-portlet</portlet-name>
<friendly-url-mapper-class>com.liferay.portal.kernel.portlet.DefaultFriendlyURLMapper</friendly-url-mapper-class>
<friendly-url-mapping>hotel</friendly-url-mapping>
<friendly-url-routes>friendly-url-routes.xml</friendly-url-routes>
...
</portlet>
The hotel part of the url that comes right after /-/ is defined by <friendly-url-mapping>hotel</friendly-url-mapping> value.
The configuration refers to the routes defined in friendly-url-routes.xml. Just one route definition is necessary:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE routes PUBLIC "-//Liferay//DTD Friendly URL Routes 6.2.0//EN" "http://www.liferay.com/dtd/liferay-friendly-url-routes_6_2_0.dtd">
<routes>
<route>
<pattern>/{region}/{town}/{hotel}</pattern>
</route>
</routes>
Sample Liferay MVCPortlet method reading the parameters:
#Override
public void doView(RenderRequest renderRequest, RenderResponse renderResponse) {
String region = ParamUtil.getString(renderRequest, "region");
String town = ParamUtil.getString(renderRequest, "town");
String hotel = ParamUtil.getString(renderRequest, "hotel");
...
}
Sample Spring controller method reading the parameters:
#RenderMapping
public String renderHotel(#RequestParam String region, #RequestParam String town, #RequestParam String hotel) {
...
return "hotel/view";
}
See FriendlyURLMapper for detailed coverage.

Another solution is writing a separate servlet filter hook plugin project.The idea is to transfer the current URL into Liferay specific URL.
As an example:
https://localhost/travel/hotel/Lanzarote/Costa Teguise/Hotel Beatriz Costa & Spa
Converted in to,
https://localhost/web/guest/travel/hotel?p_p_id=hotelsearch_WAR_hotelportlet&p_p_lifecycle=1&p_p_state=normal&p_p_mode=view&_hotelsearch_WAR_hotelportlet_param1=travel&_hotelsearch_WAR_hotelportlet_param2=hotel&_hotelsearch_WAR_hotelportlet_param3=Lanzarote&_hotelsearch_WAR_hotelportlet_param4=Costa%20Teguise&_hotelsearch_WAR_hotelportlet_param5=Hotel%20Beatriz%20Costa%20&%20Spa
First configure the mapping in sample hook project liferay-hook.xml :
<?xml version="1.0"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 6.2.0//EN"
"http://www.liferay.com/dtd/liferay-hook_6_2_0.dtd">
<hook>
<servlet-filter>
<servlet-filter-name>CustomURLPatternFilter</servlet-filter-name>
<servlet-filter-impl>com.hotel.travel.portlet.customefilter.CustomURLPatternFilter</servlet-filter-impl>
<init-param>
<param-name>hello</param-name>
<param-value>world</param-value>
</init-param>
</servlet-filter>
<servlet-filter-mapping>
<servlet-filter-name>CustomURLPatternFilter</servlet-filter-name>
<url-pattern>/travel/hotel/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</servlet-filter-mapping>
Sample servlet filter class :
public class CustomURLPatternFilter implements Filter {
#Override
public void destroy() {
LOG.info("CustomURLPatternFilter.destroy()");
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
String requestURI = request.getRequestURI();
try {
String[] urlPaths = StringUtil.split(requestURI, StringPool.FORWARD_SLASH);
System.out.println(urlPaths[0]);
System.out.println(urlPaths[1]);
System.out.println(urlPaths[2]);
System.out.println(urlPaths[3]);
System.out.println(urlPaths[4]);
System.out.println(urlPaths[5]);
if (urlPaths.length == 6) {
String forwardPath = "/web/guest/travel/hotel?p_p_id=hotelsearch_WAR_hotelportlet&p_p_lifecycle=1&p_p_state=normal&p_p_mode=view"
+ "&_hotelsearch_WAR_hotelportlet_param1=" + urlPaths[1]
+ "&_hotelsearch_WAR_hotelportlet_param2=" + urlPaths[2]
+ "&_hotelsearch_WAR_hotelportlet_param3=" + urlPaths[3]
+ "&_hotelsearch_WAR_hotelportlet_param4=" + urlPaths[4]
+ "&_hotelsearch_WAR_hotelportlet_param5=" + urlPaths[5];
req.getRequestDispatcher(forwardPath).forward(req, res);
}
else {
chain.doFilter(req, res);
}
} catch (Exception e) {
chain.doFilter(req, res);
e.printStackTrace();
}
}
#Override
public void init(FilterConfig filterConfig) {
System.out.println("Called SampleFilter.init(" + filterConfig + ")");
}
private static final Log LOG =
LogFactoryUtil.getLog(CustomURLPatternFilter.class);
}
At the end in your original hotel portlet ,
HttpServletRequest httpReq = PortalUtil.getOriginalServletRequest(PortalUtil.getHttpServletRequest(req));
System.out.println( httpReq.getParameter("_hotelsearch_WAR_hotelportlet_param1") );
System.out.println( httpReq.getParameter("_hotelsearch_WAR_hotelportlet_param2") );
System.out.println( httpReq.getParameter("_hotelsearch_WAR_hotelportlet_param3") );
System.out.println( httpReq.getParameter("_hotelsearch_WAR_hotelportlet_param4") );
System.out.println( httpReq.getParameter("_hotelsearch_WAR_hotelportlet_param5") );

Related

Liferay : How can I land to custom url on signout

I'm not sure if I can achieve this by simply configuration or I need to override LogoutAction for it.
I've configured multiple organisation and each organisation has there own site which I want to navigate to my custom url for different site instead of default url on logout from liferay.
[EDITED]
I want to navigate on different url for each site, not a common url.
Thanks
For this you can use default.logout.page.path property (in portal-ext.properties file)
default.logout.page.path=
#default.logout.page.path=/web/guest/logout
I think you can achieve this by overriding LogoutPostAction through a hook.
Define your LogoutPostAction class in portal.properties of your hook:
logout.events.post=com.my.action.MyLogoutPostAction
Here is a sample code for the class to redirect to your desired page:
public class MyLogoutPostAction extends Action {
#Override
public void run(HttpServletRequest request, HttpServletResponse response)
throws ActionException {
try {
doRun(request, response);
}
catch (Exception e) {
throw new ActionException(e);
}
}
protected void doRun(HttpServletRequest request, HttpServletResponse response)
throws Exception {
long groupId = PortalUtil.getScopeGroupId(httpReq);
// code to fetch the Group
// ....
// ....
//
String postLogoutURL = "create your own URL";
// if required: add a parameter
postLogoutURL = HttpUtil.setParameter(postLogoutURL, "my_param", "my_param_value");
// redirect to that URL
response.sendRedirect(postLogoutURL);
}
}
The only thing that can be a road block with this approach would be if Liferay has lost context of the current group from which the user was logged-out. I have not tested the code.

Liferay 5.2.3 portlet configuration page issue

I wrote a portlet that has custom conf page here is configuration-action-class:
public class ConfigurationActionImpl implements ConfigurationAction {
private static Logger log = Logger.getLogger(ConfigurationActionImpl.class);
private config conf=config.getInstance();
public String render(PortletConfig config, RenderRequest renderRequest, RenderResponse renderResponse) throws Exception {
if(renderRequest.isUserInRole("administrator")){
log.info("UserRole:::admin");
return "/config.jsp";
}else if(renderRequest.isUserInRole("guest")){
log.info("UserRole:::guest");
}else if(renderRequest.isUserInRole("power-user")){
log.info("UserRole:::power-user");
return "/config.jsp";
}else if(renderRequest.isUserInRole("user")){
log.info("UserRole:::user");
}else{
log.info("UserRole:::dafug");
}
return "/config.jsp?mode=guest";
}
public void processAction(PortletConfig config, ActionRequest actionRequest, ActionResponse actionResponse) throws Exception {
conf.Names.clear();
String count = ParamUtil.getString(actionRequest, "count");
String portletResource = ParamUtil.getString(actionRequest, "portletResource");
PortletPreferences prefs = PortletPreferencesFactoryUtil.getPortletSetup(actionRequest, portletResource);
String[] list=count.split("/");
for(String a : list){
if( a!=null&& !a.equals("")){
String en = ParamUtil.getString(actionRequest,"En"+a);
String pa = ParamUtil.getString(actionRequest,"Pa"+a);
if(!en.equals("")&&!pa.equals("")){
conf.Names.put(pa,en);
log.info("word::"+en+"::::"+pa);
prefs.setValue("En"+a,en);
prefs.setValue("Pa"+a,pa);
}else if(a!=null&& !a.equals("")){
count=count.substring(0,count.lastIndexOf("/"+a))+count.substring(count.lastIndexOf("/"+a)+("/"+a).length());
}
}
}
prefs.setValue("count",count);
prefs.store();
}
public void serveResource(ResourceRequest request, ResourceResponse response){
log.info("HERE in conf");
}
}
This class worked fine for only one time after clicking on return to full page, the button that locate in right corner of portlets does not work and I cannot go to configuration page again!
and also the menu bar in up right corner of portal did not work after getting back from configuration page unless I delete my portlet and all of them will work fine again!
I solved this problem.
My problem is that first I should change all the to
and I change all of my JQuery code to JavaScript because some how these version of JQuery that I used generates some error with this version of Liferay (5.2.3)

How to avoid ;jsessionid=XXX on the first call to a page? it works if first page is jsp

I have an application which uses the welcome-page index.jsp with an <iframe></iframe> the contents of the iframe is a jsf page. If I access index.jsp I see a cookie already on the first get in firebug:
Set-Cookie JSESSIONID=C615DA89B6EF73F801973EA3DCD3B226; Path=/
The page of the <iframe> inherits this jsessionid. BUT: when I directly access the page of the <iframe/> I get the jsessionId rewritten to all URLs without a cookie - on the first request. Afterwards the cookie is used. This is all fine - if:
The security system would allow me to perform url rewrites.
I run jboss 4.2.2
I want to achieve the same behaviour as I have with the index.jsp - e.g. always use cookies and always avoid http rewrite.
[EDIT]
thanks to balusc's answer I wrote this:
public class JsessionIdAvoiderFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
boolean allowFilterChain = redirectToAvoidJsessionId((HttpServletRequest) req, (HttpServletResponse)res);
//I'm doing this because if I execute the request completely, it will perform a pretty heavy lookup operation. No need to do it twice.
if(allowFilterChain)
chain.doFilter(req, res);
}
public static boolean redirectToAvoidJsessionId(HttpServletRequest req, HttpServletResponse res) {
HttpSession s = req.getSession();
if(s.isNew()) {
//after the redirect we don't want to redirect again.
if(!(req.isRequestedSessionIdFromCookie()&&req.isRequestedSessionIdFromURL()))
{
//yeah we have request parameters actually on that request.
String qs = req.getQueryString();
String requestURI = req.getRequestURI();
try {
res.sendRedirect(requestURI+"?"+qs);
return false;
} catch (IOException e) {
logger.error("Error sending redirect. " + e.getMessage());
}
}
}
return true;
}
}
Don't forget to add it to your web.xml
<filter>
<display-name>JsessionId Filter</display-name>
<filter-name>jsessionIdAvoiderFilter</filter-name>
<filter-class>my.namespace.JsessionIdAvoiderFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>jsessionIdAvoiderFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
Since Servlet 3.0 you could use <tracking-mode>COOKIE</tracking-mode> for this. But as JBoss 4.2.2 isn't Servlet 3.0 compilant, this isn't an option.
Easiest would be to create a servlet filter which sends a redirect to HttpServletRequest#getRequestURI() when HttpSession#isNew() returns true. Don't forget to check the HttpServletRequest#isRequestedSessionIdFromCookie() to prevent an infinite redirect loop when the client doesn't support cookies at all.
Based on Christopher Schultz recommendation I tried this and it works.
package com.rama.test.jsessionfilter
public class JsessionIdAvoiderFilter implements Filter {
protected static final Logger LOGGER = LogManager.getLogger(JsessionIdAvoiderFilter.class);
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
if (!(req instanceof HttpServletRequest)) {
chain.doFilter(req, res);
return;
}
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// Redirect requests with JSESSIONID in URL to clean old links
/* If you really want clean up some old links which have Jsession id bookmarked clean it. If its new app
this below check is not required. */
if (request.isRequestedSessionIdFromURL()) {
String url = request.getRequestURL().append(request.getQueryString() != null ? "?"
+ request.getQueryString() : "").toString();
response.setHeader("Location", url);
response.sendError(HttpServletResponse.SC_MOVED_PERMANENTLY);
LOGGER.info(" Found url with jsession id in it:"+ request.getRequestURL() +": url="+url);
return;
}
// Prevent rendering of JSESSIONID in URLs for all outgoing links
HttpServletResponseWrapper wrappedResponse = new HttpServletResponseWrapper(
response) {
#Override
public String encodeRedirectUrl(String url) {
return url;
}
#Override
public String encodeRedirectURL(String url) {
return url;
}
#Override
public String encodeUrl(String url) {
return url;
}
#Override
public String encodeURL(String url) {
return url;
}
};
chain.doFilter(req, wrappedResponse);
}
public void destroy() {
}
public void init(FilterConfig arg0) throws ServletException {
}
}
and the following entry in web.xml
<filter>
<display-name>JsessionId Filter</display-name>
<filter-name>jsessionIdAvoiderFilter</filter-name>
<filter-class>com.rama.test.jsessionfilter.JsessionIdAvoiderFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>jsessionIdAvoiderFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
Works great !!!.
This can be done with a simple Filter that wraps the request with an HttpServletRequest which overrides HttpServletRequest.encodeURL and HttpServletRequest.encodeRedirectURL. Simply return the String argument passed to it and you will disable URL re-writing. Note that this will only work for a single webapp unless you want to either configure it in conf/web.xml (not recommended) or configure it in all of your separate webapps.
This technique is superior to that posted later in your question because it does not require redirection which can slow-down your requests. IMO, it's also cleaner.

How to handle HTTP 403 with Spring Security 3.0.x

I'm facing a little issue with Spring Security 3.0.x (3.0.2 in particular at the moment). The whole application I'm working on is working perfectly except when someone who doesn't have the authorities tries to log on.
When it occurs, the users is redirected to the "welcome" page, since his username/password are valid, and he receive a cute white page with this : "Error 403: Access is denied"
So, I've been looking on the net trying to find how this behavior can be handled. So far I've come to the conclusion, please correct me if I'm wrong, that it is managed by the ExceptionTranslationFilter. But I don't quite understand how to make any good use of this information.
I've tryied to edit my SecurityContext.xml to add a access-denied-handler tag to my http tag, but it doesn't work. Do I need to add more than this tag to make it work? Is there any other possibilities to make my application more user-friendly?
Edit : I would like to redirect to a page, let's says 403.html, for example.
Sincerly,
Thanks
I still don't get why you had to implement your own access handler... I have currently faced same task:
<security:access-denied-handler error-page="/accessDenied"/> - works like charm.
Don't forget to specify handler in your Controller:
#RequestMapping(value = "/accessDenied")
public String accessDenied() {
return "accessDenied"; // logical view name
}
Update for Spring Boot(2014 Oct):
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().accessDeniedHandler(customHandler) OR .accessDeniedPage("/somePage.html").and
.formLogin()
.failureHandler(ajaxAuthenticationFailureHandler)}
Nowadays we don't really return views for such task since angular js kicks in so you can use your failure/success handler and return tailored JSON responses. For us it was sufficient to use failure handler but you get to choose where you want your control to kick in. We generally don't use view resolvers as there are UI tiles frameworks(such as angular partials) able to construct pieces into single page for you. Html pieces are stored on the server and served simply as static resources.
Lets play with Embedded Tomcat to achieve similar behavior to web.xml !
#Configuration
#EnableAutoConfiguration
public class ApplicationWebXml extends SpringBootServletInitializer {
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.profiles(addDefaultProfile())
.showBanner(false)
.sources(Application.class);
}
//required for container customizer to work, the numerous tutorials didn't work for me, so I simply tried overriding the default one
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
return tomcat;
}
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer(
) {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
TomcatEmbeddedServletContainerFactory containerFactory = (TomcatEmbeddedServletContainerFactory) container;
containerFactory.setSessionTimeout(1); // just for your interest, remove as necessary
containerFactory.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN,"/views/accessDenied.html"),
new ErrorPage(HttpStatus.NOT_FOUND,"/views/notFound.html"));
containerFactory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
#Override
public void customize(Connector connector) {
connector.setPort(8082);// just for your interest, remove as necessary
}
});
}
};
}
}
A cleaner way to handle error redirects is to use the <error-page> and <error-code> tags in your web.xml. See below for an example:
<!-- Custom 403 Error Page -->
<!--
NOTE: Security will throw this error when a user has been authenticated successfully
but lacks the permissions to perform the requested action.
-->
<error-page>
<error-code>403</error-code>
<location>/403.jsp</location>
</error-page>
This block of code will redirect to the specified location whenever it encounters the specified error code.
This eliminates the need for authorization code inside your application logic.
I've found how to do this. By implementing the AccessDeniedHandler interface and the corresponding handle method I can, easily, control the way the Http 403 error is handled.
This way, you can add various items in the session and then intercept them on your jsp.
The xml file then looks like this :
<sec:http>
<!-- lots of urls here -->
<sec:access-denied-handler ref="accessDeniedHandler" />
<sec:anonymous/>
</sec:http>
<bean id="accessDeniedHandler" class="foo.bar.CustomAccessDeniedHandler">
<property name="accessDeniedUrl" value="403.html" />
</bean>
The java class :
package foo.bar;
public class CustomAccessDeniedHandler implements org.springframework.security.web.access.AccessDeniedHandler {
private String accessDeniedUrl;
public CustomAccessDeniedHandler() {
}
public CustomAccessDeniedHandler(String accessDeniedUrl) {
this.accessDeniedUrl = accessDeniedUrl;
}
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.sendRedirect(accessDeniedUrl);
request.getSession().setAttribute("CustomSessionAttribute", "value here");
}
public String getAccessDeniedUrl() {
return accessDeniedUrl;
}
public void setAccessDeniedUrl(String accessDeniedUrl) {
this.accessDeniedUrl = accessDeniedUrl;
}
}
And a jsp example :
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:if test="${!empty CustomSessionAttribute}">
<br/>
ACCESS IS DENIED
<br/>
</c:if>
<!-- other stuff down here -->
The way to make this work is to define a handler in your entry point:
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, org.springframework.security.core.AuthenticationException authException) throws IOException, ServletException {
if (authException != null) {
// you can check for the spefic exception here and redirect like this
response.sendRedirect("403.html");
}
}
}
You can define this as your entry point by setting this as you entry point in the xml config file:
<http entry-point-ref="customAuthenticationEntryPoint">
...
</http>
You have checked the tag in an application and to me it seems to work.
<sec:access-denied-handler error-page="/handle403Url" />
where handle403Url I want to call to handle this error (for example to show an error).
Don't forget that you have to allow this url in the filters so it can be reached by this user authority, so in the start of the flters you have to add something like this:
<sec:intercept-url pattern="/handle403Url" filters="none" />

How to insert response size and time into the page itself, at least partially?

I realize it's a chicken and egg problem and that it's not possible to accurately resolve the time it took to render a page (or the size of response) and insert that number into the page itself without affecting either measure. Nevertheless, I'm looking for a way to insert either number partially in a page of a JSF/Facelets/Seam application.
E.g., at the bottom of a .jsf page somewhere:
<!-- page size: 10.3Kb -->
<!-- render time: 0.2s -->
I've come across JSFUnit's JSFTimer, which is really handy. However, the phase listener approach doesn't allow the results of RENDER_RESPONSE phase to be inserted into the page. Not sure how to access the size of the response encoded so far either.
Is there a quick and dirty way to hook up to some sort of post-processing event at or after the end of RENDER_RESPONSE and to inject both numbers into the page about to be rendered? One way of approaching this is perhaps through servlet filters, but I'm looking for something simpler; perhaps a trick with Seam or Facelets...
Thanks,
-A
This is a perfect use case for the Apache Commons IO CountingOutputStream. You need to create a Filter which uses HttpServletResponseWrapper to replace the OutputStream of the response with this one and replaces the Writer as well which should wrap the wrapped OutputStream. Then get hold of the HttpServletResponseWrapper instance in the request scope so that you can get the getByteCount() from the CountingOutputStream.
Here's a kickoff example of the CountingFilter:
public class CountingFilter implements Filter {
#Override
public void init(FilterConfig arg0) throws ServletException {
// NOOP.
}
#Override
public void doFilter(ServletRequest request, final ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpres = (HttpServletResponse) response;
CountingServletResponse counter = new CountingServletResponse(httpres);
HttpServletRequest httpreq = (HttpServletRequest) request;
httpreq.setAttribute("counter", counter);
chain.doFilter(request, counter);
counter.flushBuffer(); // Push the last bits containing HTML comment.
}
#Override
public void destroy() {
// NOOP.
}
}
The CountingServletResponse:
public class CountingServletResponse extends HttpServletResponseWrapper {
private final long startTime;
private final CountingServletOutputStream output;
private final PrintWriter writer;
public CountingServletResponse(HttpServletResponse response) throws IOException {
super(response);
startTime = System.nanoTime();
output = new CountingServletOutputStream(response.getOutputStream());
writer = new PrintWriter(output, true);
}
#Override
public ServletOutputStream getOutputStream() throws IOException {
return output;
}
#Override
public PrintWriter getWriter() throws IOException {
return writer;
}
#Override
public void flushBuffer() throws IOException {
writer.flush();
}
public long getElapsedTime() {
return System.nanoTime() - startTime;
}
public long getByteCount() throws IOException {
flushBuffer(); // Ensure that all bytes are written at this point.
return output.getByteCount();
}
}
The CountingServletOutputStream:
public class CountingServletOutputStream extends ServletOutputStream {
private final CountingOutputStream output;
public CountingServletOutputStream(ServletOutputStream output) {
this.output = new CountingOutputStream(output);
}
#Override
public void write(int b) throws IOException {
output.write(b);
}
#Override
public void flush() throws IOException {
output.flush();
}
public long getByteCount() {
return output.getByteCount();
}
}
You can use it in any (even non-JSF) page as follows:
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Counting demo</title>
</h:head>
<h:body>
<h1>Hello World</h1>
</h:body>
</html>
<!-- page size: #{counter.byteCount / 1000}KB -->
<!-- render time: #{counter.elapsedTime / 1000000}ms -->
I wrote a blog post explaining how you could create an Interceptor that would measure each method call your seam compontents where using.
You can find the blog post here.
You need to scroll down to the second part.
Basically, all you need to do is annotate the method you want to measure with
#MeasureCalls and it will automatically be picked up by the interceptor
#Name("fooBean")
#MeasureCalls
public class FooBean
An output would be something like this, showing the time it took in milliseconds and how many times each method was called:
284.94 ms 1 FooBean.getRandomDroplets()
284.56 ms 1 GahBean.getRandomDroplets()
201.60 ms 2 SohBean.searchRatedDoodlesWithinHead()
185.94 ms 1 FroBean.doSearchPopular()
157.63 ms 1 FroBean.doSearchRecent()
42.34 ms 1 FooBean.fetchMostRecentYodel()
41.94 ms 1 GahBean.getMostRecentYodel()
15.89 ms 1 FooBean.getNoOfYodels()
15.00 ms 1 GahBean.getNoOfYodels()
9.14 ms 1 SohBean.mainYodels()
1.11 ms 2 SohBean.trackHoorayEvent()
0.32 ms 1 FroBean.reset()
0.22 ms 43 NohBean.thumbPicture()
0.03 ms 18 FooBean.getMostRecentYodels()
0.01 ms 1 NohBean.profilePicture()
0.01 ms 1 FroBean.setToDefault()
0.01 ms 1 FroBean.getRecentMarker()
Or have an asynch Javascript call that gets the response time and size from the server after its ready? Treat it like a callback to be performed after the page is done loading and the values are ready to insert.

Resources