001/* 002 * Copyright 2012 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.runtime.servlet; 017 018import java.io.File; 019import java.text.ParseException; 020import java.text.SimpleDateFormat; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Date; 024import java.util.HashMap; 025import java.util.Map; 026 027import org.apache.avalon.framework.configuration.Configuration; 028import org.apache.commons.lang3.StringUtils; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032import org.ametys.runtime.util.AmetysHomeHelper; 033 034/** 035 * Java representation of the WEB-INF/param/runtime.xml file.<br> 036 * Contains all runtime configuration values. 037 */ 038public final class RuntimeConfig 039{ 040 // shared instance 041 private static RuntimeConfig __config; 042 043 private boolean _safeMode; 044 045 private String _defaultWorkspace; 046 private String _initClass; 047 private final Collection<String> _pluginsLocations = new ArrayList<>(); 048 private final Collection<String> _excludedPlugins = new ArrayList<>(); 049 private final Collection<String> _excludedFeatures = new ArrayList<>(); 050 private final Collection<String> _excludedWorkspaces = new ArrayList<>(); 051 private final Map<String, String> _overridenThemes = new HashMap<>(); // Association workspace name - selected theme 052 private final Map<String, String> _components = new HashMap<>(); 053 054 private Logger _logger = LoggerFactory.getLogger(RuntimeConfig.class); 055 056 private String _contextPath; 057 058 private String _version; 059 private Date _buildDate; 060 061 /* External location of the kernel, if any */ 062 private File _externalKernel; 063 064 /* Locations of external plugins */ 065 private Map<String, File> _externalPlugins = new HashMap<>(); 066 067 /* Locations of external workspaces */ 068 private Map<String, File> _externalWorkspaces = new HashMap<>(); 069 070 private File _ametysHome; 071 072 private RuntimeConfig() 073 { 074 // empty constructor 075 } 076 077 /** 078 * Returns the shared instance of the <code>RuntimeConfig</code> 079 * @return the shared instance of the <code>RuntimeConfig</code> 080 */ 081 public static RuntimeConfig getInstance() 082 { 083 if (__config == null) 084 { 085 throw new IllegalStateException("RuntimeConfig has not been initialized."); 086 } 087 088 return __config; 089 } 090 091 /** 092 * Returns true if the Runtime has been configured (ie. if the {@link #configure(Configuration, Configuration, File, String)} method has been called. 093 * @return true if the Runtime has been configured. 094 */ 095 public static boolean isConfigured() 096 { 097 return __config != null; 098 } 099 100 /** 101 * Configures the Runtime kernel.<br> 102 * This method must be called <i>before</i> getting the RuntimConfig instance.<br><br> 103 * <b>Warning : the implementation allows this method to be called twice or more. This is only to allow the Runtime to be re-started dynamically.<br> 104 * Be aware that this can cause the application to become unstable.</b> 105 * @param runtimeConf the Configuration of the Runtime kernel (ie the contents of the WEB-INF/param/runtime.xml file) 106 * @param externalConf the Configuration of external locations (ie the contents of the WEB-INF/param/external-locations.xml file) 107 * @param ametysHome The ametys home directory 108 * @param contextPath the application context path 109 */ 110 public static synchronized void configure(Configuration runtimeConf, Configuration externalConf, File ametysHome, String contextPath) 111 { 112 __config = new RuntimeConfig(); 113 114 __config._contextPath = contextPath; 115 __config._ametysHome = ametysHome; 116 117 // create home directories 118 File dataHome = new File(ametysHome, AmetysHomeHelper.AMETYS_HOME_DATA_DIR); 119 dataHome.mkdirs(); 120 121 File configHome = new File(ametysHome, AmetysHomeHelper.AMETYS_HOME_CONFIG_DIR); 122 configHome.mkdirs(); 123 124 File tmpDir = new File(ametysHome, AmetysHomeHelper.AMETYS_HOME_TMP_DIR); 125 tmpDir.mkdirs(); 126 127 // runtimeConfig is null if the runtime.xml could not be read for any reason 128 if (runtimeConf != null) 129 { 130 __config._safeMode = false; 131 __config._initClass = runtimeConf.getChild("initClass").getValue(null); 132 133 __config._configureWorkspaces(runtimeConf.getChild("workspaces")); 134 __config._configurePlugins(runtimeConf.getChild("plugins")); 135 __config._configureComponents(runtimeConf.getChild("components")); 136 __config._configureApplication(runtimeConf.getChild("application")); 137 } 138 else 139 { 140 __config._safeMode = true; 141 __config._pluginsLocations.add("plugins/"); 142 } 143 144 if (externalConf != null) 145 { 146 __config._configureExternal(externalConf); 147 } 148 } 149 150 private void _configureWorkspaces(Configuration config) 151 { 152 _defaultWorkspace = config.getAttribute("default", null); 153 154 for (Configuration excluded : config.getChild("exclude").getChildren("workspace")) 155 { 156 String workspace = excluded.getValue(null); 157 158 if (workspace != null) 159 { 160 _excludedWorkspaces.add(workspace); 161 } 162 } 163 164 for (Configuration theme : config.getChild("theme").getChildren("workspace")) 165 { 166 String workspace = theme.getAttribute("name", null); 167 String newTheme = theme.getValue(null); 168 if (StringUtils.isNoneBlank(workspace, newTheme)) 169 { 170 _overridenThemes.put(workspace, newTheme); 171 } 172 } 173 } 174 175 private void _configurePlugins(Configuration config) 176 { 177 for (Configuration excluded : config.getChild("exclude").getChildren("plugin")) 178 { 179 String plugin = excluded.getValue(null); 180 181 if (plugin != null) 182 { 183 _excludedPlugins.add(plugin); 184 } 185 } 186 187 for (Configuration excluded : config.getChild("exclude").getChildren("feature")) 188 { 189 String feature = excluded.getValue(null); 190 191 if (feature != null) 192 { 193 _excludedFeatures.add(feature); 194 } 195 } 196 197 for (Configuration locationConf : config.getChild("locations").getChildren("location")) 198 { 199 String location = locationConf.getValue(null); 200 201 if (location != null) 202 { 203 _pluginsLocations.add(location); 204 } 205 } 206 207 // On ajoute aux emplacements de plugins le répertoire "plugins" 208 if (!_pluginsLocations.contains("plugins") && !_pluginsLocations.contains("plugins/")) 209 { 210 _pluginsLocations.add("plugins/"); 211 } 212 213 } 214 215 private void _configureComponents(Configuration config) 216 { 217 for (Configuration extension : config.getChildren()) 218 { 219 String point = extension.getName(); 220 String id = extension.getValue(null); 221 222 if (id != null) 223 { 224 __config._components.put(point, id); 225 } 226 } 227 } 228 229 private void _configureApplication(Configuration config) 230 { 231 String version = config.getChild("version").getValue(""); 232 if (!"@VERSION@".equals(version) && !"VERSION".equals(version)) 233 { 234 _version = version; 235 } 236 237 String strDate = config.getChild("date").getValue(null); 238 239 if (strDate != null && !"".equals(strDate) && !"@DATE@".equals(strDate) && !"DATE".equals(strDate)) 240 { 241 try 242 { 243 _buildDate = new SimpleDateFormat("yyyyMMdd'T'HHmm z").parse(strDate); 244 } 245 catch (ParseException e) 246 { 247 _logger.warn("Unable to parse date '" + strDate + "' with format \"yyyyMMdd'T'HHmm z\". It will be ignored."); 248 } 249 } 250 } 251 252 private void _configureExternal(Configuration config) 253 { 254 String externalKernel = config.getChild("kernel").getValue(null); 255 _externalKernel = _getFile(externalKernel); 256 257 for (Configuration pluginConf : config.getChild("plugins").getChildren("plugin")) 258 { 259 String name = pluginConf.getAttribute("name", null); 260 String location = pluginConf.getValue(null); 261 262 if (name != null && location != null) 263 { 264 _externalPlugins.put(name, _getFile(location)); 265 } 266 } 267 268 for (Configuration workspaceConf : config.getChild("workspaces").getChildren("workspace")) 269 { 270 String name = workspaceConf.getAttribute("name", null); 271 String location = workspaceConf.getValue(null); 272 273 if (location != null) 274 { 275 _externalWorkspaces.put(name, _getFile(location)); 276 } 277 } 278 } 279 280 /* 281 * Returns the corresponding file, either absolute or relative to the context path 282 */ 283 private File _getFile(String path) 284 { 285 File file = path == null ? null : new File(path); 286 File result = file == null ? null : file.isAbsolute() ? file : new File(_contextPath, path); 287 return result; 288 } 289 290 /** 291 * Returns true if safe mode is needed (ie. if there's been an error reading runtime.xml). 292 * @return true if safe mode is needed. 293 */ 294 public boolean isSafeMode() 295 { 296 return _safeMode; 297 } 298 299 /** 300 * Returns the name of the default workspace. Null if none. 301 * @return the name of the default workspace 302 */ 303 public String getDefaultWorkspace() 304 { 305 return _defaultWorkspace; 306 } 307 308 /** 309 * Returns the name of the class to be excuted at the end of the initialization process, if any.<br> 310 * May be null. 311 * @return Returns the name of the class to be excuted at the end of the initialization process, if any 312 */ 313 public String getInitClassName() 314 { 315 return _initClass; 316 } 317 318 /** 319 * Returns a Collection containing the locations of the plugins 320 * @return a Collection containing the locations of the plugins 321 */ 322 public Collection<String> getPluginsLocations() 323 { 324 return _pluginsLocations; 325 } 326 327 /** 328 * Returns the declared external plugins (ie. not located in the webapp context). 329 * @return the declared external plugins 330 */ 331 public Map<String, File> getExternalPlugins() 332 { 333 return _externalPlugins; 334 } 335 336 /** 337 * Returns the declared external workspaces (ie. not located in the webapp context). 338 * @return the declared external workspaces 339 */ 340 public Map<String, File> getExternalWorkspaces() 341 { 342 return _externalWorkspaces; 343 } 344 345 /** 346 * Returns the absolute external location of the kernel, if any.<br> 347 * Returns null if the kernel is not externalized. 348 * @return the absolute external location of the kernel, if any. 349 */ 350 public File getExternalKernel() 351 { 352 return _externalKernel; 353 } 354 355 /** 356 * Returns a Collection containing the names of the excluded (deactivated) plugins 357 * @return a Collection containing the names of the excluded (deactivated) plugins 358 */ 359 public Collection<String> getExcludedPlugins() 360 { 361 return _excludedPlugins; 362 } 363 364 /** 365 * Returns a Collection containing the names of the excluded (deactivated) features 366 * @return a Collection containing the names of the excluded (deactivated) features 367 */ 368 public Collection<String> getExcludedFeatures() 369 { 370 return _excludedFeatures; 371 } 372 373 /** 374 * Returns a Collection containing the names of the excluded (deactivated) workspaces 375 * @return a Collection containing the names of the excluded (deactivated) workspaces 376 */ 377 public Collection<String> getExcludedWorkspaces() 378 { 379 return _excludedWorkspaces; 380 } 381 382 /** 383 * Return a Map association a workspace name and its associated theme 384 * @return a Map association a workspace name and its associated theme 385 */ 386 public Map<String, String> getOverridenThemes() 387 { 388 return _overridenThemes; 389 } 390 391 /** 392 * Returns a Map<role, component id> containing the choosen implementation for the given component role 393 * @return a Map<role, component id> containing the choosen implementation for the given component role 394 */ 395 public Map<String, String> getComponents() 396 { 397 return _components; 398 } 399 400 /** 401 * Returns the application version name 402 * @return the application version name 403 */ 404 public String getApplicationVersion() 405 { 406 return _version; 407 } 408 409 /** 410 * Returns the application build date, if provided. May be null. 411 * @return the application build date. 412 */ 413 public Date getApplicationBuildDate() 414 { 415 return _buildDate; 416 } 417 418 /** 419 * Returns the Ametys home directory. Cannot be null. 420 * @return The Ametys home directory. 421 */ 422 public File getAmetysHome() 423 { 424 return _ametysHome; 425 } 426}