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.HashMap;
027    import java.util.List;
028    import java.util.Map;
029    
030    import javax.naming.Context;
031    import javax.naming.NamingException;
032    
033    import org.apache.xbean.recipe.ObjectRecipe;
034    import org.apache.xbean.recipe.Option;
035    import org.apache.xbean.recipe.StaticRecipe;
036    
037    /**
038     * @version $Rev: 549455 $ $Date: 2007-06-21 08:12:27 -0400 (Thu, 21 Jun 2007) $
039     */
040    public class Holder implements Serializable {
041    
042        public static final Holder EMPTY = new Holder() {
043        };
044    
045        private Map<String, List<Injection>> injectionMap;
046        private Map<String, LifecycleMethod> postConstruct;
047        private Map<String, LifecycleMethod> preDestroy;
048    
049    
050        public Holder() {
051        }
052    
053    
054        public void addInjection(String className, Injection injection) {
055            if (injectionMap == null) {
056                injectionMap = new HashMap<String, List<Injection>>();
057            }
058            List<Injection> injections = injectionMap.get(className);
059            if (injections == null) {
060                injections = new ArrayList<Injection>();
061                injectionMap.put(className, injections);
062            }
063            injections.add(injection);
064        }
065    
066        public void addPostConstructs(Map<String, LifecycleMethod> newPostConstructs) {
067            this.postConstruct = merge(postConstruct, newPostConstructs);
068        }
069    
070        public void addPreDestroys(Map<String, LifecycleMethod> newPreDestroys) {
071            this.preDestroy = merge(preDestroy, newPreDestroys);
072        }
073    
074        private Map<String, LifecycleMethod> merge(Map<String, LifecycleMethod> old, Map<String, LifecycleMethod> additional) {
075            if (old == null) {
076                return additional;
077            }
078            if (additional == null) {
079                return old;
080            }
081            old.putAll(additional);
082            return old;
083        }
084    
085        public List<Injection> getInjections(String className) {
086            if (injectionMap == null) {
087                return null;
088            }
089            return injectionMap.get(className);
090        }
091    
092        public Map<String, LifecycleMethod> getPostConstruct() {
093            return postConstruct;
094        }
095    
096        public Map<String, LifecycleMethod> getPreDestroy() {
097            return preDestroy;
098        }
099    
100        public boolean isEmpty() {
101            return (injectionMap == null || injectionMap.isEmpty())
102                    && (postConstruct == null || postConstruct.isEmpty())
103                    && (preDestroy == null || preDestroy.isEmpty());
104        }
105    
106        public Object newInstance(String className, ClassLoader classLoader, Context context) throws IllegalAccessException, InstantiationException {
107            ObjectRecipe objectRecipe = new ObjectRecipe(className);
108            objectRecipe.allow(Option.FIELD_INJECTION);
109            objectRecipe.allow(Option.PRIVATE_PROPERTIES);
110            Class clazz;
111            try {
112                clazz = classLoader.loadClass(className);
113            } catch (ClassNotFoundException e) {
114                throw (InstantiationException)new InstantiationException("Can't load class " + className + " in classloader: " + classLoader).initCause(e);
115            }
116            List<NamingException> problems = new ArrayList<NamingException>();
117            while (clazz != Object.class) {
118                addInjections(clazz.getName(), context, objectRecipe, problems);
119                clazz = clazz.getSuperclass();
120            }
121            if (!problems.isEmpty()) {
122                throw new InstantiationException("Some objects to be injected were not found in jndi: " + problems);
123            }
124            Object result = objectRecipe.create(classLoader);
125            if (getPostConstruct() != null) {
126                try {
127                    apply(result, null, postConstruct);
128                } catch (InvocationTargetException e) {
129                    Throwable cause = e.getCause();
130                    throw (InstantiationException) new InstantiationException("Could not call postConstruct method").initCause(cause);
131                }
132            }
133            return result;
134        }
135    
136        private void addInjections(String className, Context context, ObjectRecipe objectRecipe, List<NamingException> problems) {
137            List<Injection> callbackHandlerinjections = getInjections(className);
138            if (callbackHandlerinjections != null) {
139                for (Injection injection : callbackHandlerinjections) {
140                    try {
141                        String jndiName = injection.getJndiName();
142                        //our componentContext is attached to jndi at "java:comp" so we remove that when looking stuff up in it
143                        Object object = context.lookup("env/" + jndiName);
144                        if (object instanceof String) {
145                            String string = (String) object;
146                            // Pass it in raw so it could be potentially converted to
147                            // another data type by an xbean-reflect property editor
148                            objectRecipe.setProperty(injection.getTargetName(), string);
149                        } else {
150                            objectRecipe.setProperty(injection.getTargetName(), new StaticRecipe(object));
151                        }
152                    } catch (NamingException e) {
153                        problems.add(e);
154                    }
155                }
156            }
157        }
158    
159        public void destroyInstance(Object o) throws Exception {
160            Class clazz = o.getClass();
161            Map<String, LifecycleMethod> preDestroy = getPreDestroy();
162            if (preDestroy != null) {
163                apply(o, clazz, preDestroy);
164            }
165        }
166    
167        public static void apply(Object o, Class clazz, Map<String, LifecycleMethod> map) throws IllegalAccessException, InvocationTargetException {
168            if (clazz == null) {
169                clazz = o.getClass();
170            }
171            ArrayList<Class> classes = new ArrayList<Class>();
172            while (clazz != null && clazz != Object.class) {
173                classes.add(clazz);
174                clazz = clazz.getSuperclass();
175            }
176            for (int i = classes.size() - 1; i > -1; i--) {
177                Class clazz1 = classes.get(i);
178                LifecycleMethod m = map.get(clazz1.getName());
179                if (m != null) {
180                    m.call(o, clazz1);
181                }
182            }
183        }
184    
185    }