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

Modularizes the Maven project

This commit modularizes the maven project into several modules:
 - l2jserver2-common: common sources for both login and gameserver
 - l2jserver2-gameserver: the game server
 - l2jserver2-loginserver: the login server
 - l2jserver2-tools: refactored src/tools/java soure folder
This commit is contained in:
2011-10-05 17:32:04 -03:00
parent c4052ccb3b
commit 22c136ab17
18930 changed files with 4292 additions and 231 deletions

147
l2jserver2-common/pom.xml Normal file
View File

@@ -0,0 +1,147 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>l2jserver2</artifactId>
<groupId>com.l2jserver</groupId>
<version>2.0.0</version>
<relativePath>..</relativePath>
</parent>
<artifactId>l2jserver2-common</artifactId>
<dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<type>jar</type>
<scope>test</scope>
</dependency>
<!-- netty -->
<dependency>
<groupId>org.jboss.netty</groupId>
<artifactId>netty</artifactId>
<version>3.2.4.Final</version>
<scope>runtime</scope>
</dependency>
<!-- google guice -->
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-assistedinject</artifactId>
<version>3.0</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-multibindings</artifactId>
<version>3.0</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<!-- logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.6.1</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<!-- database -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.16</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.155</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<!-- cache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.4.2</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<!-- html parser/generator -->
<dependency>
<groupId>org.htmlparser</groupId>
<artifactId>htmlparser</artifactId>
<version>2.1</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<!-- utils -->
<dependency>
<groupId>javolution</groupId>
<artifactId>javolution</artifactId>
<version>5.5.1</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>10.0</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0.1</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.5.6</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math</artifactId>
<version>2.2</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,110 @@
/*
* 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.model;
import com.google.common.base.Preconditions;
import com.l2jserver.model.id.ID;
/**
* Simple model interface implementing {@link ID} related methods
*
* @param <T>
* the ID type used to represent this {@link Model}
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public abstract class AbstractModel<T extends ID<?>> implements Model<T> {
/**
* The object id
*/
protected T id;
/**
* The database object state
*/
protected transient ObjectDesire desire = ObjectDesire.INSERT;
@Override
public T getID() {
return id;
}
@Override
public void setID(T ID) {
Preconditions.checkState(id == null, "ID is already set");
desireInsert();
this.id = ID;
}
@Override
public ObjectDesire getObjectDesire() {
return desire;
}
@Override
public void setObjectDesire(ObjectDesire desire) {
if (desire == null)
desire = ObjectDesire.NONE;
this.desire = desire;
}
/**
* Set this object desire to {@link ObjectDesire#UPDATE}. If the desire is
* {@link ObjectDesire#INSERT} or {@link ObjectDesire#DELETE} the desire
* will not be changed.
*/
@SuppressWarnings("javadoc")
protected void desireUpdate() {
if (this.desire != ObjectDesire.INSERT
&& this.desire != ObjectDesire.DELETE)
this.desire = ObjectDesire.UPDATE;
}
/**
* Set this object desire to {@link ObjectDesire#INSERT}. If the desire is
* {@link ObjectDesire#DELETE} the desire will not be changed.
*/
@SuppressWarnings("javadoc")
protected void desireInsert() {
if (this.desire != ObjectDesire.DELETE)
this.desire = ObjectDesire.INSERT;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AbstractModel<?> other = (AbstractModel<?>) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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.model;
import com.l2jserver.model.id.ID;
import com.l2jserver.service.database.DatabaseService;
/**
* Base model. Each object model must implement this interface to be able to be
* inserted into the database.
*
* @param <T>
* the {@link ID} type used to represent this {@link Model}
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface Model<T extends ID<?>> {
/**
* @return the object ID
*/
T getID();
/**
* Please note that the ID can only be set one time. Once it has been set,
* it cannot be changed and a {@link IllegalStateException} will be thrown
* if trying to change the ID.
*
* @param ID
* the object ID to set
* @throws IllegalStateException
* if the ID was already set
*/
void setID(T ID) throws IllegalStateException;
/**
* Each object has an desire. Desires express what the
* {@link DatabaseService} should do with the object. The service
* automatically keep tracks of every database object (and release them when
* they are garbage collected).
*
* @return the database object desire
*/
ObjectDesire getObjectDesire();
/**
* Each object has an desire. Desires express what the
* {@link DatabaseService} should do with the object. The service
* automatically keep tracks of every database object (and release them when
* they are garbage collected).
*
* @param desire
* the database object desire to set
*/
void setObjectDesire(ObjectDesire desire);
/**
* Indicated what the object wants to do in the database. It indicates
* whether the object should be inserted, updated or deleted from the
* database.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public enum ObjectDesire {
/**
* Don't do anything
*/
NONE,
/**
* Insert a new object into database.
* <p>
* If the primary key is auto generated by the database a clone of this
* object will be created.<br>
* If the primary key is <b>not</b> auto generated by the database, an
* database exception will occur.
*/
INSERT,
/**
* Updates the object in the database.
* <p>
* If the object is not in the database nothing will happen.
*/
UPDATE,
/**
* Deletes the object from the database.
* <p>
* If tge object is not in the database nothing will happen.
*/
DELETE;
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.model.id;
import com.l2jserver.model.Model;
/**
* This is an abstract ID for most model objects.
*
* @param <T>
* the raw id type
* @param <O>
* the model this {@link ID} provides
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public abstract class AbstractModelID<T, O extends Model<? extends ID<T>>>
extends ID<T> {
/**
* @param id
* the id
*/
protected AbstractModelID(T id) {
super(id);
}
/**
* @return the {@link Model} object associated with this
* {@link AbstractModelID}. <tt>null</tt> if {@link Model} does not
* exists.
*/
public abstract O getObject();
}

View File

@@ -0,0 +1,81 @@
/*
* 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.model.id;
import com.google.inject.Inject;
/**
* The ID interface. Each object must be represented by an unique ID.
*
* @param <T>
* the raw id type
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public abstract class ID<T> {
/**
* The id itself
*/
protected final T id;
/**
* @param id
* the raw id
*/
@Inject
protected ID(T id) {
this.id = id;
}
/**
* @return the id
*/
public T getID() {
return id;
}
@Override
public String toString() {
return this.getClass().getSimpleName() + " [id=" + id + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id.hashCode() + this.getClass().hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
@SuppressWarnings("rawtypes")
ID other = (ID) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.model.id.compound;
import com.l2jserver.model.id.ID;
/**
* The compound {@link ID} is composed of two IDs.
*
* @param <T1>
* the first {@link ID} type
* @param <T2>
* the second {@link ID} type
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class AbstractCompoundID<T1, T2> extends ID<AbstractCompoundID<T1, T2>> {
/**
* The first ID
*/
private final T1 id1;
/**
* The second ID
*/
private final T2 id2;
/**
* Creates a new compound ID
*
* @param id1
* the first id
* @param id2
* the second id
*/
protected AbstractCompoundID(T1 id1, T2 id2) {
super(null);
this.id1 = id1;
this.id2 = id2;
}
/**
* @return the first id
*/
public T1 getID1() {
return id1;
}
/**
* @return the second id
*/
public T2 getID2() {
return id2;
}
@Override
public AbstractCompoundID<T1, T2> getID() {
return this;
}
}

View File

@@ -0,0 +1,163 @@
/*
* 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.model.id.object.allocator;
import java.util.BitSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.l2jserver.util.PrimeFinder;
/**
* The {@link BitSet} id allocator allocates new IDs backed by an {@link BitSet}
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class BitSetIDAllocator implements IDAllocator {
/**
* The logger
*/
private static final Logger log = LoggerFactory
.getLogger(BitSetIDAllocator.class);
/**
* Lock to guarantee synchronization
*/
private Lock lock = new ReentrantLock();
/**
* Available IDs
*/
private BitSet ids = new BitSet();
/**
* Amount of free ids
*/
private AtomicInteger freeIdCount = new AtomicInteger();
/**
* Next free ID
*/
private AtomicInteger nextId = new AtomicInteger();
/**
* Initializes this allocator
*/
public void init() {
ids = new BitSet(PrimeFinder.nextPrime(100000));
ids.clear();
freeIdCount = new AtomicInteger(ALLOCABLE_IDS);
nextId = new AtomicInteger(ids.nextClearBit(0));
log.info("BitSet IDAllocator initialized. Next available ID is {}",
nextId.get());
}
@Override
public void allocate(int id) {
if (ids.get(id - FIRST_ID))
throw new IDAllocatorException("ID not allocated");
log.debug("Allocating ID {}", id);
lock.lock();
try {
if (id < FIRST_ID)
return;
ids.set(id - FIRST_ID);
nextId = new AtomicInteger(ids.nextClearBit(0));
} finally {
lock.unlock();
}
}
@Override
public int allocate() {
lock.lock();
try {
final int newID = nextId.get();
ids.set(newID);
freeIdCount.decrementAndGet();
if (log.isDebugEnabled())
log.debug("Allocated a new ID {}", newID + FIRST_ID);
int nextFree = ids.nextClearBit(newID);
if (nextFree < 0) {
nextFree = ids.nextClearBit(0);
}
if (nextFree < 0) {
if (ids.size() < ALLOCABLE_IDS) {
increaseBitSetCapacity();
} else {
log.error("ID exhaustion");
throw new IDAllocatorException("ID exhaustion");
}
}
nextId.set(nextFree);
return newID + FIRST_ID;
} finally {
lock.unlock();
}
}
@Override
public void release(int id) {
if (id < FIRST_ID)
throw new IDAllocatorException(
"Can't release ID, smaller then initial ID");
if (!ids.get(id - FIRST_ID))
throw new IDAllocatorException("ID not allocated");
log.debug("Releasing allocated ID {}", id);
lock.lock();
try {
ids.clear(id - FIRST_ID);
freeIdCount.incrementAndGet();
} finally {
lock.unlock();
}
}
@Override
public void clear() {
ids.clear();
}
/**
* Increases the {@link BitSet} capacity
*/
private void increaseBitSetCapacity() {
log.debug("Increasing BitSet capacity from {}", ids.size());
BitSet newBitSet = new BitSet(
PrimeFinder.nextPrime((getAllocatedIDs() * 11) / 10));
newBitSet.or(ids);
ids = newBitSet;
}
@Override
public int getAllocatedIDs() {
return ALLOCABLE_IDS - getFreeIDs();
}
@Override
public int getFreeIDs() {
return freeIdCount.get();
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.model.id.object.allocator;
/**
* The ID allocator is used to alloc new ID and to release IDs that aren't used
* anymore.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface IDAllocator {
/**
* The first ID ever allocated
*/
public final static int FIRST_ID = 0x10000000;
/**
* The last ID ever allocated
*/
public final static int LAST_ID = 0x7FFFFFFF;
/**
* Total of available IDs for allocation
*/
public final static int ALLOCABLE_IDS = LAST_ID - FIRST_ID;
/**
* This is method is used to register IDs as used at startup time.
*
* @param id
* the id
*/
void allocate(int id);
/**
* Allocates a new ID
*
* @return the allocated ID value
*/
int allocate();
/**
* Release an ID
*
* @param id
* the id
*/
void release(int id);
/**
* Release all allocated IDs
*/
void clear();
/**
* Get the amount of already allocated IDs
*
* @return allocated ids count
*/
int getAllocatedIDs();
/**
* Get the amount of IDs remaining to be allocated
*
* @return free ids count
*/
int getFreeIDs();
}

View File

@@ -0,0 +1,62 @@
/*
* 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.model.id.object.allocator;
/**
* Exception thrown by an {@link IDAllocator}.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class IDAllocatorException extends RuntimeException {
/**
* The Java Serialization Serial
*/
private static final long serialVersionUID = 111195059766878062L;
/**
* Creates an empty instance of this exception
*/
public IDAllocatorException() {
super();
}
/**
* @param message
* the message
* @param cause
* the root cause
*/
public IDAllocatorException(String message, Throwable cause) {
super(message, cause);
}
/**
* @param message
* the message
*/
public IDAllocatorException(String message) {
super(message);
}
/**
* @param cause
* the root cause
*/
public IDAllocatorException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.model.id.provider;
import com.google.inject.assistedinject.Assisted;
import com.l2jserver.model.id.ID;
import com.l2jserver.model.id.compound.AbstractCompoundID;
/**
* The ID factory is used to create instances of IDs. It will automatically make
* sure the ID is free before allocating it.
*
* @param <I1>
* the first compound {@link ID} type
* @param <I2>
* the second compound {@link ID} type
* @param <T>
* the {@link CompoundIDProvider} type
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface CompoundIDProvider<I1 extends ID<?>, I2 extends ID<?>, T extends AbstractCompoundID<I1, I2>> {
/**
* Creates the ID object for an <b>EXISTING</b> ID.
*
* @param id1
* the first id
* @param id2
* the second id
* @return the created compound {@link ID}
*/
T createID(@Assisted("id1") I1 id1, @Assisted("id2") I2 id2);
}

View File

@@ -0,0 +1,49 @@
/*
* 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.model.id.provider;
import com.l2jserver.model.id.ID;
/**
* ID objects should never be directly instantiated and an provider
* implementation must be used to create and generate them.
* <p>
*
* The ID provider is used to create instances of IDs. It will automatically
* make sure the ID is free before allocating it.
* <p>
* The provider will also make sure only a single instance for each raw ID
* exits, that is for any given ID instance for raw value <b>1</b> only a single
* object will be created and following calls will always return the same
* object.
*
* @param <I>
* the raw id type
* @param <T>
* the {@link ID} implementation
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface IDProvider<I, T extends ID<I>> {
/**
* Creates the ID object for an <b>EXISTING</b> ID.
*
* @param id
* the raw id value
* @return the resolved {@link ID} object
*/
T resolveID(I id);
}

View File

@@ -0,0 +1,114 @@
/*
* 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;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An abstract service implementing basic life-cycle methods.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public abstract class AbstractService implements Service {
/**
* Running state of a service
*/
protected boolean running = false;
@Override
public final void start() throws ServiceStartException {
if (running)
throw new ServiceStartException("Service is already started");
try {
this.doStart();
this.running = true;
} catch (ServiceStartException e) {
this.running = false;
throw e;
}
}
/**
* Starts the service. This method is invoked internally by {@link #start()}
*
* @throws ServiceStartException
* if any error occur while starting the service
*/
protected void doStart() throws ServiceStartException {
}
@Override
public final void stop() throws ServiceStopException {
if (!running)
throw new ServiceStopException("Service is not started");
try {
this.doStop();
} finally {
this.running = false;
}
}
/**
* Stops the service. This method is invoked internally by {@link #stop()}
*
* @throws ServiceStopException
* if any error occur while stopping the service
*/
protected void doStop() throws ServiceStopException {
}
@Override
public void restart() throws ServiceException {
this.stop();
this.start();
}
@Override
public boolean isStarted() {
return running;
}
@Override
public boolean isStopped() {
return !running;
}
@Override
public Class<? extends Service>[] getDependencies() {
final Depends deps = this.getClass().getAnnotation(Depends.class);
if (deps == null)
return null;
return deps.value();
}
/**
* Service dependency metadata
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Depends {
/**
* @return the service's dependency
*/
Class<? extends Service>[] value();
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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;
/**
* Each Service is a provider of a given feature. Most services will want to
* implement {@link AbstractService} class instead of this interface.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface Service {
/**
* Start this service
*
* @throws ServiceStartException
* if an error occurred
*/
void start() throws ServiceStartException;
/**
* Stop this service
*
* @throws ServiceStopException
* if an error occurred
*/
void stop() throws ServiceStopException;
/**
* Stop this service
*
* @throws ServiceException
* if an error occurred
*/
void restart() throws ServiceException;
/**
* @return true if service is running
*/
boolean isStarted();
/**
* @return false if service is not running
*/
boolean isStopped();
/**
* @return the other services that the service depends on
*/
Class<? extends Service>[] getDependencies();
}

View File

