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}