Skip to content →

Tag: Java

Generating UML diagram from Java source code

Sometimes we may need to generate UML diagrams from source codes. Here’s what I do for Java code: graphviz + ant + UMLGraph. Details below:

1. Install graphviz and ant, download UMLGraph from http://www.umlgraph.org/

2. Place UMLGraph.jar in your project, for example, under
lib/

3. Write or modify your ant script, for example:


   
   The specification for the Java-based umlgraph build processes.

 
   
   
   
   
   
   
   
 
   
       
       
       
       
       
       
  
 
   
       
           
               
               
                   
               
          
           
       
   
 
   
       
           
               
               
                
               
               
               
               
               
               
               
          
       
  
 

4. Run this ant script.

ant javadocs

5. You will get the UML class diagram as expected in the generated Java docs. Here’s one example (click to enlarge):

com.andryy.hccdroid

Leave a Comment

千里之行

用Qt写计算器,花了我一个晚上终于写出来一个能用的了,能赶上大二学Java时写计算器的速度了。

用Qt Designer设计界面比较方便,用熟之后效率会更高。把界面设计好之后可以通过继承的方式给自己的类添加逻辑。如果设计的界面保存为mycaculator.ui的话,uic将会自动将这个XML文件转换成ui_mycaculator.h,其中的namespace为Ui。

#include"ui_mycaculator.h"
class Caculator : public QMainWindow, public Ui::MyCaculator

另外,Qt对字符串的处理简直跟Java一样了:

QString s1 = expStr.split(QRegExp("[+-*/]"))[0]; 
QString s2 = expStr.split(QRegExp("[+-*/]"))[1];
double d1 = s1.toDouble();
double d2 =s2.toDouble();
resStr = QVariant(d1+d2).toString();
expStr.append(resStr);

总算是迈出了第一步,再接再厉。

更新:上面的正则表达式不对,因为’+’, ‘-‘和’*’在正则表达式中有特殊意义。应该为如下:

QString s1 = expStr.split(QRegExp("[\\+\\-\\*/]"))[0];
QString s2 =expStr.split(QRegExp("[\\+\\-\\*/]"))[1];
double d1 = s1.toDouble();
double d2 = s2.toDouble();
resStr = QVariant(d1+d2).toString();
expStr.append(resStr);
Leave a Comment

Transferring Voice Data

