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  
19  package org.apache.river.api.io;
20  
21  import java.io.IOException;
22  import java.io.InvalidClassException;
23  import java.io.InvalidObjectException;
24  import java.io.ObjectInput;
25  import java.io.Externalizable;
26  import java.lang.annotation.ElementType;
27  import java.lang.annotation.Retention;
28  import java.lang.annotation.RetentionPolicy;
29  import java.lang.annotation.Target;
30  import java.lang.reflect.Constructor;
31  import java.lang.reflect.InvocationTargetException;
32  import java.lang.reflect.Modifier;
33  import java.security.AccessController;
34  import java.security.PrivilegedActionException;
35  import java.security.PrivilegedExceptionAction;
36  
37  /**
38   * Classes annotated with this are expected to have a single argument public
39   * constructor that accepts an ObjectInput instance and implement Externalizable.
40   * <p>
41   * The constructor is required to validate all input before calling a super
42   * constructor.
43   * <p>
44   * The constructor replaces the readExternal method in Externalizable.
45   * 
46   * @see Externalizable
47   * @author peter
48   */
49  @Retention(RetentionPolicy.RUNTIME)
50  @Target(ElementType.TYPE)
51  public @interface AtomicExternal {
52      
53     /**
54       * Factory to test AtomicExternal instantiation compliance.
55       */
56      public static final class Factory {
57  	private Factory(){} // Non instantiable.
58  	
59  	/**
60  	 * Convenience method for testing implementing class constructor
61  	 * signature compliance.
62  	 * <p>
63  	 * De-serializers are free to implement higher performance instantiation
64  	 * that complies with this contract.
65  	 * <p>
66  	 * Only public and package default constructors can be called by 
67  	 * de-serializers.  Package default constructors have been provided
68  	 * to prevent implementations from polluting public api,
69  	 * but should be treated as public constructors.
70  	 * <p>
71  	 * Constructors with private visibility cannot be called.
72  	 * <p>
73  	 * Constructors with protected visibility can only be called by 
74  	 * subclasses, not de-serializers.
75  	 * 
76  	 * @param <T> AtomicExternal implementation type.
77  	 * @param type AtomicExternal implementing class.
78  	 * @param arg ObjectInput argument to pass to implementing constructor.
79  	 * @return new instance of T.
80  	 * @throws java.io.InvalidClassException if constructor is non compliant
81  	 * or doesn't exist. 
82  	 * @throws java.io.InvalidObjectException if invariant check fails
83  	 * @throws NullPointerException if arg or type is null.
84  	 */
85  	public static <T> T instantiate(final Class<T> type, final ObjectInput arg)
86  		throws IOException {
87  	    if (arg == null) throw new NullPointerException();
88  	    if (type == null) throw new NullPointerException();
89  	    final Class[] param = { ObjectInput.class };
90  	    Object[] args = { arg };
91  	    Constructor<T> c;
92  	    try {
93  		c = AccessController.doPrivileged(
94  		    new PrivilegedExceptionAction<Constructor<T>>(){
95  
96  			@Override
97  			public Constructor<T> run() throws Exception {
98  			    Constructor<T> c = type.getDeclaredConstructor(param);
99  			    int mods = c.getModifiers();
100 			    switch (mods){
101 				case Modifier.PUBLIC:
102 				    c.setAccessible(true); //In case constructor is public but class not.
103 				    return c;
104 				case Modifier.PROTECTED:
105 				    throw new InvalidClassException( type.getCanonicalName(),
106 					"protected constructor cannot be called by de-serializer");
107 				case Modifier.PRIVATE:
108 				    throw new InvalidClassException( type.getCanonicalName(),
109 					"private constructor cannot be called by de-serializer");
110 				default: // Package private
111 				    throw new InvalidClassException( type.getCanonicalName(),
112 					"Package private constructor cannot be called by de-serializer");
113 			    }
114 			}
115 
116 		    });
117 		return c.newInstance(args);
118 	    } catch (PrivilegedActionException ex) {
119 		Exception e = ex.getException();
120 		if (e instanceof NoSuchMethodException) throw new InvalidClassException(type.getCanonicalName(), "No matching AtomicSerial constructor signature found");
121 		if (e instanceof SecurityException ) throw (SecurityException) e;
122 		if (e instanceof InvalidClassException ) throw (InvalidClassException) e;
123 		InvalidClassException ice = new InvalidClassException("Unexpected exception while attempting to access constructor");
124 		ice.initCause(ex);
125 		throw ice;
126 	    } catch (InvocationTargetException ex) {
127 		Throwable e = ex.getCause();
128 		if (e instanceof InvalidObjectException) throw (InvalidObjectException) e;
129 		if (e instanceof IOException) throw (IOException) e;
130 		if (e instanceof RuntimeException) throw (RuntimeException) e;
131 		InvalidObjectException ioe = new InvalidObjectException(
132 		    "Construction failed: " + type);
133 		ioe.initCause(ex);
134 		throw ioe;
135 	    } catch (IllegalAccessException ex) {
136 		throw new AssertionError("This shouldn't happen ", ex);
137 	    } catch (IllegalArgumentException ex) {
138 		throw new AssertionError("This shouldn't happen ", ex);
139 	    } catch (InstantiationException ex) {
140 		throw new InvalidClassException(type.getCanonicalName(), ex.getMessage());
141 	    }
142 	}
143     }
144 }