From cea66c93635bb466800f74aa585ab0b98427ae7a Mon Sep 17 00:00:00 2001 From: Rogiel Date: Sat, 17 Dec 2011 18:25:49 -0200 Subject: [PATCH] Implements task oriented ThreadService --- .../service/core/threading/AbstractTask.java | 25 ++ .../service/core/threading/Task.java | 27 +++ .../service/core/threading/ThreadPool.java | 10 +- .../service/core/threading/ThreadService.java | 5 +- .../core/threading/ThreadServiceImpl.java | 15 +- .../service/database/AbstractDAO.java | 30 ++- .../database/AbstractJDBCDatabaseService.java | 222 +++++++++++------- .../AbstractOrientDatabaseService.java | 18 ++ .../service/database/DataAccessObject.java | 18 +- .../service/database/DatabaseException.java | 53 +++++ .../service/database/DatabaseService.java | 54 +++++ l2jserver2-gameserver/data/template/item.xsd | 77 ++++-- .../data/template/item/1-ShortSword.xml | 53 ++--- .../service/game/AttackServiceImpl.java | 3 +- .../service/game/npc/NPCServiceImpl.java | 4 +- .../service/game/spawn/SpawnServiceImpl.java | 6 +- 16 files changed, 441 insertions(+), 179 deletions(-) create mode 100644 l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/AbstractTask.java create mode 100644 l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/Task.java create mode 100644 l2jserver2-common/src/main/java/com/l2jserver/service/database/DatabaseException.java diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/AbstractTask.java b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/AbstractTask.java new file mode 100644 index 000000000..0912f90f3 --- /dev/null +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/AbstractTask.java @@ -0,0 +1,25 @@ +/* + * This file is part of l2jserver2 . + * + * l2jserver2 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. + * + * l2jserver2 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 l2jserver2. If not, see . + */ +package com.l2jserver.service.core.threading; + +/** + * @author Rogiel + * @param + * the task return type + */ +public abstract class AbstractTask implements Task { +} diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/Task.java b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/Task.java new file mode 100644 index 000000000..b49ff6a71 --- /dev/null +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/Task.java @@ -0,0 +1,27 @@ +/* + * This file is part of l2jserver2 . + * + * l2jserver2 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. + * + * l2jserver2 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 l2jserver2. If not, see . + */ +package com.l2jserver.service.core.threading; + +import java.util.concurrent.Callable; + +/** + * @author Rogiel + * @param + * the task return type + */ +public interface Task extends Callable { +} diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadPool.java b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadPool.java index 350fa4b2c..9e20b5d48 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadPool.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadPool.java @@ -16,7 +16,6 @@ */ package com.l2jserver.service.core.threading; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; /** @@ -31,18 +30,18 @@ public interface ThreadPool { * * @param * the task return type - * @param callable + * @param task * the callable instance * @return the {@link AsyncFuture} notified once the task has completed */ - AsyncFuture async(Callable callable); + AsyncFuture async(Task task); /** * Executes an asynchronous tasks at an scheduled time. * * @param * the task return type - * @param callable + * @param task * the callable instance * @param delay * the initial delay to wait before the task is executed @@ -50,7 +49,7 @@ public interface ThreadPool { * the time unit of delay * @return the {@link AsyncFuture} notified once the task has completed */ - AsyncFuture async(long delay, TimeUnit unit, Callable callable); + AsyncFuture async(long delay, TimeUnit unit, Task task); /** * Executes an asynchronous tasks at an scheduled time. @@ -73,5 +72,4 @@ public interface ThreadPool { * execute tasks. */ void dispose(); - } diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadService.java b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadService.java index f1f6fd82d..d308ffefc 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadService.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadService.java @@ -16,7 +16,6 @@ */ package com.l2jserver.service.core.threading; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import com.l2jserver.service.Service; @@ -39,7 +38,7 @@ public interface ThreadService extends Service { * the callable instance * @return the {@link AsyncFuture} notified once the task has completed */ - AsyncFuture async(Callable callable); + AsyncFuture async(Task callable); /** * Executes an asynchronous tasks at an scheduled time. Please note that @@ -58,7 +57,7 @@ public interface ThreadService extends Service { * the time unit of delay * @return the {@link AsyncFuture} notified once the task has completed */ - AsyncFuture async(long delay, TimeUnit unit, Callable callable); + AsyncFuture async(long delay, TimeUnit unit, Task callable); /** * Executes an asynchronous tasks at an scheduled time. Please note that diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadServiceImpl.java b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadServiceImpl.java index f6744d20c..0e50e1bc6 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadServiceImpl.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadServiceImpl.java @@ -19,7 +19,6 @@ package com.l2jserver.service.core.threading; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.concurrent.Callable; import java.util.concurrent.Delayed; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -79,7 +78,7 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService } @Override - public AsyncFuture async(Callable callable) { + public AsyncFuture async(Task callable) { Preconditions.checkNotNull(callable, "callable"); log.debug("Scheduling async task: {}", callable); @@ -87,8 +86,7 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService } @Override - public AsyncFuture async(long delay, TimeUnit unit, - Callable callable) { + public AsyncFuture async(long delay, TimeUnit unit, Task callable) { Preconditions.checkArgument(delay >= 0, "delay < 0"); Preconditions.checkNotNull(unit, "unit"); Preconditions.checkNotNull(callable, "callable"); @@ -352,14 +350,14 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService } @Override - public AsyncFuture async(Callable callable) { + public AsyncFuture async(Task callable) { log.debug("Task {} submited to {}", callable, name); return new AsyncFutureImpl(executor.submit(callable)); } @Override public AsyncFuture async(long delay, TimeUnit unit, - Callable callable) { + Task callable) { if (log.isDebugEnabled()) log.debug("Task {} scheduled in {} {} to {}", new Object[] { callable, delay, unit, name }); @@ -371,8 +369,9 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService public ScheduledAsyncFuture async(long delay, TimeUnit unit, long repeat, Runnable task) { if (log.isDebugEnabled()) - log.debug("Task {} scheduled every {} {} to {}", new Object[] { - task, repeat, unit, name }); + log.debug( + "Task {} scheduled every {} {} to {}, starting in {}", + new Object[] { task, repeat, unit, name, delay }); return new ScheduledAsyncFutureImpl(executor.scheduleAtFixedRate( task, delay, repeat, unit)); } diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractDAO.java b/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractDAO.java index 8187f4332..a27e2ad0d 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractDAO.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractDAO.java @@ -17,13 +17,14 @@ package com.l2jserver.service.database; import java.util.Iterator; -import java.util.concurrent.Callable; import com.google.inject.Inject; import com.l2jserver.model.Model; import com.l2jserver.model.id.ID; +import com.l2jserver.service.core.threading.AbstractTask; import com.l2jserver.service.core.threading.AsyncFuture; import com.l2jserver.service.core.threading.ThreadService; +import com.l2jserver.service.database.DatabaseService.TransactionExecutor; /** * Abstract DAO implementations. Store an instance of {@link DatabaseService}. @@ -61,7 +62,7 @@ public abstract class AbstractDAO, I extends ID> @Override public AsyncFuture selectAsync(final I id) { - return threadService.async(new Callable() { + return threadService.async(new AbstractTask() { @Override public T call() throws Exception { return select(id); @@ -92,18 +93,23 @@ public abstract class AbstractDAO, I extends ID> @Override @SafeVarargs - public final int saveObjects(T... objects) { - int rows = 0; - for (final T object : objects) { - rows += save(object); - } - return rows; + public final int saveObjects(final T... objects) { + return database.transaction(new TransactionExecutor() { + @Override + public int perform() { + int rows = 0; + for (final T object : objects) { + rows += save(object); + } + return rows; + } + }); } @Override @SafeVarargs public final AsyncFuture saveObjectsAsync(final T... objects) { - return threadService.async(new Callable() { + return threadService.async(new AbstractTask() { @Override public Integer call() throws Exception { return saveObjects(objects); @@ -119,7 +125,7 @@ public abstract class AbstractDAO, I extends ID> @Override @SafeVarargs public final AsyncFuture insertObjectsAsync(final T... objects) { - return threadService.async(new Callable() { + return threadService.async(new AbstractTask() { @Override public Integer call() throws Exception { return insertObjects(objects); @@ -135,7 +141,7 @@ public abstract class AbstractDAO, I extends ID> @Override @SafeVarargs public final AsyncFuture updateObjectsAsync(final T... objects) { - return threadService.async(new Callable() { + return threadService.async(new AbstractTask() { @Override public Integer call() throws Exception { return updateObjects(objects); @@ -151,7 +157,7 @@ public abstract class AbstractDAO, I extends ID> @Override @SafeVarargs public final AsyncFuture deleteObjectsAsync(final T... objects) { - return threadService.async(new Callable() { + return threadService.async(new AbstractTask() { @Override public Integer call() throws Exception { return deleteObjects(objects); diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractJDBCDatabaseService.java b/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractJDBCDatabaseService.java index 85f0fd266..04a059ec8 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractJDBCDatabaseService.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractJDBCDatabaseService.java @@ -49,6 +49,8 @@ import com.l2jserver.service.cache.CacheService; import com.l2jserver.service.configuration.ConfigurationService; import com.l2jserver.service.configuration.ProxyConfigurationService.ConfigurationPropertyKey; import com.l2jserver.service.configuration.XMLConfigurationService.ConfigurationXPath; +import com.l2jserver.service.core.threading.AbstractTask; +import com.l2jserver.service.core.threading.AsyncFuture; import com.l2jserver.service.core.threading.ScheduledAsyncFuture; import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.util.ArrayIterator; @@ -129,6 +131,11 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService */ private ScheduledAsyncFuture autoSaveFuture; + /** + * The connection used inside a transaction from multiple DAOs. + */ + private ThreadLocal transactionalConnection = new ThreadLocal<>(); + /** * Configuration interface for {@link AbstractJDBCDatabaseService}. * @@ -325,6 +332,46 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService }); } + @Override + public int transaction(TransactionExecutor executor) { + Preconditions.checkNotNull(executor, "executor"); + try { + final Connection conn = dataSource.getConnection(); + log.debug("Executing transaction {} with {}", executor, conn); + try { + conn.setAutoCommit(false); + + transactionalConnection.set(conn); + final int rows = executor.perform(); + + conn.commit(); + return rows; + } catch (Exception e) { + conn.rollback(); + throw e; + } finally { + transactionalConnection.remove(); + conn.setAutoCommit(true); + conn.close(); + } + } catch (DatabaseException e) { + throw e; + } catch (Throwable e) { + throw new DatabaseException(e); + } + } + + @Override + public AsyncFuture transactionAsync( + final TransactionExecutor executor) { + return threadService.async(new AbstractTask() { + @Override + public Integer call() throws Exception { + return transaction(executor); + } + }); + } + /** * Executes an query in the database. * @@ -333,23 +380,47 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService * @param query * the query * @return an instance of T + * @throws DatabaseException + * if any exception occur (can have nested {@link SQLException}) */ - public T query(Query query) { + public T query(Query query) throws DatabaseException { Preconditions.checkNotNull(query, "query"); try { - final Connection conn = dataSource.getConnection(); + boolean inTransaction = false; + Connection conn = transactionalConnection.get(); + if (conn == null) { + log.debug( + "Transactional connection for {} is not set, creating new connection", + query); + inTransaction = false; + conn = dataSource.getConnection(); + } log.debug("Executing query {} with {}", query, conn); try { - return query.query(conn); - } catch (SQLException e) { - log.error("Error executing query", e); - return null; + if (!inTransaction) { + conn.setAutoCommit(false); + } + try { + return query.query(conn); + } finally { + if (!inTransaction) { + conn.commit(); + } + } + } catch (Exception e) { + if (!inTransaction) { + conn.rollback(); + } + throw e; } finally { - conn.close(); + if (!inTransaction) { + conn.setAutoCommit(true); + conn.close(); + } } - } catch (SQLException e) { + } catch (Throwable e) { log.error("Could not open database connection", e); - return null; + throw new DatabaseException(e); } } @@ -500,64 +571,52 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService Preconditions.checkNotNull(conn, "conn"); log.debug("Starting INSERT/UPDATE query execution"); + final String queryString = query(); + + log.debug("Preparing statement for {}", queryString); + final PreparedStatement st = conn.prepareStatement(queryString, + Statement.RETURN_GENERATED_KEYS); try { - conn.setAutoCommit(false); + int rows = 0; + while (iterator.hasNext()) { + final T object = iterator.next(); - final String queryString = query(); + log.debug("Parametizing statement {} with {}", st, object); + this.parametize(st, object); - log.debug("Preparing statement for {}", queryString); - final PreparedStatement st = conn.prepareStatement(queryString, - Statement.RETURN_GENERATED_KEYS); - try { - int rows = 0; - while (iterator.hasNext()) { - final T object = iterator.next(); + log.debug("Sending query to database for {}", object); + rows += st.executeUpdate(); + log.debug("Query inserted or updated {} rows for {}", rows, + object); - log.debug("Parametizing statement {} with {}", st, - object); - this.parametize(st, object); + // update object desire --it has been realized + if (object instanceof Model && rows > 0) { + log.debug("Updating Model ObjectDesire to NONE"); + ((Model) object).setObjectDesire(ObjectDesire.NONE); - log.debug("Sending query to database for {}", object); - rows += st.executeUpdate(); - log.debug("Query inserted or updated {} rows for {}", - rows, object); - - // update object desire --it has been realized - if (object instanceof Model && rows > 0) { - log.debug("Updating Model ObjectDesire to NONE"); - ((Model) object) - .setObjectDesire(ObjectDesire.NONE); - - final Mapper> mapper = keyMapper(); - if (mapper == null) - continue; - final ResultSet rs = st.getGeneratedKeys(); - try { - log.debug( - "Mapping generated keys with {} using {}", - mapper, rs); - while (rs.next()) { - final ID generatedID = mapper.map(rs); - log.debug("Generated ID for {} is {}", - object, generatedID); - ((Model>) object).setID(generatedID); - mapper.map(rs); - } - } finally { - rs.close(); + final Mapper> mapper = keyMapper(); + if (mapper == null) + continue; + final ResultSet rs = st.getGeneratedKeys(); + try { + log.debug( + "Mapping generated keys with {} using {}", + mapper, rs); + while (rs.next()) { + final ID generatedID = mapper.map(rs); + log.debug("Generated ID for {} is {}", object, + generatedID); + ((Model>) object).setID(generatedID); + mapper.map(rs); } + } finally { + rs.close(); } } - conn.commit(); - return rows; - } finally { - st.close(); } - } catch (SQLException e) { - conn.rollback(); - throw e; + return rows; } finally { - conn.setAutoCommit(true); + st.close(); } } @@ -650,43 +709,32 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService Preconditions.checkNotNull(conn, "conn"); log.debug("Starting DELETE query execution"); + final String queryString = query(); + + log.debug("Preparing statement for {}", queryString); + final PreparedStatement st = conn.prepareStatement(queryString); + try { - conn.setAutoCommit(false); + int rows = 0; + while (iterator.hasNext()) { + final T object = iterator.next(); - final String queryString = query(); + log.debug("Parametizing statement {} with {}", st, object); + this.parametize(st, object); - log.debug("Preparing statement for {}", queryString); - final PreparedStatement st = conn.prepareStatement(queryString); + log.debug("Sending query to database for {}", object); + rows = st.executeUpdate(); + log.debug("Query deleted {} rows for {}", rows, object); - try { - int rows = 0; - while (iterator.hasNext()) { - final T object = iterator.next(); - - log.debug("Parametizing statement {} with {}", st, - object); - this.parametize(st, object); - - log.debug("Sending query to database for {}", object); - rows = st.executeUpdate(); - log.debug("Query deleted {} rows for {}", rows, object); - - dispose(object); - if (object instanceof Model) { - database.removeCache(((Model) object) - .getObjectDesire()); - } + dispose(object); + if (object instanceof Model) { + database.removeCache(((Model) object) + .getObjectDesire()); } - conn.commit(); - return rows; - } finally { - st.close(); } - } catch (SQLException e) { - conn.rollback(); - throw e; + return rows; } finally { - conn.setAutoCommit(true); + st.close(); } } diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractOrientDatabaseService.java b/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractOrientDatabaseService.java index f605f99b7..4a0f32fc3 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractOrientDatabaseService.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractOrientDatabaseService.java @@ -39,6 +39,8 @@ import com.l2jserver.service.cache.CacheService; import com.l2jserver.service.configuration.ConfigurationService; import com.l2jserver.service.configuration.ProxyConfigurationService.ConfigurationPropertyKey; import com.l2jserver.service.configuration.XMLConfigurationService.ConfigurationXPath; +import com.l2jserver.service.core.threading.AbstractTask; +import com.l2jserver.service.core.threading.AsyncFuture; import com.l2jserver.service.core.threading.ScheduledAsyncFuture; import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.util.ArrayIterator; @@ -221,6 +223,22 @@ public abstract class AbstractOrientDatabaseService extends AbstractService }); } + @Override + public int transaction(TransactionExecutor executor) { + return executor.perform(); + } + + @Override + public AsyncFuture transactionAsync( + final TransactionExecutor executor) { + return threadService.async(new AbstractTask() { + @Override + public Integer call() throws Exception { + return transaction(executor); + } + }); + } + /** * Executes an query in the database. * diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/database/DataAccessObject.java b/l2jserver2-common/src/main/java/com/l2jserver/service/database/DataAccessObject.java index e85bd1b8d..088385891 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/service/database/DataAccessObject.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/database/DataAccessObject.java @@ -89,13 +89,8 @@ public interface DataAccessObject, I extends ID> extends int save(O object); /** - * Save several instances to the database. This method will only save if the - * object has changed. - *

- * Note that differently from {@link #insertObjects(Model...)}, - * {@link #updateObjects(Model...)} and {@link #deleteObjects(Model...)}, - * this method does not uses an transaction and could have a bigger - * performance hit. + * Save several instances to the database using a transaction (if possible). + * This method will only save if the object has changed. * * @param objects * the objects @@ -104,13 +99,8 @@ public interface DataAccessObject, I extends ID> extends int saveObjects(@SuppressWarnings("unchecked") O... objects); /** - * Asynchronously save several instances to the database. This method will - * only save if the object has changed. - *

- * Note that differently from {@link #insertObjects(Model...)}, - * {@link #updateObjects(Model...)} and {@link #deleteObjects(Model...)}, - * this method does not uses an transaction and could have a bigger - * performance hit. + * Asynchronously save several instances to the database using a transaction + * (if possible). This method will only save if the object has changed. * * @param objects * the objects diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/database/DatabaseException.java b/l2jserver2-common/src/main/java/com/l2jserver/service/database/DatabaseException.java new file mode 100644 index 000000000..68f85cb85 --- /dev/null +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/database/DatabaseException.java @@ -0,0 +1,53 @@ +/* + * This file is part of l2jserver2 . + * + * l2jserver2 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. + * + * l2jserver2 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 l2jserver2. If not, see . + */ +package com.l2jserver.service.database; + +/** + * @author Rogiel + */ +public class DatabaseException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public DatabaseException() { + super(); + } + + /** + * @param message + * the message + * @param cause + * the root cause + */ + public DatabaseException(String message, Throwable cause) { + } + + /** + * @param message + * the message + */ + public DatabaseException(String message) { + super(message); + } + + /** + * @param cause + * the root cause + */ + public DatabaseException(Throwable cause) { + super(cause); + } +} diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/database/DatabaseService.java b/l2jserver2-common/src/main/java/com/l2jserver/service/database/DatabaseService.java index 685c2c88a..6efcd4008 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/service/database/DatabaseService.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/database/DatabaseService.java @@ -20,6 +20,7 @@ import com.l2jserver.service.Service; import com.l2jserver.service.ServiceConfiguration; import com.l2jserver.service.configuration.Configuration; import com.l2jserver.service.configuration.ProxyConfigurationService.ConfigurationName; +import com.l2jserver.service.core.threading.AsyncFuture; /** * This service provides access to an database implementation. Each @@ -46,4 +47,57 @@ public interface DatabaseService extends Service { @ConfigurationName("database") public interface DatabaseConfiguration extends ServiceConfiguration { } + + /** + * Executes several operations inside a single database transaction. + *

+ * Queries inside a transaction applies to an all-or-none model. If + * any of the queries executed fails, none of them will be persisted to the + * database and no changes will be performed. Transactions are useful in + * maintaining data consistency. + *

+ * Important: You should never call any async + * {@link DataAccessObject} within a transaction. Doing so, will make it be + * executed in another transaction and might even cause data + * corruption due to queries being executed in different transactions. + *

+ * If you wish to execute an transaction asynchronously, see + * {@link #transactionAsync(TransactionExecutor)}. + * + * @param executor + * the executor implementation (normally an anonymous class) + * @return the number of affected rows + * @see DatabaseService#transactionAsync(TransactionExecutor) + */ + int transaction(TransactionExecutor executor); + + /** + * Asynchronously executes several operations inside a single database + * transaction. + *

+ * Queries inside a transaction applies to anall-or-none model. If + * any of the queries executed fails, none of them will be persisted to the + * database and no changes will be performed. Transactions are useful in + * maintaining data consistency. + * + * @param executor + * the executor implementation (normally an anonymous class) + * @return the number of affected rows + */ + AsyncFuture transactionAsync(TransactionExecutor executor); + + /** + * This class executes DAO operations inside an transaction. It is + * recommended to implement it in an anonymous class. + * + * @author Rogiel + */ + public interface TransactionExecutor { + /** + * Perform operations inside the transaction. + * + * @return the number of affected rows + */ + int perform(); + } } diff --git a/l2jserver2-gameserver/data/template/item.xsd b/l2jserver2-gameserver/data/template/item.xsd index 85a53bf26..864795339 100644 --- a/l2jserver2-gameserver/data/template/item.xsd +++ b/l2jserver2-gameserver/data/template/item.xsd @@ -9,22 +9,71 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/l2jserver2-gameserver/data/template/item/1-ShortSword.xml b/l2jserver2-gameserver/data/template/item/1-ShortSword.xml index 4f5f20638..8ab1630b0 100644 --- a/l2jserver2-gameserver/data/template/item/1-ShortSword.xml +++ b/l2jserver2-gameserver/data/template/item/1-ShortSword.xml @@ -1,32 +1,27 @@ - - - - - - - - - - - - - - - - - - - - - - - 1 - 1 - icon.etc_adena_i00 - GOLD - - + xmlns:template="http://schemas.l2jserver2.com/item" id="1" + icon="icon.weapon_small_sword_i00" name="Short Sword"> + + + + + + + + + 8 + 10 + + + 6 + + + 8 + + + 379 + + + \ No newline at end of file diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/AttackServiceImpl.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/AttackServiceImpl.java index 3da47fd02..90ca5db3e 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/AttackServiceImpl.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/AttackServiceImpl.java @@ -33,6 +33,7 @@ import com.l2jserver.model.world.NPC; import com.l2jserver.model.world.actor.event.ActorAttackHitEvent; import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService.Depends; +import com.l2jserver.service.core.threading.AbstractTask; import com.l2jserver.service.core.threading.AsyncFuture; import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.service.game.npc.NPCService; @@ -99,7 +100,7 @@ public class AttackServiceImpl extends AbstractService implements AttackService * * @author Rogiel */ - private class AttackCallable implements Callable { + private class AttackCallable extends AbstractTask { /** * The attacker */ diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/npc/NPCServiceImpl.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/npc/NPCServiceImpl.java index 30796fa1d..113854a08 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/npc/NPCServiceImpl.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/npc/NPCServiceImpl.java @@ -19,7 +19,6 @@ package com.l2jserver.service.game.npc; import java.util.Arrays; import java.util.Collection; import java.util.Map; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -42,6 +41,7 @@ import com.l2jserver.model.world.npc.event.NPCDieEvent; import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService.Depends; import com.l2jserver.service.ServiceStartException; +import com.l2jserver.service.core.threading.AbstractTask; import com.l2jserver.service.core.threading.AsyncFuture; import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.service.database.DatabaseService; @@ -244,7 +244,7 @@ public class NPCServiceImpl extends AbstractService implements NPCService { final double seconds = distance / npc.getTemplate().getRunSpeed(); // TODO this is an dirty implementation! return threadService.async((int) (seconds * 1000), - TimeUnit.MILLISECONDS, new Callable() { + TimeUnit.MILLISECONDS, new AbstractTask() { @Override public Boolean call() throws Exception { npc.setState(null); diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/spawn/SpawnServiceImpl.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/spawn/SpawnServiceImpl.java index 608136cda..ff0e894dc 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/spawn/SpawnServiceImpl.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/spawn/SpawnServiceImpl.java @@ -16,7 +16,6 @@ */ package com.l2jserver.service.game.spawn; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -42,6 +41,7 @@ import com.l2jserver.model.world.npc.event.NPCUnspawnEvent; import com.l2jserver.model.world.player.event.PlayerTeleportedEvent; import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService.Depends; +import com.l2jserver.service.core.threading.AbstractTask; import com.l2jserver.service.core.threading.AsyncFuture; import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.service.game.world.WorldService; @@ -158,7 +158,7 @@ public class SpawnServiceImpl extends AbstractService implements SpawnService { log.debug("Scheduling spawn of {} at {} in {}ms", new Object[] { object, point, unit.toMillis(time) }); - return threadService.async(time, unit, new Callable() { + return threadService.async(time, unit, new AbstractTask() { @Override public T call() throws Exception { spawn(object, point); @@ -208,7 +208,7 @@ public class SpawnServiceImpl extends AbstractService implements SpawnService { log.debug("Scheduling unspawn of {} in {}ms", object, unit.toMillis(time)); - return threadService.async(time, unit, new Callable() { + return threadService.async(time, unit, new AbstractTask() { @Override public T call() throws Exception { unspawn(object);