This is done using JMF (Java Media Framework). Voice data is read from a file using `MediaLocator’. It is used as the data source for a `Processor’. The `Processor’ specifies and converts the audio data to a certain format and then output the converted data into a `DataSink’. The `DataSink’ then transfers the stream to its destination address and port.

Leave a Comment

Multiple Sessions

A SessionManager' is used to manage all the sessions. The SessionManager’ keeps a list of sessions. When it receives data, it will first check which session the data belongs to. Then the data is given to the corresponding session. If it does not belong to any session in the list, a new session will be created and added to the session list.

The basic code looks like this:


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Daoyuan
 */
public class SessionManager extends Thread {

String user;
    String host;
    int port;
    String voiceFile;
    String defaultVoice;
    boolean listening = true;
    public static ArrayList sessionList;
    DatagramSocket serverSocket = null;

    public SessionManager(String user, String host, int port, String voiceFile,
            String defaultVoice) {
        this.user = user;
        this.host = host;
        this.port = port;
        this.voiceFile = voiceFile;
this.defaultVoice = defaultVoice;
        sessionList = new ArrayList();
        try {
            serverSocket = new DatagramSocket(port);
        } catch (SocketException ex) {
            Logger.getLogger(SessionManager.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("Unable to listen on port " + port);
        }
    }

    @Override
    public void run() {

        byte[] receiveData = newbyte[2048];

        new Thread() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(SessionManager.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    if (sessionList.size() != 0) {
                        for (int i= 0; i < sessionList.size(); i++) {
                            if (sessionList.get(i).status.equals("destroyed")) {
                                sessionList.remove(i);
                            }
                        }
                    }
                }
            }
        }.start();

        while (listening) {
            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
try {
                serverSocket.receive(receivePacket);
            } catch (IOException ex) {
                Logger.getLogger(SessionManager.class.getName()).log(Level.SEVERE, null, ex);
            }

            if (isNewSession(receivePacket)) {
                System.out.println("New session created!");
            }
        }

        serverSocket.close();
    }

    public void stopListening() {
        listening = false;
}

    private boolean isNewSession(DatagramPacket receivePacket) {
        InetAddress IPAddress = receivePacket.getAddress();
        int remotePort = receivePacket.getPort();
        String sessionName = IPAddress + ":" + remotePort;
        if (sessionList.size() == 0) {
            Session s = new Session(this.serverSocket, IPAddress, remotePort, voiceFile, defaultVoice, user);
            s.setName(sessionName);
s.requests.add(receivePacket);
            sessionList.add(s);
            s.start();
            return true;
        } else {
            for (int i = 0; i < sessionList.size(); i++) {
                if (sessionList.get(i).getName().equals(sessionName)) {
                    sessionList.get(i).requests.add(receivePacket);
                    return false;
                }
            }

            Session s = newSession(this.serverSocket, IPAddress, remotePort, voiceFile, defaultVoice, user);
            s.setName(sessionName);
            s.requests.add(receivePacket);
            sessionList.add(s);
            s.start();
            return true;
        }
    }
}
Leave a Comment

Session Establishment and Tearing Down

Each session has a ‘status’, it can be ‘new’, ‘establishing’, ‘cancelling’, ‘established’, ‘tearingdown’ and ‘destroyed’.

When a new session is created, its status is ‘new’.

When an ‘INVITE’ is received, it sends out an ‘OK’ message and change its status to ‘establishing’.

After receiving an ‘ACK’ message the status will be changed to ‘established’.

Then begins the transferring of voice data using RTP.

When the sending finishes the status will become ‘tearingdown’.

A ‘BYE’ message is also sent to the client.

The status becomes ‘destroyed’ after getting ‘OK’ from the client.

When a ‘CANCEL’ message is received, the status becomes ‘cancelling’.

Then it sends back ‘OK’ and ‘Request Terminated’ messages.

After received an ‘ACK’, the status becomes ‘destroyed’.

The thread is as follows:


@Override
public void run() {

    while (true) {
        if (requests.size() == 0) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else {
            final DatagramPacket packet = requests.get(0);
            final String request = new String(packet.getData());
            System.err.println(request);
            sipRequest = new SIPRequest(request);
            if (request.toUpperCase().startsWith("INVITE")) {
                System.out.println("Got INVITE");
                if (!status.equals("new")) {
                    requests.remove(0);
                    continue;
                }

                String requestLine = request.substring(6).trim();
                String uri = requestLine.substring(0, requestLine.indexOf(' '));
                //System.out.println(uri);
                String usr = "";
                if (uri.toUpperCase().startsWith("SIP:")) {
                    usr = uri.substring(4, uri.indexOf('@'));
                } else {
                    usr = uri.substring(0, uri.indexOf('@'));
                }
                if (!usr.equalsIgnoreCase(user)) {
                    sendUserNotFound(sipRequest);
                    status = "tearingdown";
                    System.out.println("Status: " + status);
                    continue;
                }

                sendTrying(sipRequest);
                sendRinging(sipRequest);
                final String rtpport = request.substring(request.indexOf("m=audio") + 8, request.indexOf("m=audio") + 13);
                final String sdp = getSDP(request);
                new Thread() {

                    @Override
                    public void run() {
                        File file = new File("wav/" + voiceFile);
                        if (file.exists()) {
                            locator = new MediaLocator("file:/" + file.getAbsolutePath());
                        } else {
                            System.out.println("Using default voice file!");
                            locator = new MediaLocator("file:/" + file.getAbsolutePath());
                        }
                        transmitter = new Transmitter(locator, packet.getAddress().toString().substring(1), rtpport);
                        transmitter.prepare();
                    }
                }.start();

                new Thread() {

                    @Override
                    public void run() {
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException ex) {
                            Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                        }
                        if (status.equals("establishing")) {
                            sendOK(sipRequest, sdp, rtpport);
                        } else {
                            try {
                                transmitter.stop();
                            } catch (NullPointerException ex) {
                                Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                                System.out.println("Transmitter already stopped!");
                            }
                        }
                    }
                }.start();
                requests.remove(0);
                status = "establishing";
                System.out.println("Status: " + status);
            } else if (request.toUpperCase().startsWith("ACK")) {
                System.out.println("Got ACK");
                //sendOK(sipRequest);
                requests.remove(0);
                if (status.equals("establishing")) {
                    status = "established";
                    System.out.println("Status: " + status);
                    new Thread() {

                        @Override
                        public void run() {
                            transmitter.start();

                            long duration = new VoiceInfo(locator).getDuration();
                            try {
                                Thread.sleep(duration);
                            } catch (InterruptedException ex) {
                                Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                            }

                            try {
                                transmitter.stop();
                            } catch (NullPointerException ex) {
                                Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                                System.out.println("Transmitter already stopped!");
                            }
                            if (!status.equals("tearingdown") && !status.equals("destroyed")) {
                                sendBye(sipRequest);
                                status = "tearingdown";
                                System.out.println("Status: " + status);
                            }
                        }
                    }.start();
                } else if (status.equals("tearingdown")) {
                    status = "destroyed";
                    System.out.println("Status: " + status);
                    break;
                } else if (status.equals("cancelling")) {
                    status = "destroyed";
                    System.out.println("Status: " + status);
                    break;
                }
            } else if (request.toUpperCase().startsWith("BYE")) {
                System.out.println("Got BYE");
                sendOKWithoutSDP(sipRequest);
                requests.remove(0);
                try {
                    transmitter.stop();
                } catch (NullPointerException ex) {
                    Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                    System.out.println("Transmitter already stopped!");
                }
                status = "destroyed";
                System.out.println("Status: " + status);
                break;
            } else if (request.toUpperCase().startsWith("SIP/2.0 200")) {
                requests.remove(0);
                System.out.println("Got OK");
                if (status.equals("tearingdown")) {
                    status = "destroyed";
                    System.out.println("Status: " + status);
                    break;
                }
            } else if (request.toUpperCase().startsWith("CANCEL")) {
                requests.remove(0);
                System.out.println("Got CANCEL");
                status = "cancelling";
                System.out.println("Status: " + status);
                sendOKWithoutSDP(sipRequest);
                sendTerminated(sipRequest);
            } else {
                requests.remove(0);
            }
        }
    }
}
Leave a Comment

Generating Voice File

When the web server receives a POST' message from the web page, it will first check whether the message is valid. If so, freeTTS‘ is used to generate a voice file and the file is saved in the `wav’ directory with pre-configured file name.

