001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *  http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    
021    package org.apache.geronimo.j2ee.annotation;
022    
023    import java.io.Serializable;
024    import java.lang.reflect.InvocationTargetException;
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.HashMap;
028    import java.util.HashSet;
029    import java.util.List;
030    import java.util.Map;
031    import java.util.Set;
032    
033    import javax.naming.Context;
034    import javax.naming.NamingException;
035    
036    import org.apache.xbean.recipe.ObjectRecipe;
037    import org.apache.xbean.recipe.Option;
038    import org.apache.xbean.recipe.StaticRecipe;
039    
040    /**
041     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
042     */
043    public class Holder implements Serializable {
044    
045        public static final Holder EMPTY = new Holder() {
046        };
047    
048        private Map<String, Set<Injection>> injectionMap;
049        private Map<String, LifecycleMethod> postConstruct;
050        private Map<String, LifecycleMethod> preDestroy;
051    
052    
053        public Holder() {
054        }
055    
056        public Holder(Holder source) {
057            if (source.getInjectionMap() != null) {
058                this.injectionMap = new HashMap<String, Set<Injection>>();
059                addInjectionMap(source.getInjectionMap());
060            }
061            
062            if (source.getPostConstruct() != null) {
063                this.postConstruct = new HashMap<String, LifecycleMethod>();
064                addPostConstructs(source.getPostConstruct());
065            }
066            
067            if (source.getPreDestroy() != null) {
068                this.preDestroy = new HashMap<String, LifecycleMethod>();
069                addPreDestroys(source.getPreDestroy());
070            }
071        }
072        
073        private Set<Injection> getInjectionList(String className) {
074            if (injectionMap == null) {
075                injectionMap = new HashMap<String, Set<Injection>>();
076            }
077            Set<Injection> injections = injectionMap.get(className);
078            if (injections == null) {
079                injections = new HashSet<Injection>();
080                injectionMap.put(className, injections);
081            }
082            return injections;
083        }
084        
085        public void addInjection(String className, Injection newInjection) {
086            Set<Injection> injections = getInjectionList(className);
087            injections.add(newInjection);
088        }
089        
090        public void addInjections(String className, Collection<Injection> newInjections) {
091            Set<Injection> injections = getInjectionList(className);        
092            for (Injection injection : newInjections) {
093                injections.add(injection);
094            }
095        }
096    
097        public void addPostConstructs(Map<String, LifecycleMethod> newPostConstructs) {
098            this.postConstruct = merge(postConstruct, newPostConstructs);
099        }
100    
101        public void addPreDestroys(Map<String, LifecycleMethod> newPreDestroys) {
102            this.preDestroy = merge(preDestroy, newPreDestroys);
103        }
104    
105        private Map<String, LifecycleMethod> merge(Map<String, LifecycleMethod> old, Map<String, LifecycleMethod> additional) {
106            if (old == null) {
107                return additional;
108            }
109            if (additional == null) {
110                return old;
111            }
112            old.putAll(additional);
113            return old;
114        }
115    
116        public void addInjectionMap(Map<String, Set<Injection>> injectionMap) {
117            if (injectionMap == null) {
118                return;
119            }
120            for (Map.Entry<String, Set<Injection>> entry : injectionMap.entrySet()) {
121                String className = entry.getKey();
122                Set<Injection> injections = entry.getValue();
123                addInjections(className, injections);            
124            }
125        }
126        
127        public List<Injection> getInjections(String className) {
128            if (injectionMap != null) {                  
129                Set<Injection> injections = injectionMap.get(className);
130                if (injections != null) {
131                    return new ArrayList<Injection>(injections);
132                }
133            }
134            return null;
135        }
136    
137        public Map<String, Set<Injection>> getInjectionMap() {
138            return injectionMap;
139        }
140        
141        public Map<String, LifecycleMethod> getPostConstruct() {
142            return postConstruct;
143        }
144    
145        public Map<String, LifecycleMethod> getPreDestroy() {
146            return preDestroy;
147        }
148    
149        public boolean isEmpty() {
150            return (injectionMap == null || injectionMap.isEmpty())
151                    && (postConstruct == null || postConstruct.isEmpty())
152                    && (preDestroy == null || preDestroy.isEmpty());
153        }
154    
155        public Object newInstance(String className, ClassLoader classLoader, Context context) throws IllegalAccessException, InstantiationException {
156            ObjectRecipe objectRecipe = new ObjectRecipe(className);
157            objectRecipe.allow(Option.FIELD_INJECTION);
158            objectRecipe.allow(Option.PRIVATE_PROPERTIES);
159            Class clazz;
160            try {
161                clazz = classLoader.loadClass(className);
162            } catch (ClassNotFoundException e) {
163                throw (InstantiationException)new InstantiationException("Can't load class " + className + " in classloader: " + classLoader).initCause(e);
164            }
165            List<NamingException> problems = new ArrayList<NamingException>();
166            while (clazz != Object.class) {
167                addInjections(clazz.getName(), context, objectRecipe, problems);
168                clazz = clazz.getSuperclass();
169            }
170            if (!problems.isEmpty()) {
171                throw new InstantiationException("Some objects to be injected were not found in jndi: " + problems);
172            }
173            Object result = objectRecipe.create(classLoader);
174            if (getPostConstruct() != null) {
175                try {
176                    apply(result, null, postConstruct);
177                } catch (InvocationTargetException e) {
178                    Throwable cause = e.getCause();
179                    throw (InstantiationException) new InstantiationException("Could not call postConstruct method").initCause(cause);
180                }
181            }
182            return result;
183        }
184    
185        private void addInjections(String className, Context context, ObjectRecipe objectRecipe, List<NamingException> problems) {
186            List<Injection> callbackHandlerinjections = getInjections(className);
187            if (callbackHandlerinjections != null) {
188                for (Injection injection : callbackHandlerinjections) {
189                    try {
190                        String jndiName = injection.getJndiName();
191                        //our componentContext is attached to jndi at "java:comp" so we remove that when looking stuff up in it
192                        Object object = context.lookup("env/" + jndiName);
193                        if (object instanceof String) {
194                            String string = (String) object;
195                            // Pass it in raw so it could be potentially converted to
196                            // another data type by an xbean-reflect property editor
197                            objectRecipe.setProperty(injection.getTargetName(), string);
198                        } else {
199                            objectRecipe.setProperty(injection.getTargetName(), new StaticRecipe(object));
200                        }
201                    } catch (NamingException e) {
202                        problems.add(e);
203                    }
204                }
205            }
206        }
207    
208        public void destroyInstance(Object o) throws Exception {
209            Class clazz = o.getClass();
210            Map<String, LifecycleMethod> preDestroy = getPreDestroy();
211            if (preDestroy != null) {
212                apply(o, clazz, preDestroy);
213            }
214        }
215    
216        public static void apply(Object o, Class clazz, Map<String, LifecycleMethod> map) throws IllegalAccessException, InvocationTargetException {
217            if (clazz == null) {
218                clazz = o.getClass();
219            }
220            ArrayList<Class> classes = new ArrayList<Class>();
221            while (clazz != null && clazz != Object.class) {
222                classes.add(clazz);
223                clazz = clazz.getSuperclass();
224            }
225            for (int i = classes.size() - 1; i > -1; i--) {
226                Class clazz1 = classes.get(i);
227                LifecycleMethod m = map.get(clazz1.getName());
228                if (m != null) {
229                    m.call(o, clazz1);
230                }
231            }
232        }
233    
234    }