001    /*
002      Copyright (C) 2001-2003 Renaud Pawlak <renaud@aopsys.com>
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful, but
010      WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
012      Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public
015      License along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
017      USA */
018    
019    package org.objectweb.jac.core;
020    
021    import java.util.Arrays;
022    import java.util.Collection;
023    import java.util.HashSet;
024    import java.util.Iterator;
025    import java.util.List;
026    import java.util.Vector;
027    import org.apache.log4j.Logger;
028    import org.objectweb.jac.core.rtti.ClassItem;
029    import org.objectweb.jac.core.rtti.ClassRepository;
030    import org.objectweb.jac.core.rtti.CollectionItem;
031    import org.objectweb.jac.core.rtti.FieldItem;
032    import org.objectweb.jac.core.rtti.RttiAC;
033    import org.objectweb.jac.util.ExtArrays;
034    import org.objectweb.jac.util.Predicate;
035    import org.objectweb.jac.util.Strings;
036    
037    /**
038     * This class allows the JAC applications to access objects that are
039     * managed within the current VM or that can be defined outside of it
040     * by the currently woven aspects.
041     *
042     * <p>By default, the object repository methods only return the JAC
043     * objects that are accessible through the
044     * <code>JacObject.getObject</code> methods. However, since it upcalls
045     * the <code>ACManager.whenGetObjects()</code> method, all the aspect
046     * components can change the returned object set.
047     *
048     * <p>As typical examples:
049     *
050     * <ul><li>the persistence aspects may load objects that correspond to
051     * the get filter so that the application can see the objects stored
052     * in the database
053     *
054     * <li>the owning aspect can remove some objects from the returned
055     * collection if the current user is not allowed to access them
056     *
057     * <li>the distribution aspect can initiate a distributed seek over
058     * all the containers of the topology to add some objects that
059     * correspond to the filter but that may have not been declared yet in
060     * this VM</ul>
061     *
062     * @see ACManager#whenGetObjects(Collection,ClassItem)
063     * @see AspectComponent#whenGetObjects(Collection,ClassItem)
064     */
065    
066    public class ObjectRepository {
067        static Logger logger = Logger.getLogger("repository");
068        static Logger loggerGet = Logger.getLogger("repository.getobjects");
069    
070        /** Memorize the JAC object instances of the system. */
071        transient static org.objectweb.jac.util.WeakHashMap instances = new org.objectweb.jac.util.WeakHashMap();
072    
073        /** Fast access for the JAC objects. Integer -> Object */
074        transient static java.util.WeakHashMap reverseInstances = new java.util.WeakHashMap();
075    
076        /** The number of created JAC objects. */
077        transient static int instancecount = 0;
078    
079        public static void register(Object wrappee) {
080            // This test is not useful anymore since register should be
081            // called only once
082            //if(instances.contains(wrappee)) return;
083            // do not register collections
084            if (wrappee instanceof org.objectweb.jac.lib.java.util.Vector || 
085                wrappee instanceof org.objectweb.jac.lib.java.util.HashSet || 
086                wrappee instanceof org.objectweb.jac.lib.java.util.Hashtable ||
087                wrappee instanceof org.objectweb.jac.lib.java.util.HashMap) {
088                return;
089            }
090            //System.out.println("registering "+wrappee);
091            Integer inst = new Integer(instancecount++);
092            instances.put(inst,wrappee);
093            logger.debug(wrappee+"->"+inst);
094            reverseInstances.put(wrappee,inst);
095        }
096    
097        /**
098         * This method deletes the given JAC object by removing it from
099         * the different collections it has been inserted into by the
100         * system.
101         *
102         * <p>The JAVA GC, will then be able to free it from memory on its
103         * next run. 
104         */
105        public static void delete(Wrappee object) {
106            logger.debug("delete "+object);
107            // remove from system list
108            instances.remove(reverseInstances.get(object));
109            reverseInstances.remove(object);
110            // remove from name repository
111            NameRepository.get().unregisterObject(object);
112            ACManager.getACM().whenDeleted(object);
113        }
114    
115        public static void free(Wrappee object) {
116            // remove from system list
117            instances.remove(object);
118            reverseInstances.remove(object);
119            // remove from name repository
120            NameRepository.get().unregisterObject(object);
121            ACManager.getACM().whenFree(object);
122        }
123    
124        /**
125         * This method returns the nth object that has been created
126         * in the Jac system.
127         *
128         * @param nth  the object index
129         * @return     the requested object
130         */
131        public static Object getMemoryObject(int nth) {
132            return instances.get(new Integer(nth));
133        }
134    
135    
136        /**
137         * This method returns the index of anobject that has been created
138         * in the Jac system.
139         *
140         * @param obj  the object
141         * @return     the index of the object
142         */
143        public static int getMemoryObjectIndex(Object obj) {
144            return ((Integer)reverseInstances.get(obj)).intValue();
145        }
146    
147    
148        /**
149         * This method returns the object counter of the Jac system (last
150         * created object is <code>getObject(objectCount()-1)</code>).
151         *
152         * @return  the number of created objects
153         */
154        public static int memoryObjectCount() {
155            return instances.size(); //instancecount;
156        }
157        
158        
159        /**
160         * This method returns all the JAC objects that are in the current
161         * JVM memory (use <code>getObjects()</code> to get all the objects
162         * handled by the woven aspects -- such as distribution or
163         * persistence).
164         *
165         * @return all the JAC objects in memory 
166         *
167         * @see #getObjects()
168         * @see #getObjects(ClassItem)
169         * @see #getMemoryObjects(ClassItem)
170         */
171        public static Collection getMemoryObjects() {
172            return instances.values();
173        }
174    
175    
176        /**
177         * This method returns all the Jac objects of a given type as an
178         * array.
179         *
180         * @param type the type to get
181         * @return all the Jac objects of a given type 
182         *
183         * @see #getObjects()
184         * @see #getObjects(ClassItem)
185         * @see #getMemoryObjects(ClassItem)
186         */
187        public static Object[] getMemoryObjects(String type) {
188            ClassItem cl = null;
189            try {
190                cl = ClassRepository.get().getClass(type);
191            } catch( Exception e ) { 
192                e.printStackTrace(); 
193                return ExtArrays.emptyObjectArray;
194            }
195            return getMemoryObjects(cl);
196        }
197    
198    
199        /**
200         * This method returns all the JAC objects of a given type as an
201         * array.
202         *
203         * @param cl the type to get
204         * @return all the JAC objects of a given type 
205         *
206         * @see #getObjects()
207         * @see #getObjects(ClassItem)
208         * @see #getMemoryObjects()
209         */
210        public static Object[] getMemoryObjects(ClassItem cl) {
211            Vector objects = new Vector();
212    
213            Class type = cl.getActualClass();
214            Iterator it = getMemoryObjects().iterator();
215            while(it.hasNext()) {
216                Object cur = it.next();
217                if (type.isAssignableFrom(cur.getClass())) {
218                    objects.add(cur);
219                }
220            }
221    
222            logger.debug("getMemoryObjects("+cl+") -> "+objects);
223            return objects.toArray();
224        }
225    
226        /**
227         * This method returns all the JAC objects that match any of the
228         * given types.
229         *
230         * @param types the types to get
231         * @return all the JAC objects that match any of the given types 
232         */   
233        public static Object[] getMemoryObjects(String[] types) {
234            Vector objects = new Vector();
235            for (int i=0; i<types.length; i++) {
236                try {
237                    objects.addAll( 
238                        Arrays.asList(
239                            getMemoryObjects(ClassRepository.get().getClass(types[i]))) );
240                } catch( Exception e ) { 
241                    e.printStackTrace(); 
242                }
243            }       
244            return objects.toArray();
245        }
246    
247        /**
248         * This method returns all the Jac objects that match any of the
249         * given types.
250         *
251         * @param types the types to get
252         * @return all the Jac objects that match any of the given types 
253         */   
254        public static Object[] getMemoryObjects(ClassItem[] types) {
255            Vector objects = new Vector();
256            for (int i=0; i<types.length; i++) {
257                objects.addAll( Arrays.asList(getMemoryObjects(types[i])) );
258            }       
259            return objects.toArray();
260        }
261    
262        /**
263         * Gets all the instantiated JAC objects on the current VM and on
264         * all the external objects sources known by the aspects (maybe
265         * resticed by some aspects).
266         *
267         * @return a collection of acessible objects
268         *
269         * @see #getObjects(ClassItem) 
270         * @see #getMemoryObjects() 
271         */
272        public static Collection getObjects() {
273            HashSet objects = new HashSet(instances.values());
274            ((ACManager)ACManager.get()).whenGetObjects(objects,null);
275            return objects;
276        }
277    
278        /**
279         * Gets all the JAC objects instances of a given class on the
280         * current VM and on all the external objects sources known by the
281         * aspects (maybe resticed by some aspects).
282         *
283         * @return a collection of acessible instances of <code>cl</code>
284         *
285         * @see #getObjects(Class) 
286         */
287        public static Collection getObjects(ClassItem cl) {
288            loggerGet.debug("getObjects "+cl);
289            String repName = (String)cl.getAttribute(RttiAC.REPOSITORY_NAME);
290            CollectionItem repCollection  = 
291                (CollectionItem)cl.getAttribute(RttiAC.REPOSITORY_COLLECTION);
292            Object repository = null;
293            if (repName!=null) {
294                repository = NameRepository.get().getObject(repName);
295                if (repository==null)
296                    loggerGet.error(cl+": no such repository object "+repName);
297            }
298            if (repository!=null  && repCollection!=null) {
299                loggerGet.debug("Using repository "+repName+"."+repCollection.getName());
300                return FieldItem.getPathLeaves(repCollection,repository);
301            } else {
302                List objects = new Vector(Arrays.asList(getMemoryObjects(cl)));
303                ((ACManager)ACManager.get()).whenGetObjects(objects,cl);
304                return objects;
305            }
306        }
307    
308        /**
309         * Gets all the JAC objects instances of a given class on the
310         * current VM and on all the external objects sources known by the
311         * aspects (maybe resticed by some aspects).
312         *
313         * @return a collection of acessible instances of <code>cl</code>
314         * @see #getObjects(ClassItem) */
315        public static Collection getObjects(Class cl) {
316            return getObjects(ClassRepository.get().getClass(cl));
317        }
318    
319        /**
320         * Get all instances of a class whose field relation contains the
321         * given value. If a repository has been defined for the class,
322         * only objects belonging to the repository are returned.
323         *
324         * @param cl the class
325         * @param relation the relation
326         * @param value the value that the relation must contain 
327         *
328         * @see org.objectweb.jac.core.rtti.RttiConf#defineRepository(ClassItem,String,CollectionItem)
329         */
330        public static Collection getObjectsWhere(ClassItem cl, 
331                                                 FieldItem relation, Object value) {
332            loggerGet.debug("getObjectsWhere "+cl+","+relation+","+value);
333            Collection objects = getObjects(cl);
334            Vector result = new Vector();
335            FieldItem field = relation.getField();
336            Iterator it = objects.iterator();
337            while (it.hasNext()) {
338                Object object = it.next();
339                for (Iterator j=relation.getSubstances(object).iterator();
340                     j.hasNext();) {
341                    Object substance = j.next();
342                    if (field instanceof CollectionItem) {
343                        if (((CollectionItem)field).
344                            getActualCollectionThroughAccessor(substance).contains(value)) {
345                            result.add(object);
346                            break;
347                        }
348                    } else if (field.isReference()) {
349                        if (field.getThroughAccessor(substance)==value) {
350                            result.add(object);
351                            break;
352                        }
353                    } else {
354                        Object testedValue = field.getThroughAccessor(substance);
355                        if ((value==null && testedValue==null) || (value!=null && value.equals(testedValue))) {
356                            result.add(object);
357                            break;
358                        }
359                    }
360                }
361            }
362            return result;
363        }
364    
365    
366        /**
367         * Get all instances of class cl which match a predicate
368         * @param cl the class
369         * @param filter the predicate to be matched
370         * @return a collection whose all items are such that filter(item)==true
371         */
372        public static Collection getObjectsWhere(ClassItem cl, Predicate filter) {
373            loggerGet.debug("getObjectsWhere "+cl+","+filter.getClass().getName());
374            Collection objects = getObjects(cl);
375            Vector result = new Vector();
376            Iterator it = objects.iterator();
377            while (it.hasNext()) {
378                Object object = it.next();
379                if (filter.apply(object)) {
380                    result.add(object);
381                }
382            }
383            return result;
384        }
385    
386        public void dump() {
387        }      
388    
389    }