1
0
mirror of https://github.com/Rogiel/l2jserver2 synced 2025-12-05 23:22:47 +00:00

Implements task oriented ThreadService

This commit is contained in:
2011-12-17 18:25:49 -02:00
parent ccbc29d330
commit cea66c9363
16 changed files with 441 additions and 179 deletions

View File

@@ -0,0 +1,25 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.core.threading;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
* @param <T>
* the task return type
*/
public abstract class AbstractTask<T> implements Task<T> {
}

View File

@@ -0,0 +1,27 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.core.threading;
import java.util.concurrent.Callable;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
* @param <T>
* the task return type
*/
public interface Task<T> extends Callable<T> {
}

View File

@@ -16,7 +16,6 @@
*/ */
package com.l2jserver.service.core.threading; package com.l2jserver.service.core.threading;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@@ -31,18 +30,18 @@ public interface ThreadPool {
* *
* @param <T> * @param <T>
* the task return type * the task return type
* @param callable * @param task
* the callable instance * the callable instance
* @return the {@link AsyncFuture} notified once the task has completed * @return the {@link AsyncFuture} notified once the task has completed
*/ */
<T> AsyncFuture<T> async(Callable<T> callable); <T> AsyncFuture<T> async(Task<T> task);
/** /**
* Executes an asynchronous tasks at an scheduled time. * Executes an asynchronous tasks at an scheduled time.
* *
* @param <T> * @param <T>
* the task return type * the task return type
* @param callable * @param task
* the callable instance * the callable instance
* @param delay * @param delay
* the initial delay to wait before the task is executed * the initial delay to wait before the task is executed
@@ -50,7 +49,7 @@ public interface ThreadPool {
* the time unit of delay * the time unit of delay
* @return the {@link AsyncFuture} notified once the task has completed * @return the {@link AsyncFuture} notified once the task has completed
*/ */
<T> AsyncFuture<T> async(long delay, TimeUnit unit, Callable<T> callable); <T> AsyncFuture<T> async(long delay, TimeUnit unit, Task<T> task);
/** /**
* Executes an asynchronous tasks at an scheduled time. * Executes an asynchronous tasks at an scheduled time.
@@ -73,5 +72,4 @@ public interface ThreadPool {
* execute tasks. * execute tasks.
*/ */
void dispose(); void dispose();
} }

View File

