domenica 2 ottobre 2011

xfce4-cpugraph-plugin on Fedora 15

This plugin isn't in Fedora repositories. You can download it from source, compile and install it. I spent 1 hours to find the correct parameters to pass to configure script...

 [root@precision2 ~]# ./configure --libexecdir=/usr/libexec/ --prefix=/usr/  
 [root@precision2 ~]# make  
 [root@precision2 ~]# make install  

giovedì 23 giugno 2011

JGroups and slf4j

JGroups offers a mechanism to extend its logger implemented two interface, CustomLogFactory and Log. Here are the two implementations for use slf4j.


JGroupsCustomLoggerFactory:
 import org.jgroups.logging.CustomLogFactory;  
 import org.jgroups.logging.Log;  
 import org.slf4j.Logger;  
 import org.slf4j.LoggerFactory;  
 public class JGroupsCustomLoggerFactory implements CustomLogFactory {  
      @Override  
      @SuppressWarnings("rawtypes")  
      public Log getLog(Class clazz) {  
           Logger logger = LoggerFactory.getLogger(LogUtils.formatClassName(clazz));  
           return new JGroupsCustomLog(logger);  
      }  
      @Override  
      public Log getLog(String category) {  
           Logger logger = LoggerFactory.getLogger(category);  
           return new JGroupsCustomLog(logger);  
      }  
 }  



