View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.river.reggie.proxy;
19  
20  import java.io.IOException;
21  import java.io.ObjectOutputStream;
22  import java.io.ObjectStreamField;
23  import java.io.Serializable;
24  import java.lang.reflect.Proxy;
25  import java.rmi.MarshalException;
26  import java.rmi.NoSuchObjectException;
27  import java.rmi.Remote;
28  import java.rmi.RemoteException;
29  import java.rmi.server.RemoteObject;
30  import java.util.List;
31  import java.util.logging.Level;
32  import java.util.logging.Logger;
33  import net.jini.core.lookup.ServiceID;
34  import net.jini.core.lookup.ServiceItem;
35  import net.jini.security.Security;
36  import org.apache.river.action.GetBooleanAction;
37  import org.apache.river.api.io.AtomicSerial;
38  import org.apache.river.api.io.AtomicSerial.GetArg;
39  import org.apache.river.api.io.Valid;
40  import org.apache.river.logging.Levels;
41  import org.apache.river.proxy.MarshalledWrapper;
42  import org.apache.river.proxy.Bootstrap;
43  
44  /**
45   * An Item contains the fields of a ServiceItem packaged up for
46   * transmission between client-side proxies and the registrar server.
47   * Instances are never visible to clients, they are private to the
48   * communication between the proxies and the server.
49   * <p>
50   * This class only has a bare minimum of methods, to minimize
51   * the amount of code downloaded into clients.
52   *
53   * @author Sun Microsystems, Inc.
54   *
55   */
56  @AtomicSerial
57  public final class Item implements Serializable, Cloneable {
58  
59      private static final long serialVersionUID = 2L;
60      private static final ObjectStreamField[] serialPersistentFields = 
61      { 
62          /** @serialField ServiceItem.serviceID. */
63          new ObjectStreamField("serviceID", ServiceID.class),
64          /** @serialField The Class of ServiceItem.service converted to ServiceType. */
65          new ObjectStreamField("serviceType", ServiceType.class),
66  	/** @serialField The codebase of the service object. */
67          new ObjectStreamField("codebase", String.class),
68  	/** @serialField ServiceItem.service as a MarshalledWrapper. */
69          new ObjectStreamField("service", MarshalledWrapper.class),
70  	/** @serialField ServiceItem.attributeSets converted to EntryReps. */
71          new ObjectStreamField("attributeSets", EntryRep[].class),
72  	/** @serialField ServiceItem.attributeSets converted to EntryReps. */
73          new ObjectStreamField("bootstrapProxy", Proxy.class)   
74      };
75  
76      /** Logger for Reggie. */
77      private static final Logger logger = 
78  	Logger.getLogger("org.apache.river.reggie");
79      
80      /**
81       * Flag to enable JRMP impl-to-stub replacement during marshalling of
82       * service proxy.
83       */
84      private static final boolean enableImplToStubReplacement;
85      static {
86  	Boolean b;
87  	try {
88  	    b = (Boolean) Security.doPrivileged(new GetBooleanAction(
89  		"org.apache.river.reggie.enableImplToStubReplacement"));
90  	} catch (SecurityException e) {
91  	    logger.log(Levels.HANDLED, "failed to read system property", e);
92  	    b = Boolean.FALSE;
93  	}
94  	enableImplToStubReplacement = b.booleanValue();
95      }
96  
97      /**
98       * ServiceItem.serviceID.
99       *
100      * @serial
101      */
102     private ServiceID serviceID; // mutated by RegistrarImpl
103     /**
104      * The Class of ServiceItem.service converted to ServiceType.
105      *
106      * @serial
107      */
108     public final ServiceType serviceType;
109     /**
110      * The codebase of the service object.
111      *
112      * @serial
113      */
114     public final String codebase;
115     /**
116      * ServiceItem.service as a MarshalledWrapper.
117      *
118      * @serial
119      */
120     public final MarshalledWrapper service;
121     /**
122      * ServiceItem.attributeSets converted to EntryReps.
123      *
124      * @serial
125      */
126     private EntryRep[] attributeSets; // mutated by RegistrarImpl
127     
128     /**
129      * Bootstrap proxy for registrar default method.
130      * @serial
131      */
132     Proxy bootstrapProxy;
133      
134     /**
135      * Checks all invariants are satisfied during de-serialization.
136      * @param arg
137      * @return
138      * @throws IOException 
139      * @throws ClassCastException
140      * @throws NullPointerException
141      */
142     private static boolean check(GetArg arg) throws IOException{
143 	arg.get("serviceID", null, ServiceID.class);
144 	// serviceID is assigned by Reggie if null.
145 	arg.get("serviceType", null, ServiceType.class);
146 	// serviceType allowed to be null
147 	arg.get("codebase", null, String.class);
148 	// codebase allowed to be null
149 	Valid.notNull(
150 		arg.get("service", null, MarshalledWrapper.class), 
151 		"service cannot be null");
152 	// attributeSets can be null and can contain null
153 	arg.get("attributeSets", null, EntryRep[].class);
154 	Proxy bootstrapProxy = arg.get("bootstrapProxy", null, Proxy.class);
155 	if (bootstrapProxy != null) {
156 	    if (Proxy.isProxyClass(bootstrapProxy.getClass())) return true;
157 	}
158 	return true;
159     }
160     
161     /**
162      * Failure is atomic, if any invariants aren't satisfied, construction fails
163      * before an instance can be created.
164      * @param arg
165      * @throws IOException 
166      */
167     public Item(GetArg arg) throws IOException{
168 	this(arg, check(arg));
169     };
170     
171     private Item(GetArg arg, boolean check) throws IOException{
172 	super(); // instance has been created here.
173 	serviceID = arg.get("serviceID", null, ServiceID.class);
174 	serviceType = arg.get("serviceType", null, ServiceType.class);
175 	codebase = arg.get("codebase", null, String.class);
176 	service = arg.get("service", null, MarshalledWrapper.class);
177 	attributeSets = Valid.copy(arg.get("attributeSets", null, EntryRep[].class));
178 	bootstrapProxy = arg.get("bootstrapProxy", null, Proxy.class);
179     }
180 
181     /**
182      * Converts a ServiceItem to an Item.  Any exception that results
183      * is bundled up into a MarshalException.
184      */
185     public Item(ServiceItem item) throws RemoteException {
186 	Object svc = item.service;
187 	if (enableImplToStubReplacement && svc instanceof Remote) {
188 	    try {
189 		svc = RemoteObject.toStub((Remote) svc);
190 		if (logger.isLoggable(Level.FINER)) {
191 		    logger.log(Level.FINER, "replacing {0} with {1}",
192 			       new Object[]{ item.service, svc });
193 		}
194 	    } catch (NoSuchObjectException e) {
195 	    }
196 	}
197 	//  Now we need to create the bootstrap proxy for the new lookup method.
198 	bootstrapProxy = Bootstrap.create(svc);
199 	serviceID = item.serviceID;
200 	ServiceTypeBase stb = ClassMapper.toServiceTypeBase(svc.getClass());
201 	serviceType = stb.type;
202 	codebase = stb.codebase;
203 	try {
204 	    service = new MarshalledWrapper(svc);
205 	} catch (IOException e) {
206 	    throw new MarshalException("error marshalling arguments", e);
207 	}
208 	attributeSets = EntryRep.toEntryRep(item.attributeSets, true);
209     }
210     
211     public Item(ServiceID serviceID, ServiceType serviceType, String codebase, MarshalledWrapper service, EntryRep[] attrSets, Proxy bootstrap)
212     {
213         this.serviceID = serviceID;
214         this.serviceType = serviceType;
215         this.codebase = codebase;
216         this.service = service;
217         attributeSets = attrSets != null ? attrSets.clone() : new EntryRep[0];
218 	this.bootstrapProxy = bootstrap;
219     }
220 
221     /**
222      * Convert back to a ServiceItem.  If the service object cannot be
223      * constructed, it is set to null.  If an Entry cannot be constructed,
224      * it is set to null.  If a field of an Entry cannot be unmarshalled,
225      * it is set to null.
226      */
227     public ServiceItem get() {
228 	Object obj = null;
229 	try {
230 	    obj = service.get();
231 	} catch (Throwable e) {
232 	    RegistrarProxy.handleException(e);
233 	}
234 	synchronized (this){
235 	    return new ServiceItem(serviceID,
236 			       obj,
237 			       EntryRep.toEntry(attributeSets));
238 	}
239     }
240     
241     public Proxy getProxy() {
242 	return bootstrapProxy;
243     }
244 
245     /**
246      * Deep clone.  This is really only needed in the server,
247      * but it's very convenient to have here.
248      */
249     @Override
250     public Object clone() {
251 	    EntryRep[] attrSets = (EntryRep[])attributeSets.clone();
252 	    for (int i = attrSets.length; --i >= 0; ) {
253 		attrSets[i] = (EntryRep)attrSets[i].clone();
254 	    }
255 	    return new Item(serviceID, serviceType, codebase, service, attrSets, null);
256     }
257 
258     /**
259      * Converts an ArrayList of Item to an array of ServiceItem.
260      */
261     public static ServiceItem[] toServiceItem(List reps)
262     {
263 	ServiceItem[] items = null;
264 	if (reps != null) {
265 	    items = new ServiceItem[reps.size()];
266 	    for (int i = items.length; --i >= 0; ) {
267 		items[i] = ((Item)reps.get(i)).get();
268 	    }
269 	}
270 	return items;
271     }
272     
273     private synchronized void writeObject(ObjectOutputStream out) throws IOException {
274 	out.defaultWriteObject();
275 }
276 
277     /**
278      * @return the serviceID
279      */
280     public synchronized ServiceID getServiceID() {
281 	return serviceID;
282     }
283 
284     /**
285      * @param serviceID the serviceID to set
286      */
287     public synchronized void setServiceID(ServiceID serviceID) {
288 	this.serviceID = serviceID;
289     }
290 
291     /**
292      * @return the attributeSets
293      */
294     public synchronized EntryRep[] getAttributeSets() {
295 	return attributeSets == null ? null : attributeSets.clone();
296     }
297     
298     public synchronized EntryRep getAttributeSetAtIndex(int i){
299 	return attributeSets == null ? null : attributeSets[i];
300     }
301     
302     public synchronized int getAttributeSetsLength(){
303 	return attributeSets == null ? 0 : attributeSets.length;
304     }
305 
306     /**
307      * @param attributeSets the attributeSets to set
308      */
309     public synchronized void setAttributeSets(EntryRep[] attributeSets) {
310 	this.attributeSets = attributeSets;
311     }
312 }