package com.gesoftware.venta.network.model; import com.gesoftware.venta.utility.CompressionUtility; import java.nio.charset.Charset; import java.io.Serializable; import java.util.Arrays; /* * * Message class definition * */ public final class Message implements Serializable { /* Time */ private final long m_Timestamp; /* Message data */ private final byte[] m_Data; /* * * METHOD: Message class constructor * PARAM: [IN] data - bytes array data * AUTHOR: Eliseev Dmitry * */ public Message(final byte data[]) { m_Timestamp = System.currentTimeMillis(); m_Data = data; } /* End of 'Message::Message' method */ /* * * METHOD: Message class constructor * PARAM: [IN] data - bytes array data * AUTHOR: Eliseev Dmitry * */ public Message(final String data) { this(data.getBytes()); } /* End of 'Message::Message' method */ /* * * METHOD: Message class constructor * PARAM: [IN] object - some serializable object * AUTHOR: Eliseev Dmitry * */ public Message(final Object object) { this(CompressionUtility.compress(object)); } /* End of 'Message::Message' method */ /* * * METHOD: Bytes data representation getter * RETURN: Data bytes representation * AUTHOR: Eliseev Dmitry * */ public final byte[] getData() { return m_Data; } /* End of 'Message::getData' method */ /* * * METHOD: Gets message size * RETURN: Data size in bytes * AUTHOR: Eliseev Dmitry * */ public final int getSize() { return (m_Data != null)?m_Data.length:0; } /* End of 'Message::getSize' method */ @Override public final String toString() { return (m_Data != null)?new String(m_Data, Charset.forName("UTF-8")):null; } /* End of 'Message::toString' method */ /* * * METHOD: Compares two messages sizes * RETURN: TRUE if messages has same sizes, FALSE otherwise * PARAM: [IN] message - message to compare with this one * AUTHOR: Eliseev Dmitry * */ private boolean messagesHasSameSizes(final Message message) { return m_Data != null && m_Data.length == message.m_Data.length; } /* End of 'Message::messagesHasSameSize' method */ /* * * METHOD: Compares two messages by their values * RETURN: TRUE if messages has same sizes, FALSE otherwise * PARAM: [IN] message - message to compare with this one * AUTHOR: Eliseev Dmitry * */ private boolean messagesAreEqual(final Message message) { /* Messages has different sizes */ if (!messagesHasSameSizes(message)) return false; /* At least one of characters is not equal to same at another message */ for (int i = 0; i < message.m_Data.length; i++) if (m_Data[i] != message.m_Data[i]) return false; /* Messages are equal */ return true; } /* End of 'Message::messagesAreEqual' method */ /* * * METHOD: Tries to restore object, that may be packed in message * RETURN: Restored object if success, null otherwise * AUTHOR: Eliseev Dmitry * */ public final Object getObject() { return CompressionUtility.decompress(m_Data); } /* End of 'Message::getObject' method */ /* * * METHOD: Gets message sending time (in server time) * RETURN: Message sending time * AUTHOR: Eliseev Dmitry * */ public final long getTimestamp() { return m_Timestamp; } /* End of 'Message::getTimestamp' method */ @Override public final boolean equals(Object obj) { return obj instanceof Message && messagesAreEqual((Message) obj); } /* End of 'Message::equals' method */ @Override public final int hashCode() { return Arrays.hashCode(m_Data); } /* End of 'Message::hashCode' method */ } /* End of 'Message' class */
package com.gesoftware.venta.network.handlers; import com.gesoftware.venta.network.model.Message; import com.gesoftware.venta.network.model.ServerResponse; import java.net.InetAddress; /* Server handler interface declaration */ public interface IServerHandler { /* * * METHOD: Will be called right after new client connected * RETURN: True if you accept connected client, false if reject * PARAM: [IN] clientID - client identifier (store it somewhere) * PARAM: [IN] clientAddress - connected client information * AUTHOR: Eliseev Dmitry * */ public abstract boolean onConnect(final String clientID, final InetAddress clientAddress); /* * * METHOD: Will be called right after server accept message from any connected client * RETURN: Response (see ServerResponse class), or null if you want to disconnect client * PARAM: [IN] clientID - sender identifier * PARAM: [IN] message - received message * AUTHOR: Eliseev Dmitry * */ public abstract ServerResponse onReceive(final String clientID, final Message message); /* * * METHOD: Will be called right after any client disconnected * PARAM: [IN] clientID - disconnected client identifier * AUTHOR: Eliseev Dmitry * */ public abstract void onDisconnect(final String clientID); } /* End of 'IServerHandler' interface */
package com.gesoftware.venta.network.handlers; import com.gesoftware.venta.network.model.Message; import com.gesoftware.venta.network.model.ServerResponse; import java.net.InetAddress; /* Server handler interface declaration */ public interface IServerHandler { /* * * METHOD: Will be called right after new client connected * RETURN: True if you accept connected client, false if reject * PARAM: [IN] clientID - client identifier (store it somewhere) * PARAM: [IN] clientAddress - connected client information * AUTHOR: Eliseev Dmitry * */ public abstract boolean onConnect(final String clientID, final InetAddress clientAddress); /* * * METHOD: Will be called right after server accept message from any connected client * RETURN: Response (see ServerResponse class), or null if you want to disconnect client * PARAM: [IN] clientID - sender identifier * PARAM: [IN] message - received message * AUTHOR: Eliseev Dmitry * */ public abstract ServerResponse onReceive(final String clientID, final Message message); /* * * METHOD: Will be called right after any client disconnected * PARAM: [IN] clientID - disconnected client identifier * AUTHOR: Eliseev Dmitry * */ public abstract void onDisconnect(final String clientID); } /* End of 'IServerHandler' interface */
package com.gesoftware.venta.network; import com.gesoftware.venta.logging.LoggingUtility; import com.gesoftware.venta.network.handlers.IClientHandler; import com.gesoftware.venta.network.handlers.IServerHandler; import com.gesoftware.venta.network.model.Message; import com.gesoftware.venta.network.model.ServerResponse; import java.net.InetAddress; import java.util.TimerTask; public final class NetworkTest { private final static int c_Port = 5502; private static void startServer() { final Server server = new Server(c_Port, new IServerHandler() { @Override public boolean onConnect(final String clientID, final InetAddress clientAddress) { LoggingUtility.info("Client connected: " + clientID); return true; } @Override public ServerResponse onReceive(final String clientID, final Message message) { LoggingUtility.info("Client send message: " + message.toString()); return new ServerResponse(message); } @Override public void onDisconnect(final String clientID) { LoggingUtility.info("Client disconnected: " + clientID); } }); (new Thread(server)).start(); } private static class Task extends TimerTask { private final Connection m_Connection; public Task(final Connection connection) { m_Connection = connection; } @Override public void run() { m_Connection.send(new Message("Hello, current time is: " + System.currentTimeMillis())); } } private static void startClient() { final Connection connection = new Connection("localhost", c_Port, new IClientHandler() { @Override public void onReceive(final Message message) { LoggingUtility.info("Server answer: " + message.toString()); } @Override public void onConnectionLost(final String message) { LoggingUtility.info("Connection lost: " + message); } }); connection.connect(); (new java.util.Timer("Client")).schedule(new Task(connection), 0, 1000); } public static void main(final String args[]) { LoggingUtility.setLoggingLevel(LoggingUtility.LoggingLevel.LEVEL_DEBUG); startServer(); startClient(); } }
package com.gesoftware.venta.db; import com.gesoftware.venta.logging.LoggingUtility; import com.jolbox.bonecp.BoneCPConfig; import com.jolbox.bonecp.BoneCP; import java.io.InputStream; import java.util.AbstractList; import java.util.LinkedList; import java.util.HashMap; import java.util.Map; import java.sql.*; /** * DB connection class definition **/ public final class DBConnection { /* Connections pool */ private BoneCP m_Pool; /** * DB Statement class definition **/ public final class DBStatement { private final PreparedStatement m_Statement; private final Connection m_Connection; /* * * METHOD: Class constructor * PARAM: [IN] connection - current connection * PARAM: [IN] statement - statement, created from connection * AUTHOR: Dmitry Eliseev * */ private DBStatement(final Connection connection, final PreparedStatement statement) { m_Connection = connection; m_Statement = statement; } /* End of 'DBStatement::DBStatement' class */ /* * * METHOD: Integer parameter setter * RETURN: True if success, False otherwise * PARAM: [IN] index - parameter position * PARAM: [IN] value - parameter value * AUTHOR: Dmitry Eliseev * */ public final boolean setInteger(final int index, final int value) { try { m_Statement.setInt(index, value); return true; } catch (final SQLException e) { LoggingUtility.debug("Can't set integer value: " + value + " because of " + e.getMessage()); } return false; } /* End of 'DBStatement::setInteger' class */ /* * * METHOD: Long parameter setter * RETURN: True if success, False otherwise * PARAM: [IN] index - parameter position * PARAM: [IN] value - parameter value * AUTHOR: Dmitry Eliseev * */ public final boolean setLong(final int index, final long value) { try { m_Statement.setLong(index, value); return true; } catch (final SQLException e) { LoggingUtility.debug("Can't set long value: " + value + " because of " + e.getMessage()); } return false; } /* End of 'DBStatement::setLong' class */ /* * * METHOD: String parameter setter * RETURN: True if success, False otherwise * PARAM: [IN] index - parameter position * PARAM: [IN] value - parameter value * AUTHOR: Dmitry Eliseev * */ public final boolean setString(final int index, final String value) { try { m_Statement.setString(index, value); } catch (final SQLException e) { LoggingUtility.debug("Can't set string value: " + value + " because of " + e.getMessage()); } return false; } /* End of 'DBStatement::setString' class */ /* * * METHOD: Enum parameter setter * RETURN: True if success, False otherwise * PARAM: [IN] index - parameter position * PARAM: [IN] value - parameter value * AUTHOR: Dmitry Eliseev * */ public final boolean setEnum(final int index, final Enum value) { return setString(index, value.name()); } /* End of 'DBStatement::setEnum' method */ /* * * METHOD: Binary stream parameter setter * RETURN: True if success, False otherwise * PARAM: [IN] index - parameter position * PARAM: [IN] stream - stream * PARAM: [IN] long - data length * AUTHOR: Dmitry Eliseev * */ public final boolean setBinaryStream(final int index, final InputStream stream, final long length) { try { m_Statement.setBinaryStream(index, stream); return true; } catch (final SQLException e) { LoggingUtility.debug("Can't set stream value: " + stream + " because of " + e.getMessage()); } return false; } /* End of 'DBStatement::setBinaryStream' method */ } /* End of 'DBConnection::DBStatement' class */ /* * * METHOD: Class constructor * PARAM: [IN] host - Database service host * PARAM: [IN] port - Database service port * PARAM: [IN] name - Database name * PARAM: [IN] user - Database user's name * PARAM: [IN] pass - Database user's password * AUTHOR: Dmitry Eliseev * */ public DBConnection(final String host, final int port, final String name, final String user, final String pass) { final BoneCPConfig config = new BoneCPConfig(); config.setJdbcUrl("jdbc:mysql://" + host + ":" + port + "/" + name); config.setUsername(user); config.setPassword(pass); /* Pool size configuration */ config.setMaxConnectionsPerPartition(5); config.setMinConnectionsPerPartition(5); config.setPartitionCount(1); try { m_Pool = new BoneCP(config); } catch (final SQLException e) { LoggingUtility.error("Can't initialize connections pool: " + e.getMessage()); m_Pool = null; } } /* End of 'DBConnection::DBConnection' method */ @Override protected final void finalize() throws Throwable { super.finalize(); if (m_Pool != null) m_Pool.shutdown(); } /* End of 'DBConnection::finalize' method */ /* * * METHOD: Prepares statement using current connection * RETURN: Prepared statement * PARAM: [IN] query - SQL query * AUTHOR: Dmitry Eliseev * */ public final DBStatement createStatement(final String query) { try { LoggingUtility.debug("Total: " + m_Pool.getTotalCreatedConnections() + "; Free: " + m_Pool.getTotalFree() + "; Leased: " + m_Pool.getTotalLeased()); final Connection connection = m_Pool.getConnection(); return new DBStatement(connection, connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)); } catch (final SQLException e) { LoggingUtility.error("Can't create prepared statement using query: " + e.getMessage()); } catch (final Exception e) { LoggingUtility.error("Connection wasn't established: " + e.getMessage()); } return null; } /* End of 'DBConnection::createStatement' method */ /* * * METHOD: Closes prepared statement * PARAM: [IN] sql - prepared statement * AUTHOR: Dmitry Eliseev * */ private void closeStatement(final DBStatement query) { if (query == null) return; try { if (query.m_Statement != null) query.m_Statement.close(); if (query.m_Connection != null) query.m_Connection.close(); } catch (final SQLException ignored) {} } /* End of 'DBConnection::closeStatement' method */ /* * * METHOD: Executes prepared statement like INSERT query * RETURN: Inserted item identifier if success, 0 otherwise * PARAM: [IN] sql - prepared statement * AUTHOR: Dmitry Eliseev * */ public final long insert(final DBStatement query) { try { /* Query execution */ query.m_Statement.execute(); /* Obtain last insert ID */ final ResultSet resultSet = query.m_Statement.getGeneratedKeys(); if (resultSet.next()) return resultSet.getInt(1); } catch (final SQLException e) { LoggingUtility.error("Can't execute insert query: " + query.toString()); } finally { closeStatement(query); } /* Insertion failed */ return 0; } /* End of 'DBConnection::insert' method */ /* * * METHOD: Executes prepared statement like UPDATE query * RETURN: True if success, False otherwise * PARAM: [IN] sql - prepared statement * AUTHOR: Dmitry Eliseev * */ public final boolean update(final DBStatement query) { try { query.m_Statement.execute(); return true; } catch (final SQLException e) { LoggingUtility.error("Can't execute update query: " + query.m_Statement.toString()); } finally { closeStatement(query); } /* Update failed */ return false; } /* End of 'DBConnection::update' method */ /* * * METHOD: Executes prepared statement like COUNT != 0 query * RETURN: True if exists, False otherwise * PARAM: [IN] sql - prepared statement * AUTHOR: Dmitry Eliseev * */ public final boolean exists(final DBStatement query) { final AbstractList<Map<String, Object>> results = select(query); return results != null && results.size() != 0; } /* End of 'DBConnection::DBConnection' method */ /* * * METHOD: Executes prepared statement like SELECT query * RETURN: List of records (maps) if success, null otherwise * PARAM: [IN] sql - prepared statement * AUTHOR: Dmitry Eliseev * */ public final AbstractList<Map<String, Object>> select(final DBStatement query) { try { /* Container for result set */ final AbstractList<Map<String, Object>> results = new LinkedList<Map<String, Object>>(); /* Query execution */ query.m_Statement.execute(); /* Determine columns meta data */ final ResultSetMetaData metaData = query.m_Statement.getMetaData(); /* Obtain real data */ final ResultSet resultSet = query.m_Statement.getResultSet(); while (resultSet.next()) { final Map<String, Object> row = new HashMap<String, Object>(); /* Copying fetched data */ for (int columnID = 1; columnID <= metaData.getColumnCount(); columnID++) row.put(metaData.getColumnName(columnID), resultSet.getObject(columnID)); /* Add row to results */ results.add(row); } /* That's it */ return results; } catch (final SQLException e) { LoggingUtility.error("Can't execute select query: " + query.toString()); } finally { closeStatement(query); } /* Return empty result */ return null; } /* End of 'DBConnection::select' method */ } /* End of 'DBConnection' class */
package com.gesoftware.venta.db; import com.gesoftware.venta.logging.LoggingUtility; import java.util.*; /** * DB controller class definition **/ public abstract class DBController<T> { /* Real DB connection */ protected final DBConnection m_Connection; /* * * METHOD: Class constructor * PARAM: [IN] connection - real DB connection * AUTHOR: Dmitry Eliseev * */ protected DBController(final DBConnection connection) { m_Connection = connection; LoggingUtility.core(getClass().getCanonicalName() + " controller initialized"); } /* End of 'DBController::DBController' method */ /* * * METHOD: Requests collection of T objects using select statement * RETURN: Collection of objects if success, empty collection otherwise * PARAM: [IN] selectStatement - prepared select statement * AUTHOR: Dmitry Eliseev * */ protected final Collection<T> getCollection(final DBConnection.DBStatement selectStatement) { if (selectStatement == null) return new LinkedList<T>(); final AbstractList<Map<String, Object>> objectsCollection = m_Connection.select(selectStatement); if ((objectsCollection == null)||(objectsCollection.size() == 0)) return new LinkedList<T>(); final Collection<T> parsedObjectsCollection = new ArrayList<T>(objectsCollection.size()); for (final Map<String, Object> object : objectsCollection) parsedObjectsCollection.add(parse(object)); return parsedObjectsCollection; } /* End of 'DBController::getCollection' method */ /* * * METHOD: Requests one T object using select statement * RETURN: Object if success, null otherwise * PARAM: [IN] selectStatement - prepared select statement * AUTHOR: Dmitry Eliseev * */ protected final T getObject(final DBConnection.DBStatement selectStatement) { if (selectStatement == null) return null; final AbstractList<Map<String, Object>> objectsCollection = m_Connection.select(selectStatement); if ((objectsCollection == null)||(objectsCollection.size() != 1)) return null; return parse(objectsCollection.get(0)); } /* End of 'DBController::getObject' method */ /* * * METHOD: Parses object's map representation to real T object * RETURN: T object if success, null otherwise * PARAM: [IN] objectMap - object map, obtained by selection from DB * AUTHOR: Dmitry Eliseev * */ protected abstract T parse(final Map<String, Object> objectMap); } /* End of 'DBController' class */
package com.gesoftware.stickers.server.handlers; import com.gesoftware.stickers.model.common.Definitions; public final class StickersHandler implements IServerHandler { private final Map<Class, StickersQueryHandler> m_Handlers = new SynchronizedMap<Class, StickersQueryHandler>(); private final StickersManager m_Context; private final JobsManager m_JobsManager; public StickersHandler(final DBConnection connection) { m_Context = new StickersManager(connection); m_JobsManager = new JobsManager(Definitions.c_TasksThreadSleepTime); registerQueriesHandlers(); registerJobs(); } private void registerJobs() { m_JobsManager.addTask(new TaskGameUpdateStatus(m_Context)); m_JobsManager.addTask(new TaskGameUpdatePhase(m_Context)); } private void registerQueriesHandlers() { /* Menu handlers */ m_Handlers.put(QueryAuthorization.class, new QueryAuthorizationHandler(m_Context)); m_Handlers.put(QueryRegistration.class, new QueryRegistrationHandler(m_Context)); m_Handlers.put(QueryRating.class, new QueryRatingHandler(m_Context)); /* Logout */ m_Handlers.put(QueryLogout.class, new QueryLogoutHandler(m_Context)); /* Rooms handlers */ m_Handlers.put(QueryRoomRefreshList.class, new QueryRoomRefreshListHandler(m_Context)); m_Handlers.put(QueryRoomCreate.class, new QueryRoomCreateHandler(m_Context)); m_Handlers.put(QueryRoomSelect.class, new QueryRoomSelectHandler(m_Context)); m_Handlers.put(QueryRoomLeave.class, new QueryRoomLeaveHandler(m_Context)); /* Games handler */ m_Handlers.put(QueryGameLeave.class, new QueryGameLeaveHandler(m_Context)); m_Handlers.put(QueryGameIsStarted.class, new QueryGameIsStartedHandler(m_Context)); m_Handlers.put(QueryGameWhichPhase.class, new QueryGameWhichPhaseHandler(m_Context)); /* Question handler */ m_Handlers.put(QueryGameAsk.class, new QueryGameAskHandler(m_Context)); /* Answer handler */ m_Handlers.put(QueryGameAnswer.class, new QueryGameAnswerHandler(m_Context)); /* Voting handler */ m_Handlers.put(QueryGameVote.class, new QueryGameVoteHandler(m_Context)); /* Users handler */ m_Handlers.put(QueryUserHasInvites.class, new QueryUserHasInvitesHandler(m_Context)); m_Handlers.put(QueryUserAvailable.class, new QueryUserAvailableHandler(m_Context)); m_Handlers.put(QueryUserInvite.class, new QueryUserInviteHandler(m_Context)); } @SuppressWarnings("unchecked") private synchronized Serializable userQuery(final String clientID, final Object query) { final StickersQueryHandler handler = getHandler(query.getClass()); if (handler == null) { LoggingUtility.error("Handler is not registered for " + query.getClass()); return new ResponseCommonMessage("Internal server error: can't process: " + query.getClass()); } return handler.processQuery(m_Context.getClientsManager().getClient(clientID), query); } private StickersQueryHandler getHandler(final Class c) { return m_Handlers.get(c); } private ServerResponse answer(final Serializable object) { return new ServerResponse(new Message(object)); } @Override public boolean onConnect(final String clientID, final InetAddress clientAddress) { LoggingUtility.info("User <" + clientID + "> connected from " + clientAddress.getHostAddress()); m_Context.getClientsManager().clientConnected(clientID); return true; } @Override public final ServerResponse onReceive(final String clientID, final Message message) { final Object object = message.getObject(); if (object == null) { LoggingUtility.error("Unknown object accepted"); return answer(new ResponseCommonMessage("Internal server error: empty object")); } return new ServerResponse(new Message(userQuery(clientID, object))); } @Override public void onDisconnect(final String clientID) { m_Context.getClientsManager().clientDisconnected(clientID); LoggingUtility.info("User <" + clientID + "> disconnected"); } public void stop() { m_JobsManager.stop(); } }
package com.gesoftware.stickers.server.handlers.registration; import com.gesoftware.stickers.model.enums.UserStatus; import com.gesoftware.stickers.model.objects.User; import com.gesoftware.stickers.model.queries.registration.QueryRegistration; import com.gesoftware.stickers.model.responses.registration.ResponseRegistrationInvalidEMail; import com.gesoftware.stickers.model.responses.registration.ResponseRegistrationFailed; import com.gesoftware.stickers.model.responses.registration.ResponseRegistrationSuccessfully; import com.gesoftware.stickers.model.responses.registration.ResponseUserAlreadyRegistered; import com.gesoftware.stickers.server.handlers.StickersQueryHandler; import com.gesoftware.stickers.server.managers.StickersManager; import com.gesoftware.venta.logging.LoggingUtility; import com.gesoftware.venta.utility.ValidationUtility; import java.io.Serializable; public final class QueryRegistrationHandler extends StickersQueryHandler<QueryRegistration> { public QueryRegistrationHandler(final StickersManager context) { super(context); } @Override public final Serializable process(final User user, final QueryRegistration query) { if (!ValidationUtility.isEMailValid(query.m_EMail)) return new ResponseRegistrationInvalidEMail(); if (m_Context.getUsersManager().isUserRegistered(query.m_EMail)) return new ResponseUserAlreadyRegistered(); if (!m_Context.getUsersManager().registerUser(query.m_EMail, query.m_PasswordHash, query.m_Name)) return new ResponseRegistrationFailed(); LoggingUtility.info("User <" + user.m_ClientID + "> registered as " + query.m_EMail); return new ResponseRegistrationSuccessfully(); } @Override public final UserStatus getStatus() { return UserStatus.NotLogged; } }
Source: https://habr.com/ru/post/198054/
All Articles