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