Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Friday, October 31, 2014

Gem of Java: BTrace; Debugging production code on the fly


But the question is now how do u debug? Generally the loggers we kept are in-sufficient or in production they disabled debug, info levels of log4j; now how? or say we forgot logging a variable.. now how to debug the issue?

wouldnt it be nice if i have an agent sitting outside the main JVM and printing out the variable i wanted? is it possible? the answer is YES!!!


BTrace is the gem in Java-s Crown. So it works like this

  1. The original JVM runs on some PID
  2. The Btrace program runs outside and connects to this pid; printing ur variable as u wanted
You need to download the BTrace package; Create a java program; Typically it looks as follows



package my;


import services.reports.ReportContext;

import com.insideview.industryProfiles.IndustryProfile;
import com.insideview.industryProfiles.IndustrySic;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.Return;


@BTrace(unsafe=true)
public class Tracer {
        @OnMethod( clazz="services.reports.company.CompanyIndustryProfile",  method="appendSection", location=@Location(Kind.RETURN))
        public static void operate(@Return ReportContext rc  ){
         IndustryProfile industryProfile = (IndustryProfile) (rc.get("industryProfile"));
         BTraceUtils.printFields(industryProfile);
         BTraceUtils.println();
         IndustrySic sic = industryProfile.getSic();
  BTraceUtils.printFields(sic);
        }
}

If u see the highlighted area; they are the custom objects in the JVM running; now this program will print them when ever the method: CompanyIndustryProfile.appendSection() is hit injecting the return variable (i.e.. @Return)

In the dowloaded package, open the btrace file located in bin folder and in the end change to the below. The line is to the end of the file

 -Dcom.sun.btrace.debug=true



it will be initially set to false; Now run the command; below is the sample output.


[jboss@iv build]$ ../bin/btrace -cp "btrace-agent.jar:btrace-boot.jar:btrace-client.jar:velocity-1.7-dep.jar:company.jar" 26009  Tracer.java

DEBUG: btrace debug mode is set
DEBUG: btrace unsafe mode is set
DEBUG: accepting classpath btrace-agent.jar:btrace-boot.jar:btrace-client.jar:velocity-1.7-dep.jar:commpany.jar
DEBUG: assuming default port 2020
DEBUG: compiling Tracer.java
DEBUG: compiled Tracer.java
DEBUG: attaching to 8995
DEBUG: checking port availability: 2020
DEBUG: attached to 8995
DEBUG: loading /tmp/btrace/build/btrace-agent.jar
DEBUG: agent args: port=2020,debug=true,unsafe=true,systemClassPath=/usr/jdk1.6.0_29/lib/tools.jar,probeDescPath=.
DEBUG: loaded /tmp/btrace/build/btrace-agent.jar
DEBUG: registering shutdown hook
DEBUG: registering signal handler for SIGINT
DEBUG: submitting the BTrace program
DEBUG: opening socket to 2020
DEBUG: sending instrument command
DEBUG: entering into command loop
DEBUG: received com.sun.btrace.comm.OkayCommand@566711b0
DEBUG: received com.sun.btrace.comm.RetransformationStartNotification@8ba6621
DEBUG: received com.sun.btrace.comm.OkayCommand@5870501
DEBUG: received com.sun.btrace.comm.MessageCommand@2e2b5ba8
{industryDescription=null, industryId=564,abc=xyx,etc=etc], sizeStructureDescription=etc_etc}
DEBUG: received com.sun.btrace.comm.MessageCommand@26284112

DEBUG: received com.sun.btrace.comm.MessageCommand@1cc81850
$$$$$$$$$$$DEBUG: received com.sun.btrace.comm.MessageCommand@14b43af3
{id=7375, description=CBI Services, }


Issues to lookout
  1. If u havent changed btrace file, ran the above process and then modifed the unsafe to true on running the btrace u see VerificationError.
  2. As said here it happens because the 1st run made the target JVM to mark any unsafe to false
  3. for this u will have to restart the target m/c





Wednesday, February 6, 2013

Spring register a new JSON Converter