@@ -16,7 +16,6 @@
*/ */
package com.l2jserver.service.core.threading; package com.l2jserver.service.core.threading;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import com.l2jserver.service.Service; import com.l2jserver.service.Service;
@@ -39,7 +38,7 @@ public interface ThreadService extends Service {
* the callable instance * the callable instance
* @return the {@link AsyncFuture} notified once the task has completed * @return the {@link AsyncFuture} notified once the task has completed
*/ */
<T> AsyncFuture<T> async(Callable<T> callable); <T> AsyncFuture<T> async(Task<T> callable);
/** /**
* Executes an asynchronous tasks at an scheduled time. <b>Please note that * Executes an asynchronous tasks at an scheduled time. <b>Please note that
@@ -58,7 +57,7 @@ public interface ThreadService extends Service {
* the time unit of delay * the time unit of delay
* @return the {@link AsyncFuture} notified once the task has completed * @return the {@link AsyncFuture} notified once the task has completed
*/ */
<T> AsyncFuture<T> async(long delay, TimeUnit unit, Callable<T> callable); <T> AsyncFuture<T> async(long delay, TimeUnit unit, Task<T> callable);
/** /**
* Executes an asynchronous tasks at an scheduled time. <b>Please note that * Executes an asynchronous tasks at an scheduled time. <b>Please note that

View File

@@ -19,7 +19,6 @@ package com.l2jserver.service.core.threading;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.Delayed; import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -79,7 +78,7 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService
} }
@Override @Override
public <T> AsyncFuture<T> async(Callable<T> callable) { public <T> AsyncFuture<T> async(Task<T> callable) {
Preconditions.checkNotNull(callable, "callable"); Preconditions.checkNotNull(callable, "callable");
log.debug("Scheduling async task: {}", callable); log.debug("Scheduling async task: {}", callable);
@@ -87,8 +86,7 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService
} }
@Override @Override
public <T> AsyncFuture<T> async(long delay, TimeUnit unit, public <T> AsyncFuture<T> async(long delay, TimeUnit unit, Task<T> callable) {
Callable<T> callable) {
Preconditions.checkArgument(delay >= 0, "delay < 0"); Preconditions.checkArgument(delay >= 0, "delay < 0");
Preconditions.checkNotNull(unit, "unit"); Preconditions.checkNotNull(unit, "unit");
Preconditions.checkNotNull(callable, "callable"); Preconditions.checkNotNull(callable, "callable");
@@ -352,14 +350,14 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService
} }
@Override @Override
public <T> AsyncFuture<T> async(Callable<T> callable) { public <T> AsyncFuture<T> async(Task<T> callable) {
log.debug("Task {} submited to {}", callable, name); log.debug("Task {} submited to {}", callable, name);
return new AsyncFutureImpl<T>(executor.submit(callable)); return new AsyncFutureImpl<T>(executor.submit(callable));
} }
@Override @Override
public <T> AsyncFuture<T> async(long delay, TimeUnit unit, public <T> AsyncFuture<T> async(long delay, TimeUnit unit,
Callable<T> callable) { Task<T> callable) {
if (log.isDebugEnabled()) if (log.isDebugEnabled())
log.debug("Task {} scheduled in {} {} to {}", new Object[] { log.debug("Task {} scheduled in {} {} to {}", new Object[] {
callable, delay, unit, name }); callable, delay, unit, name });
@@ -371,8 +369,9 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService
public ScheduledAsyncFuture async(long delay, TimeUnit unit, public ScheduledAsyncFuture async(long delay, TimeUnit unit,
long repeat, Runnable task) { long repeat, Runnable task) {
if (log.isDebugEnabled()) if (log.isDebugEnabled())
log.debug("Task {} scheduled every {} {} to {}", new Object[] { log.debug(
task, repeat, unit, name }); "Task {} scheduled every {} {} to {}, starting in {}",
new Object[] { task, repeat, unit, name, delay });
return new ScheduledAsyncFutureImpl(executor.scheduleAtFixedRate( return new ScheduledAsyncFutureImpl(executor.scheduleAtFixedRate(
task, delay, repeat, unit)); task, delay, repeat, unit));
} }

View File

@@ -17,13 +17,14 @@
package com.l2jserver.service.database; package com.l2jserver.service.database;
import java.util.Iterator; import java.util.Iterator;
import java.util.concurrent.Callable;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.l2jserver.model.Model; import com.l2jserver.model.Model;
import com.l2jserver.model.id.ID; 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.AsyncFuture;
import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.service.core.threading.ThreadService;
import com.l2jserver.service.database.DatabaseService.TransactionExecutor;
/** /**
* Abstract DAO implementations. Store an instance of {@link DatabaseService}. * Abstract DAO implementations. Store an instance of {@link DatabaseService}.
@@ -61,7 +62,7 @@ public abstract class AbstractDAO<T extends Model<?>, I extends ID<?>>
@Override @Override
public AsyncFuture<T> selectAsync(final I id) { public AsyncFuture<T> selectAsync(final I id) {
return threadService.async(new Callable<T>() { return threadService.async(new AbstractTask<T>() {
@Override @Override
public T call() throws Exception { public T call() throws Exception {
return select(id); return select(id);
@@ -92,18 +93,23 @@ public abstract class AbstractDAO<T extends Model<?>, I extends ID<?>>
@Override @Override
@SafeVarargs @SafeVarargs
public final int saveObjects(T... objects) { public final int saveObjects(final T... objects) {
int rows = 0; return database.transaction(new TransactionExecutor() {
for (final T object : objects) { @Override
rows += save(object); public int perform() {
} int rows = 0;
return rows; for (final T object : objects) {
rows += save(object);
}
return rows;
}
});
} }
@Override @Override
@SafeVarargs @SafeVarargs
public final AsyncFuture<Integer> saveObjectsAsync(final T... objects) { public final AsyncFuture<Integer> saveObjectsAsync(final T... objects) {
return threadService.async(new Callable<Integer>() { return threadService.async(new AbstractTask<Integer>() {
@Override @Override
public Integer call() throws Exception { public Integer call() throws Exception {
return saveObjects(objects); return saveObjects(objects);
@@ -119,7 +125,7 @@ public abstract class AbstractDAO<T extends Model<?>, I extends ID<?>>
@Override @Override
@SafeVarargs @SafeVarargs
public final AsyncFuture<Integer> insertObjectsAsync(final T... objects) { public final AsyncFuture<Integer> insertObjectsAsync(final T... objects) {
return threadService.async(new Callable<Integer>() { return threadService.async(new AbstractTask<Integer>() {
@Override @Override
public Integer call() throws Exception { public Integer call() throws Exception {
return insertObjects(objects); return insertObjects(objects);
@@ -135,7 +141,7 @@ public abstract class AbstractDAO<T extends Model<?>, I extends ID<?>>
@Override @Override
@SafeVarargs @SafeVarargs
public final AsyncFuture<Integer> updateObjectsAsync(final T... objects) { public final AsyncFuture<Integer> updateObjectsAsync(final T... objects) {
return threadService.async(new Callable<Integer>() { return threadService.async(new AbstractTask<Integer>() {
@Override @Override
public Integer call() throws Exception { public Integer call() throws Exception {
return updateObjects(objects); return updateObjects(objects);
@@ -151,7 +157,7 @@ public abstract class AbstractDAO<T extends Model<?>, I extends ID<?>>
@Override @Override
@SafeVarargs @SafeVarargs
public final AsyncFuture<Integer> deleteObjectsAsync(final T... objects) { public final AsyncFuture<Integer> deleteObjectsAsync(final T... objects) {
return threadService.async(new Callable<Integer>() { return threadService.async(new AbstractTask<Integer>() {
@Override @Override
public Integer call() throws Exception { public Integer call() throws Exception {
return deleteObjects(objects); return deleteObjects(objects);

View File

@@ -49,6 +49,8 @@ import com.l2jserver.service.cache.CacheService;
import com.l2jserver.service.configuration.ConfigurationService; import com.l2jserver.service.configuration.ConfigurationService;
import com.l2jserver.service.configuration.ProxyConfigurationService.ConfigurationPropertyKey; import com.l2jserver.service.configuration.ProxyConfigurationService.ConfigurationPropertyKey;
import com.l2jserver.service.configuration.XMLConfigurationService.ConfigurationXPath; 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.ScheduledAsyncFuture;
import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.service.core.threading.ThreadService;
import com.l2jserver.util.ArrayIterator; import com.l2jserver.util.ArrayIterator;
@@ -129,6 +131,11 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService
*/ */
private ScheduledAsyncFuture autoSaveFuture; private ScheduledAsyncFuture autoSaveFuture;
/**
* The connection used inside a transaction from multiple DAOs.
*/
private ThreadLocal<Connection> transactionalConnection = new ThreadLocal<>();
/** /**
* Configuration interface for {@link AbstractJDBCDatabaseService}. * 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<Integer> transactionAsync(
final TransactionExecutor executor) {
return threadService.async(new AbstractTask<Integer>() {
@Override
public Integer call() throws Exception {
return transaction(executor);
}
});
}
/** /**
* Executes an <tt>query</tt> in the database. * Executes an <tt>query</tt> in the database.
* *
@@ -333,23 +380,47 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService
* @param query * @param query
* the query * the query
* @return an instance of <tt>T</tt> * @return an instance of <tt>T</tt>
* @throws DatabaseException
* if any exception occur (can have nested {@link SQLException})
*/ */
public <T> T query(Query<T> query) { public <T> T query(Query<T> query) throws DatabaseException {
Preconditions.checkNotNull(query, "query"); Preconditions.checkNotNull(query, "query");
try { 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); log.debug("Executing query {} with {}", query, conn);
try { try {
return query.query(conn); if (!inTransaction) {
} catch (SQLException e) { conn.setAutoCommit(false);
log.error("Error executing query", e); }
return null; try {
return query.query(conn);
} finally {
if (!inTransaction) {
conn.commit();
}
}
} catch (Exception e) {
if (!inTransaction) {
conn.rollback();
}
throw e;
} finally { } finally {
conn.close(); if (!inTransaction) {
conn.setAutoCommit(true);
conn.close();
}
} }
} catch (SQLException e) { } catch (Throwable e) {
log.error("Could not open database connection", 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"); Preconditions.checkNotNull(conn, "conn");
log.debug("Starting INSERT/UPDATE query execution"); 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 { 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); log.debug("Sending query to database for {}", object);
final PreparedStatement st = conn.prepareStatement(queryString, rows += st.executeUpdate();
Statement.RETURN_GENERATED_KEYS); log.debug("Query inserted or updated {} rows for {}", rows,
try { object);
int rows = 0;
while (iterator.hasNext()) {
final T object = iterator.next();
log.debug("Parametizing statement {} with {}", st, // update object desire --it has been realized
object); if (object instanceof Model && rows > 0) {
this.parametize(st, object); log.debug("Updating Model ObjectDesire to NONE");
((Model<?>) object).setObjectDesire(ObjectDesire.NONE);
log.debug("Sending query to database for {}", object); final Mapper<? extends ID<?>> mapper = keyMapper();
rows += st.executeUpdate(); if (mapper == null)
log.debug("Query inserted or updated {} rows for {}", continue;
rows, object); final ResultSet rs = st.getGeneratedKeys();
try {
// update object desire --it has been realized log.debug(
if (object instanceof Model && rows > 0) { "Mapping generated keys with {} using {}",
log.debug("Updating Model ObjectDesire to NONE"); mapper, rs);
((Model<?>) object) while (rs.next()) {
.setObjectDesire(ObjectDesire.NONE); final ID<?> generatedID = mapper.map(rs);
log.debug("Generated ID for {} is {}", object,
final Mapper<? extends ID<?>> mapper = keyMapper(); generatedID);
if (mapper == null) ((Model<ID<?>>) object).setID(generatedID);
continue; mapper.map(rs);
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<ID<?>>) object).setID(generatedID);
mapper.map(rs);
}
} finally {
rs.close();
} }
} finally {
rs.close();
} }
} }
conn.commit();
return rows;
} finally {
st.close();
} }
} catch (SQLException e) { return rows;
conn.rollback();
throw e;
} finally { } finally {
conn.setAutoCommit(true); st.close();
} }
} }
@@ -650,43 +709,32 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService
Preconditions.checkNotNull(conn, "conn"); Preconditions.checkNotNull(conn, "conn");
log.debug("Starting DELETE query execution"); log.debug("Starting DELETE query execution");
final String queryString = query();
log.debug("Preparing statement for {}", queryString);
final PreparedStatement st = conn.prepareStatement(queryString);
try { 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); log.debug("Sending query to database for {}", object);
final PreparedStatement st = conn.prepareStatement(queryString); rows = st.executeUpdate();
log.debug("Query deleted {} rows for {}", rows, object);
try { dispose(object);
int rows = 0; if (object instanceof Model) {
while (iterator.hasNext()) { database.removeCache(((Model<?>) object)
final T object = iterator.next(); .getObjectDesire());
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());
}
} }
conn.commit();
return rows;
} finally {
st.close();
} }
} catch (SQLException e) { return rows;
conn.rollback();
throw e;
} finally { } finally {
conn.setAutoCommit(true); st.close();
} }
} }

