1
0
mirror of https://github.com/Rogiel/l2jserver2 synced 2025-12-09 08:52:51 +00:00

Service improvements

Signed-off-by: Rogiel <rogiel@rogiel.com>
This commit is contained in:
2011-05-16 13:41:55 -03:00
parent adb285fdf9
commit 4952a6a47b
52 changed files with 1099 additions and 296 deletions

View File

@@ -19,6 +19,10 @@ package com.l2jserver.service;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import com.google.inject.Scopes;
import com.l2jserver.service.blowfish.BlowfishKeygenService;
import com.l2jserver.service.blowfish.SecureBlowfishKeygenService;
import com.l2jserver.service.cache.CacheService;
import com.l2jserver.service.cache.EhCacheService;
import com.l2jserver.service.configuration.ConfigurationService;
import com.l2jserver.service.configuration.ProxyConfigurationService;
import com.l2jserver.service.database.DatabaseService;
@@ -49,9 +53,13 @@ public class ServiceModule extends AbstractModule {
Scopes.SINGLETON);
bind(ConfigurationService.class).to(ProxyConfigurationService.class)
.in(Scopes.SINGLETON);
bind(CacheService.class).to(EhCacheService.class).in(
Scopes.SINGLETON);
bind(DatabaseService.class).to(MySQLDatabaseService.class).in(
Scopes.SINGLETON);
bind(BlowfishKeygenService.class).to(SecureBlowfishKeygenService.class)
.in(Scopes.SINGLETON);
bind(NetworkService.class).to(NettyNetworkService.class).in(
Scopes.SINGLETON);
bind(ScriptingService.class).to(ScriptingServiceImpl.class).in(

View File

@@ -0,0 +1,23 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.blowfish;
import com.l2jserver.service.Service;
public interface BlowfishKeygenService extends Service {
byte[] generate();
}

View File

@@ -0,0 +1,58 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.blowfish;
import java.util.Random;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
public class PseudoRandomBlowfishKeygenService extends AbstractService
implements BlowfishKeygenService {
private Random random;
@Override
public void start() throws ServiceStartException {
random = new Random();
}
@Override
public byte[] generate() {
final byte[] key = new byte[16];
// randomize the 8 first bytes
for (int i = 0; i < key.length; i++) {
key[i] = (byte) random.nextInt(255);
}
// the last 8 bytes are static
key[8] = (byte) 0xc8;
key[9] = (byte) 0x27;
key[10] = (byte) 0x93;
key[11] = (byte) 0x01;
key[12] = (byte) 0xa1;
key[13] = (byte) 0x6c;
key[14] = (byte) 0x31;
key[15] = (byte) 0x97;
return key;
}
@Override
public void stop() throws ServiceStopException {
random = null;
}
}

View File

@@ -0,0 +1,59 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.blowfish;
import org.apache.commons.math.random.RandomData;
import org.apache.commons.math.random.RandomDataImpl;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
public class SecureBlowfishKeygenService extends AbstractService implements
BlowfishKeygenService {
private RandomData random;
@Override
public void start() throws ServiceStartException {
random = new RandomDataImpl();
}
@Override
public byte[] generate() {
final byte[] key = new byte[16];
// randomize the 8 first bytes
for (int i = 0; i < key.length; i++) {
key[i] = (byte) random.nextSecureInt(0, 255);
}
// the last 8 bytes are static
key[8] = (byte) 0xc8;
key[9] = (byte) 0x27;
key[10] = (byte) 0x93;
key[11] = (byte) 0x01;
key[12] = (byte) 0xa1;
key[13] = (byte) 0x6c;
key[14] = (byte) 0x31;
key[15] = (byte) 0x97;
return key;
}
@Override
public void stop() throws ServiceStopException {
random = null;
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.l2jserver.service.cache;
import net.sf.ehcache.Cache;
import com.l2jserver.service.Service;
/**
@@ -33,15 +35,54 @@ import com.l2jserver.service.Service;
*/
public interface CacheService extends Service {
/**
* Decores the <tt>instance</tt> with the cache
* Decorates the <tt>instance</tt> with the cache. Note that
* <tt>interfaceType</tt> must be an interface!
*
* @param <T>
* the <tt>instance</tt> type
* @param interfaceType
* the interface type
* @param instance
* the instance
* the instance implementing the interface
* @return the cache-decorated object
*/
<T extends Cacheable> T decorate(Class<T> interfaceType, T instance);
/**
* Creates a new cache with default configurations. Eviction mode is LRU
* (Last Recently Used). If you wish more customization, you should manually
* create the cache and register it using {@link #register(Cache)}.
*
* @param name
* the cache name
* @size the maximum cache size
* @return the created cache
*/
Cache createCache(String name, int size);
/**
* Creates a new cache with default configurations. The default cache size
* is 200.
*
* @param name
* the cache name
* @return the created cache
*/
Cache createCache(String name);
/**
* Registers a new cache
*
* @param cache
* the cache
*/
void register(Cache cache);
/**
* Unregisters an already registered cache
*
* @param cache
* the cache
*/
void unregister(Cache cache);
}

View File

@@ -20,20 +20,38 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Map;
import java.util.WeakHashMap;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
import com.l2jserver.service.AbstractService;
import com.l2jserver.util.factory.CollectionFactory;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
/**
* Simple cache that stores invocation results in a {@link WeakHashMap}.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class SimpleCacheService extends AbstractService implements CacheService {
private final Map<MethodInvocation, Object> cache = CollectionFactory
.newWeakMap(MethodInvocation.class, Object.class);
public class EhCacheService extends AbstractService implements CacheService {
/**
* The cache manager
*/
private CacheManager manager;
/**
* The interface cache
*/
private Cache interfaceCache;
@Override
public void start() throws ServiceStartException {
manager = new CacheManager();
interfaceCache = createCache("interface-cache");
}
@Override
public <T extends Cacheable> T decorate(final Class<T> interfaceType,
@@ -51,10 +69,11 @@ public class SimpleCacheService extends AbstractService implements CacheService
return method.invoke(instance, args);
final MethodInvocation invocation = new MethodInvocation(
method, args);
Object result = cache.get(invocation);
Object result = interfaceCache.get(invocation)
.getObjectValue();
if (result == null) {
result = method.invoke(instance, args);
cache.put(invocation, result);
interfaceCache.put(new Element(invocation, result));
}
return result;
}
@@ -62,7 +81,43 @@ public class SimpleCacheService extends AbstractService implements CacheService
return proxy;
}
private class MethodInvocation {
@Override
public Cache createCache(String name, int size) {
Cache cache = new Cache(
new CacheConfiguration("database-service", size)
.memoryStoreEvictionPolicy(
MemoryStoreEvictionPolicy.LRU)
.overflowToDisk(true).eternal(false)
.timeToLiveSeconds(60).timeToIdleSeconds(30)
.diskPersistent(false)
.diskExpiryThreadIntervalSeconds(0));
register(cache);
return cache;
}
@Override
public Cache createCache(String name) {
return createCache(name, 200);
}
@Override
public void register(Cache cache) {
manager.addCache(cache);
}
@Override
public void unregister(Cache cache) {
manager.removeCache(cache.getName());
}
@Override
public void stop() throws ServiceStopException {
manager.removalAll();
manager.shutdown();
interfaceCache = null;
}
private static class MethodInvocation {
private final Method method;
private final Object[] args;

View File

@@ -27,4 +27,27 @@ import com.l2jserver.service.AbstractService;
*/
public class DB4ODatabaseService extends AbstractService implements
DatabaseService {
@Override
public Object getCachedObject(Object id) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean hasCachedObject(Object id) {
// TODO Auto-generated method stub
return false;
}
@Override
public void updateCache(Object key, Object value) {
// TODO Auto-generated method stub
}
@Override
public void removeCache(Object key) {
// TODO Auto-generated method stub
}
}

View File

@@ -25,5 +25,40 @@ import com.l2jserver.service.Service;
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface DatabaseService extends Service {
/**
* Get the database object cached. Will return null if the object is not in
* cache
*
* @param id
* the object id
* @return the cached object
*/
Object getCachedObject(Object id);
/**
* Tests if the object is cached
*
* @param id
* the object id
* @return true if object is in cache
*/
boolean hasCachedObject(Object id);
/**
* Adds or update the cache object
*
* @param key
* the cache key
* @param value
* the object
*/
void updateCache(Object key, Object value);
/**
* Removes an object from the cache
*
* @param key
* the cache key
*/
void removeCache(Object key);
}

View File

@@ -25,6 +25,11 @@ import java.util.List;
import javax.sql.DataSource;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
@@ -35,9 +40,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import com.l2jserver.model.id.ObjectID;
import com.l2jserver.model.world.WorldObject;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
import com.l2jserver.service.cache.CacheService;
import com.l2jserver.service.configuration.ConfigurationService;
import com.l2jserver.util.ArrayIterator;
import com.l2jserver.util.factory.CollectionFactory;
@@ -58,6 +66,11 @@ public class MySQLDatabaseService extends AbstractService implements
*/
private final Logger logger = LoggerFactory
.getLogger(MySQLDatabaseService.class);
// services
/**
* The cache service
*/
private final CacheService cacheService;
/**
* The database connection pool
@@ -77,9 +90,16 @@ public class MySQLDatabaseService extends AbstractService implements
*/
private PoolingDataSource dataSource;
/**
* An cache object
*/
private Cache objectCache;
@Inject
public MySQLDatabaseService(ConfigurationService configService) {
public MySQLDatabaseService(ConfigurationService configService,
CacheService cacheService) {
config = configService.get(MySQLDatabaseConfiguration.class);
this.cacheService = cacheService;
}
@Override
@@ -90,6 +110,14 @@ public class MySQLDatabaseService extends AbstractService implements
poolableConnectionFactory = new PoolableConnectionFactory(
connectionFactory, connectionPool, null, null, false, true);
dataSource = new PoolingDataSource(connectionPool);
objectCache = new Cache(new CacheConfiguration("database-service",
10 * 1000)
.memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LRU)
.overflowToDisk(true).eternal(false).timeToLiveSeconds(60)
.timeToIdleSeconds(30).diskPersistent(false)
.diskExpiryThreadIntervalSeconds(0));
cacheService.register(objectCache);
}
/**
@@ -118,8 +146,34 @@ public class MySQLDatabaseService extends AbstractService implements
}
}
@Override
public Object getCachedObject(Object id) {
final Element element = objectCache.get(id);
if (element == null)
return null;
return element.getObjectValue();
}
@Override
public boolean hasCachedObject(Object id) {
return objectCache.get(id) != null;
}
@Override
public void updateCache(Object key, Object value) {
objectCache.put(new Element(key, value));
}
@Override
public void removeCache(Object key) {
objectCache.remove(key);
}
@Override
public void stop() throws ServiceStopException {
if (objectCache != null)
objectCache.dispose();
objectCache = null;
try {
if (connectionPool != null)
connectionPool.close();
@@ -368,4 +422,73 @@ public class MySQLDatabaseService extends AbstractService implements
*/
T map(ResultSet rs) throws SQLException;
}
/**
* The cached mapper will try to lookup the result in the cache, before
* create a new instance. If the instance is not found in the cache, then
* the {@link Mapper} implementation is called to create the object. Note
* that the ID, used for the cache lookup, will be reused. After creation,
* the cache is updated.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <T>
* the object type
* @param <I>
* the id type
*/
public abstract static class CachedMapper<T extends WorldObject, I extends ObjectID<T>>
implements Mapper<T> {
/**
* The database service instance
*/
private final MySQLDatabaseService database;
/**
* Creates a new instance
*
* @param database
* the database service
*/
public CachedMapper(MySQLDatabaseService database) {
this.database = database;
}
@Override
@SuppressWarnings("unchecked")
public final T map(ResultSet rs) throws SQLException {
final I id = createID(rs);
if (database.hasCachedObject(id))
return (T) database.getCachedObject(id);
final T object = map(id, rs);
if (object != null)
database.updateCache(id, object);
return object;
}
/**
* Creates an ID for an object
*
* @param rs
* the jdbc result set
* @return the id
* @throws SQLException
*/
protected abstract I createID(ResultSet rs) throws SQLException;
/**
* Maps an uncached object. Once mapping is complete, it will be added
* to the cache.
*
* @param id
* the object id
* @param rs
* the jdbc result set
* @return the created object
* @throws SQLException
*/
protected abstract T map(I id, ResultSet rs) throws SQLException;
}
}

View File

@@ -19,7 +19,7 @@ package com.l2jserver.service.logging;
import org.apache.log4j.BasicConfigurator;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.ServiceStopException;
import com.l2jserver.service.ServiceStartException;
/**
* Logging service implementation for Log4J
@@ -29,7 +29,7 @@ import com.l2jserver.service.ServiceStopException;
public class Log4JLoggingService extends AbstractService implements
LoggingService {
@Override
public void stop() throws ServiceStopException {
public void start() throws ServiceStartException {
BasicConfigurator.configure();
}
}

View File

@@ -16,9 +16,12 @@
*/
package com.l2jserver.service.network;
import java.util.Set;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ServerChannel;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.logging.InternalLoggerFactory;
@@ -26,9 +29,12 @@ import org.jboss.netty.logging.Slf4JLoggerFactory;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.l2jserver.game.net.Lineage2Connection;
import com.l2jserver.game.net.Lineage2PipelineFactory;
import com.l2jserver.model.id.object.CharacterID;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.configuration.ConfigurationService;
import com.l2jserver.util.factory.CollectionFactory;
/**
* Netty network service implementation
@@ -37,10 +43,28 @@ import com.l2jserver.service.configuration.ConfigurationService;
*/
public class NettyNetworkService extends AbstractService implements
NetworkService {
/**
* The network configuration object
*/
private final NetworkConfiguration config;
/**
* The Google Guice {@link Injector}
*/
private final Injector injector;
/**
* The server bootstrap
*/
private ServerBootstrap server;
/**
* The server channel
*/
private ServerChannel channel;
/**
* The client list. This list all active clients in the server
*/
private Set<Lineage2Connection> clients = CollectionFactory
.newSet(Lineage2Connection.class);
@Inject
public NettyNetworkService(ConfigurationService configService,
@@ -55,10 +79,42 @@ public class NettyNetworkService extends AbstractService implements
server = new ServerBootstrap(new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
server.setPipelineFactory(new Lineage2PipelineFactory(injector));
server.setPipelineFactory(new Lineage2PipelineFactory(injector, this));
channel = (ServerChannel) server.bind(config.getListenAddress());
}
@Override
public void register(final Lineage2Connection client) {
clients.add(client);
client.getChannel().getCloseFuture()
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future)
throws Exception {
unregister(client);
}
});
}
@Override
public void unregister(Lineage2Connection client) {
clients.remove(client);
}
@Override
public Lineage2Connection discover(CharacterID character) {
for (final Lineage2Connection client : clients) {
if (character.equals(client.getCharacterID()))
return client;
}
return null;
}
@Override
public void cleanup() {
// TODO
}
@Override
public void stop() {
try {

View File

@@ -16,6 +16,8 @@
*/
package com.l2jserver.service.network;
import com.l2jserver.game.net.Lineage2Connection;
import com.l2jserver.model.id.object.CharacterID;
import com.l2jserver.service.Service;
/**
@@ -25,4 +27,33 @@ import com.l2jserver.service.Service;
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface NetworkService extends Service {
/**
* Registers a new client
*
* @param client
* the client
*/
void register(Lineage2Connection client);
/**
* Unregisters a client
*
* @param client
* the client
*/
void unregister(Lineage2Connection client);
/**
* Discover the client using <tt>character</tt>
*
* @param character
* the character
* @return the found connection
*/
Lineage2Connection discover(CharacterID character);
/**
* Searches for idle connection and removes them
*/
void cleanup();
}