Using pinned self-signed SSL certificate with Android Volley

Recently for one of my Android apps I wanted to use a self-signed certificate on the server-side. However, if you try to connect to such a server with default settings, the connection is going to be refused. This is because it has been signed by you (your server domain), and ‘you’ is not trusted by any system.

Read the excellent article – Android security – Implementation of Self-signed SSL certificate for your App. This describes all the concepts and the pros and cons of pinning. However, it does not describe how to do this in Volley. The internet is littered with many articles on this, and most of them are outdated and can’t be used now.

So, here is an updated guide. I have tested this on my app which uses Android API 22 and Volley code downloaded on Jan 2015.

First use the official guide to create a singleton class to get the request queue. Now below is the modified code which takes care of the SSL pinning.

public class MySingleton {
    private static char[] KEYSTORE_PASSWORD = "YourKeyStorePass".toCharArray();

    ...

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            // getApplicationContext() is key, it keeps you from leaking the
            // Activity or BroadcastReceiver if someone passes one in.
            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(), new HurlStack(null, newSslSocketFactory()));
        }
        return mRequestQueue;
    }

    ...

    private SSLSocketFactory newSslSocketFactory() {
        try {
            // Get an instance of the Bouncy Castle KeyStore format
            KeyStore trusted = KeyStore.getInstance("BKS");
            // Get the raw resource, which contains the keystore with
            // your trusted certificates (root and any intermediate certs)
            InputStream in = mCtx.getApplicationContext().getResources().openRawResource(R.raw.codeprojectssl);
            try {
                // Initialize the keystore with the provided trusted certificates
                // Provide the password of the keystore
                trusted.load(in, KEYSTORE_PASSWORD);
            } finally {
                in.close();
            }

            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(trusted);

            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, tmf.getTrustManagers(), null);

            SSLSocketFactory sf = context.getSocketFactory();
            return sf;
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }
}

Please note, that the above solution will work for Android API 9 and above. To support the below versions you need to pass an instance of HttpClientStack instead of HurlStack. So, replacing the line

mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(), new HurlStack(null, newSslSocketFactory()));

with

mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(), new HttpClientStack(new MyHttpClient()));

might work. Where MyHttpClient is the one defined in the CodeProject article. I haven’t tested this part though.

Find one’s complement of a decimal number.

One’s complement of a binary number (1001_{2}) is (0110_{2}). That is, we simply flip the bits. This is easy to do in binary. However, if the number is in base 10, then do we do that. Convert that to base 2 then flip the bits and change it back to base 2? Maybe, but if the number is stored in native data types (like int) then we can simply use bitwise not to invert the bits, without any conversion.

In my case it wasn’t so simple. I had a really big number, which had to be stored in BigDecimal. Converting to base 2, back and forth too is very costly. So the efficient algo for that is…

(x’) is the one’s complement of (x) and it has (b) bits, or we are interested in only (b) bits of it.

$$ \begin{align} &x + x’ = 2^{b+1} – 1 \tag{all ones for all b bits} \ &\Rightarrow x’ = 2^{b+1} – 1 – x \ \end{align} $$

So, the equivalent Java code for the above is…

public static BigDecimal onesComplement(BigDecimal n, int totalBits) {
    return new BigDecimal(2).pow(totalBits + 1).subtract(BigDecimal.ONE).subtract(n);
}

Calculate Root of Any Whole Number in Java

The following Java program can find any root of a whole number. The logic is simple. Start by guessing some number and check if that is the correct value. If that overshoots then subtract some from the guess number else add to it. We add or subtract some fixed constant. If that constant is too big then we divide that constant by 10 to get a finer number.

#!java
public class CubeRoot {

    public static void main(String[] args) {
        System.out.println(String.format("%.2f", cubeRoot(0, 2)));
        System.out.println(String.format("%.0f", cubeRoot(8, 0)));
        System.out.println(String.format("%.4f", cubeRoot(10, 4)));

        System.out.println(String.format("%.4f", root(10, 2, 4))); // Square Root
        System.out.println(String.format("%.4f", root(100, 5, 4))); // 5th Root
    }

    private static double cubeRoot(int n, int precision) {
        return root(n, 3, precision);
    }