Yesterday I had to modify the JSON during creation from an object. I want to add validation hints along with the json that travelled to the server.  Spring has converters and uses MappingJacksonHttpMessageConverter to convert the object returned by the controller. SO i want to modify the converter to include the hints in the JSON generated.

A lot of googling happened for no luck finally I got the solution which reads as follows

First override the default handler: MappingJsonHttpMessageConverter

public class JSONHttpMessageConverter extends MappingJacksonHttpMessageConverter {

@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {




now in spring xml modify the mvc:annotation-config; Please make sure that u dont have another annotation-config with in any of the spring xmls in ur classpath.

    <mvc:annotation-driven  validator="ExtendedValidatationAdaptor" >
<mvc:message-converters register-defaults="true" >
<bean class="wavecrest.foundation.validate.web.JSONHttpMessageConverter" p:somebean-ref="somref" />
</mvc:message-converters>
</mvc:annotation-driven>
and this should do. 
Tested with spring-web-3.1.1.RELEASE.jar

Thursday, July 5, 2012

Near random–Converting a sequence no to random no

Q: so the statement of the problem is as follows. I want to generate unique numbers from 1 to Max (say max is Long.MAX_LONG). The generated sequence should not be predictable. SO it means we need to generate uniq random nubers and ensuer they dont repeate. Remember we cannot use current time stamp + some increment number as we have limited range and we need to use it as affectively as possible.

So i wrote a func that transforms a function to (seemingly) random number. Its as follows.

public class NearRandomIncrements {
private static final long salt = 0xABCDEF12345628ACL;
private static final long salt2 = 0xeE72CC072837198DL;

public static void main(String[] s) {
long prev = 0;
for (long i = 1L; i > 0 && i < Long.MAX_VALUE; i++) {
long withSalt = Long.reverseBytes(Long.rotateLeft(i, 31) ^ salt);
long withSalt2 = Long.rotateLeft(withSalt, 17) ^ salt2;
long variation = withSalt2 - prev;
prev = withSalt2;
System.out.println(i + " " + withSalt2 + " variation:" + Math.abs(variation));
}
}
}



This genates the output as follows

1 4835434266867286493 variation:4835434266867286493
2 4763378871852614109 variation:72055395014672384
3 4835436465890542045 variation:72057594037927936
4 4763381070875869661 variation:72055395014672384
5 4835438664913797597 variation:72057594037927936
6 4763383269899125213 variation:72055395014672384
7 4835440863937053149 variation:72057594037927936
8 4763367876736336349 variation:72072987200716800
9 4835425470774264285 variation:72057594037927936
10 4763370075759591901 variation:72055395014672384

So if u closely observe based on variation we see a pattern; and we can predict the next number. But if u see the i=8 part; we see a new variation creeping in. So even if a person gets hold of the sequence he may predict only limited number of numbers only. The solution may not be perfect, but i think its a gud start. What do u think?

Thursday, January 12, 2012

Java concurrency Qn

So here is the problem..

I need to make sure only 3 thread access a resource per second. I have n number of threads that make this request. Any solutions?

Thursday, December 22, 2011

Getting db column size using JDBC

Looking at ResultSetMetaData api below was my first approach to the problem.

    private static String getColDbType(Connection conn, String table, String col) throws Exception {
String query = " select * from " + table + " where 2='9999'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);
ResultSetMetaData rsmd = rs.getMetaData();
try {
for (int i = 1; i < rsmd.getColumnCount() + 1; i++) {
String columnName = rsmd.getColumnName(i);
if (columnName.equals(col)) {
String s = rsmd.getColumnTypeName(i);
if (s.equalsIgnoreCase("varchar")) {
s += "(" + rsmd.getPrecision(i) + ")";
}
return s;
}
}
return null;
} finally {
rs.close();
stmt.close();
}
}



However the column sizes were not accurate; googling helped me understand that ResultSetMetaData is in the context of the result set returned by the query and it need not reflect the actual table details. So below is the 2nd approach that finally worked. Tried this with MySQL 5.x db instance

    private static String getColDbType(Connection connection, String table, String col) throws Exception {
DatabaseMetaData metadata = connection.getMetaData();
int i = url.lastIndexOf("/") + 1;
String schema = url.substring(i);
ResultSet resultSet = metadata.getColumns(connection.getCatalog(), schema.trim(), table, col);
while (resultSet.next()) {
String name = resultSet.getString("COLUMN_NAME");
String type = resultSet.getString("TYPE_NAME");
int size = resultSet.getInt("COLUMN_SIZE");
if (type.equalsIgnoreCase("varchar")) {
return type + "(" + size + ")";
}
return type;
}
return null;
}

Wednesday, December 22, 2010

formatting a double number

public class Format{
    public static void main(String args[]){
    double amount = 2192.015;
    NumberFormat formatter = new DecimalFormat("#0.000");
    System.out.println("The Decimal Value is:"+formatter.format(amount));
    }
}

Wednesday, December 15, 2010

Giving log4j.properties, jmxport, classpath in ant file

 

<target name="testTarget">

<java classname="com.company.ClassWIthMainFile" fork="true">

<jvmarg value="-Dcom.sun.management.jmxremote.port=4001" />

<jvmarg value="-Dcom.sun.management.jmxremote.authenticate=false" />

<jvmarg value="-Dcom.sun.management.jmxremote.ssl=false" />

<jvmarg value="-Dlog4j.debug" />

<jvmarg value="-Dlog4j.configuration=file:///${path}/log4j-receiver.properties" />

<classpath refid="project.class.main.path" />

</java>

</target>

 

Monday, December 13, 2010

Viewing the source code

  • search for the fully qualified java class name: eg org.apache.camel.component.jms.JmsConfiguration in google.com
  • Now in address bar u'll see .../search/... 
  • rename the search to -> codesearch

 

Friday, October 8, 2010

log4j configuration:: file and console logging

Very often i am required to do the log4j configuration and some how it doesnt work; so i thought i will have a working sample copy with me so that i can extend from this whenever required; I guess this helps somebody as well!
#logging both to R and C appenders
log4j.rootCategory=DEBUG, R, C


#FILE APPENDER:R
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%c - %d{yyyy-MM-dd hh:mm:ss} %-5p [%t] %x (%F) - %m%n
log4j.appender.R.File=D:\\default.log
log4j.appender.R.Append=true
log4j.appender.R.Threshold=DEBUG
log4j.appender.R.DatePattern=.yyyy-MM-dd


#console appender:C
log4j.appender.C=org.apache.log4j.ConsoleAppender
log4j.appender.C.layout=org.apache.log4j.PatternLayout
log4j.appender.C.layout.ConversionPattern=%c - %d{yyyy-MM-dd hh:mm:ss} %-5p [%t] %x (%F) - %m%n
log4j.appender.C.Append=true
log4j.appender.C.Threshold=DEBUG

#log starting with name abc are directed to R appender i.e.. file
log4j.logger.abc=DEBUG,R
log4j.additivity.abc=false


  • Say you want to figure out which log file is being pickedup then give the below at the java startup
    • java  -Dlog4j.debug=true abc.java
  • Say there are many log4j.properties around and you want only one of them to be pickedup then
    • java  -Dlog4j.debug=true abc.java -Dlog4j.configuration=file:///path/to/log4j/log4j.properties

Friday, June 19, 2009

Date to String & viceversa

Yet another simple utility:: date to string and string to date conversion.

Date to String:
            SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy-HH:mm:ss");
            format.setLenient(false); //if the string is not inthe expected format:: throws Exception.
            Date date = (Date)o;
            return format.format(date);

String to Date
            if(str ==null )
                return null;
            SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy-HH:mm:ss");
            format.setLenient(false);
            Date d = format.parse(str);
            return d;





Tuesday, June 2, 2009

running a Main file located in a jar

This is the frequent problem that i am getting so i thought i will put it here so that it can help somone looking for it.

1. I have a jar file in which i have a class file holding public static void main
2. The execution should also pickup a properties file.

Command:
java -cp jar_having_main_class.jar;some.properties;       com.comp.root.TheMainClass

Every time i get this problem i am re-googling forit. so thought i will post it here.