| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.catalina.storeconfig; |
| |
| import java.beans.IndexedPropertyDescriptor; |
| import java.beans.Introspector; |
| import java.beans.PropertyDescriptor; |
| import java.io.PrintWriter; |
| import java.net.InetAddress; |
| import java.util.Iterator; |
| |
| import org.apache.tomcat.util.IntrospectionUtils; |
| import org.apache.tomcat.util.descriptor.web.ResourceBase; |
| import org.apache.tomcat.util.security.Escape; |
| |
| /** |
| * StoreAppends generate really the xml tag elements |
| */ |
| public class StoreAppender { |
| |
| /** |
| * The set of classes that represent persistable properties. |
| */ |
| private static final Class<?>[] persistables = { String.class, Integer.class, Integer.TYPE, Boolean.class, |
| Boolean.TYPE, Byte.class, Byte.TYPE, Character.class, Character.TYPE, Double.class, Double.TYPE, |
| Float.class, Float.TYPE, Long.class, Long.TYPE, Short.class, Short.TYPE, InetAddress.class }; |
| |
| private int pos = 0; |
| |
| /** |
| * Print the closing tag. |
| * |
| * @param aWriter The output writer |
| * @param aDesc Store description of the current element |
| * |
| * @throws Exception A store error occurred |
| */ |
| public void printCloseTag(PrintWriter aWriter, StoreDescription aDesc) throws Exception { |
| aWriter.print("</"); |
| aWriter.print(aDesc.getTag()); |
| aWriter.println(">"); |
| } |
| |
| /** |
| * Print only the open tag with all attributes. |
| * |
| * @param aWriter The output writer |
| * @param indent Indentation level |
| * @param bean The current bean that is stored |
| * @param aDesc Store description of the current element |
| * |
| * @throws Exception A store error occurred |
| */ |
| public void printOpenTag(PrintWriter aWriter, int indent, Object bean, StoreDescription aDesc) throws Exception { |
| aWriter.print("<"); |
| aWriter.print(aDesc.getTag()); |
| if (aDesc.isAttributes() && bean != null) { |
| printAttributes(aWriter, indent, bean, aDesc); |
| } |
| aWriter.println(">"); |
| } |
| |
| /** |
| * Print tag with all attributes |
| * |
| * @param aWriter The output writer |
| * @param indent Indentation level |
| * @param bean The current bean that is stored |
| * @param aDesc Store description of the current element |
| * |
| * @throws Exception A store error occurred |
| */ |
| public void printTag(PrintWriter aWriter, int indent, Object bean, StoreDescription aDesc) throws Exception { |
| aWriter.print("<"); |
| aWriter.print(aDesc.getTag()); |
| if (aDesc.isAttributes() && bean != null) { |
| printAttributes(aWriter, indent, bean, aDesc); |
| } |
| aWriter.println("/>"); |
| } |
| |
| /** |
| * Print the value from tag as content. |
| * |
| * @param aWriter The output writer |
| * @param tag The element name |
| * @param content Element content |
| * |
| * @throws Exception A store error occurred |
| */ |
| public void printTagContent(PrintWriter aWriter, String tag, String content) throws Exception { |
| aWriter.print("<"); |
| aWriter.print(tag); |
| aWriter.print(">"); |
| aWriter.print(Escape.xml(content)); |
| aWriter.print("</"); |
| aWriter.print(tag); |
| aWriter.println(">"); |
| } |
| |
| /** |
| * Print an array of values. |
| * |
| * @param aWriter The output writer |
| * @param tag The element name |
| * @param indent Indentation level |
| * @param elements Array of element values |
| */ |
| public void printTagValueArray(PrintWriter aWriter, String tag, int indent, String[] elements) { |
| if (elements != null && elements.length > 0) { |
| printIndent(aWriter, indent + 2); |
| aWriter.print("<"); |
| aWriter.print(tag); |
| aWriter.print(">"); |
| for (int i = 0; i < elements.length; i++) { |
| printIndent(aWriter, indent + 4); |
| aWriter.print(elements[i]); |
| if (i + 1 < elements.length) { |
| aWriter.println(","); |
| } |
| } |
| printIndent(aWriter, indent + 2); |
| aWriter.print("</"); |
| aWriter.print(tag); |
| aWriter.println(">"); |
| } |
| } |
| |
| /** |
| * Print an array of elements. |
| * |
| * @param aWriter The output writer |
| * @param tag The element name |
| * @param indent Indentation level |
| * @param elements Array of elements |
| * |
| * @throws Exception Store error occurred |
| */ |
| public void printTagArray(PrintWriter aWriter, String tag, int indent, String[] elements) throws Exception { |
| if (elements != null) { |
| for (String element : elements) { |
| printIndent(aWriter, indent); |
| printTagContent(aWriter, tag, element); |
| } |
| } |
| } |
| |
| /** |
| * Print some spaces. |
| * |
| * @param aWriter The output writer |
| * @param indent The number of spaces |
| */ |
| public void printIndent(PrintWriter aWriter, int indent) { |
| for (int i = 0; i < indent; i++) { |
| aWriter.print(' '); |
| } |
| pos = indent; |
| } |
| |
| /** |
| * Store the relevant attributes of the specified JavaBean, plus a <code>className</code> attribute defining the |
| * fully qualified Java class name of the bean. |
| * |
| * @param writer PrintWriter to which we are storing |
| * @param indent Indentation level |
| * @param bean Bean whose properties are to be rendered as attributes, |
| * @param desc Store description of the current element |
| * |
| * @exception Exception if an exception occurs while storing |
| */ |
| public void printAttributes(PrintWriter writer, int indent, Object bean, StoreDescription desc) throws Exception { |
| |
| printAttributes(writer, indent, true, bean, desc); |
| |
| } |
| |
| /** |
| * Store the relevant attributes of the specified JavaBean. |
| * |
| * @param writer PrintWriter to which we are storing |
| * @param indent Indentation level |
| * @param include Should we include a <code>className</code> attribute? |
| * @param bean Bean whose properties are to be rendered as attributes, |
| * @param desc RegistryDescriptor from this bean |
| * |
| * @exception Exception if an exception occurs while storing |
| */ |
| public void printAttributes(PrintWriter writer, int indent, boolean include, Object bean, StoreDescription desc) |
| throws Exception { |
| |
| // Render a className attribute if requested |
| if (include && !desc.isStandard()) { |
| writer.print(" className=\""); |
| writer.print(bean.getClass().getName()); |
| writer.print("\""); |
| } |
| |
| // Acquire the list of properties for this bean |
| PropertyDescriptor[] descriptors = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors(); |
| if (descriptors == null) { |
| descriptors = new PropertyDescriptor[0]; |
| } |
| |
| // Create blank instance |
| Object bean2 = defaultInstance(bean); |
| for (PropertyDescriptor descriptor : descriptors) { |
| Object value = checkAttribute(desc, descriptor, descriptor.getName(), bean, bean2); |
| if (value != null) { |
| printAttribute(writer, indent, bean, desc, descriptor.getName(), bean2, value); |
| } |
| } |
| |
| if (bean instanceof ResourceBase resource) { |
| for (Iterator<String> iter = resource.listProperties(); iter.hasNext();) { |
| String name = iter.next(); |
| Object value = resource.getProperty(name); |
| if (!isPersistable(value.getClass())) { |
| continue; |
| } |
| if (desc.isTransientAttribute(name)) { |
| continue; // Skip the specified exceptions |
| } |
| printValue(writer, indent, name, value); |
| |
| } |
| } |
| } |
| |
| /** |
| * Check if the attribute should be printed. |
| * |
| * @param desc RegistryDescriptor from this bean |
| * @param descriptor PropertyDescriptor from this bean property |
| * @param attributeName The attribute name to store |
| * @param bean The current bean |
| * @param bean2 A default instance of the bean for comparison |
| * |
| * @return null if the value should be skipped, the value to print otherwise |
| */ |
| protected Object checkAttribute(StoreDescription desc, PropertyDescriptor descriptor, String attributeName, |
| Object bean, Object bean2) { |
| if (descriptor instanceof IndexedPropertyDescriptor) { |
| return null; // Indexed properties are not persisted |
| } |
| if (!isPersistable(descriptor.getPropertyType()) || (descriptor.getReadMethod() == null) || |
| (descriptor.getWriteMethod() == null)) { |
| return null; // Must be a read-write primitive or String |
| } |
| if (desc.isTransientAttribute(descriptor.getName())) { |
| return null; // Skip the specified exceptions |
| } |
| Object value = IntrospectionUtils.getProperty(bean, descriptor.getName()); |
| if (value == null) { |
| return null; // Null values are not persisted |
| } |
| Object value2 = IntrospectionUtils.getProperty(bean2, descriptor.getName()); |
| if (value.equals(value2)) { |
| // The property has its default value |
| return null; |
| } |
| return value; |
| } |
| |
| /** |
| * Store the specified of the specified JavaBean. |
| * |
| * @param writer PrintWriter to which we are storing |
| * @param indent Indentation level |
| * @param bean The current bean |
| * @param desc RegistryDescriptor from this bean |
| * @param attributeName The attribute name to store |
| * @param bean2 A default instance of the bean for comparison |
| * @param value The attribute value |
| */ |
| protected void printAttribute(PrintWriter writer, int indent, Object bean, StoreDescription desc, |
| String attributeName, Object bean2, Object value) { |
| if (isPrintValue(bean, bean2, attributeName, desc)) { |
| printValue(writer, indent, attributeName, value); |
| } |
| } |
| |
| /** |
| * Determine if the attribute value needs to be stored. |
| * |
| * @param bean original bean |
| * @param bean2 default bean |
| * @param attrName attribute name |
| * @param desc StoreDescription from bean |
| * |
| * @return <code>true</code> if the value should be stored |
| */ |
| public boolean isPrintValue(Object bean, Object bean2, String attrName, StoreDescription desc) { |
| return true; |
| } |
| |
| /** |
| * Generate default Instance for the specified bean. |
| * |
| * @param bean The bean |
| * |
| * @return an object from same class as bean parameter |
| * |
| * @throws ReflectiveOperationException Error creating a new instance |
| */ |
| public Object defaultInstance(Object bean) throws ReflectiveOperationException { |
| return bean.getClass().getConstructor().newInstance(); |
| } |
| |
| /** |
| * Print an attribute value. |
| * |
| * @param writer PrintWriter to which we are storing |
| * @param indent Indentation level |
| * @param name Attribute name |
| * @param value Attribute value |
| */ |
| public void printValue(PrintWriter writer, int indent, String name, Object value) { |
| // Convert IP addresses to strings so they will be persisted |
| if (value instanceof InetAddress) { |
| value = ((InetAddress) value).getHostAddress(); |
| } |
| if (!(value instanceof String)) { |
| value = value.toString(); |
| } |
| String strValue = Escape.xml((String) value); |
| pos = pos + name.length() + strValue.length(); |
| if (pos > 60) { |
| writer.println(); |
| printIndent(writer, indent + 4); |
| } else { |
| writer.print(' '); |
| } |
| writer.print(name); |
| writer.print("=\""); |
| writer.print(strValue); |
| writer.print("\""); |
| } |
| |
| |
| /** |
| * Is the specified property type one for which we should generate a persistence attribute? |
| * |
| * @param clazz Java class to be tested |
| * |
| * @return <code>true</code> if the specified class should be stored |
| */ |
| protected boolean isPersistable(Class<?> clazz) { |
| |
| for (Class<?> persistable : persistables) { |
| if (persistable == clazz || persistable.isAssignableFrom(clazz)) { |
| return true; |
| } |
| } |
| return false; |
| |
| } |
| } |