@@ -0,0 +1,28 @@
/*
* 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;
import com.l2jserver.service.configuration.Configuration;
/**
* Base interface for all {@link Service} {@link Configuration} classes.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface ServiceConfiguration extends Configuration {
}

View File

@@ -0,0 +1,70 @@
/*
* 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;
import com.l2jserver.util.exception.L2Exception;
/**
* Exception for an {@link Service}
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public abstract class ServiceException extends L2Exception {
/**
* The Java Serialization API serial
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new instance of this exception
*/
public ServiceException() {
super();
}
/**
* Creates a new instance of this exception
*
* @param message
* the message
* @param cause
* the root cause
*/
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new instance of this exception
*
* @param message
* the message
*/
public ServiceException(String message) {
super(message);
}
/**
* Creates a new instance of this exception
*
* @param cause
* the root cause
*/
public ServiceException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,248 @@
/*
* 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;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.l2jserver.service.core.LoggingService;
import com.l2jserver.util.ClassUtils;
import com.l2jserver.util.factory.CollectionFactory;
/**
* The {@link ServiceManager} is responsible for starting and stopping services
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ServiceManager {
/**
* The logger
*/
private final Logger logger;
/**
* The Guice Injector
*/
private final Injector injector;
/**
* List of all known services by this manager
*/
private final Set<Service> knownServices = CollectionFactory.newSet();
/**
* @param injector
* the {@link Guice} {@link Injector}
*/
@Inject
public ServiceManager(Injector injector) {
this.injector = injector;
final LoggingService service = injector
.getInstance(LoggingService.class);
knownServices.add(service);
try {
service.start();
} catch (ServiceStartException e) {
throw new RuntimeException(e);
}
logger = LoggerFactory.getLogger(ServiceManager.class);
}
/**
* @param <T>
* the service type
* @param serviceClass
* the service interface <tt>Class&lt;T&gt;</tt>
* @return the service implementation
*/
public <T extends Service> T get(Class<T> serviceClass) {
return injector.getInstance(serviceClass);
}
/**
* Starts the given service implementation
*
* @param <T>
* the service interface type
* @param serviceClass
* the service interface
* @return the service implementation
* @throws ServiceStartException
* if any error occur while starting service
*/
public <T extends Service> T start(Class<T> serviceClass)
throws ServiceStartException {
final T service = injector.getInstance(serviceClass);
if (service == null)
return null;
if (service.isStarted())
return service;
knownServices.add(service);
try {
startDependencies(service.getDependencies());
logger.debug("{}: Starting service...",
serviceClass.getSimpleName());
service.start();
logger.info("{} started", serviceClass.getSimpleName());
return service;
} catch (ServiceStartException e) {
logger.error("{}: Error starting service: {}",
serviceClass.getSimpleName(), e);
throw e;
} catch (Exception e) {
logger.error("{}: Error starting service: {}",
serviceClass.getSimpleName(), e);
throw new ServiceStartException(e);
}
}
/**
* Starts the dependencies
*
* @param dependencies
* the dependencies
* @throws ServiceStartException
* if any error occur while starting dependencies
*/
private void startDependencies(Class<? extends Service>[] dependencies)
throws ServiceStartException {
if (dependencies == null)
return;
if (dependencies.length == 0)
return;
for (final Class<? extends Service> serviceClass : dependencies) {
this.start(serviceClass);
}
}
/**
* Stops the given service implementation
*
* @param serviceClass
* the service interface
* @throws ServiceStopException
* if any error occur while stopping service
*/
public void stop(Class<? extends Service> serviceClass)
throws ServiceStopException {
final Service service = injector.getInstance(serviceClass);
if (service == null)
return;
if (service.isStopped())
return;
knownServices.add(service);
try {
logger.debug("{0}: Stopping service...",
serviceClass.getSimpleName());
stopDependencies(service);
service.stop();
logger.info("{0}: Service stopped!", serviceClass.getSimpleName());
} catch (ServiceStopException e) {
logger.error("{0}: Error stopping service: {1}",
serviceClass.getSimpleName(), e.getCause());
throw e;
}
}
/**
* Stops the dependencies
*
* @param service
* the service
* @throws ServiceStopException
* if any error occur while stopping dependencies
*/
private void stopDependencies(Service service) throws ServiceStopException {
final Set<Class<? extends Service>> dependencies = createStopDependencies(
null, service);
for (final Class<? extends Service> dependency : dependencies) {
this.stop(dependency);
}
}
/**
* Creates a {@link Set} of all dependecies to be stopped
*
* @param depends
* the service
* @param serviceClass
* the service class
* @return the {@link Set} of all depedendecies to be stopped
*/
private Set<Class<? extends Service>> createStopDependencies(
Set<Class<? extends Service>> depends, Service serviceClass) {
if (depends == null)
depends = CollectionFactory.newSet();
for (final Service service : knownServices) {
if (service.getDependencies() == null
|| service.getDependencies().length == 0)
continue;
for (final Class<? extends Service> dependency : service
.getDependencies()) {
if (!ClassUtils.isSubclass(service.getClass(), dependency))
continue;
depends.add(dependency);
createStopDependencies(depends,
injector.getInstance(dependency));
}
}
return depends;
}
/**
* Restarts the given service
*
* @param <T>
* the service type
* @param serviceClass
* the service interface
* @return the service implementation
* @throws ServiceException
* if any error occur while starting or stopping the service
*/
public <T extends Service> T restart(Class<T> serviceClass)
throws ServiceException {
final T service = injector.getInstance(serviceClass);
if (service == null)
return null;
if (service.isStopped())
throw new ServiceStopException("Service is already stopped");
knownServices.add(service);
try {
logger.debug("{0}: Restaring service...",
serviceClass.getSimpleName());
service.restart();
logger.info("{0}: Service restarted!", serviceClass.getSimpleName());
return service;
} catch (ServiceStartException e) {
logger.error("{0}: Error starting service: {1}",
serviceClass.getSimpleName(), e.getCause());
throw e;
} catch (ServiceStopException e) {
logger.error("{0}: Error stopping service: {1}",
serviceClass.getSimpleName(), e.getCause());
throw e;
} catch (ServiceException e) {
logger.error("{0}: Error restarting service: {1}",
serviceClass.getSimpleName(), e.getCause());
throw e;
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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;
/**
* Thrown when an service failed to restart. It's <tt>cause</tt> can be an
* {@link ServiceStartException} or {@link ServiceStopException}.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ServiceRestartException extends ServiceException {
/**
* The Java Serialization API serial
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new instance of this exception
*/
public ServiceRestartException() {
super();
}
/**
* Creates a new instance of this exception
*
* @param message
* the message
* @param cause
* the root cause
*/
public ServiceRestartException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new instance of this exception
*
* @param message
* the message
*/
public ServiceRestartException(String message) {
super(message);
}
/**
* Creates a new instance of this exception
*
* @param cause
* the root cause
*/
public ServiceRestartException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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;
/**
* Exception thrown when a service failed to start.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ServiceStartException extends ServiceException {
/**
* The Java Serialization API serial
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new instance of this exception
*/
public ServiceStartException() {
super();
}
/**
* Creates a new instance of this exception
*
* @param message
* the message
* @param cause
* the root cause
*/
public ServiceStartException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new instance of this exception
*
* @param message
* the message
*/
public ServiceStartException(String message) {
super(message);
}
/**
* Creates a new instance of this exception
*
* @param cause
* the root cause
*/
public ServiceStartException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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;
/**
* Exception thrown when a service failed to stop
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ServiceStopException extends ServiceException {
/**
* The Java Serialization API serial
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new instance of this exception
*/
public ServiceStopException() {
super();
}
/**
* Creates a new instance of this exception
*
* @param message
* the message
* @param cause
* the root cause
*/
public ServiceStopException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new instance of this exception
*
* @param message
* the message
*/
public ServiceStopException(String message) {
super(message);
}
/**
* Creates a new instance of this exception
*
* @param cause
* the root cause
*/
public ServiceStopException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,153 @@
/*
* 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.cache;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.Iterator;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.l2jserver.util.factory.CollectionFactory;
/**
* Base class for weak caches and soft caches
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <K>
* the key type
* @param <V>
* the value type
*/
abstract class AbstractReferenceCache<K, V> implements Cache<K, V> {
/**
* The logger
*/
protected final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* The cache name
*/
protected final String cacheName;
/**
* Map storing references to cached objects
*/
protected final Map<K, Reference<V>> cacheMap = CollectionFactory.newMap();
/**
* The reference queue
*/
protected final ReferenceQueue<V> refQueue = CollectionFactory
.newReferenceQueue();
/**
* @param cacheName
* the cache name
*/
protected AbstractReferenceCache(String cacheName) {
this.cacheName = cacheName;
}
@Override
public void put(K key, V value) {
cleanQueue();
Reference<V> entry = newReference(key, value, refQueue);
cacheMap.put(key, entry);
log.debug("{}: added for key: {}", cacheName, key);
}
@Override
public V get(K key) {
cleanQueue();
Reference<V> reference = cacheMap.get(key);
if (reference == null)
return null;
V res = reference.get();
if (res != null)
log.debug("{}: obtained for key: {}", cacheName, key);
return res;
}
@Override
public boolean contains(K key) {
cleanQueue();
return cacheMap.containsKey(key);
}
/**
* Cleans the queue
*/
protected abstract void cleanQueue();
@Override
public void remove(K key) {
cacheMap.remove(key);
log.debug("{}: removed for key: {}", cacheName, key);
}
@Override
public void clear() {
cacheMap.clear();
log.debug("{}: cleared", cacheName);
}
@Override
public Iterator<V> iterator() {
cleanQueue();
return new Iterator<V>() {
private final Iterator<Reference<V>> iterator = cacheMap.values()
.iterator();
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public V next() {
return iterator.next().get();
}
@Override
public void remove() {
iterator.remove();
}
};
}
/**
* Creates a new reference
*
* @param key
* the reference key
* @param value
* the reference value
* @param queue
* the queue
* @return the new reference
*/
protected abstract Reference<V> newReference(K key, V value,
ReferenceQueue<V> queue);
}

View File

@@ -0,0 +1,73 @@
/*
* 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.cache;
/**
* This interface represents a Map structure for cache usage.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
* @param <K>
* the key type
* @param <V>
* the value type
*/
public interface Cache<K, V> extends Iterable<V> {
/**
* Adds a pair <key,value> to cache.<br>
* <br>
*
* <font color='red'><b>NOTICE:</b> </font> if there is already a value with
* given id in the map, {@link IllegalArgumentException} will be thrown.
*
* @param key
* the key name
* @param value
* the cache content value
*/
void put(K key, V value);
/**
* Returns cached value correlated to given key.
*
* @param key
* the key
* @return the cached value for this key
*/
V get(K key);
/**
* Checks whether this map contains a value related to given key.
*
* @param key
* the key
* @return true if key has an value
*/
boolean contains(K key);
/**
* Removes an entry from the map, that has given key.
*
* @param key
* the key
*/
void remove(K key);
/**
* Clears this cache
*/
void clear();
}

View File

@@ -0,0 +1,110 @@
/*
* 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.cache;
import com.l2jserver.service.Service;
/**
* This is an transparent Cache system. It proxies an interface implementing
* {@link Cacheable}. Once the first call is done through the proxy, the result
* is cached in the underlying cache engine. When the second and sucedind calls
* are made the cache is looked up, if a match (method and arguments pair) is
* found, this result is returned.
* <p>
* If you do not desire to cache an method, annotate it with
* {@link IgnoreCaching}
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface CacheService extends Service {
/**
* 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. Remember, this must be an interface!
* @param 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). The size is only a guarantee that you can store
* <b>at least</b> <tt>n</tt> items.
*
* @param <K>
* the cache key type
* @param <V>
* the cache value type
* @param name
* the cache name
* @param size
* the maximum cache size
* @size the maximum cache size
* @return the created cache
*/
<K, V> Cache<K, V> createCache(String name, int size);
/**
* Creates a new eternal cache with default configurations. An eternal cache
* is guaranteed to never automatically expire items. The size is only a
* guarantee that you can store <b>at least</b> <tt>n</tt> items.
*
* @param <K>
* the cache key type
* @param <V>
* the cache value type
* @param name
* the cache name
* @param size
* the maximum cache size
* @size the maximum cache size
* @return the created cache
*/
<K, V> Cache<K, V> createEternalCache(String name, int size);
/**
* Creates a new cache with default configurations. The default cache size
* is 200. The size is only a guarantee that you can store <b>at least</b>
* 200 items.
*
* @param <K>
* the cache key type
* @param <V>
* the cache value type
* @param name
* the cache name
* @return the created cache
*/
<K, V> Cache<K, V> createCache(String name);
/**
* Disposes the cache. Once the cache is disposed it cannot be used anymore.
*
* @param <K>
* the cache key type
* @param <V>
* the cache value type
* @param cache
* the cache
*/
<K, V> void dispose(Cache<K, V> cache);
}

View File

@@ -0,0 +1,29 @@
/*
* 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.cache;
/**
* Flags an interface that is capable of caching.
* <p>
* <b>Note</b>: only interfaces can be cached!
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public interface Cacheable {
}

View File

@@ -0,0 +1,224 @@
/*
* 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.cache;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Iterator;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.Configuration;
import net.sf.ehcache.config.DiskStoreConfiguration;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
/**
* Simple cache that stores invocation results in a EhCache
* {@link net.sf.ehcache.Cache Cache}.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class EhCacheService extends AbstractService implements CacheService {
/**
* The logger
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* The cache manager
*/
private CacheManager manager;
/**
* The interface cache
*/
private Cache<MethodInvocation, Object> interfaceCache;
@Override
protected void doStart() throws ServiceStartException {
manager = new CacheManager(new Configuration().updateCheck(false)
.diskStore(new DiskStoreConfiguration().path("data/cache")));
interfaceCache = createCache("interface-cache");
}
@Override
public <T extends Cacheable> T decorate(final Class<T> interfaceType,
final T instance) {
Preconditions.checkNotNull(interfaceType, "interfaceType");
Preconditions.checkNotNull(instance, "instance");
Preconditions.checkArgument(interfaceType.isInterface(),
"interfaceType is not an interface");
log.debug("Decorating {} with cache", interfaceType);
@SuppressWarnings("unchecked")
final T proxy = (T) Proxy.newProxyInstance(this.getClass()
.getClassLoader(), new Class[] { interfaceType },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (method.isAnnotationPresent(IgnoreCaching.class))
return method.invoke(instance, args);
final MethodInvocation invocation = new MethodInvocation(
method, args);
Object result = interfaceCache.get(invocation);
if (result == null)
return doInvoke(invocation, proxy, method, args);
return result;
}
private Object doInvoke(MethodInvocation invocation,
Object proxy, Method method, Object[] args)
throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
Object result = method.invoke(instance, args);
interfaceCache.put(invocation, result);
return result;
}
});
return proxy;
}
@Override
public <K, V> Cache<K, V> createCache(String name, int size) {
Preconditions.checkNotNull(name, "name");
Preconditions.checkArgument(size > 0, "size <= 0");
log.debug("Creating cache {} with minimum size of {}", name, size);
net.sf.ehcache.Cache cache = new net.sf.ehcache.Cache(
new CacheConfiguration(name, size)
.memoryStoreEvictionPolicy(
MemoryStoreEvictionPolicy.LRU)
.overflowToDisk(true).eternal(false)
.timeToLiveSeconds(60).timeToIdleSeconds(30)
.diskPersistent(false)
.diskExpiryThreadIntervalSeconds(0));
manager.addCache(cache);
return new EhCacheFacade<K, V>(cache);
}
@Override
public <K, V> Cache<K, V> createEternalCache(String name, int size) {
Preconditions.checkNotNull(name, "name");
Preconditions.checkArgument(size > 0, "size <= 0");
log.debug("Creating eternal cache {} with minimum size of {}", name,
size);
net.sf.ehcache.Cache cache = new net.sf.ehcache.Cache(
new CacheConfiguration(name, size)
.memoryStoreEvictionPolicy(
MemoryStoreEvictionPolicy.LRU)
.overflowToDisk(true).eternal(true)
.diskExpiryThreadIntervalSeconds(0));
manager.addCache(cache);
return new EhCacheFacade<K, V>(cache);
}
@Override
public <K, V> Cache<K, V> createCache(String name) {
return createCache(name, 200);
}
@Override
public <K, V> void dispose(Cache<K, V> cache) {
if (cache instanceof EhCacheFacade) {
log.debug("Disposing cache {}", cache);
manager.removeCache(((EhCacheFacade<K, V>) cache).cache.getName());
} else {
log.warn("Trying to dispose {} cache when it is not EhCacheFacade type");
}
}
@Override
protected void doStop() throws ServiceStopException {
manager.removalAll();
manager.shutdown();
interfaceCache = null;
}
/**
* {@link net.sf.ehcache.Cache EhCache} facade
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <K>
* the key type
* @param <V>
* the value type
*/
private class EhCacheFacade<K, V> implements Cache<K, V> {
/**
* The backing cache
*/
private final net.sf.ehcache.Cache cache;
/**
* @param cache
* the backing cache
*/
public EhCacheFacade(net.sf.ehcache.Cache cache) {
this.cache = cache;
}
@Override
public void put(K key, V value) {
cache.put(new Element(key, value));
}
@Override
@SuppressWarnings("unchecked")
public V get(K key) {
final Element element = cache.get(key);
if (element == null)
return null;
return (V) element.getObjectValue();
}
@Override
public boolean contains(K key) {
return cache.get(key) != null;
}
@Override
public void remove(K key) {
cache.remove(key);
}
@Override
public void clear() {
cache.removeAll();
}
@Override
public Iterator<V> iterator() {
return null;
}
}
}

View File

@@ -0,0 +1,97 @@
/*
* 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.cache;
import java.util.Iterator;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.l2jserver.util.factory.CollectionFactory;
/**
* Cache class for an eternal cache. Entries in this cache instance won't ever
* be automatically removed, even if the JVM is running out of memory.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <K>
* the key type
* @param <V>
* the value type
*/
public class EternalCache<K, V> implements Cache<K, V> {
/**
* The logger
*/
protected final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* The cache name
*/
protected final String cacheName;
/**
* Map storing references to cached objects
*/
protected final Map<K, V> cacheMap = CollectionFactory.newMap();
/**
* @param cacheName
* the cache name
*/
protected EternalCache(String cacheName) {
this.cacheName = cacheName;
}
@Override
public void put(K key, V value) {
cacheMap.put(key, value);
log.debug("{}: added for key: {}", cacheName, key);
}
@Override
public V get(K key) {
V obj = cacheMap.get(key);
if (obj != null)
log.debug("{}: obtained for key: {}", cacheName, key);
return obj;
}
@Override
public boolean contains(K key) {
return cacheMap.containsKey(key);
}
@Override
public void remove(K key) {
cacheMap.remove(key);
log.debug("{}: removed for key: {}", cacheName, key);
}
@Override
public void clear() {
cacheMap.clear();
log.debug("{}: cleared", cacheName);
}
@Override
public Iterator<V> iterator() {
return cacheMap.values().iterator();
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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.cache;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicate to the proxy that this method should not be cached.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface IgnoreCaching {
}

View File

@@ -0,0 +1,75 @@
/*
* 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.cache;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* Simple class used to store method invocations for the proxied cache.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
class MethodInvocation {
/**
* The invoked method
*/
private final Method method;
/**
* The invocation arguments
*/
private final Object[] args;
/**
* @param method
* the method invoked
* @param args
* the invocation arguments
*/
public MethodInvocation(Method method, Object[] args) {
this.method = method;
this.args = args;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(args);
result = prime * result + ((method == null) ? 0 : method.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MethodInvocation other = (MethodInvocation) obj;
if (!Arrays.equals(args, other.args))
return false;
if (method == null) {
if (other.method != null)
return false;
} else if (!method.equals(other.method))
return false;
return true;
}
}

View File

@@ -0,0 +1,200 @@
/*
* 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.cache;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
/**
* This {@link Cache} service implementation uses a {@link SoftReference} to
* store values.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class SoftCacheService extends AbstractService implements CacheService {
/**
* The logger
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* The interface cache
*/
private Cache<MethodInvocation, Object> interfaceCache;
@Override
protected void doStart() throws ServiceStartException {
interfaceCache = createCache("interface-cache");
}
@Override
public <T extends Cacheable> T decorate(final Class<T> interfaceType,
final T instance) {
Preconditions.checkNotNull(interfaceType, "interfaceType");
Preconditions.checkNotNull(instance, "instance");
Preconditions.checkArgument(interfaceType.isInterface(),
"interfaceType is not an interface");
log.debug("Decorating {} with cache", interfaceType);
@SuppressWarnings("unchecked")
final T proxy = (T) Proxy.newProxyInstance(this.getClass()
.getClassLoader(), new Class[] { interfaceType },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (method.isAnnotationPresent(IgnoreCaching.class))
return method.invoke(instance, args);
final MethodInvocation invocation = new MethodInvocation(
method, args);
Object result = interfaceCache.get(invocation);
if (result == null)
return doInvoke(invocation, proxy, method, args);
return result;
}
private Object doInvoke(MethodInvocation invocation,
Object proxy, Method method, Object[] args)
throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
Object result = method.invoke(instance, args);
interfaceCache.put(invocation, result);
return result;
}
});
return proxy;
}
@Override
public <K, V> Cache<K, V> createCache(String name, int size) {
log.debug("Creating cache {} with minimum size of {}", name, size);
return new SoftCache<K, V>(name);
}
@Override
public <K, V> Cache<K, V> createEternalCache(String name, int size) {
log.debug("Creating eternal cache {} with minimum size of {}", name,
size);
return new EternalCache<K, V>(name);
}
@Override
public <K, V> Cache<K, V> createCache(String name) {
return createCache(name, 200);
}
@Override
public <K, V> void dispose(Cache<K, V> cache) {
log.debug("Disposing {}", cache);
cache.clear();
}
@Override
protected void doStop() throws ServiceStopException {
dispose(interfaceCache);
interfaceCache = null;
}
/**
* This class is a simple map implementation for cache usage.<br>
* <br>
* Value may be stored in map really long, but it for sure will be removed
* if there is low memory (and of course there isn't any strong reference to
* value object)
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
* @param <K>
* the key type
* @param <V>
* the value type
*/
private class SoftCache<K, V> extends AbstractReferenceCache<K, V>
implements Cache<K, V> {
/**
* This class is a {@link SoftReference} with additional responsibility
* of holding key object
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
private class SoftEntry extends SoftReference<V> {
/**
* The key
*/
private K key;
/**
* @param key
* the key
* @param referent
* the value
* @param q
* the queue
*/
SoftEntry(K key, V referent, ReferenceQueue<? super V> q) {
super(referent, q);
this.key = key;
}
/**
* @return the key
*/
K getKey() {
return key;
}
}
/**
* @param cacheName
* the cache name
*/
public SoftCache(String cacheName) {
super(cacheName);
}
@Override
@SuppressWarnings("unchecked")
protected synchronized void cleanQueue() {
SoftEntry en = null;
while ((en = (SoftEntry) refQueue.poll()) != null) {
K key = en.getKey();
if (log.isDebugEnabled())
log.debug("{} : cleaned up {} for key: {}", cacheName, key);
cacheMap.remove(key);
}
}
@Override
protected Reference<V> newReference(K key, V value,
ReferenceQueue<V> vReferenceQueue) {
return new SoftEntry(key, value, vReferenceQueue);
}
}
}