    private static double root(int n, int root, int precision) {
        double x = n / 5.0; // 5 is better than 4 since this will have bigger
                            // step. 3 is very bad choice since there are some
                            // no.s which will never have rational output when
                            // divided by 3, e.g. 5.
        double powX;
        double d = 10;
        double lastX = 0;
        double lastLastX = 0;
        do {
            powX = Math.pow(x, root);
            if (matches(powX, n, precision))
                return x;
            else {
                if (matches(lastLastX, x, precision)) {
                    // If the lastLast x value is same as current then we are
                    // trapped in a loop, since the current d is not small
                    // enough. We need to now step at finer precisions.
                    d /= 10;
                    if (matches(d, 0, precision + 1)) {
                        return x;
                    }
                }
                lastLastX = lastX;
                lastX = x;
                if (n < powX) {
                    x -= d;
                } else {
                    x += d;
                }
                // System.out.println("(x=" + x + ", d=" + d + ")");
            }
        } while (true);
    }

    private static boolean matches(double a, double b, int precession) {
        return ((int) (a * (long) Math.pow(10, precession)))
                - ((int) (b * (long) Math.pow(10, precession))) == 0;
    }

}

Java subList() gotcha.


Recently I stumbled upon an issue of stuck thread. The thread seemed to be stuck with stack trace something like the one below:-

java.util.SubList$1.nextIndex(AbstractList.java:713)
java.util.SubList$1.nextIndex(AbstractList.java:713) ...Repeated 100s of times...
java.util.SubList$1.nextIndex(AbstractList.java:713)
java.util.SubList$1.hasNext(AbstractList.java:691)
java.util.SubList$1.next(AbstractList.java:695)
java.util.SubList$1.next(AbstractList.java:696) ...Again repeated 100s of times...
java.util.SubList$1.next(AbstractList.java:696)
java.util.AbstractList.hashCode(AbstractList.java:526)
java.util.Collections$UnmodifiableList.hashCode(Collections.java:1152)
org.apache.myfaces.trinidad.util.CollectionUtils$DelegatingCollection.hashCode(CollectionUtils.java:603) ...

My initial reaction was – huh?! Well googling revealed a blog post related to this issue – Java subList Gotcha. Ya I too have used the same title, as it seems to be the most appropriate one.

The gotcha

The author of that blog post explained, that contrary to what you may expect but subList() does not return a new instance of the list but it returns a ‘view’ of it. What this means is that subList() will return an instance of class SubList or RandomAccessSubList, based on if the original list doesn’t or does implement RandomAccess interface, and they contain a reference to the original list. Note that both these classes are actually inner classes of AbstractList. For most part you can simply disregard this piece of information but in some cases it has some serious consequences. Check out the code below.

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class Sublist {
    public static void main(String[] args) {
        weirdList(new LinkedList<Integer>()); // Will result in generation of
                                              // SubList
        weirdList(new ArrayList<Integer>()); // Will result in generation of
                                             // RandomAccessSubList
    }

    static void weirdList(List<Integer> list) {
        List<Integer> alist = list, blist = list;
        list.clear();
        for (int i = 1; i <= 5000; i++) {
            list.add(i);
        }
        for (int i = 1; i <= 500; i++) {
            alist = alist.subList(1, alist.size()); // Wrong
        }
        for (int i = 1; i <= 500; i++) {
            blist = new ArrayList<Integer>(blist.subList(1, blist.size())); // Correct
        }
        long startTime = System.nanoTime();
        System.out.println(alist.hashCode());
        long endTime = System.nanoTime();
        System.out.println("Total time taken for hasCode() = " + (endTime - startTime) / 1000000.0 + "ms");
        startTime = System.nanoTime();
        System.out.println(blist.hashCode());
        endTime = System.nanoTime();
        System.out.println("It should take = " + (endTime - startTime) / 1000000.0 + "ms\n");
    }
}

If you run the above code then you will notice that alist.hasCode() will typically take 4s more than blist.hasCode(). Even though they both pointed to the very same list!

How the hell hashCode() end up triggering this mess?

Before I continue, I would urge that you take a look at how AbstractList.hashCode() is implemented. This will help you to understand as to as how did the code end up calling next() in SubList. See below.

public int hashCode() {
    int hashCode = 1;
    for (E e : this)
        hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
    return hashCode;
}

