Minecraft is a game which also has multiplayer capabilities. You need to have an account (which needs to be verified) for playing on servers. The authentication procedure looks like this:Authenticate at the Mojang (creators of Minecraft) server with username and passwordRetrieve a session idContact the server you want to play on and send your username and session id (for verification that you own that name)Send every 5 minutes a keep-alive request to the Mojang server so that the id does not timeoutThis class is supposed to handle step 1, 2 and 4.The class is used like this:Authentication authentication = new Authentication(username, password);AuthenticationResponse response = AuthenticationResponse.UNKNOWN;try {    response = authentication.authenticate();} catch (UnsupportedEncodingException ex) {    LOGGER.log(Level.SEVERE, Authentication failed!, ex);} catch (MalformedURLException ex) {    LOGGER.log(Level.SEVERE, Authentication failed!, ex);} catch (IOException ex) {    LOGGER.log(Level.SEVERE, Authentication failed!, ex);}if (response == AuthenticationResponse.SUCCESS) {    // Do something with the session id (authentication.getSessionId())}What you need to know along the way:Credentials is a simple mutable container for a username and a passwordAuthenticationResponse is an enumI designed it with the thought to give complete control to the client, that's why every single field is exposed to be changedHere's the code:package org.bonsaimind.minecraftmiddleknife.pre16;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.UnsupportedEncodingException;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;import java.net.URLEncoder;import org.bonsaimind.minecraftmiddleknife.Credentials;/** * Deals with the authentication at the Mojang, or any other server. */public class Authentication extends Credentials {    /**     * The default version which will be reported.     */    public static final String LAUNCHER_VERSION = 884;    /**     * The addressof the Mojang server.     */    public static final String MOJANG_SERVER = https://login.minecraft.net;    private long currentVersion;    private String deprecated;    private boolean keepAliveUsesRealUsername = true;    private String realUsername;    private String server = MOJANG_SERVER;    private String sessionId;    private String userId;    private String version = LAUNCHER_VERSION;    public Authentication() {    }    public Authentication(Credentials credentials) {        super(credentials.getUsername(), credentials.getPassword());    }    public Authentication(String username, String password) {        super(username, password);    }    public Authentication(String server, String version, String username, String password) {        super(username, password);        this.server = server;        this.version = version;    }    /**     * Do the authentication.     * @return     * @throws UnsupportedEncodingException     * @throws MalformedURLException     * @throws IOException     */    public AuthenticationResponse authenticate() throws UnsupportedEncodingException, MalformedURLException, IOException {        String request = String.format(                user=%s&password=%s&version=%s,                URLEncoder.encode(getUsername(), UTF-8),                URLEncoder.encode(getPassword(), UTF-8),                URLEncoder.encode(getVersion(), UTF-8));        String response = httpRequest(getServer(), request);        String[] splitted = response.split(:);        if (splitted.length < 5) {            return AuthenticationResponse.getResponse(response);        }        currentVersion = Long.parseLong(splitted[0]);        deprecated = splitted[1];        realUsername = splitted[2];        sessionId = splitted[3];        userId = splitted[4];        return AuthenticationResponse.SUCCESS;    }    /**     * Returns the current version of Minecraft.     * @return The current version.     */    public long getCurrentVersion() {        return currentVersion;    }    /**     * The DEPRECATED field of the login response, should always be DEPRECATED.     * @return Nothing useful.     */    public String getDeprecated() {        return deprecated;    }    /**     * Returns true of the keep-alive will be using the real username returned     * by the authentication server.     * @return If keep-alive uses the real username.     */    public boolean isKeepAliveUsesRealUsername() {        return keepAliveUsesRealUsername;    }    /**     * Returns the real username (case corrected f.e.).     * @return The real username.     */    public String getRealUsername() {        return realUsername;    }    /**     * Returns the server which will be used for authentication. Default value     * is the Mojang server.     * @return The server.     */    public String getServer() {        return server;    }    /**     * Returns the session ID as acquired by the login process.     * @return The Session ID.     */    public String getSessionId() {        return sessionId;    }    /**     * Returns the user ID as acquired by the login process.     * @return The user ID.     */    public String getUserId() {        return userId;    }    /**     * Returns the version (of the launcher) which will be reported to     * the server. Default value is the default one.     * @return The launcher version.     */    public String getVersion() {        return version;    }    /**     * Sends a keep-alive to the authentication server so that the session     * does not expire.     * @throws UnsupportedEncodingException     * @throws MalformedURLException     * @throws IOException     */    public void keepAlive() throws UnsupportedEncodingException, MalformedURLException, IOException {        String request = String.format(                ?name={0}&session={1},                URLEncoder.encode(isKeepAliveUsesRealUsername() ? getRealUsername() : getUsername(), UTF-8),                URLEncoder.encode(getSessionId(), UTF-8));        httpRequest(getServer(), request);    }    /**     * Determines if the keepa-live uses the real username returned by     * the authentication server or the username set by the user.     * @param keepAliveUsesRealUsername If keep-alive uses the real username.     */    public void setKeepAliveUsesRealUsername(boolean keepAliveUsesRealUsername) {        this.keepAliveUsesRealUsername = keepAliveUsesRealUsername;    }    /**     * Set the real username, this is most likely returned by the auth server.     * @param realUsername The real username.     */    public void setRealUsername(String realUsername) {        this.realUsername = realUsername;    }    /**     * Set the session id, used for keep-alive.     * @param sessionId The session id.     */    public void setSessionId(String sessionId) {        this.sessionId = sessionId;    }    /**     * Set the server which will be used for authentication.     * @param server The (full) address of the server.     */    public void setServer(String server) {        this.server = server;    }    /**     * Set the version (of the launcher) which will be reported to     * the authentication server. This should be a valid int, even     * though it is a string.     * @param version The version of the launcher, a valid int would be nice.     */    public void setVersion(String version) {        this.version = version;    }    private static String httpRequest(String url, String content) throws UnsupportedEncodingException, MalformedURLException, IOException {        byte[] contentBytes = content.getBytes(UTF-8);        URLConnection connection = new URL(url).openConnection();        connection.setDoInput(true);        connection.setDoOutput(true);        connection.setRequestProperty(Accept-Charset, UTF-8);        connection.setRequestProperty(Content-Type, application/x-www-form-urlencoded);        connection.setRequestProperty(Content-Length, Integer.toString(contentBytes.length));        OutputStream requestStream = connection.getOutputStream();        requestStream.write(contentBytes, 0, contentBytes.length);        requestStream.close();        String response = ;        BufferedReader responseStream = new BufferedReader(new InputStreamReader(connection.getInputStream(), UTF-8));        response = responseStream.readLine();        responseStream.close();        return response;    }}The problems I see with this are:I need to improve my JavaDoc skills as fast as possibleThe inheriting Credentials might be a stupid ideaHandling all these values feels messyI just realized that handling feels odd overallI'm a little bit stuck in my own head when it comes to the design of this class, any ideas?Full source code is available at the GitHub repository.
Authenticating with the official Mojang (Minecraft) server
java;authentication
The logic and data encapsulation are a little disconnected here. I think it may because I have misunderstood where this class fits in to your class hierarchy.... but I don't think so. There are a few things I don't like:This Authenticate is really a 'AuthenticatedSession' and not a special type of Credential. i.e. this class uses a credential to create a session, and then it keeps that session alive.Why do you have so many constructors?you say that this class takes care of the 5-minute keep-alive, but all I see is a method and no timerI would expect this class to be immutable - no setters should be necessary .... will the login details and login server change at all?The httpRequest throws too many exceptions. I would catch all the exceptions in httpRequest and throw a single custom exception (initialized with the right cause) like AuthenticationExceptionSo, from the context you provide, I would have called this something like an AutheticatedSession, and I would have a factory method on it that takes a server, and credential as parameters. There should be no need to 'remember' the credentials after the initial login.Given the challenge:Create a session logged in to an authentication server, that requires a regular heartbeat, and provides some additional data about the user.I would have a class something like:public class AuthenticatedSession {    /**     * Factory method to create AuthenticatedSession instances.     */    public static AuthenticatedSession connect(String serverurl, Credential credential) throws AuthenticationException {        // do the work of setting up the right URL and content.        String response = httpRequest(serverurl, content);        // use the string to build your AuthenticatedSession...        return new AuthenticatedSession(....session details....);    }    // this timer will run the heart-beat.    private static final ScheduledThreadPoolExecutor TIMEDEXECUTOR = new ScheduledThreadPoolExecutor (1);    // This is the runnable instance that will sit in the timer schedule    private final class KeepAlive implements Runnable {        public void run() {            // call the keepalive method on the main class.            // if keepalive throws an exception it will stop this timer thread.            keepAlive();        }    }    // if a heartbeat fails, will be set false.    private final AtomicBoolean linkAlive = new AtomicBoolean(true);    private final String hbserver;    private final String hbcontent;    private final ...... // all the fields that are supplied by the server.    // private constructor, called by the factory.    private AuthenticatedSession(String .... all the fields that are supplied by the server) {         this. ... fields  = input fields;         this.hbserver = ...; // details for the heartbeat URL         this.hbcontent = ...; // details for the heartbeat Content.          // start the keepalive.         TIMEDEXECUTOR.scheduleAtFixedRate(new KeepAlive(), long 5, long 5, TimeUnit.MINUTE);    }    public boolean isAlive() {        return linkAlive.get();    }    // the heartbeat can be private.    private void heartBeat() {        try {            httpRequest(hbserver, hbcontent);        } catch (AuthenticationException afe) {            // need to throw an exception so that the timer on the heartbeat thread stops.            // communicate the session is dead.            linkActive.set(false);            throw new IllegalStateException(Unable to renew session, ioe);        }    }    public String get....() {        // have getters for the session's values (real name, etc.)    }}The advantages of this class are that:it is self-containedit is immutable, and thus fully thread-safeit is self-managing - no need for external intervention to keep-aliveit does not hang on to unnecessary data (credentials).