View File

@@ -0,0 +1,199 @@
/*
* 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.cache;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
/**
* This {@link Cache} service implementation uses a {@link WeakReference} to
* store values.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class WeakCacheService extends AbstractService implements CacheService {
/**
* The logger
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* The interface cache
*/
private Cache<MethodInvocation, Object> interfaceCache;
@Override
protected void doStart() throws ServiceStartException {
interfaceCache = createCache("interface-cache");
}
@Override
public <T extends Cacheable> T decorate(final Class<T> interfaceType,
final T instance) {
Preconditions.checkNotNull(interfaceType, "interfaceType");
Preconditions.checkNotNull(instance, "instance");
Preconditions.checkArgument(interfaceType.isInterface(),
"interfaceType is not an interface");
log.debug("Decorating {} with cache", interfaceType);
@SuppressWarnings("unchecked")
final T proxy = (T) Proxy.newProxyInstance(this.getClass()
.getClassLoader(), new Class[] { interfaceType },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (method.isAnnotationPresent(IgnoreCaching.class))
return method.invoke(instance, args);
final MethodInvocation invocation = new MethodInvocation(
method, args);
Object result = interfaceCache.get(invocation);
if (result == null)
return doInvoke(invocation, proxy, method, args);
return result;
}
private Object doInvoke(MethodInvocation invocation,
Object proxy, Method method, Object[] args)
throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
Object result = method.invoke(instance, args);
interfaceCache.put(invocation, result);
return result;
}
});
return proxy;
}
@Override
public <K, V> Cache<K, V> createCache(String name, int size) {
log.debug("Creating cache {} with minimum size of {}", name, size);
return new WeakCache<K, V>(name);
}
@Override
public <K, V> Cache<K, V> createEternalCache(String name, int size) {
log.debug("Creating eternal cache {} with minimum size of {}", name,
size);
return new EternalCache<K, V>(name);
}
@Override
public <K, V> Cache<K, V> createCache(String name) {
return createCache(name, 200);
}
@Override
public <K, V> void dispose(Cache<K, V> cache) {
log.debug("Disposing {}", cache);
cache.clear();
}
@Override
protected void doStop() throws ServiceStopException {
dispose(interfaceCache);
interfaceCache = null;
}
/**
* This class is a simple map implementation for cache usage.<br>
* <br>
* Values from the map will be removed after the first garbage collector run
* if there isn't any strong reference to the value object.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
* @param <K>
* the key type
* @param <V>
* the value type
*/
private class WeakCache<K, V> extends AbstractReferenceCache<K, V>
implements Cache<K, V> {
/**
* This class is a {@link WeakReference} with additional responsibility
* of holding key object
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
private class Entry extends WeakReference<V> {
/**
* The key
*/
private K key;
/**
* @param key
* the key
* @param referent
* the value
* @param q
* the queue
*/
Entry(K key, V referent, ReferenceQueue<? super V> q) {
super(referent, q);
this.key = key;
}
/**
* @return the key
*/
K getKey() {
return key;
}
}
/**
* @param cacheName
* the cache name
*/
WeakCache(String cacheName) {
super(cacheName);
}
@Override
@SuppressWarnings("unchecked")
protected synchronized void cleanQueue() {
Entry en = null;
while ((en = (Entry) refQueue.poll()) != null) {
K key = en.getKey();
if (log.isDebugEnabled())
log.debug("{}: cleaned up for key: {}", cacheName, key);
cacheMap.remove(key);
}
}
@Override
protected Reference<V> newReference(K key, V value,
ReferenceQueue<V> vReferenceQueue) {
return new Entry(key, value, vReferenceQueue);
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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.configuration;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.l2jserver.service.configuration.ProxyConfigurationService.ConfigurationName;
/**
* Configuration interface
* <p>
* Each service desiring to use the configuration system must extend the
* interface and define methods for getters and setters. Each method must be
* annotated either with {@link ConfigurationPropertyGetter} or
* {@link ConfigurationPropertySetter}
* <p>
* Each interface may optionally be annotated with {@link ConfigurationName}.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface Configuration {
/**
* The getter method for an configuration property
* <p>
* The method must have no arguments.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
public @interface ConfigurationPropertyGetter {
/**
* @return the default value to be used
*/
String defaultValue() default "";
}
/**
* The setter method for an configuration property.
* <p>
* The method must have a single argument and return type void.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
public @interface ConfigurationPropertySetter {
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.configuration;
import com.l2jserver.service.Service;
/**
* The configuration service is responsible for reading and writing in
* configuration storage. Each configuration is represented by an interface.
* Implementations of this interface are implementaion specific.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface ConfigurationService extends Service {
/**
* Get the configuration object for <tt>config</tt>
*
* @param <C>
* the configuration type
* @param config
* the configuration interface class
* @return the configuration object
*/
<C extends Configuration> C get(Class<C> config);
}

View File

@@ -0,0 +1,355 @@
/*
* 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.configuration;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.AbstractService.Depends;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.cache.CacheService;
import com.l2jserver.service.configuration.Configuration.ConfigurationPropertyGetter;
import com.l2jserver.service.configuration.Configuration.ConfigurationPropertySetter;
import com.l2jserver.service.core.LoggingService;
import com.l2jserver.util.factory.CollectionFactory;
import com.l2jserver.util.transformer.Transformer;
import com.l2jserver.util.transformer.TransformerFactory;
/**
* Creates {@link Configuration} object through Java {@link Proxy}. Uses the
* annotations in the interface to retrieve and store values.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
@Depends({ LoggingService.class, CacheService.class })
public class ProxyConfigurationService extends AbstractService implements
ConfigurationService {
/**
* The directory in which configuration files are stored
*/
private final File directory = new File("./config/properties");
/**
* The logger
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* The cache of configuration objects
*/
private Map<Class<?>, Object> cache = CollectionFactory.newWeakMap();
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(value = ElementType.METHOD)
public @interface ConfigurationPropertiesKey {
String value();
}
/**
* Each configuration can define the name of its configuration. This will be
* used by implementations to look for the configuration.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(value = ElementType.TYPE)
public @interface ConfigurationName {
/**
* @return the configuration name
*/
String value();
}
@Override
protected void doStart() throws ServiceStartException {
if (!directory.exists())
if (!directory.mkdirs())
throw new ServiceStartException("Failed to create directories");
}
@Override
@SuppressWarnings("unchecked")
public <C extends Configuration> C get(Class<C> config) {
Preconditions.checkNotNull(config, "config");
if (cache.containsKey(config))
return (C) cache.get(config);
log.debug("Trying to create {} proxy", config);
Properties properties;
try {
properties = findProperties(config);
} catch (IOException e) {
properties = new Properties();
log.warn(
"Configuration file for {} not found, falling back to default values",
config);
}
C proxy = (C) Proxy.newProxyInstance(this.getClass().getClassLoader(),
new Class<?>[] { config }, new ConfigInvocationHandler(
properties));
cache.put(config, proxy);
return proxy;
}
/**
* The invocation handler for configuration interfaces
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
private class ConfigInvocationHandler implements InvocationHandler {
/**
* The invocation handler properties
*/
private final Properties properties;
/**
* The invocation cache
*/
private Map<String, Object> cache = CollectionFactory.newWeakMap();
/**
* @param properties
* the properties
*/
public ConfigInvocationHandler(Properties properties) {
this.properties = properties;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
log.debug("Configuration service, method invoked: {}",
method.getName());
if (args == null || args.length == 0) {
final ConfigurationPropertyGetter getter = method
.getAnnotation(ConfigurationPropertyGetter.class);
final ConfigurationPropertiesKey propertiesKey = method
.getAnnotation(ConfigurationPropertiesKey.class);
if (getter == null)
return null;
if (propertiesKey == null)
return null;
return get(getter, propertiesKey, method.getReturnType());
} else if (args.length == 1) {
final ConfigurationPropertySetter setter = method
.getAnnotation(ConfigurationPropertySetter.class);
final ConfigurationPropertiesKey propertiesKey = method
.getAnnotation(ConfigurationPropertiesKey.class);
if (setter == null)
return null;
if (propertiesKey == null)
return null;
set(propertiesKey, args[0], method.getParameterTypes()[0]);
}
return null;
}
/**
* Get the untransformed value of an property
*
* @param getter
* the getter annotation
* @param propertiesKey
* if properties key annotation
* @param type
* the transformed type
* @return the untransformed property
*/
private Object get(ConfigurationPropertyGetter getter,
ConfigurationPropertiesKey propertiesKey, Class<?> type) {
if (cache.containsKey(propertiesKey.value()))
return cache.get(propertiesKey.value());
Object o = untransform(
getRaw(propertiesKey.value(), getter.defaultValue()), type);
cache.put(propertiesKey.value(), o);
return o;
}
/**
* Set the transformed value of an property
*
* @param setter
* the setter annotation
* @param value
* the untransformed value
* @param type
* the transformed type
*/
private void set(ConfigurationPropertiesKey setter, Object value,
Class<?> type) {
if (value != null) {
properties.setProperty(setter.value(),
transform(value.toString(), type));
cache.remove(setter.value());
} else {
properties.remove(setter.value());
cache.remove(setter.value());
}
}
/**
* Untransforms an value
*
* @param value
* the raw value
* @param type
* the output type
* @return the untransformed value
*/
private Object untransform(String value, Class<?> type) {
if (value == null)
return null;
if (type == String.class)
return value;
final Transformer<?> transformer = TransformerFactory
.getTransfromer(type);
if (transformer == null)
return null;
return transformer.untransform(value);
}
/**
* Transforms an value
*
* @param value
* the raw typed value
* @param type
* the input type
* @return the string representing <tt>value</tt>
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private String transform(Object value, Class<?> type) {
if (value == null)
return null;
if (value instanceof String)
return (String) value;
final Transformer transformer = TransformerFactory
.getTransfromer(type);
if (transformer == null)
return null;
return transformer.transform(value);
}
/**
* Retrieve the raw value from the property file
*
* @param key
* the key
* @param defaultValue
* the default value
* @return the value found or default value
*/
private String getRaw(String key, String defaultValue) {
if (properties == null)
return defaultValue;
if (properties.containsKey(key)) {
return (String) properties.get(key);
}
return defaultValue;
}
}
/**
* Tries to locate an .properties file
*
* @param clazz
* configuration interface class
* @return the found property
* @throws IOException
* if any i/o error occur
*/
private Properties findProperties(Class<?> clazz) throws IOException {
Preconditions.checkNotNull(clazz, "clazz");
ConfigurationName config = findAnnotation(ConfigurationName.class,
clazz);
Properties prop;
if (config == null) {
for (final Class<?> parent : clazz.getInterfaces()) {
prop = findProperties(parent);
if (prop != null)
return prop;
}
return null;
}
prop = new Properties();
final File file = new File(directory, config.value() + ".properties");
final InputStream in = new FileInputStream(file);
try {
prop.load(in);
} finally {
in.close();
}
return prop;
}
/**
* Tries to find an annotation in the class or any parent-class.
*
* @param <T>
* the annotation type
* @param annotationClass
* the annotation class
* @param clazz
* the class to look for annotations
* @return the annotation found
*/
private <T extends Annotation> T findAnnotation(Class<T> annotationClass,
Class<?> clazz) {
Preconditions.checkNotNull(annotationClass, "annotationClass");
Preconditions.checkNotNull(clazz, "clazz");
T ann = clazz.getAnnotation(annotationClass);
if (ann != null)
return ann;
for (Class<?> clazz2 : annotationClass.getInterfaces()) {
if (clazz2 == clazz)
continue;
ann = findAnnotation(annotationClass, clazz2);
if (ann != null)
return ann;
}
return null;
}
/**
* @return the configuration store directory
*/
public File getDirectory() {
return directory;
}
}

View File

@@ -0,0 +1,343 @@
/*
* 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.configuration;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.AbstractService.Depends;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.cache.CacheService;
import com.l2jserver.service.configuration.Configuration.ConfigurationPropertyGetter;
import com.l2jserver.service.core.LoggingService;
import com.l2jserver.util.factory.CollectionFactory;
import com.l2jserver.util.transformer.Transformer;
import com.l2jserver.util.transformer.TransformerFactory;
/**
* Creates {@link Configuration} object through Java {@link Proxy}. Uses the
* annotations in the interface to retrieve and store values.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
@Depends({ LoggingService.class, CacheService.class })
public class XMLConfigurationService extends AbstractService implements
ConfigurationService {
/**
* The directory in which configuration files are stored
*/
private File file = new File("./config/config.xml");
/**
* The logger
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* The DOM {@link DocumentBuilderFactory}
*/
private DocumentBuilderFactory factory;
/**
* The DOM {@link DocumentBuilder}
*/
private DocumentBuilder builder;
private Document properties;
/**
* The cache of configuration objects
*/
private Map<Class<?>, Object> cache = CollectionFactory.newWeakMap();
@Retention(RetentionPolicy.RUNTIME)
public @interface ConfigurationXPath {
String value();
}
/**
* Creates a new empty instance
*/
@Inject
protected XMLConfigurationService() {
}
/**
* Creates a new service instance. <b>This is used for tests</b>
*
* @param file
* the configuration file
*/
protected XMLConfigurationService(File file) {
this.file = file;
}
@Override
protected void doStart() throws ServiceStartException {
factory = DocumentBuilderFactory.newInstance();
try {
builder = factory.newDocumentBuilder();
properties = builder.parse(file);
} catch (ParserConfigurationException e) {
throw new ServiceStartException(e);
} catch (SAXException e) {
throw new ServiceStartException(e);
} catch (IOException e) {
throw new ServiceStartException(e);
}
}
@Override
@SuppressWarnings("unchecked")
public <C extends Configuration> C get(Class<C> config) {
Preconditions.checkNotNull(config, "config");
if (cache.containsKey(config))
return (C) cache.get(config);
log.debug("Trying to create {} proxy", config);
C proxy = (C) Proxy.newProxyInstance(this.getClass().getClassLoader(),
new Class<?>[] { config }, new ConfigInvocationHandler(
properties));
cache.put(config, proxy);
return proxy;
}
/**
* The invocation handler for configuration interfaces
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
private class ConfigInvocationHandler implements InvocationHandler {
/**
* The invocation handler properties
*/
private final Document properties;
/**
* The invocation cache
*/
private Map<String, Object> cache = CollectionFactory.newWeakMap();
/**
* @param properties
* the properties
*/
public ConfigInvocationHandler(Document properties) {
this.properties = properties;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
log.debug("Configuration service, method invoked: {}",
method.getName());
if (args == null || args.length == 0) {
final ConfigurationPropertyGetter getter = method
.getAnnotation(ConfigurationPropertyGetter.class);
final ConfigurationXPath xpath = method
.getAnnotation(ConfigurationXPath.class);
if (getter == null)
return null;
if (xpath == null)
return null;
return get(getter, xpath, method.getReturnType());
} else if (args.length == 1) {
final ConfigurationXPath xpath = method
.getAnnotation(ConfigurationXPath.class);
if (xpath == null)
return null;
set(xpath, args[0], method.getParameterTypes()[0]);
}
return null;
}
/**
* Get the untransformed value of an property
*
* @param getter
* the getter annotation
* @param xpath
* the xpath annotation
* @param type
* the transformed type
* @return the untransformed property
*/
private Object get(ConfigurationPropertyGetter getter,
ConfigurationXPath xpath, Class<?> type) {
if (cache.containsKey(xpath.value()))
return cache.get(xpath.value());
Object o;
try {
o = untransform(getRaw(xpath.value(), getter.defaultValue()),
type);
} catch (XPathExpressionException e) {
return null;
}
cache.put(xpath.value(), o);
return o;
}
/**
* Set the transformed value of an property
*
* @param xpath
* the xpath annotation
* @param value
* the untransformed value
* @param type
* the transformed type
* @throws XPathExpressionException
* if any error occur while compiling the XPath
*/
private void set(ConfigurationXPath xpath, Object value, Class<?> type)
throws XPathExpressionException {
Node node = (Node) XPathFactory.newInstance().newXPath()
.compile(xpath.value())
.evaluate(properties, XPathConstants.NODE);
if (value != null) {
node.setNodeValue(transform(value.toString(), type));
cache.put(xpath.value(), value);
} else {
node.getParentNode().removeChild(node);
cache.remove(xpath.value());
}
}
/**
* Untransforms an value
*
* @param value
* the raw value
* @param type
* the output type
* @return the untransformed value
*/
private Object untransform(String value, Class<?> type) {
if (value == null)
return null;
if (type == String.class)
return value;
final Transformer<?> transformer = TransformerFactory
.getTransfromer(type);
if (transformer == null)
return null;
return transformer.untransform(value);
}
/**
* Transforms an value
*
* @param value
* the raw typed value
* @param type
* the input type
* @return the string representing <tt>value</tt>
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private String transform(Object value, Class<?> type) {
if (value == null)
return null;
if (value instanceof String)
return (String) value;
final Transformer transformer = TransformerFactory
.getTransfromer(type);
if (transformer == null)
return null;
return transformer.transform(value);
}
/**
* Retrieve the raw value from the property file
*
* @param key
* the key
* @param defaultValue
* the default value
* @return the value found or default value
* @throws XPathExpressionException
* if any XPath exception occur
*/
private String getRaw(String key, String defaultValue)
throws XPathExpressionException {
if (properties == null)
return defaultValue;
String value = XPathFactory.newInstance().newXPath()
.evaluate(key, properties);
if (value == null || value.length() == 0)
return defaultValue;
return value;
}
}
/**
* Tries to find an annotation in the class or any parent-class.
*
* @param <T>
* the annotation type
* @param annotationClass
* the annotation class
* @param clazz
* the class to look for annotations
* @return the annotation found
*/
private <T extends Annotation> T findAnnotation(Class<T> annotationClass,
Class<?> clazz) {
Preconditions.checkNotNull(annotationClass, "annotationClass");
Preconditions.checkNotNull(clazz, "clazz");
T ann = clazz.getAnnotation(annotationClass);
if (ann != null)
return ann;
for (Class<?> clazz2 : annotationClass.getInterfaces()) {
if (clazz2 == clazz)
continue;
ann = findAnnotation(annotationClass, clazz2);
if (ann != null)
return ann;
}
return null;
}
/**
* @return the configuration store directory
*/
public File getFile() {
return file;
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.core;
import com.l2jserver.service.Service;
/**
* Service used to manage unhandled exceptions
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface ExceptionService extends Service {
/**
* Handles an unhandled exception
*
* @param t
* the exception
*/
void thrown(Throwable t);
/**
* Handles an unhandled exception and rethrows it again
*
* @param <T>
* the exception type
* @param t
* the exception
* @throws T
* same exception in <tt>t</tt>
*/
<T extends Throwable> void rethrown(T t) throws T;
}