The voice generator looks as follows:

import com.sun.speech.freetts.Voice;
import com.sun.speech.freetts.VoiceManager;
import com.sun.speech.freetts.audio.SingleFileAudioPlayer;

/**
 *
 * @author Daoyuan
 */
public class VoiceGenerator {

    String message;

    public VoiceGenerator(String message){
        this.message = message;
    }

    public void generateVoiceFile(String filename){

Voice voice;
        VoiceManager vm = VoiceManager.getInstance();
        voice = vm.getVoice("kevin16");
        voice.allocate();

        String baseName = filename.substring(0, filename.toLowerCase().indexOf(".wav"));
        SingleFileAudioPlayer sfap = new SingleFileAudioPlayer("wav/" + baseName, javax.sound.sampled.AudioFileFormat.Type.WAVE);
        voice.setAudioPlayer(sfap);
        voice.speak(message);

        sfap.close();

voice.deallocate();
    }
}
Leave a Comment

Non-ASCII characters in Email subject

In this system, Email subjects are encoded in Q' scheme, a scheme similar to quoted printable’. The format is “=?charset?Q?encoded subject?=”, in our case, the charset is `ISO-8859-15′.

For example, the Email object in this system is as follows:


importjava.text.SimpleDateFormat;
import java.util.Calendar;

/**
 * Email object.
 *
 * @author Daoyuan Li
 */
