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.CmsStringUtil; 046import org.opencms.xml.content.CmsXmlContent; 047 048import java.lang.reflect.InvocationHandler; 049import java.lang.reflect.InvocationTargetException; 050import java.lang.reflect.Method; 051import java.lang.reflect.Proxy; 052import java.util.ArrayList; 053import java.util.Collections; 054import java.util.List; 055 056import org.apache.commons.logging.Log; 057 058import org.alfresco.jlan.server.SrvSession; 059import org.alfresco.jlan.server.filesys.DiskDeviceContext; 060import org.alfresco.jlan.server.filesys.DiskInterface; 061import org.alfresco.jlan.server.filesys.DiskSharedDevice; 062import org.alfresco.jlan.server.filesys.TreeConnection; 063 064import com.google.common.collect.Lists; 065 066/** 067 * Repository class for configuring repositories for Alfresco JLAN.<p> 068 */ 069public class CmsJlanRepository implements I_CmsRepository { 070 071 /** Request context attribute to control error handling for write errors. */ 072 public static final String JLAN_IGNORE_WRITE_ERRORS = "jlan.ignoreWriteErrors"; 073 074 /** Parameter for controlling whether byte order marks should be added to plaintext files. */ 075 public static final String PARAM_ADD_BOM = "addBOM"; 076 077 /** Parameter that controls whether to ignore file write errors. */ 078 public static final String PARAM_IGNORE_WRITE_ERRORS = "ignoreWriteErrors"; 079 080 /** The parameter for the project in which this repository should operate. */ 081 public static final String PARAM_PROJECT = "project"; 082 083 /** Name of the parameter to configure the root directory. */ 084 public static final String PARAM_ROOT = "root"; 085 086 /** Name of the parameter to configure resource wrappers. */ 087 public static final String PARAM_WRAPPER = "wrapper"; 088 089 /** The logger instance for this class. */ 090 private static final Log LOG = CmsLog.getLog(CmsJlanRepository.class); 091 092 /** Flag which controls whether the CmsObjectWrapper should add byte order marks for plain files. */ 093 private boolean m_addByteOrderMark; 094 095 /** The CMS context. */ 096 private CmsObject m_cms; 097 098 /** The configuration for this repository. */ 099 private CmsParameterConfiguration m_configuration = new CmsParameterConfiguration(); 100 101 /** The shared disk device. */ 102 private DiskSharedDevice m_device; 103 104 /** The JLAN device context for this repository. */ 105 private DiskDeviceContext m_deviceContext; 106 107 /** The JLAN disk interface for this repository. */ 108 private DiskInterface m_diskInterface; 109 110 /** Flag which controls whether write errors should be ignored. */ 111 private boolean m_ignoreWriteErrors; 112 113 /** The name of the repository. */ 114 private String m_name; 115 116 /** The disk interface. */ 117 private CmsJlanDiskInterface m_originalDiskInterface; 118 119 /** The configured project. */ 120 private CmsProject m_project; 121 122 /** The name of the configured project. */ 123 private String m_projectName; 124 125 /** The root VFS directory of the repository. */ 126 private String m_root; 127 128 /** The list of wrappers configured for this repository. */ 129 private List<I_CmsResourceWrapper> m_wrappers = Lists.newArrayList(); 130 131 /** 132 * Creates a new repository instance.<p> 133 */ 134 public CmsJlanRepository() { 135 136 m_deviceContext = new CmsJlanDeviceContext(this); 137 m_deviceContext.enableChangeHandler(true); 138 m_deviceContext.setFileServerNotifications(true); 139 m_originalDiskInterface = new CmsJlanDiskInterface(); 140 m_diskInterface = createLoggingProxy(m_originalDiskInterface); 141 } 142 143 /** 144 * Creates a dynamic proxy for a disk interface which logs the method calls and their results.<p> 145 * 146 * @param impl the disk interface for which a logging proxy should be created 147 * 148 * @return the dynamic proxy which logs methods calls 149 */ 150 public static DiskInterface createLoggingProxy(final DiskInterface impl) { 151 152 return (DiskInterface)Proxy.newProxyInstance( 153 Thread.currentThread().getContextClassLoader(), 154 new Class[] {DiskInterface.class}, 155 new InvocationHandler() { 156 157 @SuppressWarnings("synthetic-access") 158 public Object invoke(Object target, Method method, Object[] params) throws Throwable { 159 160 // Just to be on the safe side performance-wise, we only log the parameters/result 161 // if the info channel is enabled 162 if (LOG.isInfoEnabled()) { 163 List<String> paramStrings = new ArrayList<String>(); 164 for (Object param : params) { 165 paramStrings.add("" + param); 166 } 167 String paramsAsString = CmsStringUtil.listAsString(paramStrings, ", "); 168 LOG.info("Call: " + method.getName() + " " + paramsAsString); 169 } 170 try { 171 Object result = method.invoke(impl, params); 172 if (LOG.isInfoEnabled()) { 173 LOG.info("Returned from " + method.getName() + ": " + result); 174 } 175 return result; 176 } catch (InvocationTargetException e) { 177 Throwable cause = e.getCause(); 178 if ((cause != null) && (cause instanceof CmsSilentWrapperException)) { 179 // not really an error 180 LOG.info(cause.getCause().getLocalizedMessage(), cause.getCause()); 181 } else { 182 LOG.error(e.getLocalizedMessage(), e); 183 } 184 throw e.getCause(); 185 } 186 } 187 }); 188 } 189 190 /** 191 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#addConfigurationParameter(java.lang.String, java.lang.String) 192 */ 193 public void addConfigurationParameter(String paramName, String paramValue) { 194 195 m_configuration.add(paramName, paramValue); 196 197 } 198 199 /** 200 * Checks if a user may access this repository.<p> 201 * 202 * @param user the name of the user 203 * 204 * @return true if the user may access the repository 205 */ 206 public boolean allowAccess(String user) { 207 208 try { 209 return m_cms.getPermissions(m_root, user).requiresViewPermission(); 210 } catch (CmsException e) { 211 LOG.error(e.getLocalizedMessage(), e); 212 return true; 213 } 214 } 215 216 /** 217 * Creates a CmsObjectWrapper for the current session.<p> 218 * 219 * @param session the current session 220 * @param connection the tree connection 221 * 222 * @return the correctly configured CmsObjectWrapper for this session 223 * 224 * @throws CmsException if something goes wrong 225 */ 226 public CmsObjectWrapper getCms(SrvSession session, TreeConnection connection) throws CmsException { 227 228 String userName = session.getClientInformation().getUserName(); 229 userName = CmsJlanUsers.translateUser(userName); 230 CmsContextInfo contextInfo = new CmsContextInfo(m_cms.getRequestContext()); 231 contextInfo.setUserName(userName); 232 CmsObject newCms = OpenCms.initCmsObject(m_cms, contextInfo); 233 newCms.getRequestContext().setSiteRoot(getRoot()); 234 newCms.getRequestContext().setCurrentProject(getProject()); 235 CmsObjectWrapper result = new CmsObjectWrapper(newCms, getWrappers()); 236 result.setAddByteOrderMark(m_addByteOrderMark); 237 result.getRequestContext().setAttribute(CmsXmlContent.AUTO_CORRECTION_ATTRIBUTE, Boolean.TRUE); 238 result.getRequestContext().setAttribute(JLAN_IGNORE_WRITE_ERRORS, Boolean.valueOf(m_ignoreWriteErrors)); 239 return result; 240 } 241 242 /** 243 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#getConfiguration() 244 */ 245 public CmsParameterConfiguration getConfiguration() { 246 247 return m_configuration; 248 } 249 250 /** 251 * Gets the device context for this repository.<p> 252 * 253 * @return the device context 254 */ 255 public DiskDeviceContext getDeviceContext() { 256 257 return m_deviceContext; 258 } 259 260 /** 261 * Gets the disk interface for this repository.<p> 262 * 263 * @return the disk interface 264 */ 265 public DiskInterface getDiskInterface() { 266 267 return m_diskInterface; 268 } 269 270 /** 271 * @see org.opencms.repository.I_CmsRepository#getFilter() 272 */ 273 public CmsRepositoryFilter getFilter() { 274 275 return null; 276 } 277 278 /** 279 * @see org.opencms.repository.I_CmsRepository#getName() 280 */ 281 public String getName() { 282 283 return m_name; 284 } 285 286 /** 287 * Gets the configured project.<p> 288 * 289 * @return the configured project 290 */ 291 public CmsProject getProject() { 292 293 return m_project; 294 } 295 296 /** 297 * Gets the root directory configured for this repository.<p> 298 * 299 * @return the root directory 300 */ 301 public String getRoot() { 302 303 return m_root; 304 } 305 306 /** 307 * Gets the resource wrappers which have been configured for this repository.<p> 308 * 309 * @return the resource wrappers which have been configured 310 */ 311 public List<I_CmsResourceWrapper> getWrappers() { 312 313 return m_wrappers; 314 } 315 316 /** 317 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration() 318 */ 319 public void initConfiguration() throws CmsConfigurationException { 320 321 List<I_CmsResourceWrapper> wrapperObjects = CmsRepositoryManager.createResourceWrappersFromConfiguration( 322 getConfiguration(), 323 PARAM_WRAPPER, 324 LOG); 325 m_wrappers = Collections.unmodifiableList(wrapperObjects); 326 m_root = getConfiguration().getString(PARAM_ROOT, "").trim(); 327 m_projectName = getConfiguration().getString(PARAM_PROJECT, "Offline").trim(); 328 String addByteOrderMarkStr = getConfiguration().getString(PARAM_ADD_BOM, "" + true).trim(); 329 m_addByteOrderMark = Boolean.parseBoolean(addByteOrderMarkStr); 330 m_ignoreWriteErrors = Boolean.parseBoolean(getConfiguration().getString(PARAM_IGNORE_WRITE_ERRORS, "false")); 331 } 332 333 /** 334 * @see org.opencms.repository.I_CmsRepository#initializeCms(org.opencms.file.CmsObject) 335 */ 336 public void initializeCms(CmsObject cms) throws CmsException { 337 338 m_cms = cms; 339 if (CmsShell.isJlanDisabled()) { 340 return; 341 } 342 m_project = m_cms.readProject(m_projectName); 343 m_device = new DiskSharedDevice(getName(), getDiskInterface(), getDeviceContext(), 0); 344 m_device.addAccessControl(new CmsRepositoryAccessControl(this)); 345 } 346 347 /** 348 * Returns true if file write errors should be ignored. 349 * 350 * @return true if file write errors should be ignored 351 */ 352 public boolean isIgnoreWriteFileErrors() { 353 354 return m_ignoreWriteErrors; 355 } 356 357 /** 358 * @see org.opencms.repository.I_CmsRepository#setFilter(org.opencms.repository.CmsRepositoryFilter) 359 */ 360 public void setFilter(CmsRepositoryFilter filter) { 361 362 // do nothing 363 } 364 365 /** 366 * @see org.opencms.repository.I_CmsRepository#setName(java.lang.String) 367 */ 368 public void setName(String name) { 369 370 // case sensitive share names don't work 371 m_name = name.toUpperCase(); 372 } 373 374 /** 375 * Gets the shared device for this repository.<p> 376 * 377 * @return the shared device 378 */ 379 DiskSharedDevice getSharedDevice() { 380 381 return m_device; 382 } 383 384}