View File

@@ -39,6 +39,8 @@ import com.l2jserver.service.cache.CacheService;
import com.l2jserver.service.configuration.ConfigurationService; import com.l2jserver.service.configuration.ConfigurationService;
import com.l2jserver.service.configuration.ProxyConfigurationService.ConfigurationPropertyKey; import com.l2jserver.service.configuration.ProxyConfigurationService.ConfigurationPropertyKey;
import com.l2jserver.service.configuration.XMLConfigurationService.ConfigurationXPath; 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.ScheduledAsyncFuture;
import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.service.core.threading.ThreadService;
import com.l2jserver.util.ArrayIterator; 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<Integer> transactionAsync(
final TransactionExecutor executor) {
return threadService.async(new AbstractTask<Integer>() {
@Override
public Integer call() throws Exception {
return transaction(executor);
}
});
}
/** /**
* Executes an <tt>query</tt> in the database. * Executes an <tt>query</tt> in the database.
* *

View File

@@ -89,13 +89,8 @@ public interface DataAccessObject<O extends Model<?>, I extends ID<?>> extends
int save(O object); int save(O object);
/** /**
* Save several instances to the database. This method will only save if the * Save several instances to the database using a transaction (if possible).
* object has changed. * This method will only save if the object has changed.
* <p>
* 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.
* *
* @param objects * @param objects
* the objects * the objects
@@ -104,13 +99,8 @@ public interface DataAccessObject<O extends Model<?>, I extends ID<?>> extends
int saveObjects(@SuppressWarnings("unchecked") O... objects); int saveObjects(@SuppressWarnings("unchecked") O... objects);
/** /**
* Asynchronously save several instances to the database. This method will * Asynchronously save several instances to the database using a transaction
* only save if the object has changed. * (if possible). This method will only save if the object has changed.
* <p>
* 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.
* *
* @param objects * @param objects
* the objects * the objects

View File

@@ -0,0 +1,53 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
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);
}
}

View File

@@ -20,6 +20,7 @@ import com.l2jserver.service.Service;
import com.l2jserver.service.ServiceConfiguration; import com.l2jserver.service.ServiceConfiguration;
import com.l2jserver.service.configuration.Configuration; import com.l2jserver.service.configuration.Configuration;
import com.l2jserver.service.configuration.ProxyConfigurationService.ConfigurationName; import com.l2jserver.service.configuration.ProxyConfigurationService.ConfigurationName;
import com.l2jserver.service.core.threading.AsyncFuture;
/** /**
* This service provides access to an database implementation. Each * This service provides access to an database implementation. Each
@@ -46,4 +47,57 @@ public interface DatabaseService extends Service {
@ConfigurationName("database") @ConfigurationName("database")
public interface DatabaseConfiguration extends ServiceConfiguration { public interface DatabaseConfiguration extends ServiceConfiguration {
} }
/**
* Executes several operations inside a single database transaction.
* <p>
* Queries inside a transaction applies to an <i>all-or-none</i> 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.
* <p>
* <b>Important</b>: You should <b>never</b> call any async
* {@link DataAccessObject} within a transaction. Doing so, will make it be
* executed in <b>another transaction</b> and might even cause data
* corruption due to queries being executed in different transactions.
* <p>
* 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.
* <p>
* Queries inside a transaction applies to an<i>all-or-none</i> 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<Integer> transactionAsync(TransactionExecutor executor);
/**
* This class executes DAO operations inside an transaction. It is
* recommended to implement it in an anonymous class.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface TransactionExecutor {
/**
* Perform operations inside the transaction.
*
* @return the number of affected rows
*/
int perform();
}
} }

