JxtaMulticastSocket覆写了java.net.MulticastSocket的bind方法:
@Override
    public void bind(SocketAddress addr) throws SocketException {
        if (isBound()) {
            throw new SocketException("Already bound");
        }
    }

 

 
要求在JxtaMulticastSocket的构造函数中会调用bind方法,确保构造函数调用时socket没有被绑定,否则抛出SocketException异常
然而JxtaMulticastSocket的基类java.net.MulticastSocket的构造函数在jdk7和jdk8中的处理是不同的
 
jdk7:
    public MulticastSocket(SocketAddress bindaddr) throws IOException {
        super((SocketAddress) null);

        // Enable SO_REUSEADDR before binding
        setReuseAddress(true);

        if (bindaddr != null) {
            bind(bindaddr);
        }
    }

jdk8:

    public MulticastSocket(SocketAddress bindaddr) throws IOException {
        super((SocketAddress) null);

        // Enable SO_REUSEADDR before binding
        setReuseAddress(true);

        if (bindaddr != null) {
            try {
                bind(bindaddr);
            } finally {
                if (!isBound())
                    close();
            }
        }
    }
可见在jdk8中,如果未绑定,会额外调用close()方法,就是这个close()方法导致抛出了异常
其实在这里不能调用close()方法,从代码上看JxtaMulticastSocket在实现的时候要求此时不能绑定(见其覆写的bind方法)
而close方法也被JxtaMulticastSocket覆写了
/**
     * Closes this MutlicastSocket.
     */
    @Override
    public synchronized void close() {
        if (closed) {
            return;
        }
        bound = false;
        closed = true;
        in.close();
        outputPipe.close();
        in = null;
    }

此时in尚未初始化,故in.close()会导致java.lang.NullPointerException异常

————————————

1 简单的话,可以修改JxtaMulticastSocket的构造函数,使其不再调用父类的构造函数以避免close()问题(父类构造函数中的相关代码要在子类中重新实现一遍)。
2 或者不修改JxtaMulticastSocket的源码,我们干脆自己实现一个子类,提供自己的构造函数初始化代码。

3 较好的方式那就要全面考虑JxtaMulticastSocket的实现了,那就比较复杂了

 ————————————————————————

。。。

1和2现在看起来不大现实,因为父类java.net.MulticastSocket最终都调用了如下构造方法:

    /**
     * Create a MulticastSocket bound to the specified socket address.
     * <p>
     * Or, if the address is {@code null}, create an unbound socket.
     *
     * <p>If there is a security manager,
     * its {@code checkListen} method is first called
     * with the SocketAddress port as its argument to ensure the operation is allowed.
     * This could result in a SecurityException.
     * <p>
     * When the socket is created the
     * {@link DatagramSocket#setReuseAddress(boolean)} method is
     * called to enable the SO_REUSEADDR socket option.
     *
     * @param bindaddr Socket address to bind to, or {@code null} for
     *                 an unbound socket.
     * @exception IOException if an I/O exception occurs
     * while creating the MulticastSocket
     * @exception  SecurityException  if a security manager exists and its
     *             {@code checkListen} method doesn't allow the operation.
     * @see SecurityManager#checkListen
     * @see java.net.DatagramSocket#setReuseAddress(boolean)
     *
     * @since 1.4
     */
    public MulticastSocket(SocketAddress bindaddr) throws IOException {
        super((SocketAddress) null);

        // Enable SO_REUSEADDR before binding
        setReuseAddress(true);

        if (bindaddr != null) {
            try {
                bind(bindaddr);
            } finally {
                if (!isBound())
                    close();
            }
        }
    }

这意味着子类无论直接还是间接,最终都要调用到这个方法,故close()难以避免的啊!

那就只有修改JxtaMulticastSocket覆写的close()方法了,原close()方法:

    /**
     * Closes this MutlicastSocket.
     */
    @Override
    public synchronized void close() {
        if (closed) {
            return;
        }
        bound = false;
        closed = true;
        in.close();
        outputPipe.close();
        in = null;
    }

修改后:如果尚未绑定的话,close()不执行任何操作

    /**
     * Closes this MutlicastSocket.
     */
    @Override
    public synchronized void close() {
        // modified by cuizhf, 20131126
        // @see http://www.cnblogs.com/cuizhf/admin/EditPosts.aspx?postid=3443599
        if(!bound) {
            return;
        }
        if (closed) {
            return;
        }
        bound = false;
        closed = true;
        in.close();
        outputPipe.close();
        in = null;
    }

或者这样写:

    /**
     * Closes this MutlicastSocket.
     */
    @Override
    public synchronized void close() {
        // modified by cuizhf, 20131126
        // @see http://www.cnblogs.com/cuizhf/admin/EditPosts.aspx?postid=3443599
        if (!bound || closed) {
            return;
        }
        bound = false;
        closed = true;
        in.close();
        outputPipe.close();
        in = null;
    }

虽然不是很优雅,至少应该能解决眼前的问题。(实话说Jxse的代码确实算不上优雅)

 ————————————————————————————————————————————————————————

另外一种修改方式就是将java.net.MulticastSocket单独从JDK中提出到在自己的工程中,然后按自己的需要修改(在构造函数中去掉close()的调用),最后使JxtaMulticastSocket继承自修改后的这个类。

 



 
 
 
 —————————————————————————————————————————————————————————
好吧,从今天开始,secondegg项目的开发全面转向JDK8(JavaFX8)。
posted on 2013-11-26 16:35  网络大豆  阅读(403)  评论(0编辑  收藏  举报