View File

@@ -0,0 +1,74 @@
/*
* 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.core;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
/**
* Logging service implementation for Log4J
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class Log4JLoggingService extends AbstractService implements
LoggingService {
/**
* The root logger
*/
private Logger rootLogger;
/**
* The l2j logger
*/
private Logger l2jLogger;
/**
* The netty logger
*/
private Logger nettyLogger;
@Override
protected void doStart() throws ServiceStartException {
final Layout layout = new PatternLayout(
"[%p %d{yyyy-MM-dd HH-mm-ss}] %c:%L - %m%n");
BasicConfigurator.configure();
rootLogger = Logger.getRootLogger();
l2jLogger = Logger.getLogger("com.l2jserver");
nettyLogger = Logger.getLogger("org.jboss.netty");
rootLogger.removeAllAppenders();
rootLogger.setLevel(Level.WARN);
rootLogger.addAppender(new ConsoleAppender(layout, "System.err"));
l2jLogger.setLevel(Level.INFO);
nettyLogger.setLevel(Level.DEBUG);
Logger.getLogger("com.l2jserver.model.id.object.allocator").setLevel(
Level.WARN);
}
@Override
protected void doStop() throws ServiceStopException {
BasicConfigurator.resetConfiguration();
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.core;
import com.l2jserver.service.Service;
/**
* This service initializes and configures the logging provider. Currently SLF4J
* is used and implementations must initialize SLF4J and its respective binding.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface LoggingService extends Service {
}

View File

@@ -0,0 +1,202 @@
/*
* 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.core.log4j;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.FileAppender;
import org.apache.log4j.helpers.LogLog;
/**
* This class is appender that zips old file instead of appending it.<br>
* File is recognized as old if it's lastModified() is < JVM startup time.<br>
* So we can have per-run appending. <br>
*
* Unfortunately, UNIX systems doesn't support file creation date, so we have to
* use lastModified(), windows only solution is not good.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class TruncateToZipFileAppender extends FileAppender {
/**
* String that points to root directory for backups
*/
private String backupDir = "log/backup";
/**
* String that represents date format for backup files
*/
private String backupDateFormat = "yyyy-MM-dd HH-mm-ss";
/**
* Sets and <i>opens</i> the file where the log output will go. The
* specified file must be writable.
* <p>
* If there was already an opened file, then the previous file is closed
* first.
* <p/>
* <p>
* <b>Do not use this method directly. To configure a FileAppender or one of
* its subclasses, set its properties one by one and then call
* activateOptions.</b>
* <p/>
* <br>
* Truncation is done by {@link #truncate(java.io.File)}
*
* @param fileName
* The path to the log file.
* @param append
* If true will append to fileName. Otherwise will truncate
* fileName.
*/
@Override
public void setFile(String fileName, boolean append, boolean bufferedIO,
int bufferSize) throws IOException {
if (!append) {
truncate(new File(fileName));
}
super.setFile(fileName, append, bufferedIO, bufferSize);
}
/**
* This method creates archive with file instead of deleting it.
*
* @param file
* file to truncate
*/
protected void truncate(File file) {
LogLog.debug("Compression of file: " + file.getAbsolutePath()
+ " started.");
// Linux systems doesn't provide file creation time, so we have to hope
// that log files
// were not modified manually after server starup
// We can use here Windowns-only solution but that suck :(
if (FileUtils.isFileOlder(file, ManagementFactory.getRuntimeMXBean()
.getStartTime())) {
File backupRoot = new File(getBackupDir());
if (!backupRoot.exists() && !backupRoot.mkdirs()) {
throw new Error("Can't create backup dir for backup storage");
}
SimpleDateFormat df;
try {
df = new SimpleDateFormat(getBackupDateFormat());
} catch (Exception e) {
throw new Error("Invalid date formate for backup files: "
+ getBackupDateFormat(), e);
}
String date = df.format(new Date(file.lastModified()));
File zipFile = new File(backupRoot, file.getName() + "." + date
+ ".zip");
ZipOutputStream zos = null;
FileInputStream fis = null;
try {
zos = new ZipOutputStream(new FileOutputStream(zipFile));
ZipEntry entry = new ZipEntry(file.getName());
entry.setMethod(ZipEntry.DEFLATED);
entry.setCrc(FileUtils.checksumCRC32(file));
zos.putNextEntry(entry);
fis = FileUtils.openInputStream(file);
byte[] buffer = new byte[1024];
int readed;
while ((readed = fis.read(buffer)) != -1) {
zos.write(buffer, 0, readed);
}
} catch (Exception e) {
throw new Error("Can't create zip file", e);
} finally {
if (zos != null) {
try {
zos.close();
} catch (IOException e) {
// not critical error
LogLog.warn("Can't close zip file", e);
}
}
if (fis != null) {
try {
// not critical error
fis.close();
} catch (IOException e) {
LogLog.warn("Can't close zipped file", e);
}
}
}
if (!file.delete()) {
throw new Error("Can't delete old log file "
+ file.getAbsolutePath());
}
}
}
/**
* Returns root directory for backups
*
* @return root directory for backups
*/
public String getBackupDir() {
return backupDir;
}
/**
* Sets root directory for backups
*
* @param backupDir
* new root directory for backups
*/
public void setBackupDir(String backupDir) {
this.backupDir = backupDir;
}
/**
* Returns date format that should be used for backup files represented as
* string
*
* @return date formate for backup files
*/
public String getBackupDateFormat() {
return backupDateFormat;
}
/**
* Sets date format for bakcup files represented as string
*
* @param backupDateFormat
* date format for backup files
*/
public void setBackupDateFormat(String backupDateFormat) {
this.backupDateFormat = backupDateFormat;
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.core.threading;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* This future instance extends {@link Future} but also adds some additional
* features, such as waiting for an given task to finish.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
* @param <T>
* the {@link Future} return type
*/
public interface AsyncFuture<T> extends Future<T> {
/**
* Waits until the task is executed
*
* @throws ExecutionException
* if the thread has been interrupted while waiting
*/
void await() throws ExecutionException;
/**
* Waits until the task is executed
*
* @param timeout
* the timeout
* @param unit
* the timeout unit
*
* @throws InterruptedException
* if the thread has been interrupted while waiting
* @throws TimeoutException
* if timeout was exceeded
*/
void await(long timeout, TimeUnit unit) throws InterruptedException,
TimeoutException;
/**
* Waits until the task is executed
*
* @return true if execution ended with no error, false otherwise
*/
boolean awaitUninterruptibly();
/**
* Waits until the task is executed
*
* @param timeout
* the timeout
* @param unit
* the timeout unit
*
* @return true if execution ended with no error, false otherwise. Please
* note that false will be returned if the timeout has expired too!
*/
boolean awaitUninterruptibly(long timeout, TimeUnit unit);
}

View File

@@ -0,0 +1,29 @@
/*
* 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.core.threading;
import java.util.concurrent.ScheduledFuture;
/**
* This future instance extends {@link ScheduledFuture}. An scheduled future
* cannot return values neither await for its termination because its execution
* is continuously repeated.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface ScheduledAsyncFuture extends ScheduledFuture<Object> {
}

View File

@@ -0,0 +1,77 @@
/*
* 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.core.threading;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
/**
* This is an ThreadPool you can use it to schedule tasks in the future or to
* repeat them many times.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface ThreadPool {
/**
* Executes an asynchronous tasks.
*
* @param <T>
* the task return type
* @param callable
* the callable instance
* @return the {@link AsyncFuture} notified once the task has completed
*/
<T> AsyncFuture<T> async(Callable<T> callable);
/**
* Executes an asynchronous tasks at an scheduled time.
*
* @param <T>
* the task return type
* @param callable
* the callable instance
* @param delay
* the initial delay to wait before the task is executed
* @param unit
* the time unit of delay
* @return the {@link AsyncFuture} notified once the task has completed
*/
<T> AsyncFuture<T> async(long delay, TimeUnit unit, Callable<T> callable);
/**
* Executes an asynchronous tasks at an scheduled time.
*
* @param delay
* the initial delay to wait before the task is executed
* @param unit
* the time unit of delay
* @param repeat
* the repeating interval for this task
* @param task
* the task to be executed
* @return the {@link AsyncFuture} notified once the task has completed
*/
ScheduledAsyncFuture async(long delay, TimeUnit unit, long repeat,
Runnable task);
/**
* Disposes this thread pool. After disposing, it will no longer be able to
* execute tasks.
*/
void dispose();
}

View File

@@ -0,0 +1,102 @@
/*
* 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.core.threading;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import com.l2jserver.service.Service;
/**
* This service is responsible for scheduling tasks and executing them in
* parallel.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface ThreadService extends Service {
/**
* Executes an asynchronous tasks.
* <p>
* Tasks scheduled here will go to an default shared thread pool.
*
* @param <T>
* the task return type
* @param callable
* the callable instance
* @return the {@link AsyncFuture} notified once the task has completed
*/
<T> AsyncFuture<T> async(Callable<T> callable);
/**
* Executes an asynchronous tasks at an scheduled time. <b>Please note that
* resources in scheduled thread pool are limited and tasks should be
* performed fast.</b>
* <p>
* Tasks scheduled here will go to an default shared thread pool.
*
* @param <T>
* the task return type
* @param callable
* the callable instance
* @param delay
* the initial delay to wait before the task is executed
* @param unit
* the time unit of delay
* @return the {@link AsyncFuture} notified once the task has completed
*/
<T> AsyncFuture<T> async(long delay, TimeUnit unit, Callable<T> callable);
/**
* Executes an asynchronous tasks at an scheduled time. <b>Please note that
* resources in scheduled thread pool are limited and tasks should be
* performed fast.</b>
* <p>
* Tasks scheduled here will go to an default shared thread pool.
*
* @param delay
* the initial delay to wait before the task is executed
* @param unit
* the time unit of delay
* @param repeat
* the repeating interval for this task
* @param task
* the task to be executed
* @return the {@link AsyncFuture} notified once the task has completed
*/
ScheduledAsyncFuture async(long delay, TimeUnit unit, long repeat,
Runnable task);
/**
* Creates a new thread pool.
*
* @param name
* the pool name
* @param maxThreads
* the maximum amount of threads.
* @return the new thread pool
*/
ThreadPool createThreadPool(String name, int maxThreads);
/**
* Disposes an given thread pool. After disposing the thread pool will no
* longer be usable.
*
* @param pool
* the thread pool to be disposed
*/
void dispose(ThreadPool pool);
}

View File

@@ -0,0 +1,326 @@
/*
* 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.core.threading;
import java.util.concurrent.Callable;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
/**
* The default implementation for {@link ThreadService}
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ThreadServiceImpl extends AbstractService implements ThreadService {
/**
* The logger
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* The public shared thread pool
*/
private ThreadPool pool;
@Override
protected void doStart() throws ServiceStartException {
pool = createThreadPool("shared", 20);
// scheduler = Executors.newScheduledThreadPool(10);
// async = Executors.newCachedThreadPool();
}
@Override
public <T> AsyncFuture<T> async(Callable<T> callable) {
Preconditions.checkNotNull(callable, "callable");
log.debug("Scheduling async task: {}", callable);
return pool.async(callable);
}
@Override
public <T> AsyncFuture<T> async(long delay, TimeUnit unit,
Callable<T> callable) {
Preconditions.checkArgument(delay >= 0, "delay < 0");
Preconditions.checkNotNull(unit, "unit");
Preconditions.checkNotNull(callable, "callable");
log.debug("Scheduling async task in {}ms: {}", unit.toMillis(delay),
callable);
return pool.async(delay, unit, callable);
}
@Override
public ScheduledAsyncFuture async(long delay, TimeUnit unit, long repeat,
Runnable task) {
Preconditions.checkArgument(delay >= 0, "delay < 0");
Preconditions.checkArgument(repeat >= 0, "repeat < 0");
Preconditions.checkNotNull(unit, "unit");
Preconditions.checkNotNull(task, "task");
log.debug("Scheduling repeating async task in {}ms each {}ms: {}",
new Object[] { unit.toMillis(delay), unit.toMillis(repeat),
task });
return pool.async(delay, unit, repeat, task);
}
@Override
public ThreadPool createThreadPool(String name, int maxThreads) {
log.debug("Creating new ThreadPool {} with maximum of {} threads",
name, maxThreads);
return new ThreadPoolImpl(name,
Executors.newScheduledThreadPool(maxThreads));
}
@Override
public void dispose(ThreadPool pool) {
log.debug("Disposing ThreadPool {}", pool);
if (pool instanceof ThreadPoolImpl)
((ThreadPoolImpl) pool).executor.shutdown();
throw new UnsupportedOperationException(
"The given ThreadPool is not supported by this service");
}
@Override
protected void doStop() throws ServiceStopException {
dispose(pool);
pool = null;
}
/**
* Simple delegated implementation for {@link AsyncFuture}
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
* @param <T>
* the return type
*/
private class AsyncFutureImpl<T> implements AsyncFuture<T> {
/**
* The future that is delegated in this implementation
*/
private final Future<T> future;
/**
* Creates a new instance
*
* @param future
* the future
*/
private AsyncFutureImpl(Future<T> future) {
this.future = future;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return future.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return future.isCancelled();
}
@Override
public boolean isDone() {
return future.isDone();
}
@Override
public T get() throws InterruptedException, ExecutionException {
return future.get();
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {
return future.get(timeout, unit);
}
@Override
public void await() throws ExecutionException {
try {
this.get();
} catch (InterruptedException e) {
}
}
@Override
public void await(long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
try {
this.get(timeout, unit);
} catch (ExecutionException e) {
}
}
@Override
public boolean awaitUninterruptibly() {
try {
this.get();
return true;
} catch (InterruptedException e) {
return false;
} catch (ExecutionException e) {
return false;
}
}
@Override
public boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
try {
this.get(timeout, unit);
return true;
} catch (InterruptedException e) {
return false;
} catch (ExecutionException e) {
return false;
} catch (TimeoutException e) {
return false;
}
}
}
/**
* Future implementation for asynchronous tasks
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
private class ScheduledAsyncFutureImpl implements ScheduledAsyncFuture {
/**
* The {@link ExecutorService} {@link ScheduledFuture}
*/
private final ScheduledFuture<?> future;
/**
* @param future
* the {@link ExecutorService} {@link ScheduledFuture}
*/
public ScheduledAsyncFutureImpl(ScheduledFuture<?> future) {
this.future = future;
}
@Override
public long getDelay(TimeUnit unit) {
return future.getDelay(unit);
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return future.cancel(mayInterruptIfRunning);
}
@Override
public int compareTo(Delayed o) {
return future.compareTo(o);
}
@Override
public boolean isCancelled() {
return future.isCancelled();
}
@Override
public boolean isDone() {
return future.isDone();
}
@Override
public Object get() throws InterruptedException, ExecutionException {
throw new UnsupportedOperationException();
}
@Override
public Object get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
TimeoutException {
throw new UnsupportedOperationException();
}
}
/**
* Thread pool implementation
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
private class ThreadPoolImpl implements ThreadPool {
/**
* This thread pool name
*/
private final String name;
/**
* The backing executor
*/
private final ScheduledExecutorService executor;
/**
* @param name
* the pool name
* @param executor
* the backing {@link ScheduledExecutorService}
*/
public ThreadPoolImpl(String name, ScheduledExecutorService executor) {
this.name = name;
this.executor = executor;
}
@Override
public <T> AsyncFuture<T> async(Callable<T> callable) {
log.debug("Task {} submited to {}", callable, name);
return new AsyncFutureImpl<T>(executor.submit(callable));
}
@Override
public <T> AsyncFuture<T> async(long delay, TimeUnit unit,
Callable<T> callable) {
if (log.isDebugEnabled())
log.debug("Task {} scheduled in {} {} to {}", new Object[] {
callable, delay, unit, name });
return new AsyncFutureImpl<T>(executor.schedule(callable, delay,
unit));
}
@Override
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 });
return new ScheduledAsyncFutureImpl(executor.scheduleAtFixedRate(
task, delay, repeat, unit));
}
@Override
public void dispose() {
ThreadServiceImpl.this.dispose(this);
}
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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.core.vfs;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
import com.l2jserver.service.configuration.ConfigurationService;
/**
* Implementation of {@link VFSService} using default Java7 APIs.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class Java7VFSService extends AbstractService implements VFSService {
/**
* The logger
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* The Java 7 vfs configuration
*/
private final Java7VFSConfiguration config;
/**
* The {@link FileSystem} implementation
*/
private FileSystem fs;
/**
* The root {@link Path} of the server data
*/
private Path root;
/**
* Configuration interface for {@link Java7VFSService}.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface Java7VFSConfiguration extends VFSConfiguration {
}
/**
* @param configService
* the configuration service
*/
@Inject
protected Java7VFSService(final ConfigurationService configService) {
this.config = configService.get(Java7VFSConfiguration.class);
}
@Override
protected void doStart() throws ServiceStartException {
try {
fs = FileSystems.getFileSystem(new URI("file:///"));
} catch (URISyntaxException e) {
throw new ServiceStartException(e);
}
root = config.getRoot().toAbsolutePath();
log.debug("Root path is {}", root);
}
@Override
public Path resolve(String path) {
log.debug("Resolving file {}", path);
return root.resolve(path);
}
@Override
protected void doStop() throws ServiceStopException {
try {
fs.close();
} catch (IOException e) {
throw new ServiceStopException(e);
} finally {
fs = null;
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.core.vfs;
import java.net.URI;
import java.nio.file.Path;
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.configuration.ProxyConfigurationService.ConfigurationPropertiesKey;
import com.l2jserver.service.configuration.XMLConfigurationService.ConfigurationXPath;
/**
* The VFS service is responsible for creating a Virtual File System that is
* capable of reading files inside ZIP, TAR, BZIP and GZIP.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface VFSService extends Service {
/**
* VFS service configuration
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
* @see Configuration
*/
@ConfigurationName("vfs")
public interface VFSConfiguration extends ServiceConfiguration {
/**
* @return the VFS root {@link URI}
*/
@ConfigurationPropertyGetter(defaultValue = "")
@ConfigurationPropertiesKey("vfs.root")
@ConfigurationXPath("/configuration/services/vfs/root")
Path getRoot();
/**
* @param root
* the new VFS root {@link URI}
*/
@ConfigurationPropertySetter
@ConfigurationPropertiesKey("vfs.root")
@ConfigurationXPath("/configuration/services/vfs/root")
void setRoot(Path root);
}
/**
* Resolves an file. If the file cannot be resolved, null will be returned.
* <p>
* Please note that event if the file DOES NOT exists a valid object will be
* returned.
*
* @param path
* the file path as an string
* @return the resolved file. Will return null if could not resolve.
*/
Path resolve(String path);
}

View File

@@ -0,0 +1,113 @@
/*
* 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.database;
import java.util.Iterator;
import com.google.inject.Inject;
import com.l2jserver.model.Model;
import com.l2jserver.model.id.ID;
/**
* Abstract DAO implementations. Store an instance of {@link DatabaseService}.
* Default {@link Iterator} implementation in this class supports
* {@link Iterator#remove()} and will delete the row from the database.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <T>
* the dao object type
* @param <I>
* the object id type
*/
public abstract class AbstractDAO<T extends Model<?>, I extends ID<?>>
implements DataAccessObject<T, I> {
/**
* The database service instance
*/
protected final DatabaseService database;
/**
* @param database
* the database service
*/
@Inject
protected AbstractDAO(DatabaseService database) {
this.database = database;
}
@Override
public boolean save(T object) {
return save(object, false);
}
@Override
public boolean save(T object, boolean force) {
switch (object.getObjectDesire()) {
case INSERT:
return insert(object);
case UPDATE:
return update(object);
case DELETE:
return delete(object);
case NONE:
return (force ? update(object) : false);
default:
return false;
}
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
/**
* The Iterator that will return the ID objects
*/
private final Iterator<I> iterator = AbstractDAO.this.selectIDs()
.iterator();
/**
* The last used ID (will be used to remove the last element)
*/
private I lastID;
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public T next() {
lastID = iterator.next();
if (lastID == null)
return null;
return select(lastID);
}
@Override
public void remove() {
AbstractDAO.this.delete(select(lastID));
}
};
}
/**
* @return the database service
*/
public DatabaseService getDatabase() {
return database;
}
}