View File

@@ -9,22 +9,71 @@
<xs:complexType name="ItemType"> <xs:complexType name="ItemType">
<xs:complexContent> <xs:complexContent>
<xs:extension base="AbstractTemplateType"> <xs:extension base="AbstractTemplateType">
<xs:sequence> <xs:all>
<xs:element name="weight" type="xs:int" /> <xs:element name="attributes">
<xs:element name="price" type="xs:int" /> <xs:complexType>
<xs:element name="icon" type="xs:string" minOccurs="0" /> <xs:sequence>
<xs:element name="effect" type="ItemEffectsType" <xs:element name="cost">
minOccurs="0" /> <xs:complexType>
<xs:element name="stats" type="ItemStatsType" minOccurs="0" /> <xs:attribute name="adena" />
<xs:element name="material" type="ItemMaterialType" /> </xs:complexType>
<xs:element name="itemType" type="ItemEnumType" </xs:element>
minOccurs="0" /> <xs:element name="equipment">
<xs:element name="weaponType" type="WeaponType" <xs:complexType>
minOccurs="0" /> <xs:attribute name="part" />
<xs:element name="armorType" type="ArmorType" minOccurs="0" /> </xs:complexType>
</xs:sequence> </xs:element>
</xs:sequence>
<xs:attribute name="weigth" />
<xs:attribute name="type" />
<xs:attribute name="material" />
</xs:complexType>
</xs:element>
<xs:element name="controller">
<xs:complexType>
<xs:attribute name="defaultAction" />
</xs:complexType>
</xs:element>
<xs:element name="effect">
<xs:complexType>
<xs:attribute name="type" />
</xs:complexType>
</xs:element>
<xs:choice>
<xs:element name="weapon">
<xs:complexType>
<xs:attribute name="part" />
</xs:complexType>
</xs:element>
<xs:element name="armor">
<xs:complexType>
<xs:sequence>
<xs:element name="cost">
<xs:complexType>
<xs:attribute name="adena" />
</xs:complexType>
</xs:element>
<xs:element name="equipment">
<xs:complexType>
<xs:attribute name="part" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="weigth" />
<xs:attribute name="type" />
<xs:attribute name="material" />
</xs:complexType>
</xs:element>
</xs:choice>
</xs:all>
<xs:attribute name="id" type="xs:int" use="required" /> <xs:attribute name="id" type="xs:int" use="required" />
<xs:attribute name="name" type="xs:string" use="required" /> <xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="icon" type="xs:string"></xs:attribute>
</xs:extension> </xs:extension>
</xs:complexContent> </xs:complexContent>
</xs:complexType> </xs:complexType>