JGroupsCustomLog.java:


 import org.jgroups.logging.Log;  
 import org.slf4j.Logger;  
 import org.slf4j.Marker;  
 public class JGroupsCustomLog implements Log {  
      private Logger logger;  
      public JGroupsCustomLog(Logger logger) {  
           this.logger = logger;  
      }  
      @Override  
      public boolean isFatalEnabled() {  
           return false;  
      }  
      @Override  
      public boolean isErrorEnabled() {  
           return logger.isErrorEnabled();  
      }  
      @Override  
      public boolean isWarnEnabled() {  
           return logger.isErrorEnabled();  
      }  
      @Override  
      public boolean isInfoEnabled() {  
           return logger.isInfoEnabled();  
      }  
      @Override  
      public boolean isDebugEnabled() {  
           return logger.isDebugEnabled();  
      }  
      @Override  
      public boolean isTraceEnabled() {  
           return logger.isTraceEnabled();  
      }  
      public String getName() {  
           return logger.getName();  
      }  
      public void trace(String msg) {  
           logger.trace(msg);  
      }  
      public void trace(String format, Object arg) {  
           logger.trace(format, arg);  
      }  
      public void trace(String format, Object arg1, Object arg2) {  
           logger.trace(format, arg1, arg2);  
      }  
      public void trace(String format, Object[] argArray) {  
           logger.trace(format, argArray);  
      }  
      public void trace(String msg, Throwable t) {  
           logger.trace(msg, t);  
      }  
      public boolean isTraceEnabled(Marker marker) {  
           return logger.isTraceEnabled(marker);  
      }  
      public void trace(Marker marker, String msg) {  
           logger.trace(marker, msg);  
      }  
      public void trace(Marker marker, String format, Object arg) {  
           logger.trace(marker, format, arg);  
      }  
      public void trace(Marker marker, String format, Object arg1, Object arg2) {  
           logger.trace(marker, format, arg1, arg2);  
      }  
      public void trace(Marker marker, String format, Object[] argArray) {  
           logger.trace(marker, format, argArray);  
      }  
      public void trace(Marker marker, String msg, Throwable t) {  
           logger.trace(marker, msg, t);  
      }  
      public void debug(String msg) {  
           logger.debug(msg);  
      }  
      public void debug(String format, Object arg) {  
           logger.debug(format, arg);  
      }  
      public void debug(String format, Object arg1, Object arg2) {  
           logger.debug(format, arg1, arg2);  
      }  
      public void debug(String format, Object[] argArray) {  
           logger.debug(format, argArray);  
      }  
      public void debug(String msg, Throwable t) {  
           logger.debug(msg, t);  
      }  
      public boolean isDebugEnabled(Marker marker) {  
           return logger.isDebugEnabled(marker);  
      }  
      public void debug(Marker marker, String msg) {  
           logger.debug(marker, msg);  
      }  
      public void debug(Marker marker, String format, Object arg) {  
           logger.debug(marker, format, arg);  
      }  
      public void debug(Marker marker, String format, Object arg1, Object arg2) {  
           logger.debug(marker, format, arg1, arg2);  
      }  
      public void debug(Marker marker, String format, Object[] argArray) {  
           logger.debug(marker, format, argArray);  
      }  
      public void debug(Marker marker, String msg, Throwable t) {  
           logger.debug(marker, msg, t);  
      }  
      public void info(String msg) {  
           logger.info(msg);  
      }  
      public void info(String format, Object arg) {  
           logger.info(format, arg);  
      }  
      public void info(String format, Object arg1, Object arg2) {  
           logger.info(format, arg1, arg2);  
      }  
      public void info(String format, Object[] argArray) {  
           logger.info(format, argArray);  
      }  
      public void info(String msg, Throwable t) {  
           logger.info(msg, t);  
      }  
      public boolean isInfoEnabled(Marker marker) {  
           return logger.isInfoEnabled(marker);  
      }  
      public void info(Marker marker, String msg) {  
           logger.info(marker, msg);  
      }  
      public void info(Marker marker, String format, Object arg) {  
           logger.info(marker, format, arg);  
      }  
      public void info(Marker marker, String format, Object arg1, Object arg2) {  
           logger.info(marker, format, arg1, arg2);  
      }  
      public void info(Marker marker, String format, Object[] argArray) {  
           logger.info(marker, format, argArray);  
      }  
      public void info(Marker marker, String msg, Throwable t) {  
           logger.info(marker, msg, t);  
      }  
      public void warn(String msg) {  
           logger.warn(msg);  
      }  
      public void warn(String format, Object arg) {  
           logger.warn(format, arg);  
      }  
      public void warn(String format, Object[] argArray) {  
           logger.warn(format, argArray);  
      }  
      public void warn(String format, Object arg1, Object arg2) {  
           logger.warn(format, arg1, arg2);  
      }  
      public void warn(String msg, Throwable t) {  
           logger.warn(msg, t);  
      }  
      public boolean isWarnEnabled(Marker marker) {  
           return logger.isWarnEnabled(marker);  
      }  
      public void warn(Marker marker, String msg) {  
           logger.warn(marker, msg);  
      }  
      public void warn(Marker marker, String format, Object arg) {  
           logger.warn(marker, format, arg);  
      }  
      public void warn(Marker marker, String format, Object arg1, Object arg2) {  
           logger.warn(marker, format, arg1, arg2);  
      }  
      public void warn(Marker marker, String format, Object[] argArray) {  
           logger.warn(marker, format, argArray);  
      }  
      public void warn(Marker marker, String msg, Throwable t) {  
           logger.warn(marker, msg, t);  
      }  
      public void error(String msg) {  
           logger.error(msg);  
      }  
      public void error(String format, Object arg) {  
           logger.error(format, arg);  
      }  
      public void error(String format, Object arg1, Object arg2) {  
           logger.error(format, arg1, arg2);  
      }  
      public void error(String format, Object[] argArray) {  
           logger.error(format, argArray);  
      }  
      public void error(String msg, Throwable t) {  
           logger.error(msg, t);  
      }  
      public boolean isErrorEnabled(Marker marker) {  
           return logger.isErrorEnabled(marker);  
      }  
      public void error(Marker marker, String msg) {  
           logger.error(marker, msg);  
      }  
      public void error(Marker marker, String format, Object arg) {  
           logger.error(marker, format, arg);  
      }  
      public void error(Marker marker, String format, Object arg1, Object arg2) {  
           logger.error(marker, format, arg1, arg2);  
      }  
      public void error(Marker marker, String format, Object[] argArray) {  
           logger.error(marker, format, argArray);  
      }  
      public void error(Marker marker, String msg, Throwable t) {  
           logger.error(marker, msg, t);  
      }  
      @Override  
      public void fatal(String msg) {  
           logger.error(msg);  
      }  
      @Override  
      public void fatal(String msg, Throwable throwable) {  
           logger.error(msg, throwable);  
      }  
      @Override  
      public void trace(Object msg) {  
           logger.trace(msg.toString());  
      }  
      @Override  
      public void trace(Object msg, Throwable throwable) {  
           logger.trace(msg.toString(), throwable);  
      }  
      @Override  
      public void setLevel(String level) {  
      }  
      @Override  
      public String getLevel() {  
           return "INFO";  
      }  
 }  