View File

@@ -0,0 +1,820 @@
/*
* 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.database;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.l2jserver.model.Model;
import com.l2jserver.model.Model.ObjectDesire;
import com.l2jserver.model.id.ID;
import com.l2jserver.model.id.object.allocator.IDAllocator;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
import com.l2jserver.service.cache.Cache;
import com.l2jserver.service.cache.CacheService;
import com.l2jserver.service.configuration.ConfigurationService;
import com.l2jserver.service.configuration.ProxyConfigurationService.ConfigurationPropertiesKey;
import com.l2jserver.service.configuration.XMLConfigurationService.ConfigurationXPath;
import com.l2jserver.service.core.threading.ScheduledAsyncFuture;
import com.l2jserver.service.core.threading.ThreadService;
import com.l2jserver.util.ArrayIterator;
import com.l2jserver.util.factory.CollectionFactory;
/**
* This is an implementation of {@link DatabaseService} that provides an layer
* to JDBC.
*
* <h1>Internal specification</h1> <h2>The {@link Query} object</h2>
*
* If you wish to implement a new {@link DataAccessObject} you should try not
* use {@link Query} object directly because it only provides low level access
* to the JDBC architecture. Instead, you could use an specialized class, like
* {@link InsertUpdateQuery}, {@link SelectListQuery} or
* {@link SelectSingleQuery}. If you do need low level access, feel free to use
* the {@link Query} class directly.
*
* <h2>The {@link Mapper} object</h2>
*
* The {@link Mapper} object maps an JDBC {@link ResultSet} into an Java
* {@link Object}. All {@link Model} objects support {@link CachedMapper} that
* will cache result based on its {@link ID} and always use the same object with
* the same {@link ID}.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public abstract class AbstractJDBCDatabaseService extends AbstractService
implements DatabaseService {
/**
* The configuration object
*/
private final JDBCDatabaseConfiguration config;
/**
* The logger
*/
private final Logger log = LoggerFactory
.getLogger(AbstractJDBCDatabaseService.class);
/**
* The cache service
*/
private final CacheService cacheService;
/**
* The thread service
*/
private final ThreadService threadService;
/**
* The database connection pool
*/
private GenericObjectPool connectionPool;
/**
* The dayabase connection factory
*/
private ConnectionFactory connectionFactory;
/**
* The poolable connection factory
*/
@SuppressWarnings("unused")
private PoolableConnectionFactory poolableConnectionFactory;
/**
* The connection {@link DataSource}.
*/
private PoolingDataSource dataSource;
/**
* An cache object
*/
private Cache<Object, Model<?>> objectCache;
/**
* Future for the auto-save task. Each object that has changed is auto saved
* every 1 minute.
*/
private ScheduledAsyncFuture autoSaveFuture;
/**
* Configuration interface for {@link AbstractJDBCDatabaseService}.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface JDBCDatabaseConfiguration extends DatabaseConfiguration {
/**
* @return the jdbc url
*/
@ConfigurationPropertyGetter(defaultValue = "jdbc:mysql://localhost/l2jserver2")
@ConfigurationPropertiesKey("jdbc.url")
@ConfigurationXPath("/configuration/services/database/jdbc/url")
String getJdbcUrl();
/**
* @param jdbcUrl
* the new jdbc url
*/
@ConfigurationPropertySetter
@ConfigurationPropertiesKey("jdbc.url")
@ConfigurationXPath("/configuration/services/database/jdbc/url")
void setJdbcUrl(String jdbcUrl);
/**
* @return the jdbc driver class
*/
@ConfigurationPropertyGetter(defaultValue = "com.jdbc.jdbc.Driver")
@ConfigurationPropertiesKey("jdbc.driver")
@ConfigurationXPath("/configuration/services/database/jdbc/driver")
String getDriver();
/**
* @param driver
* the new jdbc driver
*/
@ConfigurationPropertySetter
@ConfigurationPropertiesKey("jdbc.driver")
@ConfigurationXPath("/configuration/services/database/jdbc/driver")
void setDriver(Class<?> driver);
/**
* @return the jdbc database username
*/
@ConfigurationPropertyGetter(defaultValue = "l2j")
@ConfigurationPropertiesKey("jdbc.username")
@ConfigurationXPath("/configuration/services/database/jdbc/username")
String getUsername();
/**
* @param username
* the jdbc database username
*/
@ConfigurationPropertySetter
@ConfigurationPropertiesKey("jdbc.username")
@ConfigurationXPath("/configuration/services/database/jdbc/username")
void setUsername(String username);
/**
* @return the jdbc database password
*/
@ConfigurationPropertyGetter(defaultValue = "changeme")
@ConfigurationPropertiesKey("jdbc.password")
@ConfigurationXPath("/configuration/services/database/jdbc/password")
String getPassword();
/**
* @param password
* the jdbc database password
*/
@ConfigurationPropertySetter
@ConfigurationPropertiesKey("jdbc.password")
@ConfigurationXPath("/configuration/services/database/jdbc/password")
void setPassword(String password);
/**
* @return the maximum number of active connections
*/
@ConfigurationPropertyGetter(defaultValue = "20")
@ConfigurationPropertiesKey("jdbc.active.max")
@ConfigurationXPath("/configuration/services/database/connections/active-maximum")
int getMaxActiveConnections();
/**
* @param password
* the maximum number of active connections
*/
@ConfigurationPropertySetter
@ConfigurationPropertiesKey("jdbc.active.max")
@ConfigurationXPath("/configuration/services/database/connections/active-maximum")
void setMaxActiveConnections(int password);
/**
* @return the maximum number of idle connections
*/
@ConfigurationPropertyGetter(defaultValue = "20")
@ConfigurationPropertiesKey("jdbc.idle.max")
@ConfigurationXPath("/configuration/services/database/connections/idle-maximum")
int getMaxIdleConnections();
/**
* @param password
* the maximum number of idle connections
*/
@ConfigurationPropertySetter
@ConfigurationPropertiesKey("jdbc.idle.max")
@ConfigurationXPath("/configuration/services/database/connections/idle-maximum")
void setMaxIdleConnections(int password);
/**
* @return the minimum number of idle connections
*/
@ConfigurationPropertyGetter(defaultValue = "5")
@ConfigurationPropertiesKey("jdbc.idle.min")
@ConfigurationXPath("/configuration/services/database/connections/idle-minimum")
int getMinIdleConnections();
/**
* @param password
* the minimum number of idle connections
*/
@ConfigurationPropertySetter
@ConfigurationPropertiesKey("jdbc.idle.min")
@ConfigurationXPath("/configuration/services/database/connections/idle-minimum")
void setMinIdleConnections(int password);
}
/**
* @param configService
* the configuration service
* @param cacheService
* the cache service
* @param threadService
* the thread service
*/
@Inject
public AbstractJDBCDatabaseService(ConfigurationService configService,
CacheService cacheService, ThreadService threadService) {
config = configService.get(JDBCDatabaseConfiguration.class);
this.cacheService = cacheService;
this.threadService = threadService;
}
@Override
protected void doStart() throws ServiceStartException {
connectionPool = new GenericObjectPool(null);
connectionPool.setMaxActive(config.getMaxActiveConnections());
connectionPool.setMinIdle(config.getMinIdleConnections());
connectionPool.setMaxIdle(config.getMaxIdleConnections());
// test if connections are active while idle
connectionPool.setTestWhileIdle(true);
connectionFactory = new DriverManagerConnectionFactory(
config.getJdbcUrl(), config.getUsername(), config.getPassword());
poolableConnectionFactory = new PoolableConnectionFactory(
connectionFactory, connectionPool, null, "SELECT 1", false,
true);
dataSource = new PoolingDataSource(connectionPool);
// cache must be large enough for all world objects, to avoid
// duplication... this would endanger non-persistent states
objectCache = cacheService.createEternalCache("database-service",
IDAllocator.ALLOCABLE_IDS);
// start the auto save task
autoSaveFuture = threadService.async(60, TimeUnit.SECONDS, 60,
new Runnable() {
@Override
public void run() {
log.debug("Auto save task started");
int objects = 0;
for (final Model<?> object : objectCache) {
@SuppressWarnings("unchecked")
final DataAccessObject<Model<?>, ?> dao = getDAO(object
.getClass());
if (dao.save(object)) {
objects++;
}
}
log.info(
"{} objects have been saved by the auto save task",
objects);
}
});
}
/**
* Executes an <tt>query</tt> in the database.
*
* @param <T>
* the query return type
* @param query
* the query
* @return an instance of <tt>T</tt>
*/
public <T> T query(Query<T> query) {
Preconditions.checkNotNull(query, "query");
try {
final Connection 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;
} finally {
conn.close();
}
} catch (SQLException e) {
log.error("Could not open database connection", e);
return null;
}
}
/**
* Checks for the cached version of the object
*
* @param id
* the object ID
* @return the cached version, if any
*/
public Object getCachedObject(Object id) {
Preconditions.checkNotNull(id, "id");
log.debug("Fetching cached object {}", id);
return objectCache.get(id);
}
/**
* Checks for the cached version of the object
*
* @param id
* the object ID
* @return true if has an cached version,
*/
public boolean hasCachedObject(Object id) {
Preconditions.checkNotNull(id, "id");
log.debug("Locating cached object {}", id);
return objectCache.contains(id);
}
/**
* Updates an cache object
*
* @param id
* the cache key
* @param value
* the model value
*/
public void updateCache(ID<?> id, Model<?> value) {
Preconditions.checkNotNull(id, "key");
Preconditions.checkNotNull(value, "value");
log.debug("Updating cached object {} with {}", id, value);
objectCache.put(id, value);
}
/**
* Removes an cached object
*
* @param id
* the object id
*/
public void removeCache(Object id) {
Preconditions.checkNotNull(id, "key");
log.debug("Removing cached object {}", id);
objectCache.remove(id);
}
@Override
protected void doStop() throws ServiceStopException {
autoSaveFuture.cancel(true);
autoSaveFuture = null;
cacheService.dispose(objectCache);
objectCache = null;
try {
if (connectionPool != null)
connectionPool.close();
} catch (Exception e) {
log.error("Error stopping database service", e);
throw new ServiceStopException(e);
} finally {
connectionPool = null;
connectionFactory = null;
poolableConnectionFactory = null;
dataSource = null;
}
}
/**
* The query interface. The query will receive an connection an will be
* executed. The can return return a value if required.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <R>
* the return type
*/
public interface Query<R> {
/**
* Execute the query in <tt>conn</tt>
*
* @param conn
* the connection
* @return the query return value
* @throws SQLException
* if any SQL error occur
*/
R query(Connection conn) throws SQLException;
}
/**
* This query is used for the following statements:
* <ul>
* <li>INSERT INTO</li>
* <li>UPDATE</li>
* <li>DELETE FROM</li>
* </ul>
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <T>
* the query return type
*/
public static abstract class InsertUpdateQuery<T> implements Query<Integer> {
/**
* The logger
*/
private final Logger log = LoggerFactory
.getLogger(InsertUpdateQuery.class);
/**
* The iterator
*/
private final Iterator<T> iterator;
/**
* Creates a new query for <tt>objects</tt>
*
* @param objects
* the object list
*/
public InsertUpdateQuery(T... objects) {
this(new ArrayIterator<T>(objects));
}
/**
* Create a new query for objects in <tt>iterator</tt>
*
* @param iterator
* the object iterator
*/
public InsertUpdateQuery(Iterator<T> iterator) {
this.iterator = iterator;
}
@SuppressWarnings("unchecked")
@Override
public Integer query(Connection conn) throws SQLException {
Preconditions.checkNotNull(conn, "conn");
log.debug("Starting INSERT/UPDATE query execution");
int rows = 0;
while (iterator.hasNext()) {
final T object = iterator.next();
final String queryString = query();
log.debug("Preparing statement for {}: {}", object, queryString);
final PreparedStatement st = conn.prepareStatement(queryString,
Statement.RETURN_GENERATED_KEYS);
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 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<? extends ID<?>> mapper = keyMapper();
if (mapper == null)
continue;
final ResultSet rs = st.getGeneratedKeys();
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);
}
}
}
return rows;
}
/**
* Creates the <b>prepared</b> query for execution
*
* @return the <b>prepared</b> query
*/
protected abstract String query();
/**
* Set the parameters for in <tt>statement</tt> for <tt>object</tt>
*
* @param st
* the prepared statement
* @param object
* the object
* @throws SQLException
* if any SQL error occur
*/
protected abstract void parametize(PreparedStatement st, T object)
throws SQLException;
/**
* Return the key mapper. Can be null if no generated keys are used or
* are not important.
*
* @return the key mapper
*/
protected Mapper<? extends ID<?>> keyMapper() {
return null;
}
}
/**
* An select query that returns a list of objects of type <tt>T</tt>
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <T>
* the query return type
*/
public static abstract class SelectListQuery<T> implements Query<List<T>> {
/**
* The logger
*/
private final Logger log = LoggerFactory
.getLogger(SelectListQuery.class);
@Override
public List<T> query(Connection conn) throws SQLException {
Preconditions.checkNotNull(conn, "conn");
log.debug("Starting SELECT List<?> query execution");
final String queryString = query();
log.debug("Preparing statement with {}", queryString);
final PreparedStatement st = conn.prepareStatement(query());
log.debug("Parametizing statement {}", st);
parametize(st);
log.debug("Sending query to database for {}", st);
st.execute();
final List<T> list = CollectionFactory.newList();
final ResultSet rs = st.getResultSet();
final Mapper<T> mapper = mapper();
log.debug("Database returned {}", rs);
while (rs.next()) {
log.debug("Mapping row with {}", mapper);
final T obj = mapper.map(rs);
if (obj == null) {
log.debug("Mapper {} returned a null row", mapper);
continue;
}
if (obj instanceof Model)
((Model<?>) obj).setObjectDesire(ObjectDesire.NONE);
log.debug("Mapper {} returned {}", mapper, obj);
list.add(obj);
}
return list;
}
/**
* Creates the <b>prepared</b> query for execution
*
* @return the <b>prepared</b> query
*/
protected abstract String query();
/**
* Set the parameters for in <tt>statement</tt> for <tt>object</tt>
*
* @param st
* the prepared statement
* @throws SQLException
* if any SQL error occur
*/
protected void parametize(PreparedStatement st) throws SQLException {
}
/**
* Return the mapper that will bind {@link ResultSet} objects into an
* <tt>T</tt> object instance. The mapper will need to create the object
* instance.
* <p>
* <b>Note</b>: This method will be called for each row, an thus is a
* good idea to create a new instance on each call!
*
* @return the mapper instance
*/
protected abstract Mapper<T> mapper();
}
/**
* An select query that returns a single object of type <tt>T</tt>
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <T>
* the query return type
*/
public static abstract class SelectSingleQuery<T> implements Query<T> {
/**
* The logger
*/
private final Logger log = LoggerFactory
.getLogger(SelectSingleQuery.class);
@Override
public T query(Connection conn) throws SQLException {
Preconditions.checkNotNull(conn, "conn");
log.debug("Starting SELECT single query execution");
final String queryString = query();
log.debug("Preparing statement with {}", queryString);
final PreparedStatement st = conn.prepareStatement(query());
log.debug("Parametizing statement {}", st);
parametize(st);
log.debug("Sending query to database for {}", st);
st.execute();
final ResultSet rs = st.getResultSet();
final Mapper<T> mapper = mapper();
log.debug("Database returned {}", rs);
while (rs.next()) {
log.debug("Mapping row {} with {}", rs, mapper);
final T object = mapper.map(rs);
if (object instanceof Model)
((Model<?>) object).setObjectDesire(ObjectDesire.NONE);
log.debug("Mapper {} returned {}", mapper, object);
return object;
}
return null;
}
/**
* Creates the <b>prepared</b> query for execution
*
* @return the <b>prepared</b> query
*/
protected abstract String query();
/**
* Set the parameters for in <tt>statement</tt> for <tt>object</tt>
*
* @param st
* the prepared statement
* @throws SQLException
* if any SQL error occur
*/
protected void parametize(PreparedStatement st) throws SQLException {
}
/**
* Return the mapper that will bind {@link ResultSet} objects into an
* <tt>T</tt> object instance. The mapper will need to create the object
* instance.
*
* @return the mapper
*/
protected abstract Mapper<T> mapper();
}
/**
* The {@link Mapper} maps an {@link ResultSet} into an object <tt>T</tt>
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <T>
* the object type
*/
public interface Mapper<T> {
/**
* Map the result set value into an object.
* <p>
* <b>Note</b>: it is required to call {@link ResultSet#next()}, since
* it is called by the {@link Query}.
*
* @param rs
* the result set
* @return the created instance
* @throws SQLException
* if any SQL error occur
*/
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 Model<?>, I extends ID<?>>
implements Mapper<T> {
/**
* The logger
*/
private final Logger log = LoggerFactory
.getLogger(SelectSingleQuery.class);
/**
* The database service instance
*/
private final AbstractJDBCDatabaseService database;
/**
* The {@link ID} mapper
*/
private final Mapper<I> idMapper;
/**
* Creates a new instance
*
* @param database
* the database service
* @param idMapper
* the {@link ID} {@link Mapper}
*/
public CachedMapper(AbstractJDBCDatabaseService database,
Mapper<I> idMapper) {
this.database = database;
this.idMapper = idMapper;
}
@Override
@SuppressWarnings("unchecked")
public final T map(ResultSet rs) throws SQLException {
log.debug("Mapping row {} ID with {}", rs, idMapper);
final I id = idMapper.map(rs);
Preconditions.checkNotNull(id, "id");
log.debug("ID={}, locating cached object", id);
if (database.hasCachedObject(id))
return (T) database.getCachedObject(id);
log.debug("Cached object not found, creating...");
final T object = map(id, rs);
if (object != null)
database.updateCache(id, object);
log.debug("Object {} created", object);
return object;
}
/**
* 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
* if any SQL error occur
*/
protected abstract T map(I id, ResultSet rs) throws SQLException;
}
}