View File

@@ -1,32 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <template:item xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" -->
<!-- xsi:schemaLocation="http://schemas.l2jserver2.com/item ../item.xsd" -->
<!-- xmlns:template="http://schemas.l2jserver2.com/item" id="1" icon="icon.etc_adena_i00"> -->
<!-- <name>Short Sword</name> -->
<!-- <material>STEEL</material> -->
<!-- <effect type="IMMEDIATE" /> -->
<!-- <price>1</price> -->
<!-- <stats> -->
<!-- <physicalDamage> -->
<!-- <set order="128">510</set> -->
<!-- </physicalDamage> -->
<!-- <magicalDamage> -->
<!-- <set order="128">508</set> -->
<!-- </magicalDamage> -->
<!-- <criticalChance> -->
<!-- <set order="128">8</set> -->
<!-- </criticalChance> -->
<!-- <physicalAttackSpeed> -->
<!-- <set order="128">379</set> -->
<!-- </physicalAttackSpeed> -->
<!-- </stats> -->
<!-- </template:item> -->
<template:item xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <template:item xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.l2jserver2.com/item ../item.xsd" xsi:schemaLocation="http://schemas.l2jserver2.com/item ../item.xsd"
xmlns:template="http://schemas.l2jserver2.com/item" id="1" name="Short sword"> xmlns:template="http://schemas.l2jserver2.com/item" id="1"
<weight>1</weight> icon="icon.weapon_small_sword_i00" name="Short Sword">
<price>1</price> <attributes weigth="1600" type="SWORD" material="STEEL">
<icon>icon.etc_adena_i00</icon> <cost adena="590" />
<material>GOLD</material> <equipment part="RIGHT_HAND" />
<!-- <effect type="IMMEDIATE" /> --> </attributes>
</template:item> <controller defaultAction="EQUIP" />
<effect type="IMMEDIATE" />
<weapon part="RIGHT_HAND">
<physicalDamage>
<set order="128">8</set>
<random order="127">10</random>
</physicalDamage>
<magicalDamage>
<set order="128">6</set>
</magicalDamage>
<criticalChance>
<set order="128">8</set>
</criticalChance>
<physicalAttackSpeed>
<set order="128">379</set>
</physicalAttackSpeed>
</weapon>
</template:item>

