ServantManager.java
// Copyright (c) ZeroC, Inc.
package com.zeroc.Ice;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletionStage;
final class ServantManager implements Object {
private Instance _instance;
private final String _adapterName;
private final Map<Identity, Map<String, Object>> _servantMapMap =
new HashMap<Identity, Map<String, Object>>();
private final Map<String, Object> _defaultServantMap = new HashMap<String, Object>();
private final Map<String, ServantLocator> _locatorMap = new HashMap<String, ServantLocator>();
@Override
public CompletionStage<OutgoingResponse> dispatch(IncomingRequest request)
throws UserException {
final Current current = request.current;
Object servant = findServant(current.id, current.facet);
if (servant != null) {
// the simple, common path
return servant.dispatch(request);
}
// Else, check servant locators
ServantLocator locator = findServantLocator(current.id.category);
if (locator == null && current.id.category.length() > 0) {
locator = findServantLocator("");
}
java.lang.Object cookie = null;
if (locator != null) {
boolean skipEncapsulation = true;
try {
ServantLocator.LocateResult locateResult = locator.locate(current);
servant = locateResult.returnValue;
cookie = locateResult.cookie;
skipEncapsulation = false;
} finally {
if (skipEncapsulation) {
// Skip the encapsulation on exception. This allows the next batch requests in
// the same InputStream to proceed.
request.inputStream.skipEncapsulation();
}
}
}
if (servant != null) {
CompletionStage<OutgoingResponse> response;
try {
response = servant.dispatch(request);
} catch (UserException | RuntimeException | Error exception) {
// We catch Error because ServantLocator guarantees finished gets called no matter
// what.
locator.finished(current, servant, cookie);
throw exception; // unless finished above throws another exception
}
final Object servantFinal = servant;
final ServantLocator locatorFinal = locator;
final java.lang.Object cookieFinal = cookie;
return response.handle(
(r, ex) -> {
try {
locatorFinal.finished(current, servantFinal, cookieFinal);
} catch (UserException finishedEx) {
ex = finishedEx;
}
if (ex != null) {
// We only marshal errors and runtime exceptions (including
// CompletionException) at a higher level.
if (ex instanceof Error errorEx) {
throw errorEx;
}
if (ex instanceof RuntimeException runtimeEx) {
throw runtimeEx;
}
return current.createOutgoingResponse(ex);
} else {
return r;
}
});
} else {
// Skip the encapsulation. This allows the next batch requests in the same InputStream
// to proceed.
request.inputStream.skipEncapsulation();
if (hasServant(current.id)) {
throw new FacetNotExistException();
} else {
throw new ObjectNotExistException();
}
}
}
public synchronized void addServant(Object servant, Identity ident, String facet) {
assert _instance != null; // Must not be called after destruction.
if (facet == null) {
facet = "";
}
Map<String, Object> m = _servantMapMap.get(ident);
if (m == null) {
m = new HashMap<String, Object>();
_servantMapMap.put(ident, m);
} else {
if (m.containsKey(facet)) {
String id = Util.identityToString(ident, _instance.toStringMode());
if (!facet.isEmpty()) {
id += " -f " + StringUtil.escapeString(facet, "", _instance.toStringMode());
}
throw new AlreadyRegisteredException("servant", id);
}
}
m.put(facet, servant);
}
public synchronized void addDefaultServant(Object servant, String category) {
assert (_instance != null); // Must not be called after destruction
Object obj = _defaultServantMap.get(category);
if (obj != null) {
throw new AlreadyRegisteredException("default servant", category);
}
_defaultServantMap.put(category, servant);
}
public synchronized Object removeServant(Identity ident, String facet) {
assert (_instance != null); // Must not be called after destruction.
if (facet == null) {
facet = "";
}
Map<String, Object> m = _servantMapMap.get(ident);
Object obj = null;
if (m == null || (obj = m.remove(facet)) == null) {
String id = Util.identityToString(ident, _instance.toStringMode());
if (!facet.isEmpty()) {
id += " -f " + StringUtil.escapeString(facet, "", _instance.toStringMode());
}
throw new NotRegisteredException("servant", id);
}
if (m.isEmpty()) {
_servantMapMap.remove(ident);
}
return obj;
}
public synchronized Object removeDefaultServant(String category) {
assert (_instance != null); // Must not be called after destruction.
Object obj = _defaultServantMap.get(category);
if (obj == null) {
throw new NotRegisteredException("default servant", category);
}
_defaultServantMap.remove(category);
return obj;
}
public synchronized Map<String, Object> removeAllFacets(Identity ident) {
assert (_instance != null); // Must not be called after destruction.
Map<String, Object> m = _servantMapMap.get(ident);
if (m == null) {
final String id = Util.identityToString(ident, _instance.toStringMode());
throw new NotRegisteredException("servant", id);
}
_servantMapMap.remove(ident);
return m;
}
public synchronized Object findServant(Identity ident, String facet) {
//
// This assert is not valid if the adapter dispatch incoming
// requests from bidir connections. This method might be called if
// requests are received over the bidir connection after the
// adapter was deactivated.
//
// assert(_instance != null); // Must not be called after destruction.
if (facet == null) {
facet = "";
}
Map<String, Object> m = _servantMapMap.get(ident);
Object obj = null;
if (m == null) {
obj = _defaultServantMap.get(ident.category);
if (obj == null) {
obj = _defaultServantMap.get("");
}
} else {
obj = m.get(facet);
}
return obj;
}
public synchronized Object findDefaultServant(String category) {
assert (_instance != null); // Must not be called after destruction.
return _defaultServantMap.get(category);
}
public synchronized Map<String, Object> findAllFacets(Identity ident) {
assert (_instance != null); // Must not be called after destruction.
Map<String, Object> m = _servantMapMap.get(ident);
if (m != null) {
return new HashMap<String, Object>(m);
}
return new HashMap<String, Object>();
}
public synchronized boolean hasServant(Identity ident) {
//
// This assert is not valid if the adapter dispatch incoming
// requests from bidir connections. This method might be called if
// requests are received over the bidir connection after the
// adapter was deactivated.
//
// assert(_instance != null); // Must not be called after destruction.
Map<String, Object> m = _servantMapMap.get(ident);
if (m == null) {
return false;
} else {
assert (!m.isEmpty());
return true;
}
}
public synchronized void addServantLocator(ServantLocator locator, String category) {
assert (_instance != null); // Must not be called after destruction.
ServantLocator l = _locatorMap.get(category);
if (l != null) {
final String id = StringUtil.escapeString(category, "", _instance.toStringMode());
throw new AlreadyRegisteredException("servant locator", id);
}
_locatorMap.put(category, locator);
}
public synchronized ServantLocator removeServantLocator(String category) {
ServantLocator l = null;
assert (_instance != null); // Must not be called after destruction.
l = _locatorMap.remove(category);
if (l == null) {
final String id = StringUtil.escapeString(category, "", _instance.toStringMode());
throw new NotRegisteredException("servant locator", id);
}
return l;
}
public synchronized ServantLocator findServantLocator(String category) {
//
// This assert is not valid if the adapter dispatch incoming
// requests from bidir connections. This method might be called if
// requests are received over the bidir connection after the
// adapter was deactivated.
//
// assert(_instance != null); // Must not be called after destruction.
return _locatorMap.get(category);
}
//
// Only for use by ObjectAdapter.
//
public ServantManager(Instance instance, String adapterName) {
_instance = instance;
_adapterName = adapterName;
}
//
// Only for use by ObjectAdapter.
//
public void destroy() {
Map<String, ServantLocator> locatorMap = new HashMap<String, ServantLocator>();
Logger logger = null;
synchronized (this) {
//
// If the ServantManager has already been destroyed, we're done.
//
if (_instance == null) {
return;
}
logger = _instance.initializationData().logger;
_servantMapMap.clear();
_defaultServantMap.clear();
locatorMap.putAll(_locatorMap);
_locatorMap.clear();
_instance = null;
}
for (Map.Entry<String, ServantLocator> p : locatorMap.entrySet()) {
ServantLocator locator = p.getValue();
try {
locator.deactivate(p.getKey());
} catch (Exception ex) {
String s =
"exception during locator deactivation:\n"
+ "object adapter: `"
+ _adapterName
+ "'\n"
+ "locator category: `"
+ p.getKey()
+ "'\n"
+ Ex.toString(ex);
logger.error(s);
}
}
}
}