From above it seems that a list’s has-code is calculated based on the hash-codes of all its elements. The for-each loop actually gets a reference of the iterator of the list by calling the list’s iterator() method. Then it calls the next() method over the iterator to get the next element if hasNext() returned true. Now lets get back to the first Java code listing. The whole reason for bad performance of alist is due to the following line:-

alist = alist.subList(1, alist.size());

Yes this simple benign looking line. Let’s understand its reason.

Here comes the innocent devil

I already mentioned that subList() returns instance of SubList or RandomAcessSubbList. Now let’s see that code snippet from AbstractList.

public List<E> subList(int fromIndex, int toIndex) {
    return (this instanceof RandomAccess ?
        new RandomAccessSubList<E>(this, fromIndex, toIndex)
        : new SubList<E>(this, fromIndex, toIndex));
}

Well let’s take the case where alist refers to an instance of LinkedList, and since LinkedList is does not implement RandomAccess so alist.subList() will return a new instance of SubList. This instance of SubList will point to the original list which actually has the elements. So, lets represent that visually as below:-

SubList-1->OriginalList After executing the code alist = alist.subList() for the first time, the situation will be like below:-

(alist = SubList-1)->OriginalList After executing that code the second time, it becomes:-

(alist = SubList-2)->SubList-1->OriginalList So after n-iterations it will be:-

(alist = SubList-n)->SubList-(n-1)->…->SubList-1->OriginalList

Ok, now you must be wondering, what exactly does the above represent? Well then let’s see the code of Sublist.iterator(int).

public ListIterator<E> listIterator(final int index) {
    checkForComodification();
    rangeCheckForAdd(index);
    return new ListIterator<E>() {
        private final ListIterator<E> i = l.listIterator(index+offset);
        public boolean hasNext() { return nextIndex() < size; }
        public E next() {
            if (hasNext())
                return i.next();
            else
                throw new NoSuchElementException();
        }
        public boolean hasPrevious() { return previousIndex() >= 0; }
        public E previous() {
            if (hasPrevious())
                return i.previous();
            else
                throw new NoSuchElementException();
        }
        public int nextIndex() { return i.nextIndex() - offset; }
        public int previousIndex() { return i.previousIndex() - offset; }
        public void remove() {
            i.remove();
            SubList.this.modCount = l.modCount; size--;
        }
        public void set(E e) { i.set(e); }
        public void add(E e) {
            i.add(e);
            SubList.this.modCount = l.modCount; size++;
        }
    };
}

When hashCode() is called on alist, it actually calls hashCode() on the instance of SubList-n. So to get the iterator hasCode() actually invokes SubList.iterator() which in-turn invokes SubList.listIterator() and which finally invokes SubList.listIterator(int) (whose code is shown above). Note that this method returns an anonymous instance of ListIterator. Now take a close look at the stack trace (right at the top of this post). Did you notice the $1 in the stack trace? This means the method is stuck in next() of the anonymous ListIterator. So, the hashCode() code first calls SubList$1.hasNext() which in-turn invokes its own nextIndex() which further invokes the nextIndex() of the iterator of the list it points. So, in case of SubList-n this will be SubList-(n-1). This call will go on and on till the code reaches the original list. If hasNext returned true then next the hashCode() code will invoke SubList$1.next(). Which will invoke its own hasNext() and that will again go through all the steps mentioned above, and when it returns true then it will invoke next() on the iterator of the list it references. So for each iterations in hasCode() of SubList-n the code has to recursively go down the full the depth almost 3-times^1!

Finding the maxima. Err… involves some simple maths

Try to run the Java code listed at the top of the post and try changing 5000 to 1000. Then try running that program multiple times, each time increasing the values of the rest two for loops in multiples of 100. You will notice the peak performance penalty comes at iteration of 700 for 1000 elements. You might be surprised at first but think about this. When you increase the iterations so much that SubList-n has zero elements then there will be no performance penalty. This is a limiting case. So there must be some peak point between the two extremes, i.e. when the SubList-n directly refers to the original list (when n=1) but it contains n-1 elements, and the other extreme being when SubList-n has only zero elements. So, we begin. After 1st iteration the number of elements in alist is n-1 and has 1 SubList to reach the original list. After 2nd iteration the number of elements in alist is n-2 and has 2 SubLists to reach the original list. … After xth iteration the number of elements in alist is n-x and has x SubLists to reach the original list.

$$ \begin{align} O(n) &= x (n-x)\\ &= xn – x^2 \end{align} $$