View File

@@ -0,0 +1,123 @@
/*
* 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.database;
import java.util.Collection;
import java.util.Iterator;
import com.l2jserver.model.Model;
import com.l2jserver.model.id.ID;
import com.l2jserver.service.cache.IgnoreCaching;
/**
* The Data Access Object interface used used to retrieve, save and remove
* objects from the database. The underlying storage engine can be an plain text
* file, SQL Database or an serialized version of the object. This layer will
* abstract the translation of the data and ease the transition from one engine
* to another.
* <p>
* Every DAO is also an {@link Iterable}. If you wish you can iterate through
* all objects in the database very abstractly. But please note that the default
* {@link Iterator} implementation in {@link AbstractDAO} will load all the
* {@link ID} objects and for every call {@link Iterator#next()}, a new database
* query will be made requesting the given object. In a large dataset, this
* could be a huge performance issue. DAO implementations are encouraged to
* override the iterator implementation with a more specific implementation.
*
* @param <O>
* the {@link Model} supported by this DAO
* @param <I>
* the ID used to represent the {@link Model} in this DAO
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public interface DataAccessObject<O extends Model<?>, I extends ID<?>> extends
Iterable<O> {
/**
* Load the instance represented by <tt>id</tt> from the database
*
* @param id
* the id
* @return the selected object. <tt>null</tt> if could not be found in the
* database.
*/
O select(I id);
/**
* Loads an List of all {@link ID}s in the database
*
* @return the list containing all {@link ID} objects
*/
@IgnoreCaching
Collection<I> selectIDs();
/**
* Save the instance to the database. If a new database entry was created
* returns true. This method will only save if the object has changed.
*
* @param object
* the object
* @return true if the row was inserted or updated
* @see DataAccessObject#save(Model, boolean)
*/
@IgnoreCaching
boolean save(O object);
/**
* Save the instance to the database. If a new database entry was created
* returns true.
*
* @param object
* the object
* @param force
* will force an save, even if the object has not changed
* @return true if the row was inserted or updated
*/
@IgnoreCaching
boolean save(O object, boolean force);
/**
* Inserts the instance in the database.
*
* @param object
* the object
* @return true if the row was inserted
*/
@IgnoreCaching
boolean insert(O object);
/**
* Updates the instance in the database.
*
* @param object
* the object
* @return true if the row was updated
*/
@IgnoreCaching
boolean update(O object);
/**
* Deletes the instance in the database.
*
* @param object
* the object
* @return true if the row was deleted
*/
@IgnoreCaching
boolean delete(O object);
}

View File

@@ -0,0 +1,66 @@
/*
* 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.database;
import com.l2jserver.model.Model;
import com.l2jserver.model.id.ID;
import com.l2jserver.service.Service;
import com.l2jserver.service.ServiceConfiguration;
import com.l2jserver.service.configuration.Configuration;
import com.l2jserver.service.configuration.ProxyConfigurationService.ConfigurationName;
/**
* This service provides access to an database implementation. Each
* implementation had its own {@link DataAccessObject} model, however they do
* need to respect {@link DataAccessObject} interfaces located in "
* <tt>com.l2jserver.db.dao</tt>". There can be several service implementation,
* but only one of them can be active at an given time.
*
* The service does not directly provide much functionality most of its
* operations are done trough an {@link DataAccessObject}. Each service
* implementation provides an custom interface that is used to link
* {@link DataAccessObject}-{@link DatabaseService Service}. See implementation
* specific documentation for more information.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface DatabaseService extends Service {
/**
* Database service configuration
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
* @see Configuration
*/
@ConfigurationName("database")
public interface DatabaseConfiguration extends ServiceConfiguration {
}
/**
* Returns the {@link DataAccessObject} used to retrieve and save objects of
* type <M>
*
* @param <M>
* the {@link Model} type
* @param <I>
* the {@link ID} type
* @param model
* the model class
* @return the {@link DataAccessObject} for {@link Model}
*/
<M extends Model<I>, I extends ID<M>> DataAccessObject<M, I> getDAO(
Class<M> model);
}

View File

