views:

82

answers:

1

I'll make this as short and to the point as possible, but it's kind of a complex issue. I'm writing in Java on a Linux platform, for whatever that's worth.

Short version of the goal: I want to have an abstract class called Client that acts as a generic container for client connections. Client should thread each of its connections. I also have some semi-tested code that plays the server counterpart to this in similar coding fashion. The abstract Client should get implemented into something more tangible and instanceable. In my case, I have a class called FileClientGui which extends Client and overrides all of Client's abstract functions with the basic function of receiving the contents of a file from the server and displaying them. This is further complicated by the fact that the abstract Client is itself an extension of java.lang.Thread.

So here's my file structure in generic terms:

/class/path/lib/client/Client.java /class/path/com/fileclient/FileClientGui.java

There are several other custom classes that both of these files reference, but I'm not getting any errors out of them. If I need to post code for those items, let me know, and I'll post them.

So I run this long javac command on the terminal setting up the classpath and build directory and all of the relevant files that need to be compiled as well. The only error I receive for any of that code is this:

com/fileclient/FileClientGui.java:26: com.fileclient.FileClientGui is not abstract and does not override abstract method cleanClients() in lib.client.Client

My code (see below) clearly implements the function and all other abstract functions defined in Client.java. I scoured the Internet, and it seems like most people who encounter this error are trying to do something like implement an ActionListener and get confused with that implementation, and many times, it's just a simple spelling or capitalization issue. I've gone over and over my code to make sure that this isn't a simple "oops" problem like that. I suspect that it's actually some kind of collision between the name of my class and the name of some other class that somehow ended up in my classpath or in Java's native framework/libraries, but I cannot find anything obvious.

At any rate, here's my code.

Client.java:

package lib.client;

import lib.clientservercore.Connection;
import lib.simplefileaccess.Logger;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.lang.Thread;

/**
 *
 * @author Ryan Jung
 */
public abstract class Client extends Thread {

    ArrayList<Connection> connections;
    boolean isRunning;
    Logger log;

    public Client (String logFile) {
        log = new Logger(logFile);

        log.write("Initializing client...");

        connections = new ArrayList<Connection>(50);

        log.write("Client initialized.");
    }

    public void logOut(String contents) {
        log.write(contents);
    }

    public Logger getLogger() {
        return this.log;
    }

    public ArrayList<Connection> getConnections() {
        return connections;
    }

    public void addConnection(Connection c) {
        connections.add(c);
    }

    public void removeConnection(Connection c) {
        connections.remove(c);
    }

    public boolean getIsRunning() {
        return isRunning;
    }

    public void setIsRunning(boolean r) {
        isRunning = r;
    }

    public Connection connect(String host, int port) {
        log.write("Creating new connection...");

        Socket s;
        Connection c = null;

        // Validate port
        if (port <= 1024 || port > 65536) {
            log.write("Invalid server port: " + port + ".  Using 12321.");
            port = 12321;
        }

        try {
            s = new Socket(host, port);
            c = connectClient(s);
        } catch (IOException exIo) {
            log.write("Could not connect to the server at " + host + ":" + port + ".  Exception: " + exIo.getMessage());
            exIo.printStackTrace();
        }

        log.write("Connected client to " + host + ":" + port);

        return c;
    }

    @Override
    public void run() {
        log.write("Running client.");
        runClient();
        log.write("Client finished running.");
    }

    abstract Connection connectClient(Socket sock);

    abstract void runClient();

    abstract void cleanClients();

}

FileClientGui.java:

package com.fileclient;

import lib.client.Client;
import lib.clientservercore.Connection;
import lib.clientservercore.Connection.ConnectionStatus;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Iterator;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import java.lang.Thread;

/**
 *
 * @author Ryan Jung
 */
public class FileClientGui extends Client {

    JFrame frmMain;
    JPanel pnlMain;
    JPanel pnlConnect;
    JTabbedPane tabConnections;
    JLabel lblHost;
    JLabel lblPort;
    JTextField txtHost;
    JTextField txtPort;
    JButton btnConnect;