Now to find the maxima,

$$ \begin{align} \frac{dO(n)}{dx} = 0\\ &\Rightarrow \frac{d(xn – x^2)}{dx} = 0\\ &\Rightarrow \frac{ndx}{dx} – \frac{d(x^2)}{dx} = 0\\ &\Rightarrow n – 2x = 0\\ &\Rightarrow n = 2x\\ &\Rightarrow x = \frac{n}{2} \end{align} $$

So from above it is clear that the maxima is reached when number of partitioning (iterations with alist=alist.subList()) is half the total amount of data. Practical experiments show that the actual maxima is slightly shifted further away from the half point.

Summary. Phew! Finally!

If you use subList wrongly then a time will come when dealing with large data your thread will appear to be stuck. In reality the code is not really hung but is doing a hell lot of iterations. In fact if you take the snapshot of your stuck thread then you will see the stack is growing, if not then at least note the position of the line java.util.SubList$1.hasNext(AbstractList.java:691). The position of this line would be changing rapidly in the stack.

^1 More accurately n+n+(n-1)+(n-2)+...+1=\frac{n}{2}(3+n)

Struts 1 config file reference documentation

This documentation is just a copy paste of portions of official Struts DTD. As I couldn’t find any easy to find reference on it hence I have put together this just to be easily locatable.


The values in [] are default values for that attribute.

Top Level Elements:-


<struts-config> (the root of the configuration file hierarchy, and contains nested elements for all of the other configuration settings.)

  • id:

<form-beans> (describes the set of form bean descriptors.)

  • type: Fully qualified Java class to use when instantiating ActionFormBean objects. If specified, the object must be a subclass of the default class type.

    WARNING:  For Struts 1.0, this value is ignored.  You can set the default implementation class name with the “formBean” initialization parameter to the Struts controller servlet.

  • id:

<form-bean> (describes an ActionForm subclass [org.apache.struts.action.ActionForm] that can be referenced by an “action” element. It is a JavaBean that implements the org.apache.struts.action.ActionForm class.)

  • className: The configuration bean for this form bean object. If specified, the object must be a subclass of the default configuration bean. [“org.apache.struts.config.FormBeanConfig”]
  • extends: The name of the form bean that this bean will inherit configuration information from.
  • name (Required): The unique identifier for this form bean. Referenced by the <action> element to specify which form bean to use with its request.
  • type: Fully qualified Java class name of the ActionForm subclass to use with this form bean.
  • enhanced: Reserved for future use.
  • id:

<form-property> (describes a JavaBean property that can be used to configure an instance of a DynaActionForm or a subclass thereof. This element is only utilized when the “type” attribute of the enclosing “form-bean” element is [org.apache.struts.action.DynaActionForm] or a subclass of DynaActionForm. If a custom DynaActionForm subclass is used, then the “dynamic” attribute of the enclosing <form-bean> element must be set to “true”. Since Struts 1.1.)

  • className: The configuration bean for this form property object. If specified, the object must be a subclass of the default configuration bean. [“org.apache.struts.config.FormPropertyConfig”]
  • initial: String representation of the initial value for this property. If not specified, primitives will be initialized to zero and objects initialized to the zero-argument instantiation of that object class.  For example, Strings will be initialized to “”
  • name (Required): The name of the JavaBean property described by this element.
  • reset: The flag that indicates when this property should be reset to its “initial” value when the form’s “reset()” method is called.  If this is set to “true”, the property is always reset when “reset()” is called.  This can also be set to one or more HTTP methods, such as GET or POST. In such a case, the property will be reset only when the HTTP method used for the request being processed is included in this attribute’s value(s).  Multiple HTTP methods can be specified by separating them with whitespace or commas.
  • size: The number of array elements to create if the value of the “type” attribute specifies an array, but there is no value specified for the “initial” attribute.
  • type (Required): Fully qualified Java class name of the field underlying this property, optionally followed by “[]” to indicate that the field is indexed.

<global-exceptions> (describes a set of exceptions that might be thrown by an Action object. The handling of individual exception types is configured through nested exception elements. An <action> element may override a global exception handler by registering a local exception handler for the same exception type. Since Struts 1.1.)

  • id:

