001    /*
002     *   Copyright (C) Christian Schulte, 2012-22
003     *   All rights reserved.
004     *
005     *   Redistribution and use in source and binary forms, with or without
006     *   modification, are permitted provided that the following conditions
007     *   are met:
008     *
009     *     o Redistributions of source code must retain the above copyright
010     *       notice, this list of conditions and the following disclaimer.
011     *
012     *     o Redistributions in binary form must reproduce the above copyright
013     *       notice, this list of conditions and the following disclaimer in
014     *       the documentation and/or other materials provided with the
015     *       distribution.
016     *
017     *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018     *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019     *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020     *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021     *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022     *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023     *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024     *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025     *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026     *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027     *
028     *   $JOMC: ModelContextFactory.java 4200 2012-01-25 09:46:13Z schulte2005 $
029     *
030     */
031    package org.jomc.modlet;
032    
033    import java.security.AccessController;
034    import java.security.PrivilegedAction;
035    import java.text.MessageFormat;
036    import java.util.Locale;
037    import java.util.ResourceBundle;
038    
039    /**
040     * Interface to creating model contexts.
041     *
042     * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
043     * @version $JOMC: ModelContextFactory.java 4200 2012-01-25 09:46:13Z schulte2005 $
044     * @since 1.2
045     */
046    public abstract class ModelContextFactory
047    {
048    
049        /** Constant for the name of the default {@code ModelContextFactory} implementation. */
050        private static final String DEFAULT_MODEL_CONTEXT_FACTORY_CLASS_NAME = "org.jomc.modlet.DefaultModelContextFactory";
051    
052        /** Constant for the name of the system property controlling {@code ModelContextFactory} implementations. */
053        private static final String MODEL_CONTEXT_FACTORY_CLASS_NAME_PROPERTY =
054            "org.jomc.modlet.ModelContextFactory";
055    
056        /** Creates a new {@code ModelContextFactory} instance. */
057        protected ModelContextFactory()
058        {
059            super();
060        }
061    
062        /**
063         * Creates a new {@code ModelContextFactory} instance.
064         * <p>The name of the class providing the {@code ModelContextFactory} implementation loaded by this method is
065         * controlled by system property {@code org.jomc.modlet.ModelContextFactory}. If that property is not set, this
066         * methods returns a new default instance.</p>
067         *
068         * @return A new {@code ModelContextFactory} instance.
069         *
070         * @throws ModelContextFactoryError if creating a new instance fails.
071         */
072        public static ModelContextFactory newInstance() throws ModelContextFactoryError
073        {
074            return newInstance( AccessController.doPrivileged( new PrivilegedAction<String>()
075            {
076    
077                public String run()
078                {
079                    return System.getProperty( MODEL_CONTEXT_FACTORY_CLASS_NAME_PROPERTY,
080                                               DEFAULT_MODEL_CONTEXT_FACTORY_CLASS_NAME );
081    
082                }
083    
084            } ) );
085        }
086    
087        /**
088         * Creates a new {@code ModelContextFactory} instance.
089         *
090         * @param factoryClassName The name of the {@code ModelContextFactory} class to create an instance of.
091         *
092         * @return A new {@code ModelContextFactory} instance.
093         *
094         * @throws NullPointerException if {@code factoryClassName} is {@code null}.
095         * @throws ModelContextFactoryError if creating a new instance fails.
096         */
097        public static ModelContextFactory newInstance( final String factoryClassName ) throws ModelContextFactoryError
098        {
099            if ( factoryClassName == null )
100            {
101                throw new NullPointerException( "factoryClassName" );
102            }
103    
104            try
105            {
106                final Class<?> factoryClass = Class.forName( factoryClassName );
107    
108                if ( !ModelContextFactory.class.isAssignableFrom( factoryClass ) )
109                {
110                    throw new ModelContextFactoryError( getMessage( "illegalFactory", factoryClassName,
111                                                                    ModelContextFactory.class.getName() ) );
112    
113                }
114    
115                return factoryClass.asSubclass( ModelContextFactory.class ).newInstance();
116            }
117            catch ( final ClassNotFoundException e )
118            {
119                throw new ModelContextFactoryError( getMessage( "classNotFound", factoryClassName ), e );
120            }
121            catch ( final InstantiationException e )
122            {
123                final String message = getMessage( e );
124                throw new ModelContextFactoryError( getMessage( "instantiationException", factoryClassName,
125                                                                message != null ? " " + message : "" ), e );
126    
127            }
128            catch ( final IllegalAccessException e )
129            {
130                final String message = getMessage( e );
131                throw new ModelContextFactoryError( getMessage( "accessDenied", factoryClassName,
132                                                                message != null ? " " + message : "" ), e );
133    
134            }
135        }
136    
137        /**
138         * Creates a new {@code ModelContext} instance.
139         *
140         * @return A new {@code ModelContext} instance.
141         */
142        public abstract ModelContext newModelContext();
143    
144        /**
145         * Creates a new {@code ModelContext} instance.
146         *
147         * @param classLoader The class loader to create a new instance with or {@code null}, to create a new instance
148         * using the bootstrap class loader.
149         *
150         * @return A new {@code ModelContext} instance for {@code classLoader}.
151         */
152        public abstract ModelContext newModelContext( final ClassLoader classLoader );
153    
154        private static String getMessage( final String key, final Object... args )
155        {
156            return MessageFormat.format( ResourceBundle.getBundle( ModelContextFactory.class.getName().replace( '.', '/' ),
157                                                                   Locale.getDefault() ).getString( key ), args );
158    
159        }
160    
161        private static String getMessage( final Throwable t )
162        {
163            return t != null ? t.getMessage() != null ? t.getMessage() : getMessage( t.getCause() ) : null;
164        }
165    
166    }