001    // SECTION-START[License Header]
002    // <editor-fold defaultstate="collapsed" desc=" Generated License ">
003    /*
004     *   Java Object Management and Configuration
005     *   Copyright (C) Christian Schulte, 2011-313
006     *   All rights reserved.
007     *
008     *   Redistribution and use in source and binary forms, with or without
009     *   modification, are permitted provided that the following conditions
010     *   are met:
011     *
012     *     o Redistributions of source code must retain the above copyright
013     *       notice, this list of conditions and the following disclaimer.
014     *
015     *     o Redistributions in binary form must reproduce the above copyright
016     *       notice, this list of conditions and the following disclaimer in
017     *       the documentation and/or other materials provided with the
018     *       distribution.
019     *
020     *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
021     *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
022     *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
023     *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
024     *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
025     *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
026     *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
027     *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
028     *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
029     *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030     *
031     *   $JOMC: RuntimeInstance.java 4381 2012-03-04 19:29:29Z schulte2005 $
032     *
033     */
034    // </editor-fold>
035    // SECTION-END
036    package org.jomc.ri.model;
037    
038    import java.lang.ref.Reference;
039    import java.lang.ref.WeakReference;
040    import java.lang.reflect.Constructor;
041    import java.lang.reflect.Method;
042    import java.util.Map;
043    import javax.xml.bind.annotation.XmlTransient;
044    import org.jomc.model.Instance;
045    import org.jomc.model.Specification;
046    import org.jomc.util.WeakIdentityHashMap;
047    import static org.jomc.ri.model.RuntimeModelObjects.BOOTSTRAP_CLASSLOADER_KEY;
048    import static org.jomc.ri.model.RuntimeModelObjects.classesByClassLoaderAndNameCache;
049    import static org.jomc.ri.model.RuntimeModelObjects.createMap;
050    
051    // SECTION-START[Documentation]
052    // <editor-fold defaultstate="collapsed" desc=" Generated Documentation ">
053    /**
054     * Runtime {@code Instance}.
055     *
056     * <dl>
057     *   <dt><b>Identifier:</b></dt><dd>org.jomc.ri.model.RuntimeInstance</dd>
058     *   <dt><b>Name:</b></dt><dd>JOMC RI RuntimeInstance</dd>
059     *   <dt><b>Specifications:</b></dt>
060     *     <dd>org.jomc.ri.model.RuntimeModelObject @ 1.2</dd>
061     *   <dt><b>Abstract:</b></dt><dd>No</dd>
062     *   <dt><b>Final:</b></dt><dd>No</dd>
063     *   <dt><b>Stateless:</b></dt><dd>No</dd>
064     * </dl>
065     *
066     * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a> 1.2
067     * @version 1.2
068     */
069    // </editor-fold>
070    // SECTION-END
071    // SECTION-START[Annotations]
072    // <editor-fold defaultstate="collapsed" desc=" Generated Annotations ">
073    @javax.annotation.Generated( value = "org.jomc.tools.SourceFileProcessor 1.2.2", comments = "See http://jomc.sourceforge.net/jomc/1.2/jomc-tools-1.2.2" )
074    // </editor-fold>
075    // SECTION-END
076    public class RuntimeInstance extends Instance implements RuntimeModelObject
077    {
078        // SECTION-START[RuntimeInstance]
079    
080        /** Classes by class loader any instance cache. */
081        @XmlTransient
082        static final Map<ClassLoader, Map<String, Reference<Class<?>[]>>> classesByClassLoaderAndInstanceCache =
083            new WeakIdentityHashMap<ClassLoader, Map<String, Reference<Class<?>[]>>>();
084    
085        /** Constructors by class loader any instance cache. */
086        @XmlTransient
087        static final Map<ClassLoader, Map<String, Reference<Constructor<?>>>> constructorsByClassLoaderAndInstanceCache =
088            new WeakIdentityHashMap<ClassLoader, Map<String, Reference<Constructor<?>>>>();
089    
090        /** Methods by class loader any instance cache. */
091        @XmlTransient
092        static final Map<ClassLoader, Map<String, Reference<Method>>> methodsByClassLoaderAndInstanceCache =
093            new WeakIdentityHashMap<ClassLoader, Map<String, Reference<Method>>>();
094    
095        /** Assignable flags by class loader any instance cache. */
096        @XmlTransient
097        static final Map<ClassLoader, Map<String, Boolean>> assignableFlagsByClassLoaderAndInstanceCache =
098            new WeakIdentityHashMap<ClassLoader, Map<String, Boolean>>();
099    
100        /** Proxy classes by class loader any instance cache. */
101        @XmlTransient
102        static final Map<ClassLoader, Map<String, Reference<Class<?>>>> proxyClassesByClassLoaderAndInstanceCache =
103            new WeakIdentityHashMap<ClassLoader, Map<String, Reference<Class<?>>>>();
104    
105        /** Method name. */
106        @XmlTransient
107        private volatile String javaClassFactoryMethodName;
108    
109        /**
110         * Creates a new {@code RuntimeInstance} instance by deeply copying a given {@code Instance} instance.
111         *
112         * @param instance The instance to copy.
113         *
114         * @throws NullPointerException if {@code instance} is {@code null}.
115         */
116        public RuntimeInstance( final Instance instance )
117        {
118            super( instance );
119    
120            if ( this.getAuthors() != null )
121            {
122                this.setAuthors( RuntimeModelObjects.getInstance().copyOf( this.getAuthors() ) );
123            }
124            if ( this.getDependencies() != null )
125            {
126                this.setDependencies( RuntimeModelObjects.getInstance().copyOf( this.getDependencies() ) );
127            }
128            if ( this.getDocumentation() != null )
129            {
130                this.setDocumentation( RuntimeModelObjects.getInstance().copyOf( this.getDocumentation() ) );
131            }
132            if ( this.getMessages() != null )
133            {
134                this.setMessages( RuntimeModelObjects.getInstance().copyOf( this.getMessages() ) );
135            }
136            if ( this.getProperties() != null )
137            {
138                this.setProperties( RuntimeModelObjects.getInstance().copyOf( this.getProperties() ) );
139            }
140            if ( this.getSpecifications() != null )
141            {
142                this.setSpecifications( RuntimeModelObjects.getInstance().copyOf( this.getSpecifications() ) );
143            }
144        }
145    
146        /**
147         * Gets the Java class of the instance for a given class loader.
148         * <p>This method queries an internal cache for a result object to return for the given argument values. If no
149         * cached result object is available, this method queries the super-class for a result object to return and caches
150         * the outcome of that query for use on successive calls.</p>
151         * <p><b>Note:</b><br/>Method {@code RuntimeModelObjects.clear()} must be used to synchronize the state of the
152         * internal cache with the state of the class loader, should the state of the class loader change.</p>
153         *
154         * @param classLoader The class loader to get the Java class from or {@code null}, to get the Java class from the
155         * platform's bootstrap class loader.
156         *
157         * @return The Java class of the instance.
158         *
159         * @throws ClassNotFoundException if the Java class is not found.
160         *
161         * @see #getClazz()
162         * @see RuntimeModelObjects#clear()
163         */
164        @Override
165        public Class<?> getJavaClass( final ClassLoader classLoader ) throws ClassNotFoundException
166        {
167            ClassLoader classLoaderKey = classLoader;
168            if ( classLoaderKey == null )
169            {
170                classLoaderKey = BOOTSTRAP_CLASSLOADER_KEY;
171            }
172    
173            synchronized ( classesByClassLoaderAndNameCache )
174            {
175                Class<?> javaClass = null;
176                Map<String, Reference<Class<?>>> map = classesByClassLoaderAndNameCache.get( classLoaderKey );
177    
178                if ( map == null )
179                {
180                    map = createMap();
181                    classesByClassLoaderAndNameCache.put( classLoaderKey, map );
182                }
183    
184                final Reference<Class<?>> reference = map.get( this.getClazz() );
185    
186                if ( reference != null )
187                {
188                    javaClass = reference.get();
189                }
190    
191                if ( javaClass == null )
192                {
193                    javaClass = super.getJavaClass( classLoader );
194                    map.put( this.getClazz(), new WeakReference<Class<?>>( javaClass ) );
195                }
196    
197                return javaClass;
198            }
199        }
200    
201        /**
202         * Gets the Java classes of all specifications of the instance for a given class loader.
203         * <p>This method queries an internal cache for a result object to return for the given argument values. If no
204         * cached result object is available, this method queries the super-class for a result object to return and caches
205         * the outcome of that query for use on successive calls.</p>
206         * <p><b>Note:</b><br/>Method {@code RuntimeModelObjects.clear()} must be used to synchronize the state of the
207         * internal cache with the state of the instance and class loader, should the state of the instance or class loader
208         * change.</p>
209         *
210         * @param classLoader The class loader to get the Java classes from or {@code null}, to get the Java classes from
211         * the platform's bootstrap class loader.
212         *
213         * @return The Java classes of all specifications of the instance.
214         *
215         * @throws ClassNotFoundException if a Java class is not found.
216         *
217         * @see #getSpecifications()
218         * @see Specification#getClazz()
219         * @see RuntimeModelObjects#clear()
220         */
221        @Override
222        public Class<?>[] getJavaClasses( final ClassLoader classLoader ) throws ClassNotFoundException
223        {
224            ClassLoader classLoaderKey = classLoader;
225            if ( classLoaderKey == null )
226            {
227                classLoaderKey = BOOTSTRAP_CLASSLOADER_KEY;
228            }
229    
230            synchronized ( classesByClassLoaderAndInstanceCache )
231            {
232                Class<?>[] javaClasses = null;
233                Map<String, Reference<Class<?>[]>> map = classesByClassLoaderAndInstanceCache.get( classLoaderKey );
234    
235                if ( map == null )
236                {
237                    map = createMap();
238                    classesByClassLoaderAndInstanceCache.put( classLoaderKey, map );
239                }
240    
241                final Reference<Class<?>[]> reference = map.get( this.getIdentifier() );
242    
243                if ( reference != null )
244                {
245                    javaClasses = reference.get();
246                }
247    
248                if ( javaClasses == null && ( reference != null || !map.containsKey( this.getIdentifier() ) ) )
249                {
250                    javaClasses = super.getJavaClasses( classLoader );
251                    map.put( this.getIdentifier(), new WeakReference<Class<?>[]>( javaClasses ) );
252                }
253    
254                return javaClasses;
255            }
256        }
257    
258        /**
259         * Gets the Java constructor to use for creating objects of the instance.
260         * <p>This method queries an internal cache for a result object to return for the given argument values. If no
261         * cached result object is available, this method queries the super-class for a result object to return and caches
262         * the outcome of that query for use on successive calls.</p>
263         * <p><b>Note:</b><br/>Method {@code RuntimeModelObjects.clear()} must be used to synchronize the state of the
264         * internal cache with the state of the instance and class loader, should the state of the instance or class loader
265         * change.</p>
266         *
267         * @param classLoader The class loader to get the Java class from or {@code null}, to get the Java class from the
268         * platform's bootstrap class loader.
269         *
270         * @return The public default Java constructor of the Java class of the instance or {@code null}, if that class
271         * does not declare such a constructor, is abstract or is not public.
272         *
273         * @throws ClassNotFoundException if the Java class is not found.
274         *
275         * @see #getJavaClass(java.lang.ClassLoader)
276         * @see RuntimeModelObjects#clear()
277         */
278        @Override
279        public Constructor<?> getJavaConstructor( final ClassLoader classLoader ) throws ClassNotFoundException
280        {
281            ClassLoader classLoaderKey = classLoader;
282            if ( classLoaderKey == null )
283            {
284                classLoaderKey = BOOTSTRAP_CLASSLOADER_KEY;
285            }
286    
287            synchronized ( constructorsByClassLoaderAndInstanceCache )
288            {
289                Constructor<?> javaClassConstructor = null;
290                Map<String, Reference<Constructor<?>>> map = constructorsByClassLoaderAndInstanceCache.get( classLoaderKey );
291    
292                if ( map == null )
293                {
294                    map = createMap();
295                    constructorsByClassLoaderAndInstanceCache.put( classLoaderKey, map );
296                }
297    
298                final Reference<Constructor<?>> reference = map.get( this.getIdentifier() );
299    
300                if ( reference != null )
301                {
302                    javaClassConstructor = reference.get();
303                }
304    
305                if ( javaClassConstructor == null && ( reference != null || !map.containsKey( this.getIdentifier() ) ) )
306                {
307                    javaClassConstructor = super.getJavaConstructor( classLoader );
308                    map.put( this.getIdentifier(), new WeakReference<Constructor<?>>( javaClassConstructor ) );
309                }
310    
311                return javaClassConstructor;
312            }
313        }
314    
315        /**
316         * Gets the name of the Java method to use for creating objects of the instance.
317         * <p>This method queries an internal cache for a result object to return. If no cached result object is available,
318         * this method queries the super-class for a result object to return and caches the outcome of that query for use on
319         * successive calls.</p>
320         * <p><b>Note:</b><br/>Method {@code clear()} must be used to synchronize the state of the internal cache with the
321         * state of the instance, should the state of the instance change.</p>
322         *
323         * @return The name of the Java method to use for creating objects of the instance or {@code null}, if no such
324         * method name is supported.
325         *
326         * @see #getName()
327         * @see #clear()
328         */
329        @Override
330        public String getJavaFactoryMethodName()
331        {
332            if ( this.javaClassFactoryMethodName == null )
333            {
334                this.javaClassFactoryMethodName = super.getJavaFactoryMethodName();
335            }
336    
337            return this.javaClassFactoryMethodName;
338        }
339    
340        /**
341         * Gets the Java method to use for creating objects of the instance.
342         * <p>This method queries an internal cache for a result object to return for the given argument values. If no
343         * cached result object is available, this method queries the super-class for a result object to return and caches
344         * the outcome of that query for use on successive calls.</p>
345         * <p><b>Note:</b><br/>Method {@code RuntimeModelObjects.clear()} must be used to synchronize the state of the
346         * internal cache with the state of the instance and class loader, should the state of the instance or class loader
347         * change.</p>
348         *
349         * @param classLoader The class loader to get the Java class from or {@code null}, to get the Java class from the
350         * platform's bootstrap class loader.
351         *
352         * @return The public Java method of the Java class of the instance to use for creating objects of the instance or
353         * {@code null}, if that class does not declare such a method.
354         *
355         * @throws ClassNotFoundException if the Java class is not found.
356         *
357         * @see #getJavaClass(java.lang.ClassLoader)
358         * @see #getJavaFactoryMethodName()
359         * @see RuntimeModelObjects#clear()
360         */
361        @Override
362        public Method getJavaFactoryMethod( final ClassLoader classLoader ) throws ClassNotFoundException
363        {
364            ClassLoader classLoaderKey = classLoader;
365            if ( classLoaderKey == null )
366            {
367                classLoaderKey = BOOTSTRAP_CLASSLOADER_KEY;
368            }
369    
370            synchronized ( methodsByClassLoaderAndInstanceCache )
371            {
372                Method javaClassFactoryMethod = null;
373                Map<String, Reference<Method>> map = methodsByClassLoaderAndInstanceCache.get( classLoaderKey );
374    
375                if ( map == null )
376                {
377                    map = createMap();
378                    methodsByClassLoaderAndInstanceCache.put( classLoaderKey, map );
379                }
380    
381                final Reference<Method> reference = map.get( this.getIdentifier() );
382    
383                if ( reference != null )
384                {
385                    javaClassFactoryMethod = reference.get();
386                }
387    
388                if ( javaClassFactoryMethod == null && ( reference != null || !map.containsKey( this.getIdentifier() ) ) )
389                {
390                    javaClassFactoryMethod = super.getJavaFactoryMethod( classLoader );
391                    map.put( this.getIdentifier(), new WeakReference<Method>( javaClassFactoryMethod ) );
392                }
393    
394                return javaClassFactoryMethod;
395            }
396        }
397    
398        /**
399         * Gets a flag indicating the Java class of the instance is assignable to all Java classes of all specifications of
400         * the instance.
401         * <p>This method queries an internal cache for a result object to return for the given argument values. If no
402         * cached result object is available, this method queries the super-class for a result object to return and caches
403         * the outcome of that query for use on successive calls.</p>
404         * <p><b>Note:</b><br/>Method {@code RuntimeModelObjects.clear()} must be used to synchronize the state of the
405         * internal cache with the state of the instance and class loader, should the state of the instance or class loader
406         * change.</p>
407         *
408         * @param classLoader The class loader to get the Java classes from or {@code null}, to get the Java classes from
409         * the platform's bootstrap class loader.
410         *
411         * @return {@code true}, if the Java class of the instance is assignable to all Java classes of all specifications
412         * of the instance; {@code false}, if the Java class of the instance is not assignable to all Java classes of all
413         * specifications of the instance.
414         *
415         * @throws ClassNotFoundException if a Java class is not found.
416         *
417         * @see #getJavaClass(java.lang.ClassLoader)
418         * @see #getJavaClasses(java.lang.ClassLoader)
419         * @see RuntimeModelObjects#clear()
420         */
421        @Override
422        public boolean isJavaClassAssignable( final ClassLoader classLoader ) throws ClassNotFoundException
423        {
424            ClassLoader classLoaderKey = classLoader;
425            if ( classLoaderKey == null )
426            {
427                classLoaderKey = BOOTSTRAP_CLASSLOADER_KEY;
428            }
429    
430            synchronized ( assignableFlagsByClassLoaderAndInstanceCache )
431            {
432                Map<String, Boolean> map = assignableFlagsByClassLoaderAndInstanceCache.get( classLoaderKey );
433    
434                if ( map == null )
435                {
436                    map = createMap();
437                    assignableFlagsByClassLoaderAndInstanceCache.put( classLoaderKey, map );
438                }
439    
440                Boolean javaClassAssignable = map.get( this.getIdentifier() );
441    
442                if ( javaClassAssignable == null && !map.containsKey( this.getIdentifier() ) )
443                {
444                    javaClassAssignable = super.isJavaClassAssignable( classLoader );
445                    map.put( this.getIdentifier(), javaClassAssignable );
446                }
447    
448                return javaClassAssignable == null ? false : javaClassAssignable;
449            }
450        }
451    
452        /**
453         * Gets the Java proxy class for a given class loader.
454         * <p>This method queries an internal cache for a result object to return for the given argument values. If no
455         * cached result object is available, this method queries the super-class for a result object to return and caches
456         * the outcome of that query for use on successive calls.</p>
457         * <p><b>Note:</b><br/>Method {@code RuntimeModelObjects.clear()} must be used to synchronize the state of the
458         * internal cache with the state of the instance and class loader, should the state of the instance or class loader
459         * change.</p>
460         *
461         * @param classLoader The class loader to get the Java proxy class for.
462         *
463         * @return The Java proxy class for {@code classLoader} or {@code null}, if the instance does not support a Java
464         * proxy class.
465         *
466         * @throws ClassNotFoundException if a Java class is not found.
467         *
468         * @see #getJavaClasses(java.lang.ClassLoader)
469         * @see RuntimeModelObjects#clear()
470         */
471        @Override
472        public Class<?> getJavaProxyClass( final ClassLoader classLoader ) throws ClassNotFoundException
473        {
474            ClassLoader classLoaderKey = classLoader;
475            if ( classLoaderKey == null )
476            {
477                classLoaderKey = BOOTSTRAP_CLASSLOADER_KEY;
478            }
479    
480            synchronized ( proxyClassesByClassLoaderAndInstanceCache )
481            {
482                Class<?> javaProxyClass = null;
483                Map<String, Reference<Class<?>>> map = proxyClassesByClassLoaderAndInstanceCache.get( classLoaderKey );
484    
485                if ( map == null )
486                {
487                    map = createMap();
488                    proxyClassesByClassLoaderAndInstanceCache.put( classLoaderKey, map );
489                }
490    
491                final Reference<Class<?>> reference = map.get( this.getIdentifier() );
492    
493                if ( reference != null )
494                {
495                    javaProxyClass = reference.get();
496                }
497    
498                if ( javaProxyClass == null && ( reference != null || !map.containsKey( this.getIdentifier() ) ) )
499                {
500                    javaProxyClass = super.getJavaProxyClass( classLoader );
501                    map.put( this.getIdentifier(), new WeakReference<Class<?>>( javaProxyClass ) );
502                }
503    
504                return javaProxyClass;
505            }
506        }
507    
508        // SECTION-END
509        // SECTION-START[RuntimeModelObject]
510        public void gc()
511        {
512            this.gcOrClear( true, false );
513        }
514    
515        public void clear()
516        {
517            this.javaClassFactoryMethodName = null;
518            this.gcOrClear( false, true );
519        }
520    
521        private void gcOrClear( final boolean gc, final boolean clear )
522        {
523            if ( this.getAuthors() instanceof RuntimeModelObject )
524            {
525                if ( gc )
526                {
527                    ( (RuntimeModelObject) this.getAuthors() ).gc();
528                }
529                if ( clear )
530                {
531                    ( (RuntimeModelObject) this.getAuthors() ).clear();
532                }
533            }
534            if ( this.getDependencies() instanceof RuntimeModelObject )
535            {
536                if ( gc )
537                {
538                    ( (RuntimeModelObject) this.getDependencies() ).gc();
539                }
540                if ( clear )
541                {
542                    ( (RuntimeModelObject) this.getDependencies() ).clear();
543                }
544            }
545            if ( this.getDocumentation() instanceof RuntimeModelObject )
546            {
547                if ( gc )
548                {
549                    ( (RuntimeModelObject) this.getDocumentation() ).gc();
550                }
551                if ( clear )
552                {
553                    ( (RuntimeModelObject) this.getDocumentation() ).clear();
554                }
555            }
556            if ( this.getMessages() instanceof RuntimeModelObject )
557            {
558                if ( gc )
559                {
560                    ( (RuntimeModelObject) this.getMessages() ).gc();
561                }
562                if ( clear )
563                {
564                    ( (RuntimeModelObject) this.getMessages() ).clear();
565                }
566            }
567            if ( this.getProperties() instanceof RuntimeModelObject )
568            {
569                if ( gc )
570                {
571                    ( (RuntimeModelObject) this.getProperties() ).gc();
572                }
573                if ( clear )
574                {
575                    ( (RuntimeModelObject) this.getProperties() ).clear();
576                }
577            }
578            if ( this.getSpecifications() instanceof RuntimeModelObject )
579            {
580                if ( gc )
581                {
582                    ( (RuntimeModelObject) this.getSpecifications() ).gc();
583                }
584                if ( clear )
585                {
586                    ( (RuntimeModelObject) this.getSpecifications() ).clear();
587                }
588            }
589        }
590    
591        // SECTION-END
592        // SECTION-START[Constructors]
593        // <editor-fold defaultstate="collapsed" desc=" Generated Constructors ">
594        /** Creates a new {@code RuntimeInstance} instance. */
595        @javax.annotation.Generated( value = "org.jomc.tools.SourceFileProcessor 1.2.2", comments = "See http://jomc.sourceforge.net/jomc/1.2/jomc-tools-1.2.2" )
596        public RuntimeInstance()
597        {
598            // SECTION-START[Default Constructor]
599            super();
600            // SECTION-END
601        }
602        // </editor-fold>
603        // SECTION-END
604        // SECTION-START[Dependencies]
605        // SECTION-END
606        // SECTION-START[Properties]
607        // SECTION-END
608        // SECTION-START[Messages]
609        // SECTION-END
610    }