public class Mail {

    /**
     * Sender's Email address
     */
    private String from;

    /**
     * Recipient's Email address
     */
    private String to;

    /**
     * Email subject
     */
    private String subject;

    /**
     * Email message body
     */
    private Stringmessage;

    /**
     * Constructor of Mail
     * @param from Sender's Email address.
     * @param to Recipient's Email address.
     * @param subject Email subject.
     * @param message Email message.
     */
    public Mail(String from, String to, String subject, String message) {
        this.from = from;
        this.to = to;
        this.subject = subject;
        this.message = message;
    }

    /**
     * Get sender'sEmail address.
     * @return Sender's Email address.
     */
    public String getFrom() {
        return this.from;
    }

    /**
     * Get Recipient's Email address.
     * @return Recipient's Email address.
     */
    public String getTo() {
        return this.to;
    }

    /**
     * Get Email subject.
     * @return Email subject.
     */
    public String getSubject() {
        return this.subject;
}

    /**
     * Get Email message.
     * @return Email message.
     */
    public String getMessage() {
        return this.message;
    }

    /**
     * Set sender's Email address.
     * @param from Sender's Email address.
     */
    public void setFrom(String from) {
        this.from = from;
    }

    /**
     * Set Recipient's Email address.
     * @param to Recipient's Email address.
     */
public void setTo(String to) {
        this.to = to;
    }

    /**
     * Set Email subject.
     * @param subject Email subject.
     */
    public void setSubject(String subject) {
        this.subject = subject;
    }

    /**
     * Set Email message.
     * @param message Email message.
     */
    public void setMessage(String message) {
        this.message = message;
    }

    /**
     * Encode Email into MIMEformat.
     * @return The MIME encoded Email.
     */
    public String encode() {
        String FORMAT = "EEE, dd MMM yyyy HH:mm:ss Z";
        //String FORMAT = "yyyy-MM-dd HH:mm:ss Z";
        Calendar cal = null;
        cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat(FORMAT);
        //System.out.println(sdf.format(cal.getTime()));
        String s = "From: <" + this.getFrom() + ">\n"
                + "To:<" + this.getTo() + ">\n"
                //+ "Subject: " + this.getSubject() + "\n"
                + "Subject: =?ISO-8859-15?Q?"
                + new QuotedPrintableEncoder().encodeWithoutLineBreak(this.subject) + "?=\n"
                + "Date: " + sdf.format(cal.getTime()) + "\n"
                + "Message-ID: " + cal.getTimeInMillis() + "@ik2213.lab\n"
                + "MIME-Version: 1.0\n"
                + "Content-Type:text/plain; charset=ISO-8859-15\n"
                + "Content-Transfer-Encoding: quoted-printable\n";
        return s.concat(new QuotedPrintableEncoder().encode(this.message));
    }
}
Leave a Comment

MIME Encoding

Quoted printable characters are encoded in the format =XX', where XX’ stands for the hexadecimal value of the character.

The encoder looks as follows:

import java.io.UnsupportedEncodingException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * It encodes data into quoted printable format.
 *
* @author Daoyuan Li
 */
public class QuotedPrintableEncoder {
    /**
     * Encodes data into quoted printable format.
     * @param s Data to be encoded.
     * @return The encoded data.
     */
    public String encode(String s) {
        byte[] b = null;
        try {
            b = s.getBytes("ISO-8859-1");
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(Mail.class.getName()).log(Level.SEVERE, null, ex);
return "";
        }
        String code = "";
        int wc = 0;
        for (int i = 0; i < b.length; i++) {
            byte c = b[i];
            if(c == 13){
                code = code.concat("\n");
                wc = 0;
                continue;
            } else if(c == 10) {
                //do nothing
                continue;
            }
            code = code.concat("=" + Integer.toHexString(c &255).toUpperCase());
            wc += 3;
            if (wc >= 75) {
                code = code.concat("=\n");
                wc = 0;
            }
        }
        return code;
    }