@@ -0,0 +1,52 @@
/*
* 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.database.jdbc;
import com.google.inject.Inject;
import com.l2jserver.model.Model;
import com.l2jserver.model.id.ID;
import com.l2jserver.service.database.AbstractDAO;
import com.l2jserver.service.database.DatabaseService;
import com.l2jserver.service.database.AbstractJDBCDatabaseService;
/**
* {@link AbstractDAO} for JDBC DAO implementation
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <T>
* the object for the DAO
* @param <I>
* the object ID type
*/
public abstract class AbstractJDBCDAO<T extends Model<?>, I extends ID<?>>
extends AbstractDAO<T, I> {
/**
* The JDBC Database Service
*/
protected final AbstractJDBCDatabaseService database;
/**
* @param database
* the database service
*/
@Inject
protected AbstractJDBCDAO(DatabaseService database) {
super(database);
this.database = (AbstractJDBCDatabaseService) database;
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.util;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* This {@link Iterator} takes an array as input and iterate over it.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <T>
* the array type
*/
public class ArrayIterator<T> implements Iterator<T> {
/**
* The objects
*/
private final T[] objects;
/**
* the iterator index
*/
private int i = 0;
/**
* Creates a new iterator instance
*
* @param objects
* the objects
*/
public ArrayIterator(T... objects) {
this.objects = objects;
}
@Override
public boolean hasNext() {
return i < objects.length;
}
@Override
public T next() {
try {
return objects[i++];
} catch (ArrayIndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
@Override
public void remove() {
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.util;
import java.util.Arrays;
import org.jboss.netty.buffer.ChannelBuffer;
/**
* This is an Netty {@link ChannelBuffer} utility class.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class BufferUtils {
/**
* Reads an unicode string from the buffer
*
* @param buffer
* the buffer
* @return the read unicode string
*/
public static final String readString(ChannelBuffer buffer) {
char[] str = new char[buffer.readableBytes()];
int index = 0;
char c;
while ((c = buffer.readChar()) != 0) {
str[index++] = c;
}
return String.valueOf(Arrays.copyOfRange(str, 0, index));
}
/**
* Writes an unicode string to the buffer
*
* @param buffer
* the buffer
* @param str
* the string
*/
public static final void writeString(ChannelBuffer buffer, String str) {
if (str != null && str.length() > 0) {
final int len = str.length();
for (int i = 0; i < len; i++) {
buffer.writeChar(str.charAt(i));
}
}
buffer.writeShort(0);
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.util;
/**
* This class contains utilities that are used when we are working with classes
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ClassUtils {
/**
* Return true if class a is either equivalent to class b, or if class a is
* a subclass of class b, i.e. if a either "extends" or "implements" b. Note
* that either or both "Class" objects may represent interfaces.
*
* @param a
* class
* @param b
* class
* @return true if a == b or a extends b or a implements b
*/
public static boolean isSubclass(Class<?> a, Class<?> b) {
// We rely on the fact that for any given java class or
// primitive type there is a unique Class object, so
// we can use object equivalence in the comparisons.
if (a == b) {
return true;
}
if (a == null || b == null) {
return false;
}
for (Class<?> x = a; x != null; x = x.getSuperclass()) {
if (x == b) {
return true;
}
if (b.isInterface()) {
Class<?> interfaces[] = x.getInterfaces();
for (Class<?> anInterface : interfaces) {
if (isSubclass(anInterface, b)) {
return true;
}
}
}
}
return false;
}
/**
* Checks if class in member of the package
*
* @param clazz
* class to check
* @param packageName
* package
* @return true if is member
*/
public static boolean isPackageMember(Class<?> clazz, String packageName) {
return isPackageMember(clazz.getName(), packageName);
}
/**
* Checks if classNames belongs to package
*
* @param className
* class name
* @param packageName
* package
* @return true if belongs
*/
public static boolean isPackageMember(String className, String packageName) {
if (!className.contains(".")) {
return packageName == null || packageName.isEmpty();
} else {
String classPackage = className.substring(0,
className.lastIndexOf('.'));
return packageName.equals(classPackage);
}
}
}

View File

@@ -0,0 +1,211 @@
/*
* 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.util;
import com.l2jserver.util.geometry.Point;
import com.l2jserver.util.geometry.Point3D;
/**
* Class with basic math.<br>
* Thanks to:
* <ul>
* <li>http://geom-java.sourceforge.net/</li>
* <li>
* http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/DistancePoint.java</li>
* </ul>
* <br>
* <br>
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class MathUtil {
/**
* Returns distance between two 2D points
*
* @param point1
* first point
* @param point2
* second point
* @return distance between points
*/
public static double getDistance(Point point1, Point point2) {
return getDistance(point1.x, point1.y, point2.x, point2.y);
}
/**
* Returns distance between two sets of coords
*
* @param x1
* first x coord
* @param y1
* first y coord
* @param x2
* second x coord
* @param y2
* second y coord
* @return distance between sets of coords
*/
public static double getDistance(int x1, int y1, int x2, int y2) {
// using long to avoid possible overflows when multiplying
long dx = x2 - x1;
long dy = y2 - y1;
// return Math.hypot(x2 - x1, y2 - y1); // Extremely slow
// return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); // 20 times faster
// than hypot
return Math.sqrt(dx * dx + dy * dy); // 10 times faster then previous
// line
}
/**
* Returns distance between two 3D points
*
* @param point1
* first point
* @param point2
* second point
* @return distance between points
*/
public static double getDistance(Point3D point1, Point3D point2) {
return getDistance(point1.getX(), point1.getY(), point1.getZ(),
point2.getX(), point2.getY(), point2.getZ());
}
/**
* Returns distance between 3D set of coords
*
* @param x1
* first x coord
* @param y1
* first y coord
* @param z1
* first z coord
* @param x2
* second x coord
* @param y2
* second y coord
* @param z2
* second z coord
* @return distance between coords
*/
public static double getDistance(float x1, float y1, float z1, float x2,
float y2, float z2) {
float dx = x1 - x2;
float dy = y1 - y2;
float dz = z1 - z2;
// We should avoid Math.pow or Math.hypot due to performance reasons
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
/**
* Returns closest point on segment to point
*
* @param ss
* segment start point
* @param se
* segment end point
* @param p
* point to found closest point on segment
* @return closest point on segment to p
*/
public static Point getClosestPointOnSegment(Point ss, Point se, Point p) {
return getClosestPointOnSegment(ss.x, ss.y, se.x, se.y, p.x, p.y);
}
/**
* Returns closest point on segment to point
*
* @param sx1
* segment x coord 1
* @param sy1
* segment y coord 1
* @param sx2
* segment x coord 2
* @param sy2
* segment y coord 2
* @param px
* point x coord
* @param py
* point y coord
* @return closets point on segment to point
*/
public static Point getClosestPointOnSegment(int sx1, int sy1, int sx2,
int sy2, int px, int py) {
double xDelta = sx2 - sx1;
double yDelta = sy2 - sy1;
if ((xDelta == 0) && (yDelta == 0)) {
throw new IllegalArgumentException(
"Segment start equals segment end");
}
double u = ((px - sx1) * xDelta + (py - sy1) * yDelta)
/ (xDelta * xDelta + yDelta * yDelta);
final Point closestPoint;
if (u < 0) {
closestPoint = new Point(sx1, sy1);
} else if (u > 1) {
closestPoint = new Point(sx2, sy2);
} else {
closestPoint = new Point((int) Math.round(sx1 + u * xDelta),
(int) Math.round(sy1 + u * yDelta));
}
return closestPoint;
}
/**
* Returns distance to segment
*
* @param ss
* segment start point
* @param se
* segment end point
* @param p
* point to found closest point on segment
* @return distance to segment
*/
public static double getDistanceToSegment(Point ss, Point se, Point p) {
return getDistanceToSegment(ss.x, ss.y, se.x, se.y, p.x, p.y);
}
/**
* Returns distance to segment
*
* @param sx1
* segment x coord 1
* @param sy1
* segment y coord 1
* @param sx2
* segment x coord 2
* @param sy2
* segment y coord 2
* @param px
* point x coord
* @param py
* point y coord
* @return distance to segment
*/
public static double getDistanceToSegment(int sx1, int sy1, int sx2,
int sy2, int px, int py) {
Point closestPoint = getClosestPointOnSegment(sx1, sy1, sx2, sy2, px,
py);
return getDistance(closestPoint.x, closestPoint.y, px, py);
}
}

View File

@@ -0,0 +1,276 @@
// Copyright (c) 1999 CERN - European Organization for Nuclear Research.
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without fee,
// provided that the above copyright notice appear in all copies and
// that both that copyright notice and this permission notice appear in
// supporting documentation. CERN makes no representations about the
// suitability of this software for any purpose. It is provided "as is"
// without expressed or implied warranty.
/*
* 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.util;
import java.util.Arrays;
/**
* Used to keep hash table capacities prime numbers. Not of interest for users;
* only for implementors of hashtables.
*
* <p>
* Choosing prime numbers as hash table capacities is a good idea to keep them
* working fast, particularly under hash table expansions.
*
* <p>
* However, JDK 1.2, JGL 3.1 and many other toolkits do nothing to keep
* capacities prime. This class provides efficient means to choose prime
* capacities.
*
* <p>
* Choosing a prime is <tt>O(log 300)</tt> (binary search in a list of 300
* ints). Memory requirements: 1 KB static memory.
*
* @author wolfgang.hoschek@cern.ch
* @version 1.0, 09/24/99
*/
public final class PrimeFinder {
/**
* The largest prime this class can generate; currently equal to
* <tt>Integer.MAX_VALUE</tt>.
*/
public static final int LARGEST_PRIME = Integer.MAX_VALUE; // yes, it is
// prime.
/**
* The prime number list consists of 11 chunks.
*
* Each chunk contains prime numbers.
*
* A chunk starts with a prime P1. The next element is a prime P2. P2 is the
* smallest prime for which holds: P2 >= 2*P1.
*
* The next element is P3, for which the same holds with respect to P2, and
* so on.
*
* Chunks are chosen such that for any desired capacity >= 1000 the list
* includes a prime number <= desired capacity * 1.11.
*
* Therefore, primes can be retrieved which are quite close to any desired
* capacity, which in turn avoids wasting memory.
*
* For example, the list includes
* 1039,1117,1201,1277,1361,1439,1523,1597,1759,1907,2081.
*
* So if you need a prime >= 1040, you will find a prime <= 1040*1.11=1154.
*
* Chunks are chosen such that they are optimized for a hashtable
* growthfactor of 2.0;
*
* If your hashtable has such a growthfactor then, after initially
* "rounding to a prime" upon hashtable construction, it will later expand
* to prime capacities such that there exist no better primes.
*
* In total these are about 32*10=320 numbers -> 1 KB of static memory
* needed.
*
* If you are stingy, then delete every second or fourth chunk.
*/
private static final int[] PRIME_CAPACITIES = {
// chunk #0
LARGEST_PRIME,
// chunk #1
5, 11, 23, 47, 97, 197,
397,
797,
1597,
3203,
6421,
12853,
25717,
51437,
102877,
205759,
411527,
823117,
1646237,
3292489,
6584983,
13169977,
26339969,
52679969,
105359939,
210719881,
421439783,
842879579,
1685759167,
// chunk #2
433, 877, 1759,
3527,
7057,
14143,
28289,
56591,
113189,
226379,
452759,
905551,
1811107,
3622219,
7244441,
14488931,
28977863,
57955739,
115911563,
231823147,
463646329,
927292699,
1854585413,
// chunk #3
953, 1907, 3821, 7643,
15287,
30577,
61169,
122347,
244703,
489407,
978821,
1957651,
3915341,
7830701,
15661423,
31322867,
62645741,
125291483,
250582987,
501165979,
1002331963,
2004663929,
// chunk #4
1039, 2081, 4177, 8363, 16729,
33461,
66923,
133853,
267713,
535481,
1070981,
2141977,
4283963,
8567929,
17135863,
34271747,
68543509,
137087021,
274174111,
548348231,
1096696463,
// chunk #5
31, 67, 137, 277, 557, 1117, 2237, 4481, 8963, 17929, 35863, 71741,
143483,
286973,
573953,
1147921,
2295859,
4591721,
9183457,
18366923,
36733847,
73467739,
146935499,
293871013,
587742049,
1175484103,
// chunk #6
599, 1201, 2411, 4831, 9677, 19373, 38747, 77509, 155027, 310081,
620171, 1240361,
2480729,
4961459,
9922933,
19845871,
39691759,
79383533,
158767069,
317534141,
635068283,
1270136683,
// chunk #7
311, 631, 1277, 2557, 5119, 10243, 20507, 41017, 82037, 164089,
328213, 656429, 1312867, 2625761, 5251529,
10503061,
21006137,
42012281,
84024581,
168049163,
336098327,
672196673,
1344393353,
// chunk #8
3, 7, 17, 37, 79, 163, 331, 673, 1361, 2729, 5471, 10949, 21911,
43853, 87719, 175447, 350899, 701819, 1403641, 2807303, 5614657,
11229331, 22458671, 44917381,
89834777,
179669557,
359339171,
718678369,
1437356741,
// chunk #9
43, 89, 179, 359, 719, 1439, 2879, 5779, 11579, 23159, 46327,
92657, 185323, 370661, 741337, 1482707, 2965421, 5930887, 11861791,
23723597, 47447201, 94894427, 189788857, 379577741,
759155483,
1518310967,
// chunk #10
379, 761, 1523, 3049, 6101, 12203, 24407, 48817, 97649, 195311,
390647, 781301, 1562611, 3125257, 6250537, 12501169, 25002389,
50004791, 100009607, 200019221, 400038451, 800076929, 1600153859 };
static { // initializer
// The above prime numbers are formatted for human readability.
// To find numbers fast, we sort them once and for all.
Arrays.sort(PRIME_CAPACITIES);
}
/**
* Returns a prime number which is <code>&gt;= desiredCapacity</code> and
* very close to <code>desiredCapacity</code> (within 11% if
* <code>desiredCapacity &gt;= 1000</code>).
*
* @param desiredCapacity
* the capacity desired by the user.
* @return the capacity which should be used for a hashtable.
*/
public static final int nextPrime(int desiredCapacity) {
int i = Arrays.binarySearch(PRIME_CAPACITIES, desiredCapacity);
if (i < 0) {
// desired capacity not found, choose next prime greater
// than desired capacity
i = -i - 1; // remember the semantics of binarySearch...
}
return PRIME_CAPACITIES[i];
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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.util;
/**
* An RED-GREEN-BLUE color
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class RGBColor {
/**
* The red value
*/
private final byte red;
/**
* The green value
*/
private final byte green;
/**
* The blue value
*/
private final byte blue;
/**
* Creates a new RGB (red-green-blue) color
*
* @param r
* the red byte
* @param g
* the green byte
* @param b
* the blue byte
*/
protected RGBColor(byte r, byte g, byte b) {
this.red = r;
this.green = g;
this.blue = b;
}
/**
* @return the red
*/
public byte getRed() {
return red;
}
/**
* @return the green
*/
public byte getGreen() {
return green;
}
/**
* @return the blue
*/
public byte getBlue() {
return blue;
}
/**
* Converts to an byte array
*
* @return an byte array of this color
*/
public byte[] toByteArray() {
return new byte[] { red, green, blue };
}
/**
* Convers this color into an integer
*
* @return the color integer
*/
public int toInteger() {
return (red >> 24) + (green >> 16) + (blue >> 8);
}
/**
* Creates an {@link RGBColor} from an byte array
*
* @param rgb
* the RGB byte array
* @return the {@link RGBColor}
*/
public static RGBColor fromByteArray(byte[] rgb) {
return new RGBColor(rgb[0], rgb[1], rgb[2]);
}
/**
* @param color
* the color integer
* @return the {@link RGBColor}
*/
public static RGBColor fromInteger(int color) {
return new RGBColor((byte) (color << 0), (byte) (color << 8),
(byte) (color << 16));
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.util.exception;
/**
* Base exception for Lineage 2
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public abstract class L2Exception extends Exception {
/**
* Default Serial Version UID
*/
private static final long serialVersionUID = 1L;
/**
* @see Exception#Exception()
*/
public L2Exception() {
super();
}
/**
* @see Exception#Exception(String, Throwable)
*/
public L2Exception(String message, Throwable cause) {
super(message, cause);
}
/**
* @see Exception#Exception(String)
*/
public L2Exception(String message) {
super(message);
}
/**
* @see Exception#Exception(Throwable)
*/
public L2Exception(Throwable cause) {
super(cause);
}
// /**
// * Each {@link L2Exception} has an {@link SystemMessage} attacked to it.
// It
// * is an <b><u>recommendation</u></b> of which message should be sent to
// the
// * client in case the exception is thrown.
// *
// * @return the recommended system message
// */
// public abstract SystemMessage getSystemMessage();
}

View File

@@ -0,0 +1,120 @@
/*
* 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.util.factory;
import java.lang.ref.ReferenceQueue;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import javolution.util.FastList;
import javolution.util.FastMap;
import javolution.util.FastSet;
/**
* Factory class to create {@link Collection} instances.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class CollectionFactory {
/**
* Creates a new list of type <tt>T</tt>
*
* @param <T>
* the type
* @return the created list
*/
public static final <T> List<T> newList() {
return new FastList<T>();
}
/**
* Creates a new set of type <tt>T</tt>
*
* @param <T>
* the type
* @return the created set
*/
public static final <T> Set<T> newSet() {
return new FastSet<T>();
}
/**
* Creates a new concurrent queue of type <tt>T</tt>
*
* @param <T>
* the type
* @return the created queue
*/
public static final <T> Queue<T> newConcurrentQueue() {
return new ConcurrentLinkedQueue<T>();
}
/**
* Creates a new priority queue of type <tt>T</tt>
*
* @param <T>
* the type
* @return the created queue
*/
public static final <T> PriorityQueue<T> newPriorityQueue() {
return new PriorityQueue<T>();
}
/**
* Creates a new reference queue of type <tt>T</tt>
*
* @param <T>
* the type of the {@link ReferenceQueue}
*
* @return the created queue
*/
public static final <T> ReferenceQueue<T> newReferenceQueue() {
return new ReferenceQueue<T>();
}
/**
* Creates a new map.
*
* @param <K>
* the key type
* @param <V>
* the value type
* @return the new map
*/
public static final <K, V> Map<K, V> newMap() {
return new FastMap<K, V>();
}
/**
* Creates a new weak map.
*
* @param <K>
* the key type
* @param <V>
* the value type
* @return the new map
*/
public static final <K, V> Map<K, V> newWeakMap() {
return new WeakHashMap<K, V>();
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.util.geometry;
/**
* Class with basic method implementation for areas.<br>
* If possible it should be subclassed. <br>
* In other case {@link Area} should be implemented directly
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public abstract class AbstractArea implements Area {
/**
* Minimal z of area
*/
private final int minZ;
/**
* Maximal Z of area
*/
private final int maxZ;
/**
* Creates new AbstractArea with min and max z
*
* @param minZ
* min z
* @param maxZ
* max z
*/
protected AbstractArea(int minZ, int maxZ) {
if (minZ > maxZ) {
throw new IllegalArgumentException("minZ(" + minZ + ") > maxZ("
+ maxZ + ")");
}
this.minZ = minZ;
this.maxZ = maxZ;
}
@Override
public boolean isInside2D(Point point) {
return isInside2D(point.x, point.y);
}
@Override
public boolean isInside3D(Point3D point) {
return isInside3D(point.getX(), point.getY(), point.getZ());
}
@Override
public boolean isInside3D(int x, int y, int z) {
return isInsideZ(z) && isInside2D(x, y);
}
@Override
public boolean isInsideZ(Point3D point) {
return isInsideZ(point.getZ());
}
@Override
public boolean isInsideZ(int z) {
return z >= getMinZ() && z <= getMaxZ();
}
@Override
public double getDistance2D(Point point) {
return getDistance2D(point.x, point.y);
}
@Override
public double getDistance3D(Point3D point) {
return getDistance3D(point.getX(), point.getY(), point.getZ());
}
@Override
public Point getClosestPoint(Point point) {
return getClosestPoint(point.x, point.y);
}
@Override
public Point3D getClosestPoint(Point3D point) {
return getClosestPoint(point.getX(), point.getY(), point.getZ());
}
@Override
public Point3D getClosestPoint(int x, int y, int z) {
Point closest2d = getClosestPoint(x, y);
int zCoord;
if (isInsideZ(z)) {
zCoord = z;
} else if (z < getMinZ()) {
zCoord = getMinZ();
} else {
zCoord = getMaxZ();
}
return new Point3D(closest2d.x, closest2d.y, zCoord);
}
@Override
public int getMinZ() {
return minZ;
}
@Override
public int getMaxZ() {
return maxZ;
}
}

View File

@@ -0,0 +1,196 @@
/*
* 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.util.geometry;
/**
* Basic interface for all areas.<br>
* It should be implemented in different ways for performance reasons.<br>
* For instance, we don't need complex math for squares or circles, but we need
* it for more complex polygons.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface Area {
/**
* Returns true if point is inside area ignoring z value
*
* @param point
* point to check
* @return point is inside or not
*/
boolean isInside2D(Point point);
/**
* Returns true if coords are inside area ignoring z value
*
* @param x
* x coord
* @param y
* y coord
* @return coords are inside or not
*/
boolean isInside2D(int x, int y);
/**
* Returns true if point is inside area
*
* @param point
* point to check
* @return true if point is inside
*/
boolean isInside3D(Point3D point);
/**
* Returns true if coors are inside area
*
* @param x
* x coord
* @param y
* y coord
* @param z
* z coord
* @return true if coords are inside
*/
boolean isInside3D(int x, int y, int z);
/**
* Checks if z coord is insize
*
* @param point
* point to check
* @return is z inside or not
*/
boolean isInsideZ(Point3D point);
/**
* Checks is z coord is inside
*
* @param z
* z coord
* @return is z inside or not
*/
boolean isInsideZ(int z);
/**
* Returns distance from point to closest point of this area ignoring z.<br>
* Returns 0 if point is inside area.
*
* @param point
* point to calculate distance from
* @return distance or 0 if is inside area
*/
double getDistance2D(Point point);
/**
* Returns distance from point to closest point of this area ignoring z.<br>
* Returns 0 point is inside area.
*
* @param x
* x coord
* @param y
* y coord
* @return distance or 0 if is inside area
*/
double getDistance2D(int x, int y);
/**
* Returns distance from point to this area.<br>
* Returns 0 if is inside.
*
* @param point
* point to check
* @return distance or 0 if is inside
*/
double getDistance3D(Point3D point);
/**
* Returns distance from coords to this area
*
* @param x
* x coord
* @param y
* y coord
* @param z
* z coord
* @return distance or 0 if is inside
*/
double getDistance3D(int x, int y, int z);
/**
* Returns closest point of area to given point.<br>
* Returns point with coords = point arg if is inside
*
* @param point
* point to check
* @return closest point
*/
Point getClosestPoint(Point point);
/**
* Returns closest point of area to given coords.<br>
* Returns point with coords x and y if coords are inside
*
* @param x
* x coord
* @param y
* y coord
* @return closest point
*/
Point getClosestPoint(int x, int y);
/**
* Returns closest point of area to given point.<br>
* Works exactly like {@link #getClosestPoint(int, int)} if
* {@link #isInsideZ(int)} returns true.<br>
* In other case closest z edge is set as z coord.
*
* @param point
* point to check
* @return closest point of area to point
*/
Point3D getClosestPoint(Point3D point);
/**
* Returns closest point of area to given coords.<br>
* Works exactly like {@link #getClosestPoint(int, int)} if
* {@link #isInsideZ(int)} returns true.<br>
* In other case closest z edge is set as z coord.
*
* @param x
* x coord
* @param y
* y coord
* @param z
* z coord
* @return closest point of area to point
*/
Point3D getClosestPoint(int x, int y, int z);
/**
* Return minimal z of this area
*
* @return minimal z of this area
*/
int getMinZ();
/**
* Returns maximal z of this area
*
* @return maximal z of this area
*/
int getMaxZ();
}

View File

@@ -0,0 +1,120 @@
/*
* 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.util.geometry;
import org.apache.commons.math.geometry.Vector3D;
/**
* Represents an coordinate in the game world.
* <p>
* Each coordinate has 3 points: x, y and z.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class Coordinate {
/**
* The backing vector of this Coordinate
*/
protected final Vector3D vector;
/**
* Creates a new coordinate
*
* @param x
* the x point
* @param y
* the y point
* @param z
* the z point
*/
protected Coordinate(int x, int y, int z) {
this.vector = new Vector3D(x, y, z);
}
/**
* @return the x cord
*/
public int getX() {
return (int) vector.getX();
}
/**
* @return the y cord
*/
public int getY() {
return (int) vector.getY();
}
/**
* @return the z cord
*/
public int getZ() {
return (int) vector.getZ();
}
/**
* Calculates the distance between <tt>this</tt> coordinate and
* <tt>other</tt>
*
* @param other
* the other coordinate
* @return the calculated distance
*/
public double getDistance(Coordinate other) {
return Vector3D.distance(vector, other.vector);
}
/**
* Calculates the squared distance between <tt>this</tt> coordinate and
* <tt>other</tt>. This method is slighter faster then
* {@link #getDistance(Coordinate)}.
*
* @param other
* the other coordinate
* @return the calculated distance
*/
public double getDistanceSquared(Coordinate other) {
return Vector3D.distanceSq(vector, other.vector);
}
/**
* Creates a new instance from the 3 points
*
* @param x
* the x point
* @param y
* the y point
* @param z
* the z point
* @return the new {@link Coordinate} object created
*/
public static Coordinate fromXYZ(int x, int y, int z) {
return new Coordinate(x, y, z);
}
@Override
public String toString() {
return "Coordinate [" + vector + "]";
}
/**
* @return the {@link Point3D} representing this {@link Coordinate}
*/
public Point3D toPoint() {
return new Point3D(this, 0);
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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.util.geometry;
import com.l2jserver.util.MathUtil;
/**
* This class implements cylinder area
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class CylinderArea extends AbstractArea {
/**
* Center of cylinder
*/
private final int centerX;
/**
* Center of cylinder
*/
private final int centerY;
/**
* Cylinder radius
*/
private final int radius;
/**
* Creates new cylinder with given radius
*
* @param center
* center of the circle
* @param radius
* radius of the circle
* @param minZ
* min z
* @param maxZ
* max z
*/
public CylinderArea(Point center, int radius, int minZ, int maxZ) {
this(center.x, center.y, radius, minZ, maxZ);
}
/**
* Creates new cylider with given radius
*
* @param x
* center coord
* @param y
* center coord
* @param radius
* radius of the circle
* @param minZ
* min z
* @param maxZ
* max z
*/
public CylinderArea(int x, int y, int radius, int minZ, int maxZ) {
super(minZ, maxZ);
this.centerX = x;
this.centerY = y;
this.radius = radius;
}
@Override
public boolean isInside2D(int x, int y) {
return MathUtil.getDistance(centerX, centerY, x, y) < radius;
}
@Override
public double getDistance2D(int x, int y) {
if (isInside2D(x, y)) {
return 0;
} else {
return Math.abs(MathUtil.getDistance(centerX, centerY, x, y)
- radius);
}
}
@Override
public double getDistance3D(int x, int y, int z) {
if (isInside3D(x, y, z)) {
return 0;
} else if (isInsideZ(z)) {
return getDistance2D(x, y);
} else {
if (z < getMinZ()) {
return MathUtil.getDistance(centerX, centerY, getMinZ(), x, y,
z);
} else {
return MathUtil.getDistance(centerX, centerY, getMaxZ(), x, y,
z);
}
}
}
@Override
public Point getClosestPoint(int x, int y) {
if (isInside2D(x, y)) {
return new Point(x, y);
} else {
int vX = x - this.centerX;
int vY = y - this.centerY;
double magV = MathUtil.getDistance(centerX, centerY, x, y);
double pointX = centerX + vX / magV * radius;
double pointY = centerY + vY / magV * radius;
return new Point((int) Math.round(pointX), (int) Math.round(pointY));
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.util.geometry;
/**
* An two dimensional point
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class Point {
/**
* The X axis
*/
public final int x;
/**
* The Y axis
*/
public final int y;
/**
* Creates a new two dimensional point
*
* @param x
* the x axis
* @param y
* the y axis
*/
public Point(int x, int y) {
this.x = x;
this.y = y;
}
/**
* @return the x
*/
public int getX() {
return x;
}
/**
* @return the y
*/
public int getY() {
return y;
}
/**
* Creates a new point from X and Y axis
*
* @param x
* the x axis
* @param y
* the y axis
* @return the new created Point
*/
public static final Point fromXY(int x, int y) {
return new Point(x, y);
}
}

View File

@@ -0,0 +1,154 @@
/*
* 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.util.geometry;
/**
* An point is composed of an Coordinate and an angle. The angle represents the
* facing angle of the point, that is, the direction the point is "looking".
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class Point3D extends Point {
/**
* The point coordinate
*/
protected final Coordinate coordinate;
/**
* The point angle
*/
protected final double angle;
/**
* Creates a new point
*
* @param coordinate
* the coordinate
* @param angle
* the angle
*/
public Point3D(Coordinate coordinate, double angle) {
super(coordinate.getX(), coordinate.getY());
this.coordinate = coordinate;
this.angle = angle;
}
/**
* Creates a new point with 3 axis
*
* @param x
* the x axis
* @param y
* the y axis
* @param z
* the z axis
*/
public Point3D(int x, int y, int z) {
this(new Coordinate(x, y, z), 0);
}
/**
* @return the x
* @see com.l2jserver.util.geometry.Coordinate#getX()
*/
@Override
public int getX() {
return coordinate.getX();
}
/**
* @return the y
* @see com.l2jserver.util.geometry.Coordinate#getY()
*/
@Override
public int getY() {
return coordinate.getY();
}
/**
* @return the z
* @see com.l2jserver.util.geometry.Coordinate#getZ()
*/
public int getZ() {
return coordinate.getZ();
}
/**
* @param other
* the other coordinate
* @return the distance
* @see com.l2jserver.util.geometry.Coordinate#getDistance(com.l2jserver.util.geometry.Coordinate)
*/
public double getDistance(Coordinate other) {
return coordinate.getDistance(other);
}
/**
* @param other
* the other point
* @return the distance
* @see com.l2jserver.util.geometry.Coordinate#getDistance(com.l2jserver.util.geometry.Coordinate)
*/
public double getDistance(Point3D other) {
return coordinate.getDistance(other.coordinate);
}
/**
* @return the angle
*/
public double getAngle() {
return angle;
}
/**
* @return the coordinate
*/
public Coordinate getCoordinate() {
return coordinate;
}
/**
* Creates a new instance from the 3 points and an angle
*
* @param x
* the x point
* @param y
* the y point
* @param z
* the z point
* @param angle
* the angle
* @return the new {@link Point3D} object created
*/
public static final Point3D fromXYZA(int x, int y, int z, double angle) {
return new Point3D(Coordinate.fromXYZ(x, y, z), angle);
}
/**
* Creates a new instance from the 3 points. The angle will be zero.
*
* @param x
* the x point
* @param y
* the y point
* @param z
* the z point
* @return the new {@link Point3D} object created
*/
public static final Point3D fromXYZ(int x, int y, int z) {
return fromXYZA(x, y, z, 0);
}
}

View File

@@ -0,0 +1,154 @@
/*
* 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.util.geometry;
import java.awt.Polygon;
import java.util.Collection;
import com.l2jserver.util.MathUtil;
/**
* Area of free form
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class PolygonArea extends AbstractArea {
/**
* Collection of x points
*/
private final int[] xPoints;
/**
* Collection of y points
*/
private final int[] yPoints;
/**
* Polygon used to calculate isInside()
*/
private final Polygon poly;
/**
* Creates new area from given points
*
* @param points
* list of points
* @param zMin
* minimal z
* @param zMax
* maximal z
*/
public PolygonArea(Collection<Point> points, int zMin, int zMax) {
this(points.toArray(new Point[points.size()]), zMin, zMax);
}
/**
* Creates new area from given points
*
* @param points
* list of points
* @param zMin
* minimal z
* @param zMax
* maximal z
*/
public PolygonArea(Point[] points, int zMin, int zMax) {
super(zMin, zMax);
if (points.length < 3) {
throw new IllegalArgumentException(
"Not enough points, needed at least 3 but got "
+ points.length);
}
this.xPoints = new int[points.length];
this.yPoints = new int[points.length];
Polygon polygon = new Polygon();
for (int i = 0, n = points.length; i < n; i++) {
Point p = points[i];
polygon.addPoint(p.x, p.y);
xPoints[i] = p.x;
yPoints[i] = p.y;
}
this.poly = polygon;
}
@Override
public boolean isInside2D(int x, int y) {
return poly.contains(x, y);
}
@Override
public double getDistance2D(int x, int y) {
if (isInside2D(x, y)) {
return 0;
} else {
Point cp = getClosestPoint(x, y);
return MathUtil.getDistance(cp.x, cp.y, x, y);
}
}
@Override
public double getDistance3D(int x, int y, int z) {
if (isInside3D(x, y, z)) {
return 0;
} else if (isInsideZ(z)) {
return getDistance2D(x, y);
} else {
Point3D cp = getClosestPoint(x, y, z);
return MathUtil.getDistance(cp.getX(), cp.getY(), cp.getZ(), x, y,
z);
}
}
@Override
public Point getClosestPoint(int x, int y) {
Point closestPoint = null;
double closestDistance = 0;
for (int i = 0; i < xPoints.length; i++) {
int nextIndex = i + 1;
if (nextIndex == xPoints.length) {
nextIndex = 0;
}
int p1x = xPoints[i];
int p1y = yPoints[i];
int p2x = xPoints[nextIndex];
int p2y = yPoints[nextIndex];
Point point = MathUtil.getClosestPointOnSegment(p1x, p1y, p2x, p2y,
x, y);
if (closestPoint == null) {
closestPoint = point;
closestDistance = MathUtil.getDistance(closestPoint.x,
closestPoint.y, x, y);
} else {
double newDistance = MathUtil.getDistance(point.x, point.y, x,
y);
if (newDistance < closestDistance) {
closestPoint = point;
closestDistance = newDistance;
}
}
}
return closestPoint;
}
}

View File

@@ -0,0 +1,177 @@
/*
* 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.util.geometry;
import java.awt.Rectangle;
import com.l2jserver.util.MathUtil;
/**
* Rectangle area, most wide spread in the game
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class RectangleArea extends AbstractArea {
/**
* Min x point
*/
private final int minX;
/**
* Max x point
*/
private final int maxX;
/**
* Min y point
*/
private final int minY;
/**
* Max y point
*/
private final int maxY;
/**
* Creates new area from given points. Point order doesn't matter
*
* @param p1
* point
* @param p2
* point
* @param p3
* point
* @param p4
* point
* @param minZ
* minimal z
* @param maxZ
* maximal z
*/
public RectangleArea(Point p1, Point p2, Point p3, Point p4, int minZ,
int maxZ) {
super(minZ, maxZ);
// we should use a better way to do this
Rectangle r = new Rectangle();
r.add(new java.awt.Point(p1.x, p1.y));
r.add(new java.awt.Point(p2.x, p2.y));
r.add(new java.awt.Point(p3.x, p3.y));
r.add(new java.awt.Point(p4.x, p4.y));
minX = (int) r.getMinX();
maxX = (int) r.getMaxX();
minY = (int) r.getMinY();
maxY = (int) r.getMaxY();
}
/**
* Creates new are from given coords
*
* @param minX
* mimal x point
* @param minY
* minimal y point
* @param maxX
* maximal x point
* @param maxY
* maximal y point
* @param minZ
* minimal z point
* @param maxZ
* maximal z point
*/
public RectangleArea(int minX, int minY, int maxX, int maxY, int minZ,
int maxZ) {
super(minZ, maxZ);
this.minX = minX;
this.maxX = maxX;
this.minY = minY;
this.maxY = maxY;
}
@Override
public boolean isInside2D(int x, int y) {
return x >= minX && x <= maxX && y >= minY && y <= maxY;
}
@Override
public double getDistance2D(int x, int y) {
if (isInside2D(x, y)) {
return 0;
} else {
Point cp = getClosestPoint(x, y);
return MathUtil.getDistance(x, y, cp.x, cp.y);
}
}
@Override
public double getDistance3D(int x, int y, int z) {
if (isInside3D(x, y, z)) {
return 0;
} else if (isInsideZ(z)) {
return getDistance2D(x, y);
} else {
Point3D cp = getClosestPoint(x, y, z);
return MathUtil.getDistance(x, y, z, cp.getX(), cp.getY(),
cp.getZ());
}
}
@Override
public Point getClosestPoint(int x, int y) {
if (isInside2D(x, y)) {
return new Point(x, y);
} else {
// bottom edge
Point closestPoint = MathUtil.getClosestPointOnSegment(minX, minY,
maxX, minY, x, y);
double distance = MathUtil.getDistance(x, y, closestPoint.x,
closestPoint.y);
// top edge
Point cp = MathUtil.getClosestPointOnSegment(minX, maxY, maxX,
maxY, x, y);
double d = MathUtil.getDistance(x, y, cp.x, cp.y);
if (d < distance) {
closestPoint = cp;
distance = d;
}
// left edge
cp = MathUtil
.getClosestPointOnSegment(minX, minY, minX, maxY, x, y);
d = MathUtil.getDistance(x, y, cp.x, cp.y);
if (d < distance) {
closestPoint = cp;
distance = d;
}
// Right edge
cp = MathUtil
.getClosestPointOnSegment(maxX, minY, maxX, maxY, x, y);
d = MathUtil.getDistance(x, y, cp.x, cp.y);
if (d < distance) {
closestPoint = cp;
// distance = d;
}
return closestPoint;
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.util.transformer;
/**
* An transformer can transform an {@link Object} into an {@link String} and the
* {@link String} back to an equivalent object.
*
* @param <T>
* the transformed type
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface Transformer<T> {
/**
* Transform the object in a string
*
* @param value
* the object
* @return the string of the object
*/
String transform(T value);
/**
* Untransforms the string back to an object
*
* @param value
* the string
* @return the object
*/
T untransform(String value);
}

View File

@@ -0,0 +1,83 @@
/*
* 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.util.transformer;
import java.io.File;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import com.l2jserver.util.transformer.impl.BooleanTransformer;
import com.l2jserver.util.transformer.impl.ByteTransformer;
import com.l2jserver.util.transformer.impl.ClassTransformer;
import com.l2jserver.util.transformer.impl.DoubleTransformer;
import com.l2jserver.util.transformer.impl.FileTransformer;
import com.l2jserver.util.transformer.impl.FloatTransformer;
import com.l2jserver.util.transformer.impl.InetSocketAddressTransformer;
import com.l2jserver.util.transformer.impl.IntegerTransformer;
import com.l2jserver.util.transformer.impl.LongTransformer;
import com.l2jserver.util.transformer.impl.PathTransformer;
import com.l2jserver.util.transformer.impl.ShortTransformer;
import com.l2jserver.util.transformer.impl.URITransformer;
import com.l2jserver.util.transformer.impl.URLTransformer;
/**
* The {@link TransformerFactory} return the transformer instance for any given
* type.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class TransformerFactory {
/**
* return the transformer instance the given <tt>type</tt>.
*
* @param type
* the type
* @return the transformer
*/
public static final Transformer<?> getTransfromer(Class<?> type) {
if (type == Byte.class || type == Byte.TYPE) {
return ByteTransformer.SHARED_INSTANCE;
} else if (type == Short.class || type == Short.TYPE) {
return ShortTransformer.SHARED_INSTANCE;
} else if (type == Integer.class || type == Integer.TYPE) {
return IntegerTransformer.SHARED_INSTANCE;
} else if (type == Long.class || type == Long.TYPE) {
return LongTransformer.SHARED_INSTANCE;
} else if (type == Float.class || type == Float.TYPE) {
return FloatTransformer.SHARED_INSTANCE;
} else if (type == Double.class || type == Double.TYPE) {
return DoubleTransformer.SHARED_INSTANCE;
} else if (type == Boolean.class || type == Boolean.TYPE) {
return BooleanTransformer.SHARED_INSTANCE;
} else if (type == InetSocketAddress.class) {
return InetSocketAddressTransformer.SHARED_INSTANCE;
} else if (type == File.class) {
return FileTransformer.SHARED_INSTANCE;
} else if (type == Class.class) {
return ClassTransformer.SHARED_INSTANCE;
} else if (type == URI.class) {
return URITransformer.SHARED_INSTANCE;
} else if (type == URL.class) {
return URLTransformer.SHARED_INSTANCE;
} else if (type == Path.class) {
return PathTransformer.SHARED_INSTANCE;
}
return null;
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.util.transformer.impl;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link Boolean} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class BooleanTransformer implements Transformer<Boolean> {
/**
* This transformer shared instance
*/
public static final BooleanTransformer SHARED_INSTANCE = new BooleanTransformer();
@Override
public String transform(Boolean value) {
return (value ? "true" : "false");
}
@Override
public Boolean untransform(String value) {
return Boolean.parseBoolean(value);
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.util.transformer.impl;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link Integer} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ByteTransformer implements Transformer<Byte> {
/**
* This transformer shared instance
*/
public static final ByteTransformer SHARED_INSTANCE = new ByteTransformer();
@Override
public String transform(Byte value) {
return Double.toString(value);
}
@Override
public Byte untransform(String value) {
return Byte.decode(value);
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.util.transformer.impl;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link Class} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ClassTransformer implements Transformer<Class<?>> {
/**
* This transformer shared instance
*/
public static final ClassTransformer SHARED_INSTANCE = new ClassTransformer();
@Override
public String transform(Class<?> value) {
return value.getName();
}
@Override
public Class<?> untransform(String value) {
try {
return Class.forName(value);
} catch (ClassNotFoundException e) {
return null;
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.util.transformer.impl;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link Integer} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class DoubleTransformer implements Transformer<Double> {
/**
* This transformer shared instance
*/
public static final DoubleTransformer SHARED_INSTANCE = new DoubleTransformer();
@Override
public String transform(Double value) {
return Double.toString(value);
}
@Override
public Double untransform(String value) {
return Double.parseDouble(value);
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.util.transformer.impl;
import java.io.File;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link Integer} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class FileTransformer implements Transformer<File> {
/**
* This transformer shared instance
*/
public static final FileTransformer SHARED_INSTANCE = new FileTransformer();
/**
* The root to resolve relative files
*/
private final File root = new File("./");
@Override
public String transform(File value) {
return value.getAbsolutePath();
}
@Override
public File untransform(String value) {
return new File(root, value);
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.util.transformer.impl;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link Integer} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class FloatTransformer implements Transformer<Float> {
/**
* This transformer shared instance
*/
public static final FloatTransformer SHARED_INSTANCE = new FloatTransformer();
@Override
public String transform(Float value) {
return Double.toString(value);
}
@Override
public Float untransform(String value) {
return Float.parseFloat(value);
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.util.transformer.impl;
import java.net.InetSocketAddress;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link Integer} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class InetSocketAddressTransformer implements
Transformer<InetSocketAddress> {
/**
* This transformer shared instance
*/
public static final InetSocketAddressTransformer SHARED_INSTANCE = new InetSocketAddressTransformer();
@Override
public String transform(InetSocketAddress value) {
return value.getHostName() + ":" + value.getPort();
}
@Override
public InetSocketAddress untransform(String value) {
final String[] pieces = value.split(":");
if (pieces.length != 2)
return null;
return new InetSocketAddress(pieces[0], Integer.parseInt(pieces[1]));
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.util.transformer.impl;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link Integer} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class IntegerTransformer implements Transformer<Integer> {
/**
* This transformer shared instance
*/
public static final IntegerTransformer SHARED_INSTANCE = new IntegerTransformer();
@Override
public String transform(Integer value) {
return Integer.toString(value);
}
@Override
public Integer untransform(String value) {
return Integer.decode(value);
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.util.transformer.impl;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link Long} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class LongTransformer implements Transformer<Long> {
/**
* This transformer shared instance
*/
public static final LongTransformer SHARED_INSTANCE = new LongTransformer();
@Override
public String transform(Long value) {
return Long.toString(value);
}
@Override
public Long untransform(String value) {
return Long.decode(value);
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.util.transformer.impl;
import java.nio.file.Path;
import java.nio.file.Paths;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link Path} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class PathTransformer implements Transformer<Path> {
/**
* This transformer shared instance
*/
public static final PathTransformer SHARED_INSTANCE = new PathTransformer();
@Override
public String transform(Path value) {
if (value == null)
return "";
return value.toString();
}
@Override
public Path untransform(String value) {
return Paths.get(value);
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.util.transformer.impl;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link Integer} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ShortTransformer implements Transformer<Short> {
/**
* This transformer shared instance
*/
public static final ShortTransformer SHARED_INSTANCE = new ShortTransformer();
@Override
public String transform(Short value) {
return Short.toString(value);
}
@Override
public Short untransform(String value) {
return Short.decode(value);
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.util.transformer.impl;
import java.net.URI;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link URI} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class URITransformer implements Transformer<URI> {
/**
* This transformer shared instance
*/
public static final URITransformer SHARED_INSTANCE = new URITransformer();
@Override
public String transform(URI value) {
return value.toString();
}
@Override
public URI untransform(String value) {
return URI.create(value);
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.util.transformer.impl;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import com.l2jserver.util.transformer.Transformer;
/**
* Transform an {@link URI} into an string.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class URLTransformer implements Transformer<URL> {
/**
* This transformer shared instance
*/
public static final URLTransformer SHARED_INSTANCE = new URLTransformer();
@Override
public String transform(URL value) {
return value.toString();
}
@Override
public URL untransform(String value) {
try {
return new URL(value);
} catch (MalformedURLException e) {
return null;
}
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.model.id.allocator;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import org.junit.Before;
import org.junit.Test;
import com.l2jserver.model.id.object.allocator.BitSetIDAllocator;
import com.l2jserver.model.id.object.allocator.IDAllocator;
import com.l2jserver.model.id.object.allocator.IDAllocatorException;
public class BitSetIDAllocatorTest {
private final BitSetIDAllocator allocator = new BitSetIDAllocator();
@Before
public void tearUp() {
allocator.init();
}
@Test
public void testAllocate() {
final int id1 = allocator.allocate();
final int id2 = allocator.allocate();
assertFalse(id1 == id2);
assertEquals(IDAllocator.FIRST_ID, id1);
assertEquals(IDAllocator.FIRST_ID + 1, id2);
}
@Test
public void testAllocateRestore() {
final int id1 = IDAllocator.FIRST_ID;
final int id2 = IDAllocator.FIRST_ID + 1;
allocator.allocate(id1);
allocator.allocate(id2);
int id3 = allocator.allocate();
assertFalse(id1 == id3);
assertFalse(id2 == id3);
}
@Test
public void testAllocateMany() {
for (int i = 0; i < 100 * 1000; i++) {
allocator.allocate();
}
assertEquals(100000, allocator.getAllocatedIDs());
}
@Test(expected = IDAllocatorException.class)
public void testAllocateAlreadyAllocated() {
final int id1 = allocator.allocate();
allocator.allocate(id1);
}
@Test
public void testRelease() {
final int id = allocator.allocate();
allocator.release(id);
}
@Test(expected = IDAllocatorException.class)
public void testReleaseUnalloc() {
allocator.release(IDAllocator.FIRST_ID);
}
}

View File

@@ -0,0 +1,99 @@
/*
* 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.cache;
import java.util.Random;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import com.l2jserver.service.ServiceStartException;
public class SimpleCacheServiceTest {
private final EhCacheService cacheService = new EhCacheService();
@Before
public void tearUp() throws ServiceStartException {
cacheService.start();
}
@Test
public void testNoArgs() {
final TestCacheable cached = cacheService.decorate(TestCacheable.class,
new TestCacheableInstance());
int output1 = cached.random();
int output2 = cached.random();
Assert.assertEquals(output1, output2);
}
@Test
public void testSameArgs() {
final TestCacheable cached = cacheService.decorate(TestCacheable.class,
new TestCacheableInstance());
int output1 = cached.random(10);
int output2 = cached.random(10);
Assert.assertEquals(output1, output2);
}
@Test
public void testDifferentArgs() {
final TestCacheable cached = cacheService.decorate(TestCacheable.class,
new TestCacheableInstance());
int output1 = cached.random(10);
int output2 = cached.random(20);
Assert.assertFalse(output1 == output2);
}
@Test
public void testIgnoreCaching() {
final TestCacheable cached = cacheService.decorate(TestCacheable.class,
new TestCacheableInstance());
int output1 = cached.notCached();
int output2 = cached.notCached();
Assert.assertFalse(output1 == output2);
}
public interface TestCacheable extends Cacheable {
public int random();
public int random(int arg);
@IgnoreCaching
public int notCached();
}
public static class TestCacheableInstance implements TestCacheable {
private final Random random = new Random();
@Override
public int random() {
return random.nextInt(Integer.MAX_VALUE);
}
@Override
public int random(int arg) {
return random.nextInt(Integer.MAX_VALUE);
}
@Override
public int notCached() {
return random.nextInt(Integer.MAX_VALUE);
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.configuration;
import java.io.File;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.configuration.XMLConfigurationService.ConfigurationXPath;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public class XMLConfigurationServiceTest {
/**
* The {@link TestConfig} proxy
*/
private TestConfig config;
@Before
public void tearUp() throws ServiceStartException {
final XMLConfigurationService service = new XMLConfigurationService(
new File("src/test/resources/test-config.xml"));
service.start();
config = service.get(TestConfig.class);
}
@Test
public void testString() throws ServiceStartException {
Assert.assertEquals("test", config.getTestString());
}
@Test
public void testDefaultValue() throws ServiceStartException {
Assert.assertEquals("default", config.getDefaultTestString());
}
@Test
public void testInteger() throws ServiceStartException {
Assert.assertEquals(256, config.getTestInteger());
}
@Test
public void testSetter() throws ServiceStartException {
config.setTestString("new-value");
Assert.assertEquals("new-value", config.getTestString());
}
@Test
public void testNullSetter() throws ServiceStartException {
config.setTestString(null);
Assert.assertEquals("test-default", config.getTestString());
}
/**
* The TestConfig interface
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface TestConfig extends Configuration {
@ConfigurationPropertyGetter(defaultValue = "test-default")
@ConfigurationXPath("/configuration/test/testvalue")
String getTestString();
@ConfigurationPropertySetter
@ConfigurationXPath("/configuration/test/testvalue")
void setTestString(String value);
@ConfigurationPropertyGetter(defaultValue = "default")
@ConfigurationXPath("/configuration/test/nonexistentkey")
String getDefaultTestString();
@ConfigurationPropertyGetter(defaultValue = "0")
@ConfigurationXPath("/configuration/test/integer")
int getTestInteger();
@ConfigurationPropertySetter
@ConfigurationXPath("/configuration/test/integer")
void setTestInteger(Integer n);
}
}