001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.jlan; 029 030import org.opencms.configuration.CmsConfigurationException; 031import org.opencms.configuration.CmsParameterConfiguration; 032import org.opencms.file.CmsObject; 033import org.opencms.file.CmsProject; 034import org.opencms.file.wrapper.CmsObjectWrapper; 035import org.opencms.file.wrapper.CmsSilentWrapperException; 036import org.opencms.file.wrapper.I_CmsResourceWrapper; 037import org.opencms.main.CmsContextInfo; 038import org.opencms.main.CmsException; 039import org.opencms.main.CmsLog; 040import org.opencms.main.CmsShell; 041import org.opencms.main.OpenCms; 042import org.opencms.repository.CmsRepositoryFilter; 043import org.opencms.repository.CmsRepositoryManager; 044import org.opencms.repository.I_CmsRepository; 045import org.opencms.util.CmsResourceTranslator; 046import org.opencms.util.CmsStringUtil; 047import org.opencms.xml.content.CmsXmlContent; 048 049import java.lang.reflect.InvocationHandler; 050import java.lang.reflect.InvocationTargetException; 051import java.lang.reflect.Method; 052import java.lang.reflect.Proxy; 053import java.util.ArrayList; 054import java.util.Collections; 055import java.util.List; 056 057import org.apache.commons.logging.Log; 058 059import org.alfresco.jlan.server.SrvSession; 060import org.alfresco.jlan.server.filesys.DiskDeviceContext; 061import org.alfresco.jlan.server.filesys.DiskInterface; 062import org.alfresco.jlan.server.filesys.DiskSharedDevice; 063import org.alfresco.jlan.server.filesys.TreeConnection; 064 065import com.google.common.collect.Lists; 066 067/** 068 * Repository class for configuring repositories for Alfresco JLAN.<p> 069 */ 070public class CmsJlanRepository implements I_CmsRepository { 071 072 /** Request context attribute to control error handling for write errors. */ 073 public static final String JLAN_IGNORE_WRITE_ERRORS = "jlan.ignoreWriteErrors"; 074 075 /** Parameter for controlling whether byte order marks should be added to plaintext files. */ 076 public static final String PARAM_ADD_BOM = "addBOM"; 077 078 /** Parameter that controls whether to ignore file write errors. */ 079 public static final String PARAM_IGNORE_WRITE_ERRORS = "ignoreWriteErrors"; 080 081 /** The parameter for the project in which this repository should operate. */ 082 public static final String PARAM_PROJECT = "project"; 083 084 /** Name of the parameter to configure the root directory. */ 085 public static final String PARAM_ROOT = "root"; 086 087 /** Name of the parameter to configure resource wrappers. */ 088 public static final String PARAM_WRAPPER = "wrapper"; 089 090 /** The logger instance for this class. */ 091 private static final Log LOG = CmsLog.getLog(CmsJlanRepository.class); 092 093 /** Flag which controls whether the CmsObjectWrapper should add byte order marks for plain files. */ 094 private boolean m_addByteOrderMark; 095 096 /** The CMS context. */ 097 private CmsObject m_cms; 098 099 /** The configuration for this repository. */ 100 private CmsParameterConfiguration m_configuration = new CmsParameterConfiguration(); 101 102 /** The shared disk device. */ 103 private DiskSharedDevice m_device; 104 105 /** The JLAN device context for this repository. */ 106 private DiskDeviceContext m_deviceContext; 107 108 /** The JLAN disk interface for this repository. */ 109 private DiskInterface m_diskInterface; 110 111 /** Flag which controls whether write errors should be ignored. */ 112 private boolean m_ignoreWriteErrors; 113 114 /** The name of the repository. */ 115 private String m_name; 116 117 /** The disk interface. */ 118 private CmsJlanDiskInterface m_originalDiskInterface; 119 120 /** The configured project. */ 121 private CmsProject m_project; 122 123 /** The name of the configured project. */ 124 private String m_projectName; 125 126 /** The root VFS directory of the repository. */ 127 private String m_root; 128 129 /** The list of wrappers configured for this repository. */ 130 private List<I_CmsResourceWrapper> m_wrappers = Lists.newArrayList(); 131 132 /** 133 * Creates a new repository instance.<p> 134 */ 135 public CmsJlanRepository() { 136 137 m_deviceContext = new CmsJlanDeviceContext(this); 138 m_deviceContext.enableChangeHandler(true); 139 m_deviceContext.setFileServerNotifications(true); 140 m_originalDiskInterface = new CmsJlanDiskInterface(); 141 m_diskInterface = createLoggingProxy(m_originalDiskInterface); 142 } 143 144 /** 145 * Creates a dynamic proxy for a disk interface which logs the method calls and their results.<p> 146 * 147 * @param impl the disk interface for which a logging proxy should be created 148 * 149 * @return the dynamic proxy which logs methods calls 150 */ 151 public static DiskInterface createLoggingProxy(final DiskInterface impl) { 152 153 return (DiskInterface)Proxy.newProxyInstance( 154 Thread.currentThread().getContextClassLoader(), 155 new Class[] {DiskInterface.class}, 156 new InvocationHandler() { 157 158 @SuppressWarnings("synthetic-access") 159 public Object invoke(Object target, Method method, Object[] params) throws Throwable { 160 161 // Just to be on the safe side performance-wise, we only log the parameters/result 162 // if the info channel is enabled 163 if (LOG.isInfoEnabled()) { 164 List<String> paramStrings = new ArrayList<String>(); 165 for (Object param : params) { 166 paramStrings.add("" + param); 167 } 168 String paramsAsString = CmsStringUtil.listAsString(paramStrings, ", "); 169 LOG.info("Call: " + method.getName() + " " + paramsAsString); 170 } 171 try { 172 Object result = method.invoke(impl, params); 173 if (LOG.isInfoEnabled()) { 174 LOG.info("Returned from " + method.getName() + ": " + result); 175 } 176 return result; 177 } catch (InvocationTargetException e) { 178 Throwable cause = e.getCause(); 179 if ((cause != null) && (cause instanceof CmsSilentWrapperException)) { 180 // not really an error 181 LOG.info(cause.getCause().getLocalizedMessage(), cause.getCause()); 182 } else { 183 LOG.error(e.getLocalizedMessage(), e); 184 } 185 throw e.getCause(); 186 } 187 } 188 }); 189 } 190 191 /** 192 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#addConfigurationParameter(java.lang.String, java.lang.String) 193 */ 194 public void addConfigurationParameter(String paramName, String paramValue) { 195 196 m_configuration.add(paramName, paramValue); 197 198 } 199 200 /** 201 * Checks if a user may access this repository.<p> 202 * 203 * @param user the name of the user 204 * 205 * @return true if the user may access the repository 206 */ 207 public boolean allowAccess(String user) { 208 209 try { 210 return m_cms.getPermissions(m_root, user).requiresViewPermission(); 211 } catch (CmsException e) { 212 LOG.error(e.getLocalizedMessage(), e); 213 return true; 214 } 215 } 216 217 /** 218 * Creates a CmsObjectWrapper for the current session.<p> 219 * 220 * @param session the current session 221 * @param connection the tree connection 222 * 223 * @return the correctly configured CmsObjectWrapper for this session 224 * 225 * @throws CmsException if something goes wrong 226 */ 227 public CmsObjectWrapper getCms(SrvSession session, TreeConnection connection) throws CmsException { 228 229 String userName = session.getClientInformation().getUserName(); 230 userName = CmsJlanUsers.translateUser(userName); 231 CmsContextInfo contextInfo = new CmsContextInfo(m_cms.getRequestContext()); 232 contextInfo.setUserName(userName); 233 CmsObject newCms = OpenCms.initCmsObject(m_cms, contextInfo); 234 newCms.getRequestContext().setSiteRoot(getRoot()); 235 newCms.getRequestContext().setCurrentProject(getProject()); 236 CmsObjectWrapper result = new CmsObjectWrapper(newCms, getWrappers()); 237 result.setAddByteOrderMark(m_addByteOrderMark); 238 result.getRequestContext().setAttribute(CmsXmlContent.AUTO_CORRECTION_ATTRIBUTE, Boolean.TRUE); 239 result.getRequestContext().setAttribute(JLAN_IGNORE_WRITE_ERRORS, Boolean.valueOf(m_ignoreWriteErrors)); 240 return result; 241 } 242 243 /** 244 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#getConfiguration() 245 */ 246 public CmsParameterConfiguration getConfiguration() { 247 248 return m_configuration; 249 } 250 251 /** 252 * Gets the device context for this repository.<p> 253 * 254 * @return the device context 255 */ 256 public DiskDeviceContext getDeviceContext() { 257 258 return m_deviceContext; 259 } 260 261 /** 262 * Gets the disk interface for this repository.<p> 263 * 264 * @return the disk interface 265 */ 266 public DiskInterface getDiskInterface() { 267 268 return m_diskInterface; 269 } 270 271 /** 272 * @see org.opencms.repository.I_CmsRepository#getFilter() 273 */ 274 public CmsRepositoryFilter getFilter() { 275 276 return null; 277 } 278 279 /** 280 * @see org.opencms.repository.I_CmsRepository#getName() 281 */ 282 public String getName() { 283 284 return m_name; 285 } 286 287 /** 288 * Gets the configured project.<p> 289 * 290 * @return the configured project 291 */ 292 public CmsProject getProject() { 293 294 return m_project; 295 } 296 297 /** 298 * Gets the root directory configured for this repository.<p> 299 * 300 * @return the root directory 301 */ 302 public String getRoot() { 303 304 return m_root; 305 } 306 307 /** 308 * @see org.opencms.repository.I_CmsRepository#getTranslation() 309 */ 310 public CmsResourceTranslator getTranslation() { 311 312 return null; 313 } 314 315 /** 316 * Gets the resource wrappers which have been configured for this repository.<p> 317 * 318 * @return the resource wrappers which have been configured 319 */ 320 public List<I_CmsResourceWrapper> getWrappers() { 321 322 return m_wrappers; 323 } 324 325 /** 326 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration() 327 */ 328 public void initConfiguration() throws CmsConfigurationException { 329 330 List<I_CmsResourceWrapper> wrapperObjects = CmsRepositoryManager.createResourceWrappersFromConfiguration( 331 getConfiguration(), 332 PARAM_WRAPPER, 333 LOG); 334 m_wrappers = Collections.unmodifiableList(wrapperObjects); 335 m_root = getConfiguration().getString(PARAM_ROOT, "").trim(); 336 m_projectName = getConfiguration().getString(PARAM_PROJECT, "Offline").trim(); 337 String addByteOrderMarkStr = getConfiguration().getString(PARAM_ADD_BOM, "" + true).trim(); 338 m_addByteOrderMark = Boolean.parseBoolean(addByteOrderMarkStr); 339 m_ignoreWriteErrors = Boolean.parseBoolean(getConfiguration().getString(PARAM_IGNORE_WRITE_ERRORS, "false")); 340 } 341 342 /** 343 * @see org.opencms.repository.I_CmsRepository#initializeCms(org.opencms.file.CmsObject) 344 */ 345 public void initializeCms(CmsObject cms) throws CmsException { 346 347 m_cms = cms; 348 if (CmsShell.isJlanDisabled()) { 349 return; 350 } 351 m_project = m_cms.readProject(m_projectName); 352 m_device = new DiskSharedDevice(getName(), getDiskInterface(), getDeviceContext(), 0); 353 m_device.addAccessControl(new CmsRepositoryAccessControl(this)); 354 } 355 356 /** 357 * Returns true if file write errors should be ignored. 358 * 359 * @return true if file write errors should be ignored 360 */ 361 public boolean isIgnoreWriteFileErrors() { 362 363 return m_ignoreWriteErrors; 364 } 365 366 /** 367 * @see org.opencms.repository.I_CmsRepository#isTranslationEnabled() 368 */ 369 public boolean isTranslationEnabled() { 370 371 return false; 372 } 373 374 /** 375 * @see org.opencms.repository.I_CmsRepository#setFilter(org.opencms.repository.CmsRepositoryFilter) 376 */ 377 public void setFilter(CmsRepositoryFilter filter) { 378 379 // do nothing 380 } 381 382 /** 383 * @see org.opencms.repository.I_CmsRepository#setName(java.lang.String) 384 */ 385 public void setName(String name) { 386 387 // case sensitive share names don't work 388 m_name = name.toUpperCase(); 389 } 390 391 /** 392 * @see org.opencms.repository.I_CmsRepository#setTranslation(org.opencms.util.CmsResourceTranslator, boolean) 393 */ 394 public void setTranslation(CmsResourceTranslator translator, boolean enabled) { 395 396 throw new UnsupportedOperationException("file translations not supported for JLAN repositories"); 397 } 398 399 /** 400 * Gets the shared device for this repository.<p> 401 * 402 * @return the shared device 403 */ 404 DiskSharedDevice getSharedDevice() { 405 406 return m_device; 407 } 408 409}