    /**
     * Encodes data into quoted printable format, without soft line breaks.
     * @param s Data to be encoded.
     * @return The encoded data.
     */
    public String encodeWithoutLineBreak(String s) {
        byte[] b = null;
        try{
            b = s.getBytes("ISO-8859-1");
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(Mail.class.getName()).log(Level.SEVERE, null, ex);
            return "";
        }
        String code = "";
        for (int i = 0; i < b.length; i++) {
            byte c = b[i];
            if(c == 13){
                code = code.concat("\n");
                continue;
            } else if (c == 10){
//do nothing
                continue;
            }
            code = code.concat("=" + Integer.toHexString(c & 255).toUpperCase());
        }
        return code;
    }
}
Leave a Comment

URL Decoding

The charset of the form page is ISO-8859-15'. HTTP will encode the form message in URLEncoding, that is, space is replaced with +’; non-ASCII characters are encoded inthe format %XX', where XX’ stands for the hexadecimal value of the character.

The URL decoder in this system looks as follows:

importjava.io.UnsupportedEncodingException;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * UrlDecoder decodes URL encoded information into quoted printable characters.
 * 
 * @author Daoyuan Li
 */
public class UrlDecoder {

    /**
     * Decode from URL encoding to quoted printable.
     * @param url URL encoded message to be decoded.
     * @return Decoded quoted printable string.
     * @throwsException If anything wrong happens, an Exception is thrown.
     */
    public String decode(String url) throws Exception {
        String decoded = "";
        Exception ex = new Exception();
        Vector buf = new Vector();
        for (int i = 0; i < url.length(); i++) {
            if (url.charAt(i) == '%') {
                if (i + 2 >= url.length()) {
                    throw ex;
                }

                int d = -1;
try {
                    d = Integer.parseInt(url.substring(i + 1, i + 3), 16);
                } catch (NumberFormatException e) {
                    throw ex;
                }
                if (d > 255 || d < 0) {
                    throw ex;
                }
                buf.add(new Byte((byte)d));
                i += 2;
            } else if (url.charAt(i) == '+') {
                buf.add(new Byte((byte)' '));
} else {
                buf.add(new Byte((byte) url.charAt(i)));
            }
        }
        try {
            byte[] dcd = new byte[buf.size()];
            for (int j = 0; j < dcd.length; j++) {
                dcd[j] = buf.elementAt(j);
            }
            decoded = new String(dcd, "ISO-8859-1");
        } catch (UnsupportedEncodingException e) {
            Logger.getLogger(FormValidator.class.getName()).log(Level.SEVERE, null,e);
            throw ex;
        }
        return decoded;
    }
}
Leave a Comment

CrazyBus Launch Script

#!/bin/bash

PREFIX=
if [ -L "$0" ]; then
    PREFIX=`readlink -f $0`
    if [ $? -eq 0 ]; then
        PREFIX=`dirname $PREFIX`        
    else 
            PREFIX=`file $0`
            PREFIX=${PREFIX##*symbolic link to }
            PREFIX=`dirname $PREFIX`
    fi
else

PREFIX=`dirname $0`
fi

case $PREFIX in
        /*)
        ;;
        *)
        cd $PREFIX
        PREFIX=`pwd`
        ;;
esac

cp=
for i in $PREFIX/lib/*.jar; do
        cp=$i:$cp
done


$PREFIX/java/bin/java \
-Djava.library.path=$PREFIX/swt -classpath $cp \
org.crazybus.login.Login  &
Leave a Comment

每隔一定时间间隔查询是否有新消息

/**
 * 查询信息
 */
public void checkMessage(){
    TimerTask tt = new TimerTask() {
        public void run() {
            String unreadMsgNo = new CheckMessage().getUnreadMsgNo
                                    (URL, USER,
PASSWORD);
            if(unreadMsgNo.equals("0")){
                jLabelMsgTip.setText("您没有新信息!");
            } else{
                jLabelMsgTip.setText("您有" + unreadMsgNo + "条新信息!");
            }
        }
    };
    Timer t = new Timer();
    t.schedule(tt, 0, (long)1000*15);//每隔15秒执行一次
}
Leave a Comment