View File

@@ -33,6 +33,7 @@ import com.l2jserver.model.world.NPC;
import com.l2jserver.model.world.actor.event.ActorAttackHitEvent; import com.l2jserver.model.world.actor.event.ActorAttackHitEvent;
import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService;
import com.l2jserver.service.AbstractService.Depends; 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.AsyncFuture;
import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.service.core.threading.ThreadService;
import com.l2jserver.service.game.npc.NPCService; import com.l2jserver.service.game.npc.NPCService;
@@ -99,7 +100,7 @@ public class AttackServiceImpl extends AbstractService implements AttackService
* *
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
*/ */
private class AttackCallable implements Callable<AttackHit> { private class AttackCallable extends AbstractTask<AttackHit> {
/** /**
* The attacker * The attacker
*/ */

View File

@@ -19,7 +19,6 @@ package com.l2jserver.service.game.npc;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.slf4j.Logger; 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;
import com.l2jserver.service.AbstractService.Depends; import com.l2jserver.service.AbstractService.Depends;
import com.l2jserver.service.ServiceStartException; 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.AsyncFuture;
import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.service.core.threading.ThreadService;
import com.l2jserver.service.database.DatabaseService; import com.l2jserver.service.database.DatabaseService;
@@ -244,7 +244,7 @@ public class NPCServiceImpl extends AbstractService implements NPCService {
final double seconds = distance / npc.getTemplate().getRunSpeed(); final double seconds = distance / npc.getTemplate().getRunSpeed();
// TODO this is an dirty implementation! // TODO this is an dirty implementation!
return threadService.async((int) (seconds * 1000), return threadService.async((int) (seconds * 1000),
TimeUnit.MILLISECONDS, new Callable<Boolean>() { TimeUnit.MILLISECONDS, new AbstractTask<Boolean>() {
@Override @Override
public Boolean call() throws Exception { public Boolean call() throws Exception {
npc.setState(null); npc.setState(null);

View File

@@ -16,7 +16,6 @@
*/ */
package com.l2jserver.service.game.spawn; package com.l2jserver.service.game.spawn;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.slf4j.Logger; 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.model.world.player.event.PlayerTeleportedEvent;
import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService;
import com.l2jserver.service.AbstractService.Depends; 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.AsyncFuture;
import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.service.core.threading.ThreadService;
import com.l2jserver.service.game.world.WorldService; 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[] { log.debug("Scheduling spawn of {} at {} in {}ms", new Object[] {
object, point, unit.toMillis(time) }); object, point, unit.toMillis(time) });
return threadService.async(time, unit, new Callable<T>() { return threadService.async(time, unit, new AbstractTask<T>() {
@Override @Override
public T call() throws Exception { public T call() throws Exception {
spawn(object, point); spawn(object, point);
@@ -208,7 +208,7 @@ public class SpawnServiceImpl extends AbstractService implements SpawnService {
log.debug("Scheduling unspawn of {} in {}ms", object, log.debug("Scheduling unspawn of {} in {}ms", object,
unit.toMillis(time)); unit.toMillis(time));
return threadService.async(time, unit, new Callable<T>() { return threadService.async(time, unit, new AbstractTask<T>() {
@Override @Override
public T call() throws Exception { public T call() throws Exception {
unspawn(object); unspawn(object);