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}