<exception> (registers an ExceptionHandler for an exception type.)

  • bundle: Servlet context attribute for the message resources bundle associated with this handler. The default attribute is the value specified by the string constant declared at Globals.MESSAGES_KEY. [org.apache.struts.Globals.MESSAGES_KEY]
  • className: The configuration bean for this ExceptionHandler object. If specified, className must be a subclass of the default configuration bean. [“org.apache.struts.config.ExceptionConfig”]
  • extends: The name of the exception handler that this will inherit configuration information from.
  • handler: Fully qualified Java class name for this exception handler. [“org.apache.struts.action.ExceptionHandler”]
  • key: The key to use with this handler’s message resource bundle that will retrieve the error message template for this exception.
  • path: The module-relative URI to the resource that will complete the request/response if this exception occurs.
  • scope: The context (“request” or “session”) that is used to access the ActionMessage object [org.apache.struts.action.ActionMessage] for this exception.
  • type (Required): Fully qualified Java class name of the exception type to register with this handler.
  • id:

<global-forwards> (describes a set of ActionForward objects [org.apache.struts.action.ActionForward] that are available to all Action objects as a return value. The individual ActionForwards are configured through nested <forward> elements. An <action> element may override a global forward by defining a local <forward> of the same name.)

  • type: Fully qualified Java class to use when instantiating ActionForward objects.  If specified, the object must be a subclass of the default class type.

    WARNING:  For Struts 1.0, this value is ignored. You can set the default implementation class name with the “forward” initialization parameter to the Struts controller servlet.

  • id:

<forward> (describes an ActionForward that is to be made available to an Action as a return value. An ActionForward is referenced by a logical name and encapsulates a URI. A “forward” element may be used to describe both global and local ActionForwards. Global forwards are available to all the Action objects in the module. Local forwards can be nested within an <action> element and only available to an Action object when it is invoked through that ActionMapping.)

  • catalog: The name of a commons-chain catalog in which to look up a command to be executed as part of servicing this request. Only meaningful if “command” is also specified.
  • className: Fully qualified Java class name of ActionForward subclass to use for this object. [“org.apache.struts.action.ActionForward”]
  • command: The name of a commons-chain command which should be looked up and executed as part of servicing this request.
  • extends: The name of the forward configuration that this will inherit configuration information from.
  • module: The module prefix to use with this path. This value should begin with a slash (“/”).
  • name (Required): The unique identifier for this forward. Referenced by the Action object at runtime to select – by its logical name – the resource that should complete the request/response.
  • path: The module-relative path to the resources that is encapsulated by the logical name of this ActionForward. This value should begin with a slash (“/”) character.
  • redirect: Set to “true” if a redirect instruction should be issued to the user-agent so that a new request is issued for this forward’s resource. If true,  RequestDispatcher.Redirect is called. If “false”, RequestDispatcher.forward is called instead. [false]

<action-mappings> (describes a set of ActionMapping objects [org.apache.struts.action.ActionMapping] that are available to process requests matching the url-pattern our ActionServlet registered with the container. The individual ActionMappings are configured through nested <action> elements.)

  • id:
  • type: Fully qualified Java class to use when instantiating ActionMapping objects. If specified, the object must be a subclass of the default class type.

    WARNING:  For Struts 1.0, this value is ignored.  You can set the default implementation class name with the “mapping” initialization parameter to the Struts controller servlet.