Just befor initialize JGroups, call:

           try {  
                System.setProperty(Global.CUSTOM_LOG_FACTORY, "package.JGroupsCustomLoggerFactory");  
                jChannel = new JChannel();  
                jChannel.connect("channel-ver-3.0");  

martedì 10 maggio 2011

Hibernate with Guice and a little of AOP to manage transaction.

What do you think abount @Transactional annotation? I used it with spring framework on service class to manage transaction into and intra service call.
It's a very elegant and easy way to manage transactions.

I've started working on a new project and I decided to use Guice as Dependency Injection framework, IMHO more lite and easy than Spring, and Hibernate
but I would like to continue to use the @Transactional annotation on my service class.
I didn't found anything good to use and I decided to use a little of AOP to recreate this functionality.

Let's started with order, I would like that:
1) When I call a method on a service class, the operations in this method belong to a single transaction.
2) If the caller of the method 1) has a transaction, the thansanction must is the parent (caller) transaction.

In practice the "Required" attribute of EBJ's Transactional parameter, see Required Attribute

This is the first case:

And this the second one:

I'm going to use AOP and Guice to set a Method Interceptor that check is the called method has a transaction, and in case of not, create a new one and link it to thread with ThreadLocal class. I create also an annotation that identify che class and method subject and not subject to this check:

A) The Transactional Annotation:
01 package net.zonablog.persistence.aop;
02 
03 import java.lang.annotation.ElementType;
04 import java.lang.annotation.Retention;
05 import java.lang.annotation.RetentionPolicy;
06 import java.lang.annotation.Target;
07 
08 @Retention(RetentionPolicy.RUNTIME)
09 @Target({ ElementType.TYPE, ElementType.METHOD })
10 public @interface Transactional {
11 
12 }


The NoTransactional annotation, that mark the non-transactional method on a class marked as transactional:
01 package net.zonablog.persistence.aop;
02 
03 import java.lang.annotation.ElementType;
04 import java.lang.annotation.Retention;
05 import java.lang.annotation.RetentionPolicy;
06 import java.lang.annotation.Target;
07 
08 @Retention(RetentionPolicy.RUNTIME)
09 @Target({ ElementType.METHOD })
10 public @interface NoTransactional {
11 
12 }


The implementation of this interceptor:

