TemplatesImpl.java revision 1080:42c30d817faa
1/*
2 * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20/*
21 * $Id: TemplatesImpl.java,v 1.8 2007/03/26 20:12:27 spericas Exp $
22 */
23
24package com.sun.org.apache.xalan.internal.xsltc.trax;
25
26import com.sun.org.apache.xalan.internal.XalanConstants;
27import com.sun.org.apache.xalan.internal.utils.ObjectFactory;
28import com.sun.org.apache.xalan.internal.utils.SecuritySupport;
29import com.sun.org.apache.xalan.internal.xsltc.DOM;
30import com.sun.org.apache.xalan.internal.xsltc.Translet;
31import com.sun.org.apache.xalan.internal.xsltc.compiler.Constants;
32import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
33import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
34import java.io.IOException;
35import java.io.NotSerializableException;
36import java.io.ObjectInputStream;
37import java.io.ObjectOutputStream;
38import java.io.ObjectStreamField;
39import java.io.Serializable;
40import java.lang.RuntimePermission;
41import java.lang.module.Configuration;
42import java.lang.module.ModuleDescriptor;
43import java.lang.module.ModuleFinder;
44import java.lang.module.ModuleReference;
45import java.lang.module.ModuleReader;
46import java.lang.reflect.Layer;
47import java.lang.reflect.Module;
48import java.security.AccessController;
49import java.security.CodeSigner;
50import java.security.CodeSource;
51import java.security.PermissionCollection;
52import java.security.PrivilegedAction;
53import java.security.ProtectionDomain;
54import java.util.Arrays;
55import java.util.HashMap;
56import java.util.Map;
57import java.util.Optional;
58import java.util.Properties;
59import java.util.Set;
60import javax.xml.XMLConstants;
61import javax.xml.transform.Templates;
62import javax.xml.transform.Transformer;
63import javax.xml.transform.TransformerConfigurationException;
64import javax.xml.transform.URIResolver;
65
66
67/**
68 * @author Morten Jorgensen
69 * @author G. Todd Millerj
70 * @author Jochen Cordes <Jochen.Cordes@t-online.de>
71 * @author Santiago Pericas-Geertsen
72 */
73public final class TemplatesImpl implements Templates, Serializable {
74    static final long serialVersionUID = 673094361519270707L;
75    public final static String DESERIALIZE_TRANSLET = "jdk.xml.enableTemplatesImplDeserialization";
76
77    /**
78     * Name of the superclass of all translets. This is needed to
79     * determine which, among all classes comprising a translet,
80     * is the main one.
81     */
82    private static String ABSTRACT_TRANSLET
83        = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
84
85    /**
86     * Name of the main class or default name if unknown.
87     */
88    private String _name = null;
89
90    /**
91     * Contains the actual class definition for the translet class and
92     * any auxiliary classes.
93     */
94    private byte[][] _bytecodes = null;
95
96    /**
97     * Contains the translet class definition(s). These are created when
98     * this Templates is created or when it is read back from disk.
99     */
100    private Class[] _class = null;
101
102    /**
103     * The index of the main translet class in the arrays _class[] and
104     * _bytecodes.
105     */
106    private int _transletIndex = -1;
107
108    /**
109     * Contains the list of auxiliary class definitions.
110     */
111    private transient Map<String, Class<?>> _auxClasses = null;
112
113    /**
114     * Output properties of this translet.
115     */
116    private Properties _outputProperties;
117
118    /**
119     * Number of spaces to add for output indentation.
120     */
121    private int _indentNumber;
122
123    /**
124     * This URIResolver is passed to all Transformers.
125     * Declaring it transient to fix bug 22438
126     */
127    private transient URIResolver _uriResolver = null;
128
129    /**
130     * Cache the DTM for the stylesheet in a thread local variable,
131     * which is used by the document('') function.
132     * Use ThreadLocal because a DTM cannot be shared between
133     * multiple threads.
134     * Declaring it transient to fix bug 22438
135     */
136    private transient ThreadLocal _sdom = new ThreadLocal();
137
138    /**
139     * A reference to the transformer factory that this templates
140     * object belongs to.
141     */
142    private transient TransformerFactoryImpl _tfactory = null;
143
144    /**
145     * A flag to determine whether the Service Mechanism is used
146     */
147    private transient boolean _useServicesMechanism;
148
149    /**
150     * protocols allowed for external references set by the stylesheet processing instruction, Import and Include element.
151     */
152    private transient String _accessExternalStylesheet = XalanConstants.EXTERNAL_ACCESS_DEFAULT;
153
154    /**
155     * @serialField _name String The Name of the main class
156     * @serialField _bytecodes byte[][] Class definition
157     * @serialField _class Class[] The translet class definition(s).
158     * @serialField _transletIndex int The index of the main translet class
159     * @serialField _outputProperties Properties Output properties of this translet.
160     * @serialField _indentNumber int Number of spaces to add for output indentation.
161     */
162    private static final ObjectStreamField[] serialPersistentFields =
163        new ObjectStreamField[] {
164            new ObjectStreamField("_name", String.class),
165            new ObjectStreamField("_bytecodes", byte[][].class),
166            new ObjectStreamField("_class", Class[].class),
167            new ObjectStreamField("_transletIndex", int.class),
168            new ObjectStreamField("_outputProperties", Properties.class),
169            new ObjectStreamField("_indentNumber", int.class),
170        };
171
172    static final class TransletClassLoader extends ClassLoader {
173        private final Map<String, Class<?>> _loadedExternalExtensionFunctions;
174
175         TransletClassLoader(ClassLoader parent) {
176             super(parent);
177            _loadedExternalExtensionFunctions = null;
178        }
179
180        TransletClassLoader(ClassLoader parent, Map<String, Class<?>> mapEF) {
181            super(parent);
182            _loadedExternalExtensionFunctions = mapEF;
183        }
184
185        @Override
186        public Class<?> loadClass(String name) throws ClassNotFoundException {
187            Class<?> ret = null;
188            // The _loadedExternalExtensionFunctions will be empty when the
189            // SecurityManager is not set and the FSP is turned off
190            if (_loadedExternalExtensionFunctions != null) {
191                ret = _loadedExternalExtensionFunctions.get(name);
192            }
193            if (ret == null) {
194                ret = super.loadClass(name);
195            }
196            return ret;
197         }
198
199        /**
200         * Access to final protected superclass member from outer class.
201         */
202        Class defineClass(final byte[] b) {
203            return defineClass(null, b, 0, b.length);
204        }
205
206        Class defineClass(final byte[] b, ProtectionDomain pd) {
207            return defineClass(null, b, 0, b.length, pd);
208        }
209    }
210
211
212    /**
213     * Create an XSLTC template object from the bytecodes.
214     * The bytecodes for the translet and auxiliary classes, plus the name of
215     * the main translet class, must be supplied.
216     */
217    protected TemplatesImpl(byte[][] bytecodes, String transletName,
218        Properties outputProperties, int indentNumber,
219        TransformerFactoryImpl tfactory)
220    {
221        _bytecodes = bytecodes;
222        init(transletName, outputProperties, indentNumber, tfactory);
223    }
224
225    /**
226     * Create an XSLTC template object from the translet class definition(s).
227     */
228    protected TemplatesImpl(Class<?>[] transletClasses, String transletName,
229        Properties outputProperties, int indentNumber,
230        TransformerFactoryImpl tfactory)
231    {
232        _class     = transletClasses;
233        _transletIndex = 0;
234        init(transletName, outputProperties, indentNumber, tfactory);
235    }
236
237    private void init(String transletName,
238        Properties outputProperties, int indentNumber,
239        TransformerFactoryImpl tfactory) {
240        _name      = transletName;
241        _outputProperties = outputProperties;
242        _indentNumber = indentNumber;
243        _tfactory = tfactory;
244        _useServicesMechanism = tfactory.useServicesMechnism();
245        _accessExternalStylesheet = (String) tfactory.getAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET);
246    }
247    /**
248     * Need for de-serialization, see readObject().
249     */
250    public TemplatesImpl() { }
251
252    /**
253     *  Overrides the default readObject implementation since we decided
254     *  it would be cleaner not to serialize the entire tranformer
255     *  factory.  [ ref bugzilla 12317 ]
256     *  We need to check if the user defined class for URIResolver also
257     *  implemented Serializable
258     *  if yes then we need to deserialize the URIResolver
259     *  Fix for bugzilla bug 22438
260     */
261    @SuppressWarnings("unchecked")
262    private void  readObject(ObjectInputStream is)
263      throws IOException, ClassNotFoundException
264    {
265        SecurityManager security = System.getSecurityManager();
266        if (security != null){
267            String temp = SecuritySupport.getSystemProperty(DESERIALIZE_TRANSLET);
268            if (temp == null || !(temp.length()==0 || temp.equalsIgnoreCase("true"))) {
269                ErrorMsg err = new ErrorMsg(ErrorMsg.DESERIALIZE_TRANSLET_ERR);
270                throw new UnsupportedOperationException(err.toString());
271            }
272        }
273
274        // We have to read serialized fields first.
275        ObjectInputStream.GetField gf = is.readFields();
276        _name = (String)gf.get("_name", null);
277        _bytecodes = (byte[][])gf.get("_bytecodes", null);
278        _class = (Class[])gf.get("_class", null);
279        _transletIndex = gf.get("_transletIndex", -1);
280
281        _outputProperties = (Properties)gf.get("_outputProperties", null);
282        _indentNumber = gf.get("_indentNumber", 0);
283
284        if (is.readBoolean()) {
285            _uriResolver = (URIResolver) is.readObject();
286        }
287
288        _tfactory = new TransformerFactoryImpl();
289    }
290
291
292    /**
293     *  This is to fix bugzilla bug 22438
294     *  If the user defined class implements URIResolver and Serializable
295     *  then we want it to get serialized
296     */
297    private void writeObject(ObjectOutputStream os)
298        throws IOException, ClassNotFoundException {
299        if (_auxClasses != null) {
300            //throw with the same message as when Hashtable was used for compatibility.
301            throw new NotSerializableException(
302                    "com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable");
303        }
304
305        // Write serialized fields
306        ObjectOutputStream.PutField pf = os.putFields();
307        pf.put("_name", _name);
308        pf.put("_bytecodes", _bytecodes);
309        pf.put("_class", _class);
310        pf.put("_transletIndex", _transletIndex);
311        pf.put("_outputProperties", _outputProperties);
312        pf.put("_indentNumber", _indentNumber);
313        os.writeFields();
314
315        if (_uriResolver instanceof Serializable) {
316            os.writeBoolean(true);
317            os.writeObject((Serializable) _uriResolver);
318        }
319        else {
320            os.writeBoolean(false);
321        }
322    }
323
324    /**
325     * Return the state of the services mechanism feature.
326     */
327    public boolean useServicesMechnism() {
328        return _useServicesMechanism;
329    }
330
331     /**
332     * Store URIResolver needed for Transformers.
333     */
334    public synchronized void setURIResolver(URIResolver resolver) {
335        _uriResolver = resolver;
336    }
337
338    /**
339     * The TransformerFactory must pass us the translet bytecodes using this
340     * method before we can create any translet instances
341     *
342     * Note: This method is private for security reasons. See
343     * CR 6537898. When merging with Apache, we must ensure
344     * that the privateness of this method is maintained (that
345     * is why it wasn't removed).
346     */
347    private synchronized void setTransletBytecodes(byte[][] bytecodes) {
348        _bytecodes = bytecodes;
349    }
350
351    /**
352     * Returns the translet bytecodes stored in this template
353     *
354     * Note: This method is private for security reasons. See
355     * CR 6537898. When merging with Apache, we must ensure
356     * that the privateness of this method is maintained (that
357     * is why it wasn't removed).
358     */
359    private synchronized byte[][] getTransletBytecodes() {
360        return _bytecodes;
361    }
362
363    /**
364     * Returns the translet bytecodes stored in this template
365     *
366     * Note: This method is private for security reasons. See
367     * CR 6537898. When merging with Apache, we must ensure
368     * that the privateness of this method is maintained (that
369     * is why it wasn't removed).
370     */
371    private synchronized Class[] getTransletClasses() {
372        try {
373            if (_class == null) defineTransletClasses();
374        }
375        catch (TransformerConfigurationException e) {
376            // Falls through
377        }
378        return _class;
379    }
380
381    /**
382     * Returns the index of the main class in array of bytecodes
383     */
384    public synchronized int getTransletIndex() {
385        try {
386            if (_class == null) defineTransletClasses();
387        }
388        catch (TransformerConfigurationException e) {
389            // Falls through
390        }
391        return _transletIndex;
392    }
393
394    /**
395     * The TransformerFactory should call this method to set the translet name
396     */
397    protected synchronized void setTransletName(String name) {
398        _name = name;
399    }
400
401    /**
402     * Returns the name of the main translet class stored in this template
403     */
404    protected synchronized String getTransletName() {
405        return _name;
406    }
407
408
409    /**
410     * Creates a module layer with one module that is defined to the given class
411     * loader.
412     */
413    private Module createModule(ModuleDescriptor descriptor, ClassLoader loader) {
414        String mn = descriptor.name();
415
416        ModuleReference mref = new ModuleReference(descriptor, null) {
417            @Override
418            public ModuleReader open() {
419                throw new UnsupportedOperationException();
420            }
421        };
422
423        ModuleFinder finder = new ModuleFinder() {
424            @Override
425            public Optional<ModuleReference> find(String name) {
426                if (name.equals(mn)) {
427                    return Optional.of(mref);
428                } else {
429                    return Optional.empty();
430                }
431            }
432            @Override
433            public Set<ModuleReference> findAll() {
434                return Set.of(mref);
435            }
436        };
437
438        Layer bootLayer = Layer.boot();
439
440        Configuration cf = bootLayer.configuration()
441                .resolve(finder, ModuleFinder.of(), Set.of(mn));
442
443        PrivilegedAction<Layer> pa = () -> bootLayer.defineModules(cf, name -> loader);
444        Layer layer = AccessController.doPrivileged(pa);
445
446        Module m = layer.findModule(mn).get();
447        assert m.getLayer() == layer;
448
449        return m;
450    }
451
452    /**
453     * Defines the translet class and auxiliary classes.
454     * Returns a reference to the Class object that defines the main class
455     */
456    private void defineTransletClasses()
457        throws TransformerConfigurationException {
458
459        if (_bytecodes == null) {
460            ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
461            throw new TransformerConfigurationException(err.toString());
462        }
463
464        TransletClassLoader loader = (TransletClassLoader)
465            AccessController.doPrivileged(new PrivilegedAction() {
466                public Object run() {
467                    return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
468                }
469            });
470
471        try {
472            final int classCount = _bytecodes.length;
473            _class = new Class[classCount];
474
475            if (classCount > 1) {
476                _auxClasses = new HashMap<>();
477            }
478
479            // create a module for the translet
480
481            String mn = "jdk.translet";
482
483            String pn = _tfactory.getPackageName();
484            assert pn != null && pn.length() > 0;
485
486            ModuleDescriptor descriptor =
487                ModuleDescriptor.newModule(mn, Set.of(ModuleDescriptor.Modifier.SYNTHETIC))
488                                .requires("java.xml")
489                                .exports(pn)
490                                .build();
491
492            Module m = createModule(descriptor, loader);
493
494            // the module needs access to runtime classes
495            Module thisModule = TemplatesImpl.class.getModule();
496            // the module also needs permission to access each package
497            // that is exported to it
498            PermissionCollection perms =
499                new RuntimePermission("*").newPermissionCollection();
500            Arrays.asList(Constants.PKGS_USED_BY_TRANSLET_CLASSES).forEach(p -> {
501                thisModule.addExports(p, m);
502                perms.add(new RuntimePermission("accessClassInPackage." + p));
503            });
504
505            CodeSource codeSource = new CodeSource(null, (CodeSigner[])null);
506            ProtectionDomain pd = new ProtectionDomain(codeSource, perms,
507                                                       loader, null);
508
509            // java.xml needs to instantiate the translet class
510            thisModule.addReads(m);
511
512            for (int i = 0; i < classCount; i++) {
513                _class[i] = loader.defineClass(_bytecodes[i], pd);
514                final Class superClass = _class[i].getSuperclass();
515
516                // Check if this is the main class
517                if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
518                    _transletIndex = i;
519                }
520                else {
521                    _auxClasses.put(_class[i].getName(), _class[i]);
522                }
523            }
524
525            if (_transletIndex < 0) {
526                ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
527                throw new TransformerConfigurationException(err.toString());
528            }
529        }
530        catch (ClassFormatError e) {
531            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
532            throw new TransformerConfigurationException(err.toString(), e);
533        }
534        catch (LinkageError e) {
535            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
536            throw new TransformerConfigurationException(err.toString(), e);
537        }
538    }
539
540    /**
541     * This method generates an instance of the translet class that is
542     * wrapped inside this Template. The translet instance will later
543     * be wrapped inside a Transformer object.
544     */
545    private Translet getTransletInstance()
546        throws TransformerConfigurationException {
547        try {
548            if (_name == null) return null;
549
550            if (_class == null) defineTransletClasses();
551
552            // The translet needs to keep a reference to all its auxiliary
553            // class to prevent the GC from collecting them
554            AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
555            translet.postInitialization();
556            translet.setTemplates(this);
557            translet.setServicesMechnism(_useServicesMechanism);
558            translet.setAllowedProtocols(_accessExternalStylesheet);
559            if (_auxClasses != null) {
560                translet.setAuxiliaryClasses(_auxClasses);
561            }
562
563            return translet;
564        }
565        catch (InstantiationException e) {
566            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
567            throw new TransformerConfigurationException(err.toString());
568        }
569        catch (IllegalAccessException e) {
570            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
571            throw new TransformerConfigurationException(err.toString());
572        }
573    }
574
575    /**
576     * Implements JAXP's Templates.newTransformer()
577     *
578     * @throws TransformerConfigurationException
579     */
580    public synchronized Transformer newTransformer()
581        throws TransformerConfigurationException
582    {
583        TransformerImpl transformer;
584
585        transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
586            _indentNumber, _tfactory);
587
588        if (_uriResolver != null) {
589            transformer.setURIResolver(_uriResolver);
590        }
591
592        if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
593            transformer.setSecureProcessing(true);
594        }
595        return transformer;
596    }
597
598    /**
599     * Implements JAXP's Templates.getOutputProperties(). We need to
600     * instanciate a translet to get the output settings, so
601     * we might as well just instanciate a Transformer and use its
602     * implementation of this method.
603     */
604    public synchronized Properties getOutputProperties() {
605        try {
606            return newTransformer().getOutputProperties();
607        }
608        catch (TransformerConfigurationException e) {
609            return null;
610        }
611    }
612
613    /**
614     * Return the thread local copy of the stylesheet DOM.
615     */
616    public DOM getStylesheetDOM() {
617        return (DOM)_sdom.get();
618    }
619
620    /**
621     * Set the thread local copy of the stylesheet DOM.
622     */
623    public void setStylesheetDOM(DOM sdom) {
624        _sdom.set(sdom);
625    }
626}
627