Coverage Report - org.apache.turbine.services.ui.TurbineUIService
 
Classes in this File Line Coverage Branch Coverage Complexity
TurbineUIService
45%
42/92
17%
5/28
0
TurbineUIService$1
0%
0/3
N/A
0
 
 1  
 package org.apache.turbine.services.ui;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with 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,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import java.io.File;
 23  
 import java.io.FilenameFilter;
 24  
 import java.io.InputStream;
 25  
 import java.util.Properties;
 26  
 import java.util.concurrent.ConcurrentHashMap;
 27  
 
 28  
 import org.apache.commons.configuration2.Configuration;
 29  
 import org.apache.commons.lang3.StringUtils;
 30  
 import org.apache.logging.log4j.LogManager;
 31  
 import org.apache.logging.log4j.Logger;
 32  
 import org.apache.turbine.Turbine;
 33  
 import org.apache.turbine.services.InitializationException;
 34  
 import org.apache.turbine.services.TurbineBaseService;
 35  
 import org.apache.turbine.services.TurbineServices;
 36  
 import org.apache.turbine.services.pull.PullService;
 37  
 import org.apache.turbine.services.pull.tools.UITool;
 38  
 import org.apache.turbine.services.servlet.ServletService;
 39  
 import org.apache.turbine.util.ServerData;
 40  
 import org.apache.turbine.util.uri.DataURI;
 41  
 
 42  
 /**
 43  
  * The UI service provides for shared access to User Interface (skin) files,
 44  
  * as well as the ability for non-default skin files to inherit properties from
 45  
  * a default skin.  Use TurbineUI to access skin properties from your screen
 46  
  * classes and action code. UITool is provided as a pull tool for accessing
 47  
  * skin properties from your templates.
 48  
  *
 49  
  * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
 50  
  * @author <a href="mailto:james_coltman@majorband.co.uk">James Coltman</a>
 51  
  * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
 52  
  * @author <a href="mailto:seade@backstagetech.com.au">Scott Eade</a>
 53  
  * @author <a href="thomas.vandahl@tewisoft.de">Thomas Vandahl</a>
 54  
  * @version $Id$
 55  
  * @see UIService
 56  
  * @see UITool
 57  
  */
 58  54
 public class TurbineUIService
 59  
         extends TurbineBaseService
 60  
         implements UIService
 61  
 {
 62  
     /** Logging. */
 63  54
     private static final Logger log = LogManager.getLogger(TurbineUIService.class);
 64  
 
 65  
     /**
 66  
      * The location of the skins within the application resources directory.
 67  
      */
 68  
     private static final String SKINS_DIRECTORY = "/ui/skins";
 69  
 
 70  
     /**
 71  
      * The name of the directory where images are stored for this skin.
 72  
      */
 73  
     private static final String IMAGES_DIRECTORY = "/images";
 74  
 
 75  
     /**
 76  
      * Property tag for the default skin that is to be used for the web
 77  
      * application.
 78  
      */
 79  
     private static final String SKIN_PROPERTY = "tool.ui.skin";
 80  
 
 81  
     /**
 82  
      * Property tag for the image directory inside the skin that is to be used
 83  
      * for the web application.
 84  
      */
 85  
     private static final String IMAGEDIR_PROPERTY = "tool.ui.dir.image";
 86  
 
 87  
     /**
 88  
      * Property tag for the skin directory that is to be used for the web
 89  
      * application.
 90  
      */
 91  
     private static final String SKINDIR_PROPERTY = "tool.ui.dir.skin";
 92  
 
 93  
     /**
 94  
      * Property tag for the css file that is to be used for the web application.
 95  
      */
 96  
     private static final String CSS_PROPERTY = "tool.ui.css";
 97  
 
 98  
     /**
 99  
      * Property tag for indicating if relative links are wanted for the web
 100  
      * application.
 101  
      */
 102  
     private static final String RELATIVE_PROPERTY = "tool.ui.want.relative";
 103  
 
 104  
     /**
 105  
      * Default skin name. This name refers to a directory in the
 106  
      * WEBAPP/resources/ui/skins directory. There is a file called skin.props
 107  
      * which contains the name/value pairs to be made available via the skin.
 108  
      */
 109  
     public static final String SKIN_PROPERTY_DEFAULT = "default";
 110  
 
 111  
     /**
 112  
      * The skins directory, qualified by the resources directory (which is
 113  
      * relative to the webapp context). This is used for constructing URIs and
 114  
      * for retrieving skin files.
 115  
      */
 116  
     private String skinsDirectory;
 117  
 
 118  
     /**
 119  
      * The file within the skin directory that contains the name/value pairs for
 120  
      * the skin.
 121  
      */
 122  
     private static final String SKIN_PROPS_FILE = "skin.props";
 123  
 
 124  
     /**
 125  
      * The file name for the skin style sheet.
 126  
      */
 127  
     private static final String DEFAULT_SKIN_CSS_FILE = "skin.css";
 128  
 
 129  
     /**
 130  
      * The servlet service.
 131  
      */
 132  
     private ServletService servletService;
 133  
 
 134  
     /**
 135  
      * The directory within the skin directory that contains the skin images.
 136  
      */
 137  
     private String imagesDirectory;
 138  
 
 139  
     /**
 140  
      * The name of the css file within the skin directory.
 141  
      */
 142  
     private String cssFile;
 143  
 
 144  
     /**
 145  
      * The flag that determines if the links that are returned are are absolute
 146  
      * or relative.
 147  
      */
 148  54
     private boolean wantRelative = false;
 149  
 
 150  
     /**
 151  
      * The skin Properties store.
 152  
      */
 153  54
     private ConcurrentHashMap<String, Properties> skins = new ConcurrentHashMap<String, Properties>();
 154  
 
 155  
     /**
 156  
      * Refresh the service by clearing all skins.
 157  
      */
 158  
     @Override
 159  
     public void refresh()
 160  
     {
 161  0
         clearSkins();
 162  0
     }
 163  
 
 164  
     /**
 165  
      * Refresh a particular skin by clearing it.
 166  
      *
 167  
      * @param skinName the name of the skin to clear.
 168  
      */
 169  
     @Override
 170  
     public void refresh(String skinName)
 171  
     {
 172  30
         clearSkin(skinName);
 173  30
     }
 174  
 
 175  
     /**
 176  
      * Retrieve the Properties for a specific skin.  If they are not yet loaded
 177  
      * they will be.  If the specified skin does not exist properties for the
 178  
      * default skin configured for the webapp will be returned and an error
 179  
      * level message will be written to the log.  If the webapp skin does not
 180  
      * exist the default skin will be used and id that doesn't exist an empty
 181  
      * Properties will be returned.
 182  
      *
 183  
      * @param skinName the name of the skin whose properties are to be
 184  
      * retrieved.
 185  
      * @return the Properties for the named skin or the properties for the
 186  
      * default skin configured for the webapp if the named skin does not exist.
 187  
      */
 188  
     private Properties getSkinProperties(String skinName)
 189  
     {
 190  0
         Properties skinProperties = skins.get(skinName);
 191  0
         return null != skinProperties ? skinProperties : loadSkin(skinName);
 192  
     }
 193  
 
 194  
     /**
 195  
      * Retrieve a skin property from the named skin.  If the property is not
 196  
      * defined in the named skin the value for the default skin will be
 197  
      * provided.  If the named skin does not exist then the skin configured for
 198  
      * the webapp will be used.  If the webapp skin does not exist the default
 199  
      * skin will be used.  If the default skin does not exist then
 200  
      * <code>null</code> will be returned.
 201  
      *
 202  
      * @param skinName the name of the skin to retrieve the property from.
 203  
      * @param key the key to retrieve from the skin.
 204  
      * @return the value of the property for the named skin (defaulting to the
 205  
      * default skin), the webapp skin, the default skin or <code>null</code>,
 206  
      * depending on whether or not the property or skins exist.
 207  
      */
 208  
     @Override
 209  
     public String get(String skinName, String key)
 210  
     {
 211  0
         Properties skinProperties = getSkinProperties(skinName);
 212  0
         return skinProperties.getProperty(key);
 213  
     }
 214  
 
 215  
     /**
 216  
      * Retrieve a skin property from the default skin for the webapp.  If the
 217  
      * property is not defined in the webapp skin the value for the default skin
 218  
      * will be provided.  If the webapp skin does not exist the default skin
 219  
      * will be used.  If the default skin does not exist then <code>null</code>
 220  
      * will be returned.
 221  
      *
 222  
      * @param key the key to retrieve.
 223  
      * @return the value of the property for the webapp skin (defaulting to the
 224  
      * default skin), the default skin or <code>null</code>, depending on
 225  
      * whether or not the property or skins exist.
 226  
      */
 227  
     @Override
 228  
     public String get(String key)
 229  
     {
 230  0
         return get(getWebappSkinName(), key);
 231  
     }
 232  
 
 233  
     /**
 234  
      * Provide access to the list of available skin names.
 235  
      *
 236  
      * @return the available skin names.
 237  
      */
 238  
     @Override
 239  
     public String[] getSkinNames()
 240  
     {
 241  0
         File skinsDir = new File(servletService.getRealPath(skinsDirectory));
 242  0
         return skinsDir.list(new FilenameFilter()
 243  0
         {
 244  
             @Override
 245  
             public boolean accept(File dir, String name)
 246  
             {
 247  0
                 File directory = new File(dir, name);
 248  0
                 return directory.isDirectory();
 249  
             }
 250  
         });
 251  
     }
 252  
 
 253  
     /**
 254  
      * Clear the map of stored skins.
 255  
      */
 256  
     private void clearSkins()
 257  
     {
 258  60
         skins.clear();
 259  60
         log.debug("All skins were cleared.");
 260  60
     }
 261  
 
 262  
     /**
 263  
      * Clear a particular skin from the map of stored skins.
 264  
      *
 265  
      * @param skinName the name of the skin to clear.
 266  
      */
 267  
     private void clearSkin(String skinName)
 268  
     {
 269  30
         if (!skinName.equals(SKIN_PROPERTY_DEFAULT))
 270  
         {
 271  30
             skins.remove(SKIN_PROPERTY_DEFAULT);
 272  
         }
 273  30
         skins.remove(skinName);
 274  30
         log.debug("The skin \"{}\" was cleared (will also clear \"default\" skin).", skinName);
 275  30
     }
 276  
 
 277  
     /**
 278  
      * Load the specified skin.
 279  
      *
 280  
      * @param skinName the name of the skin to load.
 281  
      * @return the Properties for the named skin if it exists, or the skin
 282  
      * configured for the web application if it does not exist, or the default
 283  
      * skin if that does not exist, or an empty Parameters object if even that
 284  
      * cannot be found.
 285  
      */
 286  
     private Properties loadSkin(String skinName)
 287  
     {
 288  0
         Properties defaultSkinProperties = null;
 289  
 
 290  0
         if (!StringUtils.equals(skinName, SKIN_PROPERTY_DEFAULT))
 291  
         {
 292  0
             defaultSkinProperties = getSkinProperties(SKIN_PROPERTY_DEFAULT);
 293  
         }
 294  
 
 295  
         // The following line is okay even for default.
 296  0
         Properties skinProperties = new Properties(defaultSkinProperties);
 297  
 
 298  0
         StringBuilder sb = new StringBuilder();
 299  0
         sb.append('/').append(skinsDirectory);
 300  0
         sb.append('/').append(skinName);
 301  0
         sb.append('/').append(SKIN_PROPS_FILE);
 302  0
         log.debug("Loading selected skin from: {}", sb::toString);
 303  
 
 304  0
         try (InputStream is = servletService.getResourceAsStream(sb.toString()))
 305  
         {
 306  
             // This will NPE if the directory associated with the skin does not
 307  
             // exist, but it is handled correctly below.
 308  0
             skinProperties.load(is);
 309  0
         }
 310  0
         catch (Exception e)
 311  
         {
 312  0
             log.error("Cannot load skin: {}, from: {}", skinName, sb.toString(), e);
 313  0
             if (!StringUtils.equals(skinName, getWebappSkinName())
 314  0
                     && !StringUtils.equals(skinName, SKIN_PROPERTY_DEFAULT))
 315  
             {
 316  0
                 log.error("Attempting to return the skin configured for webapp instead of {}", skinName);
 317  0
                 return getSkinProperties(getWebappSkinName());
 318  
             }
 319  0
             else if (!StringUtils.equals(skinName, SKIN_PROPERTY_DEFAULT))
 320  
             {
 321  0
                 log.error("Return the default skin instead of {}", skinName);
 322  0
                 return skinProperties; // Already contains the default skin.
 323  
             }
 324  
             else
 325  
             {
 326  0
                 log.error("No skins available - returning an empty Properties");
 327  0
                 return new Properties();
 328  
             }
 329  0
         }
 330  
 
 331  
         // Replace in skins HashMap
 332  0
         skins.put(skinName, skinProperties);
 333  
 
 334  0
         return skinProperties;
 335  
     }
 336  
 
 337  
     /**
 338  
      * Get the name of the default skin name for the web application from the
 339  
      * TurbineResources.properties file. If the property is not present the
 340  
      * name of the default skin will be returned.  Note that the web application
 341  
      * skin name may be something other than default, in which case its
 342  
      * properties will default to the skin with the name "default".
 343  
      *
 344  
      * @return the name of the default skin for the web application.
 345  
      */
 346  
     @Override
 347  
     public String getWebappSkinName()
 348  
     {
 349  120
         return Turbine.getConfiguration()
 350  60
                 .getString(SKIN_PROPERTY, SKIN_PROPERTY_DEFAULT);
 351  
     }
 352  
 
 353  
     /**
 354  
      * Retrieve the URL for an image that is part of a skin. The images are
 355  
      * stored in the WEBAPP/resources/ui/skins/[SKIN]/images directory.
 356  
      *
 357  
      * <p>Use this if for some reason your server name, server scheme, or server
 358  
      * port change on a per request basis. I'm not sure if this would happen in
 359  
      * a load balanced situation. I think in most cases the image(String image)
 360  
      * method would probably be enough, but I'm not absolutely positive.
 361  
      *
 362  
      * @param skinName the name of the skin to retrieve the image from.
 363  
      * @param imageId the id of the image whose URL will be generated.
 364  
      * @param serverData the serverData to use as the basis for the URL.
 365  
      */
 366  
     @Override
 367  
     public String image(String skinName, String imageId, ServerData serverData)
 368  
     {
 369  0
         return getSkinResource(serverData, skinName, imagesDirectory, imageId);
 370  
     }
 371  
 
 372  
     /**
 373  
      * Retrieve the URL for an image that is part of a skin. The images are
 374  
      * stored in the WEBAPP/resources/ui/skins/[SKIN]/images directory.
 375  
      *
 376  
      * @param skinName the name of the skin to retrieve the image from.
 377  
      * @param imageId the id of the image whose URL will be generated.
 378  
      */
 379  
     @Override
 380  
     public String image(String skinName, String imageId)
 381  
     {
 382  0
         return image(skinName, imageId, Turbine.getDefaultServerData());
 383  
     }
 384  
 
 385  
     /**
 386  
      * Retrieve the URL for the style sheet that is part of a skin. The style is
 387  
      * stored in the WEBAPP/resources/ui/skins/[SKIN] directory with the
 388  
      * filename skin.css
 389  
      *
 390  
      * <p>Use this if for some reason your server name, server scheme, or server
 391  
      * port change on a per request basis. I'm not sure if this would happen in
 392  
      * a load balanced situation. I think in most cases the style() method would
 393  
      * probably be enough, but I'm not absolutely positive.
 394  
      *
 395  
      * @param skinName the name of the skin to retrieve the style sheet from.
 396  
      * @param serverData the serverData to use as the basis for the URL.
 397  
      */
 398  
     @Override
 399  
     public String getStylecss(String skinName, ServerData serverData)
 400  
     {
 401  0
         return getSkinResource(serverData, skinName, null, cssFile);
 402  
     }
 403  
 
 404  
     /**
 405  
      * Retrieve the URL for the style sheet that is part of a skin. The style is
 406  
      * stored in the WEBAPP/resources/ui/skins/[SKIN] directory with the
 407  
      * filename skin.css
 408  
      *
 409  
      * @param skinName the name of the skin to retrieve the style sheet from.
 410  
      */
 411  
     @Override
 412  
     public String getStylecss(String skinName)
 413  
     {
 414  0
         return getStylecss(skinName, Turbine.getDefaultServerData());
 415  
     }
 416  
 
 417  
     /**
 418  
      * Retrieve the URL for a given script that is part of a skin. The script is
 419  
      * stored in the WEBAPP/resources/ui/skins/[SKIN] directory.
 420  
      *
 421  
      * <p>Use this if for some reason your server name, server scheme, or server
 422  
      * port change on a per request basis. I'm not sure if this would happen in
 423  
      * a load balanced situation. I think in most cases the style() method would
 424  
      * probably be enough, but I'm not absolutely positive.
 425  
      *
 426  
      * @param skinName the name of the skin to retrieve the image from.
 427  
      * @param filename the name of the script file.
 428  
      * @param serverData the serverData to use as the basis for the URL.
 429  
      */
 430  
     @Override
 431  
     public String getScript(String skinName, String filename,
 432  
             ServerData serverData)
 433  
     {
 434  0
         return getSkinResource(serverData, skinName, null, filename);
 435  
     }
 436  
 
 437  
     /**
 438  
      * Retrieve the URL for a given script that is part of a skin. The script is
 439  
      * stored in the WEBAPP/resources/ui/skins/[SKIN] directory.
 440  
      *
 441  
      * @param skinName the name of the skin to retrieve the image from.
 442  
      * @param filename the name of the script file.
 443  
      */
 444  
     @Override
 445  
     public String getScript(String skinName, String filename)
 446  
     {
 447  0
         return getScript(skinName, filename, Turbine.getDefaultServerData());
 448  
     }
 449  
 
 450  
     private String stripSlashes(final String path)
 451  
     {
 452  180
         if (StringUtils.isEmpty(path))
 453  
         {
 454  0
             return "";
 455  
         }
 456  
 
 457  180
         String ret = path;
 458  180
         int len = ret.length() - 1;
 459  
 
 460  180
         if (ret.charAt(len) == '/')
 461  
         {
 462  180
             ret = ret.substring(0, len);
 463  
         }
 464  
 
 465  180
         if (len > 0 && ret.charAt(0) == '/')
 466  
         {
 467  180
             ret = ret.substring(1);
 468  
         }
 469  
 
 470  180
         return ret;
 471  
     }
 472  
 
 473  
     /**
 474  
      * Construct the URL to the skin resource.
 475  
      *
 476  
      * @param serverData the serverData to use as the basis for the URL.
 477  
      * @param skinName the name of the skin.
 478  
      * @param subDir the sub-directory in which the resource resides or
 479  
      * <code>null</code> if it is in the root directory of the skin.
 480  
      * @param resourceName the name of the resource to be retrieved.
 481  
      * @return the path to the resource.
 482  
      */
 483  
     private String getSkinResource(ServerData serverData, String skinName,
 484  
             String subDir, String resourceName)
 485  
     {
 486  0
         StringBuilder sb = new StringBuilder(skinsDirectory);
 487  0
         sb.append("/").append(skinName);
 488  0
         if (subDir != null)
 489  
         {
 490  0
             sb.append("/").append(subDir);
 491  
         }
 492  0
         sb.append("/").append(stripSlashes(resourceName));
 493  
 
 494  0
         DataURI du = new DataURI(serverData);
 495  0
         du.setScriptName(sb.toString());
 496  0
         return wantRelative ? du.getRelativeLink() : du.getAbsoluteLink();
 497  
     }
 498  
 
 499  
     // ---- Service initilization ------------------------------------------
 500  
 
 501  
     /**
 502  
      * Initializes the service.
 503  
      */
 504  
     @Override
 505  
     public void init() throws InitializationException
 506  
     {
 507  60
         Configuration cfg = Turbine.getConfiguration();
 508  
 
 509  60
         servletService = (ServletService)TurbineServices.getInstance().getService(ServletService.SERVICE_NAME);
 510  60
         PullService pullService = (PullService)TurbineServices.getInstance().getService(PullService.SERVICE_NAME);
 511  
         // Get the resources directory that is specified in the TR.props or
 512  
         // default to "resources", relative to the webapp.
 513  60
         StringBuilder sb = new StringBuilder();
 514  60
         sb.append(stripSlashes(pullService.getResourcesDirectory()));
 515  60
         sb.append("/");
 516  120
         sb.append(stripSlashes(
 517  60
                 cfg.getString(SKINDIR_PROPERTY, SKINS_DIRECTORY)));
 518  60
         skinsDirectory = sb.toString();
 519  
 
 520  120
         imagesDirectory = stripSlashes(
 521  60
                 cfg.getString(IMAGEDIR_PROPERTY, IMAGES_DIRECTORY));
 522  60
         cssFile = cfg.getString(CSS_PROPERTY, DEFAULT_SKIN_CSS_FILE);
 523  60
         wantRelative = cfg.getBoolean(RELATIVE_PROPERTY, false);
 524  
 
 525  60
         setInit(true);
 526  60
     }
 527  
 
 528  
     /**
 529  
      * Returns to uninitialized state.
 530  
      */
 531  
     @Override
 532  
     public void shutdown()
 533  
     {
 534  60
         clearSkins();
 535  60
         setInit(false);
 536  60
     }
 537  
 }