01 package net.zonablog.persistence.aop;
02 
03 import net.zonablog.service.persistence.BasePersistentService;
04 import net.zonablog.service.persistence.HibernateSessionService;
05 
06 import org.aopalliance.intercept.MethodInterceptor;
07 import org.aopalliance.intercept.MethodInvocation;
08 import org.hibernate.Session;
09 import org.hibernate.Transaction;
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
12 
13 import com.google.inject.Inject;
14 
15 public class TransactionInterceptor implements MethodInterceptor {
16 
17   private Logger logger = LoggerFactory.getLogger(this.getClass());
18   private ThreadLocal<Session> threadLocal;
19 
20   @Inject
21   private HibernateSessionService hibernateSessionService;
22 
23   public TransactionInterceptor() {
24     logger.info("Inizializzation of " this.getClass());
25     threadLocal = new ThreadLocal<Session>();
26   }
27 
28   @SuppressWarnings("rawtypes")
29   public Object invoke(MethodInvocation invocationthrows Throwable {
30 
31     logger.trace("Start of transactional method");
32 
33     // Verifico se vengo chiamato da un'altra applicazione che ha già la
34     // transazione attiva
35     Transaction transaction = null;
36     Session session = threadLocal.get();
37     boolean startNewTransaction = true;
38 
39     if (session == null) {
40       logger.trace("Start new transaction");
41       session = hibernateSessionService.openSession();
42       threadLocal.set(session);
43       transaction = session.getTransaction();
44       transaction.begin();
45     else {
46       logger.trace("Already in transaction");
47       startNewTransaction = false;
48     }
49 
50     if (invocation.getThis() instanceof BasePersistentService)
51       ((BasePersistentServiceinvocation.getThis()).setSession(session);
52 
53     try {
54 
55       Object result = invocation.proceed();
56 
57       if (transaction != null) {
58         logger.trace("Commit transaction");
59         transaction.commit();
60         threadLocal.remove();
61       }
62 
63       return result;
64 
65     catch (Exception e) {
66 
67       if (transaction != null) {
68         logger.debug("Rollback transaction due [" + e.getMessage() "]");
69         transaction.rollback();
70         threadLocal.remove();
71       }
72 
73       throw e;
74     finally {
75       logger.trace("Exit transactional method");
76 
77       if (startNewTransaction && session != null)
78         session.close();
79     }
80 
81   }
82 
83   @Override
84   protected void finalize() throws Throwable {
85     threadLocal.remove();
86     super.finalize();
87   }
88 
89 }

I'm using Jersey with Guice, and I use the @Transactional annotation also on webresources. Es:


01 package net.zonablog.rest.controller;
02 
03 import java.util.UUID;
04 
05 import javax.mail.internet.AddressException;
06 import javax.mail.internet.InternetAddress;
07 import javax.ws.rs.FormParam;
08 import javax.ws.rs.GET;
09 import javax.ws.rs.POST;
10 import javax.ws.rs.Path;
11 import javax.ws.rs.PathParam;
12 import javax.ws.rs.Produces;
13 import javax.ws.rs.core.MediaType;
14 import javax.ws.rs.core.Response;
15 import javax.ws.rs.core.Response.ResponseBuilder;
16 import javax.ws.rs.core.Response.Status;
17 
18 import com.google.inject.Inject;
19 import com.sun.jersey.spi.resource.PerRequest;
20 
21 @Transactional
22 @Path("services/register")
23 @PerRequest
24 public class RegisterController extends BaseController {
25 
26     @Inject
27     private MailService mailService;
28 
29     @Inject
30     private UserService userService;
31 
32     @Inject
33     public RegisterController() {
34     }
35 
36     @POST
37     @Produces(MediaType.APPLICATION_XML)
38     public Object register(@FormParam("registerDialogFormEmail"String email,
39             @FormParam("registerDialogFormPass"String password, @FormParam("registerDialogFormRPass"String rpassword) {
40 
41         ...
42     }
43 }


Note that to let Guice istantiate the webresource, in addition to configuration needed by jersey-guice module, the @Inject must present on constructor's class.

giovedì 28 aprile 2011

Jersey, @RolesAllowed annotation and 403 Forbidden

I spent last three hours to understand why I always get 403 Forbidden from a secured JAX-RS resource with @RolesAllowed...
After checking the params of Jersey Servlet:

 <init-param>  
  <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>  
  <param-value>com.sun.jersey.api.container.filter.RolesAllowedResourceFilterFactory</param-value>  
 </init-param>  

I proceeded to debug the filter under the hood, RolesAllowedResourceFilterFactory, and I noticed that UserPrincipal was null!
What I'd forgotten was to secured the resources with security-constraint:

 <security-constraint>  
  <web-resource-collection>  
   <web-resource-name>Protected Area</web-resource-name>  
   <url-pattern>/resources/*</url-pattern>  
  </web-resource-collection>  
  <auth-constraint>  
   <role-name>admin</role-name>  
   <role-name>user</role-name>  
  </auth-constraint>  
 </security-constraint>  

In role-name you've to insert all the roles that you're going to use in @RolesAllowed.

Good night!

domenica 20 febbraio 2011

Expose Apache Tomcat statistics on Cacti

Cacti is an extremly usefully tool to plot any parameters on a system.
This came out with defaults sensors for SO and Networking in general, I tried to use cacti to plot some interesting parameters about JVM and Tomcat.

I use the jmxProxy servlet came with manager webapp from tomcat's distribution to get the value of some attributes from the MBean Server.

Let we start with trace the Request Count, Error Count and Processing Time on AJP port, I do it on a Fedora 13 machine:

  1. Requirements
    1. Default Tomcat installation with manager webapp.
    2. Default Cacti installation.
    3. Set the user with manager role in tomcat-users.xml:
      1. This is a piece of my TOMCAT_HOME/conf/tomcat-users.xml:
         <tomcat-users>  
          <role rolename="manager"/>  
          <user username="manager" password="changethispassword" roles="manager"/>  
          ....  
         </tomcat-users>  
        
      2. Check the jmxProxy servlet, point your browser to http://localhost:8080/manager/jmxproxy/?qry=Catalina:type=GlobalRequestProcessor,name=http-8080 and check the result page, this is an example:
         OK - Number of results: 1  
         Name: Catalina:type=GlobalRequestProcessor,name=http-8080  
         modelerType: org.apache.tomcat.util.modeler.BaseModelMBean  
         bytesSent: 168065  
         bytesReceived: 340  
         processingTime: 5310  
         errorCount: 0  
         maxTime: 3214  
         requestCount: 16  
        
  2. Create the script to get the output of jmxProxy servlet as input for Cacti:
    1. Log on the machine as cacti user and create the file CACTI_HOME/scripts/get_tomcat_globalrequestprocessor.sh:
       #!/bin/bash   
       OPTTIONS="--user $3:$4 -f -S http://$1:$2/manager/jmxproxy/?qry=Catalina:type=GlobalRequestProcessor,name=$5"  
       OUTPUT=$(curl $OPTTIONS 2>/dev/null)  
       PT=$(printf "$OUTPUT" | grep -e "^processingTime: " | grep -oEi 'processingTime: ([0-9]*)' | sed "s/processingTime: /processingtime:/g")  
       EC=$(printf "$OUTPUT" | grep -e "^errorCount: " | grep -oEi 'errorCount: ([0-9]*)' | sed "s/errorCount: /errorcount:/g")  
       RC=$(printf "$OUTPUT" | grep -e "^requestCount: " | grep -oEi 'requestCount: ([0-9]*)' | sed "s/requestCount: /requestcount:/g")  
       echo $PT $EC $RC  
      
    2. Change it's permission: chmod 775 scripts/get_tomcat_globalrequestprocessor.sh
    3. Check the script: ./scripts/get_tomcat_globalrequestprocessor.sh localhost 8080 manager changethispassword http-8080
       -bash-4.1$ ./get_tomcat_globalrequestprocessor.sh localhost 8080 manager changethispassword http-8080  
       processingtime:73631 errorcount:2 requestcount:18253  
       -bash-4.1$   
      
  3. Configure Cacti to plot this value:

    1. Open Cacti and create a new Data Input Method. Type in the name field Tomcat GlobalRequestProcessor,
      select Script/Command as Input Type and cut&paste the script call in Input String <path_cacti>/scripts/get_tomcat_globalrequestprocessor.sh <host> <port> <username> <password> <name>.
      The name in the script wrapped between < and > are parameters that are replaced with the values came from Data Sources view (see later). Click create.

    2. Complete the Data Input Method specifying the input and output of script. Add in input field the five descriptions of input defined for the script and add in output fields list the four output value of the script (with a short description and leave check the field Update RRD File):
      1. errorcount
      2. processingtime
      3. requestcount
      And save.



    3. Add a new Data Source. Select None in the "Selected Data Template" field and an host. I've created previously a devices called Java Virtual Machine to attach all my jvm's graph, but isn't necessary, is only for ordering graph.



      Click Create and complete the Data Source with a name (Tomcat http-8080 Global Request Processor) a Data Source Path (/localhost_tomcat_globalrequestprocessor_91.rrd) and select the just created Data Input Metod (Tomcat Global Request Processor). Leave Associated RRS's, Step and Data Source Active with default value and click Create.
      Cacti has saved the DataSource and now ask for Data Source Item. In this section we have to define all data parameters that come out with Data Input Methods and specify its type (http://www.cacti.net/downloads/docs/html/templates.html Table 13-2). Start with processingtime, that is a COUNTER



      Complete the Data Source Item with other two Data Source Item, all as COUNTER type and Maximum Value 0. Complete also the Custom Data section with input value that script needs.



    4. Create the Graph. Click on Add in Graph Management and select None as Template and an host. Click Create and insert the graph name, leaving all fields with default value. Click on Create.



      Add the three Graph Items. Click Add on Graph Items, select the first our data source (Tomcat Global Request Processor (errorcount)), select a color, AREA as Graph Item Type and MAX as Consolidation Function.



      You can also add Items that display the average/max/min/last value of sensors. I added Avg and Last values for each items.



    5. The result




  4. Troubleshooting

    When your graph don't appear try to do all the step that cacti do to display the graph:
    1. Check the script's paramenters that cacti use. Go in System Utility -> View Poller Cache and write down the Script call in Details Field. Check the parameters order and try to execute the script on the shell. Check the output.
    2. Check data samples in RRD file. Use the rddtool dump command to see the file content and check if your samples are out of range (if MIN or MAX aren't zero):
       -bash-4.1$ rrdtool dump /usr/share/cacti/rra/localhost_tomcat_globalrequestprocessor_91.rrd | less  
      
    3. Check the ERROR: the RRD does not contain an RRA matching the chosen CF This error means that the statistics collected from rrd don't match the type of Consolidation Function selected on Graph Items (Console -> Graph Management -> (Edit) -> Graph Items). To see which statistics are collected by rrd, open the rrd file with rrdtool dump (as above) and see the cf element of rra node.




  5. Enhancement

    In this graph I don't use the template for Data Source/Graph, but if you have to create the same graph for multiple machines, creating template is the right choose.







I hope this help to learn cacti, which hasn't a very flat learning curve.