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  "License");
007     * you may not use this file except in compliance with the License.
008     * 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, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: TemplatesImpl.java 468653 2006-10-28 07:07:05Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.trax;
023    
024    import java.io.IOException;
025    import java.io.ObjectInputStream;
026    import java.io.ObjectOutputStream;
027    import java.io.Serializable;
028    import java.util.Properties;
029    import java.security.AccessController;
030    import java.security.PrivilegedAction;
031    
032    import javax.xml.XMLConstants;
033    import javax.xml.transform.Templates;
034    import javax.xml.transform.Transformer;
035    import javax.xml.transform.TransformerConfigurationException;
036    import javax.xml.transform.URIResolver;
037    
038    import org.apache.xalan.xsltc.DOM;
039    import org.apache.xalan.xsltc.Translet;
040    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
041    import org.apache.xalan.xsltc.runtime.AbstractTranslet;
042    import org.apache.xalan.xsltc.runtime.Hashtable;
043    
044    /**
045     * @author Morten Jorgensen
046     * @author G. Todd Millerj
047     * @author Jochen Cordes <Jochen.Cordes@t-online.de>
048     * @author Santiago Pericas-Geertsen 
049     */
050    public final class TemplatesImpl implements Templates, Serializable {
051        static final long serialVersionUID = 673094361519270707L;
052        /**
053         * Name of the superclass of all translets. This is needed to
054         * determine which, among all classes comprising a translet, 
055         * is the main one.
056         */
057        private static String ABSTRACT_TRANSLET 
058            = "org.apache.xalan.xsltc.runtime.AbstractTranslet";
059    
060        /**
061         * Name of the main class or default name if unknown.
062         */
063        private String _name = null;
064    
065        /**
066         * Contains the actual class definition for the translet class and
067         * any auxiliary classes.
068         */
069        private byte[][] _bytecodes = null;
070        
071        /**
072         * Contains the translet class definition(s). These are created when 
073         * this Templates is created or when it is read back from disk.
074         */
075        private Class[] _class = null;
076    
077        /**
078         * The index of the main translet class in the arrays _class[] and
079         * _bytecodes.
080         */
081        private int _transletIndex = -1;
082        
083        /**
084         * Contains the list of auxiliary class definitions.
085         */
086        private Hashtable _auxClasses = null;
087        
088        /**
089         * Output properties of this translet.
090         */
091        private Properties _outputProperties; 
092    
093        /**
094         * Number of spaces to add for output indentation.
095         */
096        private int _indentNumber;
097    
098        /**
099         * This URIResolver is passed to all Transformers.
100         * Declaring it transient to fix bug 22438 
101         */
102        private transient URIResolver _uriResolver = null;
103    
104        /**
105         * Cache the DTM for the stylesheet in a thread local variable,
106         * which is used by the document('') function.
107         * Use ThreadLocal because a DTM cannot be shared between
108         * multiple threads. 
109         * Declaring it transient to fix bug 22438 
110         */
111        private transient ThreadLocal _sdom = new ThreadLocal();
112        
113        /**
114         * A reference to the transformer factory that this templates
115         * object belongs to.
116         */
117        private transient TransformerFactoryImpl _tfactory = null;
118    
119        static final class TransletClassLoader extends ClassLoader {
120            TransletClassLoader(ClassLoader parent) {
121                super(parent);
122            }
123    
124            /**
125             * Access to final protected superclass member from outer class.
126             */
127            Class defineClass(final byte[] b) {
128                return defineClass(null, b, 0, b.length);
129            }
130        }
131    
132    
133        /**
134         * Create an XSLTC template object from the bytecodes.
135         * The bytecodes for the translet and auxiliary classes, plus the name of
136         * the main translet class, must be supplied.
137         */
138        protected TemplatesImpl(byte[][] bytecodes, String transletName,
139            Properties outputProperties, int indentNumber,
140            TransformerFactoryImpl tfactory) 
141        {
142            _bytecodes = bytecodes;
143            _name      = transletName;
144            _outputProperties = outputProperties;
145            _indentNumber = indentNumber;
146            _tfactory = tfactory;
147        }
148        
149        /**
150         * Create an XSLTC template object from the translet class definition(s).
151         */
152        protected TemplatesImpl(Class[] transletClasses, String transletName,
153            Properties outputProperties, int indentNumber,
154            TransformerFactoryImpl tfactory) 
155        {
156            _class     = transletClasses;
157            _name      = transletName;
158            _transletIndex = 0;
159            _outputProperties = outputProperties;
160            _indentNumber = indentNumber;
161            _tfactory = tfactory;
162        }
163        
164    
165        /**
166         * Need for de-serialization, see readObject().
167         */
168        public TemplatesImpl() { }
169    
170        /**
171         *  Overrides the default readObject implementation since we decided
172         *  it would be cleaner not to serialize the entire tranformer
173         *  factory.  [ ref bugzilla 12317 ]
174         *  We need to check if the user defined class for URIResolver also
175         *  implemented Serializable
176         *  if yes then we need to deserialize the URIResolver
177         *  Fix for bugzilla bug 22438
178         */
179        private void  readObject(ObjectInputStream is) 
180          throws IOException, ClassNotFoundException 
181        {
182            is.defaultReadObject();
183            if (is.readBoolean()) {
184                _uriResolver = (URIResolver) is.readObject();
185            }
186    
187            _tfactory = new TransformerFactoryImpl();
188        } 
189    
190    
191        /**
192         *  This is to fix bugzilla bug 22438
193         *  If the user defined class implements URIResolver and Serializable
194         *  then we want it to get serialized
195         */
196        private void writeObject(ObjectOutputStream os)
197            throws IOException, ClassNotFoundException {
198            os.defaultWriteObject();
199            if (_uriResolver instanceof Serializable) {
200                os.writeBoolean(true);
201                os.writeObject((Serializable) _uriResolver);
202            }
203            else {
204                os.writeBoolean(false);
205            }
206        }
207    
208    
209         /**
210         * Store URIResolver needed for Transformers.
211         */
212        public synchronized void setURIResolver(URIResolver resolver) {
213            _uriResolver = resolver;
214        }
215    
216        /**
217         * The TransformerFactory must pass us the translet bytecodes using this
218         * method before we can create any translet instances
219         */
220        protected synchronized void setTransletBytecodes(byte[][] bytecodes) {
221            _bytecodes = bytecodes;
222        }
223    
224        /**
225         * Returns the translet bytecodes stored in this template
226         */
227        public synchronized byte[][] getTransletBytecodes() {
228            return _bytecodes;
229        }
230    
231        /**
232         * Returns the translet bytecodes stored in this template
233         */
234        public synchronized Class[] getTransletClasses() {
235            try {
236                if (_class == null) defineTransletClasses();
237            }
238            catch (TransformerConfigurationException e) {
239                // Falls through
240            }
241            return _class;
242        }
243    
244        /**
245         * Returns the index of the main class in array of bytecodes
246         */
247        public synchronized int getTransletIndex() {
248            try {
249                if (_class == null) defineTransletClasses();
250            }
251            catch (TransformerConfigurationException e) {
252                // Falls through
253            }
254            return _transletIndex;
255        }
256    
257        /**
258         * The TransformerFactory should call this method to set the translet name
259         */
260        protected synchronized void setTransletName(String name) {
261            _name = name;
262        }
263    
264        /**
265         * Returns the name of the main translet class stored in this template
266         */
267        protected synchronized String getTransletName() {
268            return _name;
269        }
270    
271        /**
272         * Defines the translet class and auxiliary classes.
273         * Returns a reference to the Class object that defines the main class
274         */
275        private void defineTransletClasses()
276            throws TransformerConfigurationException {
277    
278            if (_bytecodes == null) {
279                ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
280                throw new TransformerConfigurationException(err.toString());
281            }
282    
283            TransletClassLoader loader = (TransletClassLoader)
284                AccessController.doPrivileged(new PrivilegedAction() {
285                    public Object run() {
286                        return new TransletClassLoader(ObjectFactory.findClassLoader());
287                    }
288                });
289    
290            try {
291                final int classCount = _bytecodes.length;
292                _class = new Class[classCount];
293    
294                if (classCount > 1) {
295                    _auxClasses = new Hashtable();
296                }
297    
298                for (int i = 0; i < classCount; i++) {
299                    _class[i] = loader.defineClass(_bytecodes[i]);
300                    final Class superClass = _class[i].getSuperclass();
301    
302                    // Check if this is the main class
303                    if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
304                        _transletIndex = i;
305                    }
306                    else {
307                        _auxClasses.put(_class[i].getName(), _class[i]);
308                    }
309                }
310    
311                if (_transletIndex < 0) {
312                    ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
313                    throw new TransformerConfigurationException(err.toString());
314                }
315            }
316            catch (ClassFormatError e) {
317                ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
318                throw new TransformerConfigurationException(err.toString());
319            }
320            catch (LinkageError e) {
321                ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
322                throw new TransformerConfigurationException(err.toString());
323            }
324        }
325    
326        /**
327         * This method generates an instance of the translet class that is
328         * wrapped inside this Template. The translet instance will later
329         * be wrapped inside a Transformer object.
330         */
331        private Translet getTransletInstance()
332            throws TransformerConfigurationException {
333            try {
334                if (_name == null) return null;
335    
336                if (_class == null) defineTransletClasses();
337    
338                // The translet needs to keep a reference to all its auxiliary 
339                // class to prevent the GC from collecting them
340                AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
341                translet.postInitialization();
342                translet.setTemplates(this);
343                if (_auxClasses != null) {
344                    translet.setAuxiliaryClasses(_auxClasses);
345                }
346                
347                return translet;
348            }
349            catch (InstantiationException e) {
350                ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
351                throw new TransformerConfigurationException(err.toString());
352            }
353            catch (IllegalAccessException e) {
354                ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
355                throw new TransformerConfigurationException(err.toString());
356            }
357        }
358    
359        /**
360         * Implements JAXP's Templates.newTransformer()
361         *
362         * @throws TransformerConfigurationException
363         */
364        public synchronized Transformer newTransformer()
365            throws TransformerConfigurationException 
366        {
367            TransformerImpl transformer;
368    
369            transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
370                _indentNumber, _tfactory);
371            
372            if (_uriResolver != null) {
373                transformer.setURIResolver(_uriResolver);
374            }
375            
376            if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
377                transformer.setSecureProcessing(true);
378            }
379            return transformer;
380        }
381    
382        /**
383         * Implements JAXP's Templates.getOutputProperties(). We need to 
384         * instanciate a translet to get the output settings, so
385         * we might as well just instanciate a Transformer and use its
386         * implementation of this method.
387         */
388        public synchronized Properties getOutputProperties() { 
389            try {
390                return newTransformer().getOutputProperties();
391            }
392            catch (TransformerConfigurationException e) {
393                return null;
394            }
395        }
396    
397        /**
398         * Return the thread local copy of the stylesheet DOM.
399         */
400        public DOM getStylesheetDOM() {
401            return (DOM)_sdom.get();
402        }
403        
404        /**
405         * Set the thread local copy of the stylesheet DOM.
406         */
407        public void setStylesheetDOM(DOM sdom) {
408            _sdom.set(sdom);
409        }
410    }