View Javadoc

1   /**
2    * This file Copyright (c) 2003-2010 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.commands;
35  
36  import info.magnolia.cms.util.AlertUtil;
37  
38  import java.util.Collections;
39  import java.util.Map;
40  
41  import org.apache.commons.beanutils.BeanUtils;
42  import org.apache.commons.beanutils.PropertyUtils;
43  import org.apache.commons.chain.Command;
44  import org.apache.commons.chain.Context;
45  import org.apache.commons.lang.exception.NestableException;
46  import org.apache.commons.pool.BasePoolableObjectFactory;
47  import org.apache.commons.pool.impl.StackObjectPool;
48  import org.slf4j.Logger;
49  import org.slf4j.LoggerFactory;
50  
51  
52  /**
53   * To make the coding of commands as easy as possible the default values set in the config are set and the values of the
54   * context are set as properties too if the naming matches. To get a better performance we use an inner object pool. The
55   * execution of the command gets a clone from the pool and executes the clone
56   * @author Philipp Bracher
57   * @version $Revision: 32667 $ ($Author: gjoseph $)
58   */
59  public abstract class MgnlCommand implements Command {
60  
61      /**
62       * Logger
63       */
64      public static Logger log = LoggerFactory.getLogger(MgnlCommand.class);
65  
66  
67      /**
68       * The default properties. Lazy bound.
69       */
70      private Map defaultProperties;
71  
72      private boolean isClone = false;
73  
74      private boolean isEnabled = true;
75  
76      /**
77       * Create clones
78       * @author Philipp Bracher
79       * @version $Id: MgnlCommand.java 32667 2010-03-13 00:37:06Z gjoseph $
80       */
81      class MgnlCommandFactory extends BasePoolableObjectFactory {
82  
83          /**
84           * The prototype we clone for faster execution
85           */
86          private MgnlCommand prototype;
87  
88          /**
89           * @param prototype
90           */
91          public MgnlCommandFactory(MgnlCommand prototype) {
92              this.prototype = prototype;
93          }
94  
95          public Object makeObject() throws Exception {
96              MgnlCommand cmd = (MgnlCommand) BeanUtils.cloneBean(this.prototype);
97              cmd.setClone(true);
98              return cmd;
99          }
100 
101         public void activateObject(Object arg0) throws Exception {
102             super.activateObject(arg0);
103             // set default properties
104             BeanUtils.populate(arg0, defaultProperties);
105         }
106 
107         public void passivateObject(Object cmd) throws Exception {
108             ((MgnlCommand) cmd).release();
109             super.passivateObject(cmd);
110         }
111 
112     }
113 
114     /**
115      * Pool of inner commands
116      */
117     private StackObjectPool pool;
118 
119     /**
120      * True if we can use object pooling. Else we synchronize the execution.
121      */
122     private boolean pooling = true;
123 
124     /**
125      * Make sure that the context is castable to a magnolia context
126      */
127     public boolean execute(Context ctx) throws Exception {
128         if (!(ctx instanceof info.magnolia.context.Context)) {
129             throw new IllegalArgumentException("context must be of type " + info.magnolia.context.Context.class);
130         }
131 
132         if (this.defaultProperties == null) {
133             initDefaultProperties();
134         }
135 
136         MgnlCommand cmd;
137 
138         if (pooling) {
139             // do not instantiate until the pool is really needed
140             // means: do not create a pool for command objects created in the pool itself
141             if (pool == null) {
142                 pool = new StackObjectPool(new MgnlCommandFactory(this));
143             }
144 
145             try {
146                 // try to use the pool
147                 cmd = (MgnlCommand) pool.borrowObject();
148             }
149             // this happens if the commons constructor is not public: anonymous classes for example
150             catch (Throwable t) {
151                 pooling = false;
152                 // start again
153                 return execute(ctx);
154             }
155         }
156         else {
157             cmd = this;
158         }
159 
160         boolean success = executePooledOrSynchronized(ctx, cmd);
161         // convert the confusing true false behavior to fit commons chain
162         return !success;
163     }
164 
165     private boolean executePooledOrSynchronized(Context ctx, MgnlCommand cmd) throws Exception {
166         boolean success = false; // break execution
167 
168         // populate the command if we are using a pool
169         if (pooling) {
170             BeanUtils.populate(cmd, ctx);
171             // cast to mgnl context class
172             try {
173                 success = cmd.execute((info.magnolia.context.Context) ctx);
174             }
175             catch (Exception e) {
176                 AlertUtil.setException(e, (info.magnolia.context.Context) ctx);
177                 throw new NestableException("exception during executing command", e);
178             }
179             finally {
180                 pool.returnObject(cmd);
181             }
182         }
183         else {
184             synchronized (cmd) {
185                 BeanUtils.populate(cmd, ctx);
186                 try {
187                     success = cmd.execute((info.magnolia.context.Context) ctx);
188                 }
189                 catch (Exception e) {
190                     AlertUtil.setException(e, (info.magnolia.context.Context) ctx);
191                     throw new NestableException("exception during executing command", e);
192                 }
193                 finally {
194                     if (pooling) {
195                         pool.returnObject(cmd);
196                     }
197                     else {
198                         cmd.release();
199                         BeanUtils.populate(cmd, defaultProperties);
200                     }
201                 }
202             }
203         }
204         return success;
205     }
206 
207     private void initDefaultProperties() {
208         try {
209             this.defaultProperties = PropertyUtils.describe(this);
210         }
211         catch (Exception e) {
212             this.defaultProperties = Collections.EMPTY_MAP;
213         }
214     }
215 
216     public abstract boolean execute(info.magnolia.context.Context context) throws Exception;
217 
218     /**
219      * If a clone is passivated we call this method. Please clean up private properties.
220      */
221     public void release() {
222     }
223 
224     /**
225      * @return the isClone
226      */
227     protected boolean isClone() {
228         return isClone;
229     }
230 
231     /**
232      * @param isClone the isClone to set
233      */
234     protected void setClone(boolean isClone) {
235         this.isClone = isClone;
236     }
237 
238 
239     public boolean isEnabled() {
240         return this.isEnabled;
241     }
242 
243 
244     public void setEnabled(boolean isEnabled) {
245         this.isEnabled = isEnabled;
246     }
247 
248 }