<action> (describes an ActionMapping object that is to be used to process a request for a specific module-relative URI.)

  • id:
  • attribute: Name of the request-scope or session-scope attribute that is used to access our ActionForm bean, if it is other than the bean’s specified “name”. Optional if “name” is specified, else not valid.
  • cancellable: Set to “true” if the Action can be cancelled. By default, when an Action is cancelled, validation is bypassed and the Action should not execute the business operation. If a request tries to cancel an Action when cancellable is not set, a “InvalidCancelException” is thrown. [false]
  • catalog: The name of a commons-chain catalog in which to look up a command to be executed as part of servicing this request. Only meaningful if “command” is also specified.
  • className: The fully qualified Java class name of the ActionMapping subclass to use for this action mapping object. Defaults to the type specified by the enclosing <action-mappings> element or to “org.apache.struts.action.ActionMapping” if not specified. [“org.apache.struts.action.ActionMapping”]
  • command: The name of a commons-chain command which should be looked up and executed as part of servicing this request.
  • extends: The path of the action mapping configuration that this will inherit configuration information from.
  • forward: Module-relative path of the servlet or other resource that will process this request, instead of the Action class specified by “type”.  The path WILL NOT be processed through the “forwardPattern” attribute that is configured on the “controller” element for this module. Exactly one of “forward”, “include”, or “type” must be specified.
  • include: Module-relative path of the servlet or other resource that will process this request, instead of the Action class specified by “type”.  The path WILL NOT be processed through the “forwardPattern” attribute that is configured on the “controller” element for this module. Exactly one of “forward”, “include”, or “type” must be specified.
  • input: Module-relative path of the action or other resource to which control should be returned if a validation error is encountered. Valid only when “name” is specified. Required if “name” is specified and the input bean returns validation errors. Optional if “name” is specified and the input bean does not return validation errors.
  • name: Name of the form bean, if any, that is associated with this action mapping.
  • parameter: General-purpose configuration parameter that can be used to pass extra information to the Action object selected by this action mapping.
  • path (Required): The module-relative path of the submitted request, starting with a “/” character, and without the filename extension if extension mapping is used.

    NOTE:  Do *not* include a period in your path name, because it will look like a filename extension and cause your Action to not be located.

  • prefix: Prefix used to match request parameter names to ActionForm property names, if any. Optional if “name” is specified, else not allowed.
  • roles: Comma-delimited list of security role names that are allowed access to this ActionMapping object. Since Struts 1.1.
  • scope: The context (“request” or “session”) that is used to access our ActionForm bean, if any.  Optional if “name” is specified, else not valid. [session]
  • suffix: Suffix used to match request parameter names to ActionForm bean property names, if any. Optional if “name” is specified, else not valid.
  • type: Fully qualified Java class name of the Action subclass [org.apache.struts.action.Action] that will process requests for this action mapping. Not valid if either the “forward” or “include” attribute is specified.  Exactly one of “forward”, “include”, or “type” must be specified.
  • unknown: Set to “true” if this object should be configured as the default action mapping for this module. If a request does not match another object, it will be passed to the ActionMapping object with unknown set to “true”. Only one ActionMapping can be marked as “unknown” within a module. [false]
  • validate: Set to “true” if the validate method of the ActionForm bean should be called prior to calling the Action object for this action mapping, or set to “false” if you do not want the validate method called. [true]

<controller> (describes the ControllerConfig bean [org.apache.struts.config.ControllerConfig] that encapsulates a module’s runtime configuration.)

  • bufferSize: The size of the input buffer used when processing file uploads. [4096]
  • catalog: Name of the catalog to use when processing requests for this module. [struts]
  • className: Fully qualified Java class name of the ControllerConfig subclass for this controller object. If specified, the object must be a subclass of the default class.[“org.apache.struts.config.ControllerConfig”]
  • command: Name of the command to execute to process a request. [servlet-standard]
  • contentType: Default content type (and optional character encoding) to be set on each response. May be overridden by the Action, JSP, or other resource to which the request is forwarded. [“text/html”]
  • forwardPattern: Replacement pattern defining how the “path” attribute of a <forward> element is mapped to a context-relative URL whenit starts with a slash (and when the contextRelative property is false). This value may consist of any combination of the following:
    • – “$M” – Replaced by the module prefix of this module
    • – “$P” – Replaced by the “path” attribute of the  selected “forward” element
    • – “$$” – Causes a literal dollar sign to be rendered
    • – “$x” – (Where “x” is any character not defined above). Silently swallowed, reserved for future use.

    If not specified, the default forwardPattern is “$M$P”, which is consistent with the previous behavior of forwards.  Since Struts 1.1.  [“$M$P”]

  • inputForward: Set to “true” if you want the “input” attribute of <action> elements to be the name of a local or global ActionForward, which will then be used to calculate the ultimate URL. Set to “false” (the default) to treat the “input” parameter of <action> elements as a module-relative path to the resource to be used as the input form. Since Struts 1.1. [false]
  • locale: Set to “true” if you want a Locale object stored in the user’s session if not already present. [true]
  • maxFileSize: The maximum size (in bytes) of a file to be accepted as a file upload.  Can be expressed as a number followed by a “K”, “M”, or “G”, which are interpreted to mean kilobytes, megabytes, or gigabytes, respectively. [“250M”]
  • memFileSize: The maximum size (in bytes) of a file whose contents will be retained in memory after uploading. Files larger than this threshold will be written to some alternative storage medium, typically a hard disk. Can be expressed as a number followed by a “K”, “M”, or “G”, which are interpreted tomean kilobytes, megabytes, or gigabytes, respectively. [“256K”]
  • multipartClass: The fully qualified Java class name of the multipart request handler class to be used with this module. [“org.apache.struts.upload.CommonsMultipartRequestHandler”]
  • nocache: Set to “true” if you want the controller to add HTTP headers for defeating caching to every response from this module. [false]
  • pagePattern: Replacement pattern defining how the “page” attribute of custom tags using it is mapped to a context-relative URL of the corresponding resource.  This value may consist of any combination of the following:
    • – “$M” – Replaced by the module prefix of this module
    • – “$P” – Replaced by the value of the “page” attribut
    • – “$$” – Causes a literal dollar sign to be rendered
    • – “$x” – (Where “x” is any character not defined above). Silently swallowed, reserved for future use.

    If not specified, the default forwardPattern is “$M$P”, which is consistent with previous hard coded behavior of URL evaluation for “page” attributes. [“$M$P”]

  • processorClass: The fully qualified Java class name of the RequestProcessor subclass to be used with this module. [“org.apache.struts.chain.ComposableRequestProcessor”]
  • tempDir: Temporary working directory to use when processing file uploads. [{Directory provided by servlet container}]
  • id: 