    public FileClientGui(String logFile) {
        super(logFile);

        logOut("Initializing client controller...");

        frmMain = new JFrame("Client");
        pnlMain = new JPanel(new BorderLayout());
        pnlConnect = new JPanel(new FlowLayout());
        tabConnections = new JTabbedPane();
        lblHost = new JLabel("Host:");
        lblPort = new JLabel("Port:");
        txtHost = new JTextField("localhost", 10);
        txtPort = new JTextField("12321", 5);
        btnConnect = new JButton("Connect");

        frmMain.setSize(450, 600);
        frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frmMain.add(pnlMain);
        pnlMain.add(pnlConnect, BorderLayout.NORTH);
        pnlMain.add(tabConnections, BorderLayout.CENTER);
        pnlConnect.add(lblHost);
        pnlConnect.add(txtHost);
        pnlConnect.add(lblPort);
        pnlConnect.add(txtPort);
        pnlConnect.add(btnConnect);

        btnConnect.addActionListener(
            new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    String host = txtHost.getText();
                    int port = Integer.parseInt(txtPort.getText());
                    try {
                        Socket sock = new Socket(host, port);
                        FileClientConnectionGui c = (FileClientConnectionGui)(connectClient(sock));
                        tabConnections.addTab(c.getInetAddress().toString(), c.getMainPanel());
                    } catch (UnknownHostException ex) {
                        logOut("Can't find host: " + host + ".  Exception: " + ex.getMessage());
                        ex.printStackTrace();
                    } catch (IOException ex) {
                        logOut("Exception: " + ex.getMessage());
                        ex.printStackTrace();
                    }
                }
            }
        );

        frmMain.setVisible(true);

        logOut("Client controller initialized.");

    }

    public void removeConnection(FileClientConnectionGui c) {
        logOut("Removing connection: " + c.getInetAddress().toString());
        tabConnections.remove(c.getMainPanel());
        logOut("Removed connection.");
    }

    Connection connectClient(Socket sock) {
        logOut("Client controller is creating a new connection...");
        FileClientConnectionGui c = new FileClientConnectionGui(sock, getLogger(), this);
        addConnection(c);
        c.start();
        logOut("Client controller created a new connection.");
        return c;
    }

    void runClient() {
        setIsRunning(true);
        logOut("Client controller is running.");

        while (getIsRunning()) {
            cleanClients();
            try {
                sleep(500);
            } catch (InterruptedException ex) {
                logOut("Sleep interrupted.  Exception: " + ex.getMessage());
                ex.printStackTrace();
            }
        }

        logOut("Client controller stopped running.");
    }

    void cleanClients() {
        Iterator i = getConnections().iterator();
        try {
            while (i.hasNext()) {
                FileClientConnectionGui c = (FileClientConnectionGui)(i.next());
                if (c.getStatus() == ConnectionStatus.CLOSED) {
                    logOut("Removing dead client at " + c.getInetAddress().toString());
                    tabConnections.remove(c.getMainPanel());
                    removeConnection(c);
                }
            }
        } catch (Exception ex) {
            logOut("cleanClients Exception: " + ex.getMessage());
        }
    }

}

I'll take any help I can get, and I thank you in advance for any suggestions you provide am thoroughly flummoxed by this.

Perhaps what is most flummoxing about this (and maybe this provides a clue to the problem?) is that I can comment out other implementations of abstract functions (runClient, for instance, or connectClient), and I get no additional problems, just the same one. Furthermore, if I add the @Override directive to one of those others like this:

    @Override
Connection connectClient(Socket sock) {
    logOut("Client controller is creating a new connection...");
    FileClientConnectionGui c = new FileClientConnectionGui(sock, getLogger(), this);
    addConnection(c);
    c.start();
    logOut("Client controller created a new connection.");
    return c;
}

I get an additional error:

com/fileclient/FileClientGui.java:96: method does not override or implement a method from a supertype

It clearly is overriding a method from its supertype (which is Client). I've tried replacing "Client" with the full classpath (lib.client.Client) and none of the errors are changed at all.

Is there something I'm missing? Something I'm not trying?

+7  A: 

I believe it's because you've got package-level abstract methods, which aren't visible in your subclass. Try making them protected instead.

Here's a simple pair of classes which reproduce the problem:

package x1;

public abstract class P1
{
    abstract void foo();
}

And then:

package x2;

public class P2 extends x1.P1
{
    void foo() {}
}

Compiling them gives:

P2.java:3: P2 is not abstract and does not override abstract method foo() in P1
public class P2 extends x1.P1
       ^
1 error

Making foo protected in both classes fixes the problem.

Jon Skeet
Classes cannot be declared protected, IIRC. Are you suggesting that the abstract class declare its abstract methods as abstract? I will attempt this and provide results. Thanks for the quick response.
GradysGhost
Beautiful! Thank you! In the abstract Client.java, I made the abstract function definitions protected, and in the implementation FileClientGui.java, I made the function implementations protected. The code immediately compiled with no errors. I executed the code and got the exact results I expected.Thanks again. This is the correct answer.
GradysGhost