/*
* AbstractRemoteServiceManager.java
*
* Copyright (C) 2010 Leo Osvald <leo.osvald@gmail.com>
*
* This file is part of SGLJ.
*
* SGLJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SGLJ 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sglj.service.rmi;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.Validate;
import org.sglj.service.rmi.annotations.RemoteMethod;
import org.sglj.util.Pair;
/**
* The abstract implementation of the {@link RemoteServiceManager} interface.<br>
*
* Services to be registered are provided via constructor as variable
* argument list.
*
*
* @author Leo Osvald
* @version 1.0
*/
abstract class AbstractRemoteServiceManager<T extends RemoteService>
implements RemoteServiceManager<T> {
/**
* Unmodifiable list of remote services
*/
private final List<T> services;
/**
*
* @param remoteServices services to be registered
* @throws IllegalArgumentException if the contract defined in
* the {@link RemoteServiceManager} interface is broken.
*/
public <T2 extends T> AbstractRemoteServiceManager(T2... remoteServices)
throws IllegalArgumentException {
Validate.noNullElements(remoteServices, "Remote service cannot be null");
Set<T> uniqueServices = new HashSet<T>(
remoteServices.length);
for (T remoteService : remoteServices) {
uniqueServices.add(remoteService);
}
// ensure that there are no duplicate services
Validate.isTrue(uniqueServices.size() == remoteServices.length,
"Duplicate services detected");
// ensure that services have distinct identifiers
Set<Byte> ids = new HashSet<Byte>(uniqueServices.size());
for (RemoteService service : remoteServices) {
ids.add(service.getId());
}
Validate.isTrue(uniqueServices.size() == ids.size(),
"Services with identical id detected");
ids.clear(); // free memory before allocating new
services = Collections.unmodifiableList(new ArrayList<T>(
uniqueServices));
// retrieve all remote methods and store them in a map by name
for (RemoteService service : services) {
Method[] methods = service.getClass().getMethods();
Set<Pair<String, Integer>> set = new HashSet<Pair<String,Integer>>();
for (Method m : methods) {
if (m.isAnnotationPresent(RemoteMethod.class)) {
int paramCount = m.getParameterTypes().length;
if (!set.add(new Pair<String, Integer>(m.getName(),
paramCount))) {
throw new IllegalArgumentException(
"Two methods with the same name \""+m.getName()
+ "\" and with " + paramCount
+ " parameters detected in service"
+ service);
}
registerRemoteMethod(service.getId(), m);
}
}
}
}
@Override
public List<T> availableServices() {
return services;
}
@Override
public boolean existsRemoteMethod(byte serviceId, String methodName,
int parameterCount) {
return getRemoteMethod(serviceId, methodName, parameterCount)
!= null;
}
/**
* A method called by the constructor to "permanently" register a
* remote method in the specified service.<br>
* The method is guaranteed to be remote so there is no need to
* check again whether it is annotated by the
* {@link RemoteMethod} annotation. The implementor also does not
* need to check whether two methods with the same name and the same
* number of the arguments belong to the same service - that check
* is already implemented by the {@link AbstractRemoteServiceManager}
* class.
*
* @param serviceId identifier of the service that this method belongs to
* @param remoteMethod the method that should registered
*/
protected abstract void registerRemoteMethod(byte serviceId,
Method remoteMethod);
}