<message-resources> (describes a MessageResources object with message templates for this module.)

  • className: The configuration bean for this message resources object. If specified, the object must be a subclass of the default configuration bean. [“org.apache.struts.config.MessageResourcesConfig”]
  • factory: Fully qualified Java class name of the MessageResourcesFactory subclass to use for this message resources object.[“org.apache.struts.util.PropertyMessageResourcesFactory”]
  • key: Servlet context attribute under which this message resources bundle will be stored. The default attribute is the value specified by the string constant at [Globals.MESSAGES_KEY]. The module prefix (if any) is appended to the key (${key}${prefix}). [org.apache.struts.Globals.MESSAGES_KEY]

    NOTE: The module  prefix includes the leading slash, so the default message resource bundle for a module named “foo” is stored under “org.apache.struts.action.MESSAGE/foo”.

  • null: Set to “true” if you want our message resources to return a null string for unknown message keys, or “false” to return a message with the bad key value.
  • parameter (Required): Configuration parameter to be passed to the createResources method of our factory object.

<plug-in> (specifies the fully qualified class name of a general-purpose application plug-in module that receives notification of application startup and shutdown events. An instance of the specified class is created for each element, and can be configured with nested <set-property> elements.)

  • id:
  • className (Required): Fully qualified Java class name of the plug-in class; must implement [org.apache.struts.action.PlugIn].


Subordinate Elements:-

<description> (contains descriptive (paragraph length) text about the surrounding element, suitable for use in GUI tools.)

  • id:

<display-name> (contains a short (one line) description of the surrounding element, suitable for use in GUI tools.)

  • id:

<icon> (contains a small-icon and large-icon element which specify the location, relative to the Struts configuration file, for small and large images used to represent the surrounding element in GUI tools.)

  • id:

<large-icon> (specifies the location, relative to the Struts configuration file, of a resource containing a large (32×32 pixel) icon image.)

  • id:

<small-icon> (specifies the location, relative to the Struts configuration file, of a resource containing a small (16×16 pixel) icon image.)

  • id:

<set-property> (specifies the method name and initial value of an additional JavaBean configuration property. When the object representing the surrounding element is instantiated, the accessor for the indicated property is called and passed the indicated value. The “set-property” element is especially useful when a custom subclass is used with <forward>, <action>, or <plug-in> elements. The subclass can be passed whatever other properties may be required to configure the object without changing how the struts-config is parsed.

Since Struts 1.3, an alternate syntax is supported.  By using the “key” attribute instead of the “property” attribute, you can set arbitrary string properties on the Config object which is populated based on the containing element.

NOTE: the “key” attribute is NOT supported for <set-property> inside a <plug-in> element.)

  • property: Name of the JavaBeans property whose setter method will be called. Exactly one of “property” or “key” must be specified.
  • key: Where supported, the key which will be used to store the specified value in the given config object.  Exactly one of “property” or “key” must be specified.
  • value (Required): String representation of the value to which this property will be set, after suitable type conversion.