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 4588 2012-06-03 06:01:30Z schulte2005 $
032 *
033 */
034// </editor-fold>
035// SECTION-END
036package org.jomc.ri.model;
037
038import java.lang.ref.Reference;
039import java.lang.ref.WeakReference;
040import java.lang.reflect.Constructor;
041import java.lang.reflect.Method;
042import java.util.Map;
043import javax.xml.bind.annotation.XmlTransient;
044import org.jomc.model.Instance;
045import org.jomc.model.Specification;
046import org.jomc.util.WeakIdentityHashMap;
047import static org.jomc.ri.model.RuntimeModelObjects.BOOTSTRAP_CLASSLOADER_KEY;
048import static org.jomc.ri.model.RuntimeModelObjects.classesByClassLoaderAndNameCache;
049import 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.3", comments = "See http://jomc.sourceforge.net/jomc/1.3/jomc-tools-1.3" )
074// </editor-fold>
075// SECTION-END
076public 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.3", comments = "See http://jomc.sourceforge.net/jomc/1.3/jomc-tools-1.3" )
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}