Author: dblevins
Date: Tue Aug 14 18:25:15 2007
New Revision: 565996
URL: http://svn.apache.org/viewvc?view=rev&rev=565996
Log:
Improved proxy creation code so that for business interfaces it includes as
many interfaces as possible
Added:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ProxyInterfaceResolver.java
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/ProxyInterfaceResolverTest.java
Modified:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/JndiBuilder.java
Modified:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/JndiBuilder.java
URL:
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/JndiBuilder.java?view=diff&rev=565996&r1=565995&r2=565996
==============================================================================
---
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/JndiBuilder.java
(original)
+++
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/JndiBuilder.java
Tue Aug 14 18:25:15 2007
@@ -19,9 +19,11 @@
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.Reference;
+import javax.naming.NameAlreadyBoundException;
import javax.jms.MessageListener;
import org.apache.openejb.DeploymentInfo;
+import org.apache.openejb.InterfaceType;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import org.apache.openejb.loader.SystemInstance;
@@ -36,6 +38,7 @@
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
+import java.util.Comparator;
/**
@@ -77,7 +80,58 @@
public static enum Interface {
- REMOTE_HOME, LOCAL_HOME, BUSINESS_LOCAL, BUSINESS_REMOTE,
SERVICE_ENDPOINT
+ REMOTE_HOME(InterfaceType.EJB_HOME, "RemoteHome", "home", ""),
+ LOCAL_HOME(InterfaceType.EJB_LOCAL_HOME, "LocalHome",
"local-home", "Local"),
+ BUSINESS_LOCAL(InterfaceType.BUSINESS_LOCAL, "Local",
"business-local", "BusinessLocal"),
+ BUSINESS_REMOTE(InterfaceType.BUSINESS_REMOTE, "Remote",
"business-remote", "BusinessRemote"),
+ SERVICE_ENDPOINT(InterfaceType.SERVICE_ENDPOINT, "Endpoint",
"service-endpoint", "ServiceEndpoint");
+
+ private final InterfaceType type;
+ private final String annotatedName;
+ private final String xmlName;
+ private final String xmlNameCc;
+ private final String openejbLegacy;
+
+ Interface(InterfaceType type, String annotatedName, String
xmlName, String openejbLegacy) {
+ this.type = type;
+ this.annotatedName = annotatedName;
+ this.xmlName = xmlName;
+ this.xmlNameCc = camelCase(xmlName);
+ this.openejbLegacy = openejbLegacy;
+ }
+
+ private String camelCase(String string){
+ StringBuilder sb = new StringBuilder();
+ String[] strings = string.split("-");
+ for (String s : strings) {
+ int l = sb.length();
+ sb.append(s);
+ sb.setCharAt(l, Character.toUpperCase(sb.charAt(l)));
+ }
+ return sb.toString();
+ }
+
+
+ public InterfaceType getType() {
+ return type;
+ }
+
+ public String getAnnotatedName() {
+ return annotatedName;
+ }
+
+ public String getXmlName() {
+ return xmlName;
+ }
+
+ public String getXmlNameCc() {
+ return xmlNameCc;
+ }
+
+ public String getOpenejbLegacy() {
+ return openejbLegacy;
+ }
+
}
public String getName(DeploymentInfo deploymentInfo, Class interfce,
Interface type);
@@ -101,7 +155,10 @@
contextData.put("ejbClass.simpleName",
deploymentInfo.getBeanClass().getSimpleName());
contextData.put("ejbName", deploymentInfo.getEjbName());
contextData.put("deploymentId",
deploymentInfo.getDeploymentID().toString());
- contextData.put("interfaceType",
deploymentInfo.getInterfaceType(interfce).name());
+ contextData.put("interfaceType", type.annotatedName);
+ contextData.put("interfaceType.xmlName", type.getXmlName());
+ contextData.put("interfaceType.xmlNameCc", type.getXmlNameCc());
+ contextData.put("interfaceType.openejbLegacyName",
type.getOpenejbLegacy());
contextData.put("interfaceClass", interfce.getName());
contextData.put("interfaceClass.simpleName",
interfce.getSimpleName());
return template.apply(contextData);
@@ -198,7 +255,7 @@
Class homeInterface = deployment.getHomeInterface();
if (homeInterface != null) {
- String name = "openejb/ejb/" + strategy.getName(deployment,
deploymentInfo.getRemoteInterface(), JndiNameStrategy.Interface.REMOTE_HOME);
+ String name = "openejb/ejb/" + strategy.getName(deployment,
deploymentInfo.getHomeInterface(), JndiNameStrategy.Interface.REMOTE_HOME);
ObjectReference ref = new
ObjectReference(deployment.getEJBHome());
bind(name, ref, bindings, beanInfo);
@@ -213,7 +270,7 @@
Class localHomeInterface = deployment.getLocalHomeInterface();
if (localHomeInterface != null) {
- String name = "openejb/ejb/" + strategy.getName(deployment,
deploymentInfo.getLocalInterface(), JndiNameStrategy.Interface.LOCAL_HOME);
+ String name = "openejb/ejb/" + strategy.getName(deployment,
deploymentInfo.getLocalHomeInterface(), JndiNameStrategy.Interface.LOCAL_HOME);
ObjectReference ref = new
ObjectReference(deployment.getEJBLocalHome());
bind(name, ref, bindings, beanInfo);
@@ -225,25 +282,22 @@
}
try {
- Class businessLocalInterface =
deployment.getBusinessLocalInterface();
- if (businessLocalInterface != null) {
+ List<Class> localInterfaces =
deployment.getBusinessLocalInterfaces();
+ Class beanClass = deployment.getBeanClass();
- String name = "openejb/ejb/" + strategy.getName(deployment,
businessLocalInterface, JndiNameStrategy.Interface.BUSINESS_LOCAL);
- DeploymentInfo.BusinessLocalHome businessLocalHome =
deployment.getBusinessLocalHome();
- bind(name, new BusinessLocalReference(businessLocalHome),
bindings, beanInfo);
-
- for (Class interfce : deployment.getBusinessLocalInterfaces())
{
- DeploymentInfo.BusinessLocalHome home =
deployment.getBusinessLocalHome(asList(interfce));
- BusinessLocalReference ref = new
BusinessLocalReference(home);
-
- name = "openejb/Deployment/" +
deployment.getDeploymentID() + "/" + interfce.getName();
- bind(name, ref, bindings, beanInfo);
-
- try {
- name = "openejb/ejb/" + strategy.getName(deployment,
interfce, JndiNameStrategy.Interface.BUSINESS_LOCAL);
- bind(name, ref, bindings, beanInfo);
- } catch (NamingException dontCareJustYet) {
- }
+ for (Class interfce : deployment.getBusinessLocalInterfaces()) {
+
+ List<Class> interfaces =
ProxyInterfaceResolver.getInterfaces(beanClass, interfce, localInterfaces);
+ DeploymentInfo.BusinessLocalHome home =
deployment.getBusinessLocalHome(interfaces);
+ BusinessLocalReference ref = new BusinessLocalReference(home);
+
+ String internalName = "openejb/Deployment/" +
deployment.getDeploymentID() + "/" + interfce.getName();
+ bind(internalName, ref, bindings, beanInfo);
+
+ try {
+ String externalName = "openejb/ejb/" +
strategy.getName(deployment, interfce,
JndiNameStrategy.Interface.BUSINESS_LOCAL);
+ bind(externalName, ref, bindings, beanInfo);
+ } catch (NamingException dontCareJustYet) {
}
}
} catch (NamingException e) {
@@ -251,27 +305,23 @@
}
try {
- Class businessRemoteInterface =
deployment.getBusinessRemoteInterface();
- if (businessRemoteInterface != null) {
- DeploymentInfo.BusinessRemoteHome businessRemoteHome =
deployment.getBusinessRemoteHome();
- BusinessRemoteReference ref = new
BusinessRemoteReference(businessRemoteHome);
+ List<Class> remoteInterfaces =
deployment.getBusinessRemoteInterfaces();
+ Class beanClass = deployment.getBeanClass();
- String name = "openejb/ejb/" + strategy.getName(deployment,
businessRemoteInterface, JndiNameStrategy.Interface.BUSINESS_REMOTE);
- bind(name, ref, bindings, beanInfo);
+ for (Class interfce : deployment.getBusinessRemoteInterfaces()) {
- for (Class interfce :
deployment.getBusinessRemoteInterfaces()) {
- DeploymentInfo.BusinessRemoteHome home =
deployment.getBusinessRemoteHome(asList(interfce));
- ref = new BusinessRemoteReference(home);
-
- name = "openejb/Deployment/" +
deployment.getDeploymentID() + "/" + interfce.getName();
- bind(name, ref, bindings, beanInfo);
-
- try {
- name = "openejb/ejb/" + strategy.getName(deployment,
interfce, JndiNameStrategy.Interface.BUSINESS_REMOTE);
- bind(name, ref, bindings, beanInfo);
- } catch (NamingException dontCareJustYet) {
- }
+ List<Class> interfaces =
ProxyInterfaceResolver.getInterfaces(beanClass, interfce, remoteInterfaces);
+ DeploymentInfo.BusinessRemoteHome home =
deployment.getBusinessRemoteHome(interfaces);
+ BusinessRemoteReference ref = new
BusinessRemoteReference(home);
+
+ String internalName = "openejb/Deployment/" +
deployment.getDeploymentID() + "/" + interfce.getName();
+ bind(internalName, ref, bindings, beanInfo);
+
+ try {
+ String externalName = "openejb/ejb/" +
strategy.getName(deployment, interfce,
JndiNameStrategy.Interface.BUSINESS_REMOTE);
+ bind(externalName, ref, bindings, beanInfo);
+ } catch (NamingException dontCareJustYet) {
}
}
} catch (NamingException e) {
@@ -295,14 +345,26 @@
private void bind(String name, Reference ref, Bindings bindings,
EnterpriseBeanInfo beanInfo) throws NamingException {
- context.bind(name, ref);
- bindings.add(name);
+
if (name.startsWith("openejb/ejb/")) {
- name = name.replaceFirst("openejb/ejb/", "");
- logger.info("Jndi(name=" + name+")");
- if (!beanInfo.jndiNames.contains(name)){
- beanInfo.jndiNames.add(name);
+
+ String externalName = name.replaceFirst("openejb/ejb/", "");
+
+ if (beanInfo.jndiNames.contains(externalName)){
+ logger.debug("Duplicate: Jndi(name=" + externalName +")");
+ return;
}
+
+ beanInfo.jndiNames.add(externalName);
+ logger.info("Jndi(name=" + externalName +")");
+ }
+
+ try {
+ context.bind(name, ref);
+ bindings.add(name);
+ } catch (NameAlreadyBoundException e) {
+ logger.error("Jndi name could not be bound; it may be taken by
another ejb. Jndi(name=" + name +")");
+ throw e;
}
}
@@ -321,6 +383,17 @@
public boolean add(String o) {
return bindings.add(o);
+ }
+ }
+
+ public static class RemoteInterfaceComparator implements Comparator<Class>
{
+
+ public int compare(java.lang.Class a, java.lang.Class b) {
+ boolean aIsRmote = java.rmi.Remote.class.isAssignableFrom(a);
+ boolean bIsRmote = java.rmi.Remote.class.isAssignableFrom(b);
+
+ if (aIsRmote == bIsRmote) return 0;
+ return (aIsRmote)? 1: -1;
}
}
}
Added:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ProxyInterfaceResolver.java
URL:
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ProxyInterfaceResolver.java?view=auto&rev=565996
==============================================================================
---
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ProxyInterfaceResolver.java
(added)
+++
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ProxyInterfaceResolver.java
Tue Aug 14 18:25:15 2007
@@ -0,0 +1,205 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.assembler.classic;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.lang.reflect.Method;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ProxyInterfaceResolver {
+
+ public static List<Class> getInterfaces(Class implementation, Class
mainInterface, List<Class> interfaces){
+ List<Class> valid = new ArrayList<Class>();
+ // The intended interface is safe to add
+ valid.add(mainInterface);
+
+ // Any interface the bean implements is safe (potentially)
+ for (Class interfce : interfaces) {
+ if (interfce.isAssignableFrom(implementation)){
+ valid.add(interfce);
+ }
+ }
+
+
+ // Here comes the trick, if any of them implement java.rmi.Remote
+ // we have to check the "remote" methods against the other methods
+ // of the same name and params to see if there are any conflicts in
+ // the exception clauses. If there are, then we need to remove the
+ // conflicting interface(s).
+ //
+ // DETAILS:
+ // The trick is that two nearly matching interface methods
+ // -InterfaceA: void doIt() throws Foo;
+ // -InterfaceB: void doIt() throws Bar;
+ //
+ // can be implemented in a class by leaving out exceptions from the
+ // throws clause that aren't declared in both interfaces methods.
+ // -Implementation: void doIt(){}
+ //
+ // This means the implementing class can not throw Foo or Bar. The
+ // same rule applies to proxies created from these two interfaces as
+ // the proxy generating code will automatically remove exceptions
+ // not shared by the two matching interface methods; eliminating
+ // the proxy's and therefore container's ability to throw those
+ // exceptions.
+ //
+ // The specific issue with java.rmi.Remote interfaces is that per
+ // spec rules many runtime exceptions (container or connection issues)
+ // are thrown to clients as java.rmi.RemoteException which is not
+ // a runtime exception and must be throwable via the proxy.
+
+
+ List<Class> remotes = new ArrayList<Class>();
+ List<Class> nonremotes = new ArrayList<Class>();
+ for (Class interfce : valid) {
+ if (java.rmi.Remote.class.isAssignableFrom(interfce)){
+ remotes.add(interfce);
+ } else {
+ nonremotes.add(interfce);
+ }
+ }
+
+ // No remote interfaces, we're good to go
+ if (remotes.size() == 0) return valid;
+
+ // -----------------------------------------------------------
+ // If we got here, we have potentially clashing interfaces
+ // We sort of have to start over and go "slower" checking
+ // methods for conflicts and not including those interfaces
+ // -----------------------------------------------------------
+
+ valid.clear();
+ remotes.remove(mainInterface);
+ nonremotes.remove(mainInterface);
+
+ // Re-add the main interface
+ valid.add(mainInterface);
+
+ // Track the method signatures of the interfaces we add
+ List<Signature> proxySignatures = getSignatures(mainInterface);
+
+
+ // Show affinity for the remote interfaces if the main
+ // interface is a java.rmi.Remote
+ if (java.rmi.Remote.class.isAssignableFrom(mainInterface)){
+ for (Class interfce : remotes) {
+ addIfNotConflicting(interfce, valid, proxySignatures);
+ }
+ for (Class interfce : nonremotes) {
+ addIfNotConflicting(interfce, valid, proxySignatures);
+ }
+ } else {
+ for (Class interfce : nonremotes) {
+ addIfNotConflicting(interfce, valid, proxySignatures);
+ }
+ for (Class interfce : remotes) {
+ addIfNotConflicting(interfce, valid, proxySignatures);
+ }
+ }
+
+ return valid;
+ }
+
+ /**
+ * Adds the interface to the list of valid interfaces for the proxy
+ * if the signatures on the interface do not conflict with the method
+ * signatures already apart of the proxy's other interfaces.
+ *
+ * @param interfce
+ * @param valid
+ * @param proxySignatures
+ */
+ private static void addIfNotConflicting(Class interfce, List<Class> valid,
List<Signature> proxySignatures) {
+
+ List<Signature> interfaceSigs = getSignatures(interfce);
+
+
+ for (Signature sig : interfaceSigs) {
+ // Contains will return true if the
+ // method signature exits *and* has
+ // a different throws clause
+ if (proxySignatures.contains(sig)){
+ return; // conflicts and cannot be added
+ }
+ }
+
+
+ // Does not conflict, add it and track the new signatures
+ valid.add(interfce);
+ proxySignatures.addAll(interfaceSigs);
+ }
+
+ private static List<Signature> getSignatures(Class mainInterface) {
+ List<Signature> sigs = new ArrayList<Signature>();
+ for (Method method : mainInterface.getMethods()) {
+ sigs.add(new Signature(mainInterface, method));
+ }
+ return sigs;
+ }
+
+ public static class Signature {
+ private final Class clazz;
+ private final Method method;
+ private final String sig;
+
+ public Signature(Class clazz, Method method) {
+ this.clazz = clazz;
+ this.method = method;
+ StringBuilder sb = new StringBuilder();
+ sb.append(method.getName());
+ sb.append('(');
+ for (Class<?> type : method.getParameterTypes()) {
+ sb.append(type.getName());
+ sb.append(',');
+ }
+ sb.append(')');
+ sig = sb.toString();
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ // This equals returns true only if the method signatures
+ // are the same *and* one is remote and one is not
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final Signature signature = (Signature) o;
+
+ if (!sig.equals(signature.sig)) return false;
+
+ boolean aIsRemote = java.rmi.Remote.class.isAssignableFrom(clazz);
+ boolean bIsRemote =
java.rmi.Remote.class.isAssignableFrom(signature.clazz);
+
+ return !(aIsRemote == bIsRemote);
+ }
+
+ public int hashCode() {
+ return sig.hashCode();
+ }
+
+ public String toString() {
+ return method.toString();
+ }
+ }
+
+}
Added:
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/ProxyInterfaceResolverTest.java
URL:
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/ProxyInterfaceResolverTest.java?view=auto&rev=565996
==============================================================================
---
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/ProxyInterfaceResolverTest.java
(added)
+++
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/ProxyInterfaceResolverTest.java
Tue Aug 14 18:25:15 2007
@@ -0,0 +1,144 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.assembler.classic;
+
+import junit.framework.TestCase;
+
+import java.rmi.RemoteException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ProxyInterfaceResolverTest extends TestCase {
+ public void test() throws Exception {
+ Class smoothie;
+ List<Class> ingredients;
+
+ // No remotes
+ smoothie = implement(Mango.class, Lime.class, Lemon.class);
+ ingredients = resolve(smoothie, Mango.class, Lime.class, Lemon.class);
+ assertEquals(3, ingredients.size());
+ assertTrue(ingredients.contains(Mango.class));
+ assertTrue(ingredients.contains(Lime.class));
+ assertTrue(ingredients.contains(Lemon.class));
+
+ // All remotes
+ smoothie = implement(Cherry.class, Honey.class, Grape.class);
+ ingredients = resolve(smoothie, Cherry.class, Honey.class,
Grape.class);
+ assertEquals(3, ingredients.size());
+ assertTrue(ingredients.contains(Cherry.class));
+ assertTrue(ingredients.contains(Grape.class));
+ assertTrue(ingredients.contains(Honey.class));
+
+ // mixed remote and non-remote, no conflicts
+ smoothie = implement(Banana.class, Honey.class, Creme.class);
+ ingredients = resolve(smoothie, Banana.class, Honey.class,
Creme.class);
+ assertEquals(3, ingredients.size());
+ assertTrue(ingredients.contains(Banana.class));
+ assertTrue(ingredients.contains(Honey.class));
+ assertTrue(ingredients.contains(Creme.class));
+
+ // mixed remote and non-remote, conflicts (cherry, grape)
+ smoothie = implement(Mango.class, Banana.class, Creme.class,
Honey.class, Cherry.class, Grape.class);
+ ingredients = resolve(smoothie, Mango.class, Banana.class,
Creme.class, Honey.class, Cherry.class, Grape.class);
+ assertEquals(4, ingredients.size());
+ assertTrue(ingredients.contains(Mango.class));
+ assertTrue(ingredients.contains(Banana.class));
+ assertTrue(ingredients.contains(Creme.class));
+ assertTrue(ingredients.contains(Honey.class));
+
+ // mixed remote and non-remote, conflicts (mango, banana)
+ smoothie = implement(Cherry.class, Mango.class, Banana.class,
Creme.class, Honey.class, Grape.class);
+ ingredients = resolve(smoothie, Cherry.class, Mango.class,
Banana.class, Creme.class, Honey.class, Grape.class);
+ assertEquals(4, ingredients.size());
+ assertTrue(ingredients.contains(Cherry.class));
+ assertTrue(ingredients.contains(Grape.class));
+ assertTrue(ingredients.contains(Creme.class));
+ assertTrue(ingredients.contains(Honey.class));
+ }
+
+ public List<Class> resolve(Class impl, Class mainInterface, Class...
interfaces) {
+ return ProxyInterfaceResolver.getInterfaces(impl, mainInterface,
Arrays.asList(interfaces));
+ }
+
+
+ public Class implement(Class<?>... interfaces) {
+ return
java.lang.reflect.Proxy.getProxyClass(this.getClass().getClassLoader(),
interfaces);
+ }
+
+ public interface Mango {
+ void exist() throws RoundException, GreenException;
+
+ void mango();
+ }
+
+ public interface Lime {
+ void exist() throws RoundException, GreenException;
+
+ void lime();
+ }
+
+ public interface Lemon {
+ void exist() throws RoundException, YellowException;
+
+ void lemon();
+ }
+
+ public interface Banana {
+ void exist() throws LongException, YellowException;
+
+ void banana();
+ }
+
+ public interface Creme {
+ void thiken();
+ }
+
+ public interface Cherry extends java.rmi.Remote {
+ void exist() throws RoundException, RemoteException;
+
+ void cherry() throws RemoteException;
+ }
+
+ public interface Grape extends java.rmi.Remote {
+ void exist() throws RoundException, RemoteException;
+
+ void grape() throws RemoteException;
+ }
+
+ public interface Honey extends java.rmi.Remote {
+ void sweeten() throws RemoteException;
+ }
+
+ //--------------//
+ public static class RoundException extends Exception {
+ }
+
+ public static class GreenException extends Exception {
+ }
+
+ public static class YellowException extends Exception {
+ }
+
+ public static class LongException extends Exception {
+ }
+
+
+}
+