001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH (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.gwt;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsProperty;
032import org.opencms.file.CmsResource;
033import org.opencms.file.CmsResourceFilter;
034import org.opencms.file.CmsUser;
035import org.opencms.lock.CmsLockActionRecord;
036import org.opencms.lock.CmsLockUtil;
037import org.opencms.main.CmsException;
038import org.opencms.main.CmsLog;
039import org.opencms.main.OpenCms;
040import org.opencms.security.CmsRole;
041import org.opencms.security.CmsRoleViolationException;
042import org.opencms.util.CmsUUID;
043
044import java.io.IOException;
045import java.util.HashMap;
046import java.util.List;
047import java.util.Map;
048
049import javax.servlet.ServletException;
050import javax.servlet.ServletRequest;
051import javax.servlet.ServletResponse;
052import javax.servlet.http.HttpServletRequest;
053import javax.servlet.http.HttpServletResponse;
054
055import org.apache.commons.logging.Log;
056
057import com.google.gwt.user.server.rpc.RemoteServiceServlet;
058import com.google.gwt.user.server.rpc.SerializationPolicy;
059
060/**
061 * Wrapper for GWT services served through OpenCms.<p>
062 *
063 * @since 8.0.0
064 */
065public class CmsGwtService extends RemoteServiceServlet {
066
067    /** The static log object for this class. */
068    private static final Log LOG = CmsLog.getLog(CmsGwtService.class);
069
070    /** Serialization id. */
071    private static final long serialVersionUID = 8119684308154724518L;
072
073    /** The service class context. */
074    private CmsGwtServiceContext m_context;
075
076    /** The current CMS context. */
077    private ThreadLocal<CmsObject> m_perThreadCmsObject;
078
079    /**
080     * Constructor.<p>
081     */
082    public CmsGwtService() {
083
084        super();
085    }
086
087    /**
088     * Checks the permissions of the current user to match the required security level.<p>
089     *
090     * Note that the current request and response are not available yet.<p>
091     *
092     * Override if needed.<p>
093     *
094     * @param cms the current cms object
095     *
096     * @throws CmsRoleViolationException if the security level can not be satisfied
097     */
098    public void checkPermissions(CmsObject cms) throws CmsRoleViolationException {
099
100        OpenCms.getRoleManager().checkRole(cms, CmsRole.ELEMENT_AUTHOR);
101    }
102
103    /**
104     * Logs and re-throws the given exception for RPC responses.<p>
105     *
106     * @param t the exception
107     *
108     * @throws CmsRpcException the converted exception
109     */
110    public void error(Throwable t) throws CmsRpcException {
111
112        logError(t);
113        throw new CmsRpcException(t);
114    }
115
116    /**
117     * Returns the current cms context.<p>
118     *
119     * @return the current cms context
120     */
121    public CmsObject getCmsObject() {
122
123        return m_perThreadCmsObject.get();
124    }
125
126    /**
127     * Returns the current request.<p>
128     *
129     * @return the current request
130     *
131     * @see #getThreadLocalRequest()
132     */
133    public HttpServletRequest getRequest() {
134
135        return getThreadLocalRequest();
136    }
137
138    /**
139     * Returns the current response.<p>
140     *
141     * @return the current response
142     *
143     * @see #getThreadLocalResponse()
144     */
145    public HttpServletResponse getResponse() {
146
147        return getThreadLocalResponse();
148    }
149
150    /**
151     * @see javax.servlet.GenericServlet#log(java.lang.String)
152     */
153    @Override
154    public void log(String msg) {
155
156        if (getResponse() != null) {
157            super.log(msg);
158        }
159        // also log to opencms.log
160        LOG.info(msg);
161    }
162
163    /**
164     * @see javax.servlet.GenericServlet#log(java.lang.String, java.lang.Throwable)
165     */
166    @Override
167    public void log(String message, Throwable t) {
168
169        if (getResponse() != null) {
170            super.log(message, t);
171        }
172        // also log to opencms.log
173        LOG.info(message, t);
174    }
175
176    /**
177     * Logs the given exception.<p>
178     *
179     * @param t the exception to log
180     */
181    public void logError(Throwable t) {
182
183        LOG.error(t.getLocalizedMessage(), t);
184    }
185
186    /**
187     * @see javax.servlet.http.HttpServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
188     */
189    @Override
190    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
191
192        try {
193            response.setCharacterEncoding(request.getCharacterEncoding());
194            super.service(request, response);
195        } finally {
196            clearThreadStorage();
197        }
198    }
199
200    /**
201     * Sets the current cms context.<p>
202     *
203     * @param cms the current cms context to set
204     */
205    public synchronized void setCms(CmsObject cms) {
206
207        if (m_perThreadCmsObject == null) {
208            m_perThreadCmsObject = new ThreadLocal<CmsObject>();
209        }
210        m_perThreadCmsObject.set(cms);
211    }
212
213    /**
214     * Sets the service context.<p>
215     *
216     * @param context the new service context
217     */
218    public synchronized void setContext(CmsGwtServiceContext context) {
219
220        m_context = context;
221    }
222
223    /**
224     * Sets the current request.<p>
225     *
226     * @param request the request to set
227     */
228    public synchronized void setRequest(HttpServletRequest request) {
229
230        if (perThreadRequest == null) {
231            perThreadRequest = new ThreadLocal<HttpServletRequest>();
232        }
233        perThreadRequest.set(request);
234    }
235
236    /**
237     * Sets the current response.<p>
238     *
239     * @param response the response to set
240     */
241    public synchronized void setResponse(HttpServletResponse response) {
242
243        if (perThreadResponse == null) {
244            perThreadResponse = new ThreadLocal<HttpServletResponse>();
245        }
246        perThreadResponse.set(response);
247    }
248
249    /**
250     * Clears the objects stored in thread local.<p>
251     */
252    protected void clearThreadStorage() {
253
254        if (m_perThreadCmsObject != null) {
255            m_perThreadCmsObject.remove();
256        }
257        if (perThreadRequest != null) {
258            perThreadRequest.remove();
259        }
260        if (perThreadResponse != null) {
261            perThreadResponse.remove();
262        }
263    }
264
265    /**
266     * We do not want that the server goes to fetch files from the servlet context.<p>
267     *
268     * @see com.google.gwt.user.server.rpc.RemoteServiceServlet#doGetSerializationPolicy(javax.servlet.http.HttpServletRequest, java.lang.String, java.lang.String)
269     */
270    @Override
271    protected SerializationPolicy doGetSerializationPolicy(
272        HttpServletRequest request,
273        String moduleBaseURL,
274        String strongName) {
275
276        return m_context.getSerializationPolicy(getCmsObject(), moduleBaseURL, strongName);
277    }
278
279    /**
280     * @see com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet#doUnexpectedFailure(java.lang.Throwable)
281     */
282    @Override
283    protected void doUnexpectedFailure(Throwable e) {
284
285        LOG.error(String.valueOf(System.currentTimeMillis()), e);
286        super.doUnexpectedFailure(e);
287    }
288
289    /**
290     * Locks the given resource with a temporary, if not already locked by the current user.
291     * Will throw an exception if the resource could not be locked for the current user.<p>
292     *
293     * @param resource the resource to lock
294     *
295     * @return the assigned lock
296     *
297     * @throws CmsException if the resource could not be locked
298     */
299    protected CmsLockActionRecord ensureLock(CmsResource resource) throws CmsException {
300
301        CmsObject cms = getCmsObject();
302        return CmsLockUtil.ensureLock(cms, resource);
303    }
304
305    /**
306     *
307     * Locks the given resource with a temporary, if not already locked by the current user.
308     * Will throw an exception if the resource could not be locked for the current user.<p>
309     *
310     * @param structureId the structure id of the resource
311     *
312     * @return the assigned lock
313     *
314     * @throws CmsException if something goes wrong
315     */
316    protected CmsLockActionRecord ensureLock(CmsUUID structureId) throws CmsException {
317
318        return ensureLock(getCmsObject().readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION));
319
320    }
321
322    /**
323     * Locks the given resource with a temporary, if not already locked by the current user.
324     * Will throw an exception if the resource could not be locked for the current user.<p>
325     *
326     * @param sitepath the site-path of the resource to lock
327     *
328     * @return the assigned lock
329     *
330     * @throws CmsException if the resource could not be locked
331     */
332    protected CmsLockActionRecord ensureLock(String sitepath) throws CmsException {
333
334        return ensureLock(getCmsObject().readResource(sitepath, CmsResourceFilter.IGNORE_EXPIRATION));
335    }
336
337    /**
338     * Ensures that the user session is still valid.<p>
339     *
340     * @throws CmsException if the current user is the guest user
341     */
342    protected void ensureSession() throws CmsException {
343
344        CmsUser user = getCmsObject().getRequestContext().getCurrentUser();
345        if (user.isGuestUser()) {
346            throw new CmsException(Messages.get().container(Messages.ERR_SESSION_EXPIRED_0));
347        }
348    }
349
350    /**
351     * Converts a list of properties to a map.<p>
352     *
353     * @param properties the list of properties
354     *
355     * @return a map from property names to properties
356     */
357    protected Map<String, CmsProperty> getPropertiesByName(List<CmsProperty> properties) {
358
359        Map<String, CmsProperty> result = new HashMap<String, CmsProperty>();
360        for (CmsProperty property : properties) {
361            String key = property.getName();
362            result.put(key, property.clone());
363        }
364        return result;
365    }
366
367    /**
368     * Tries to unlock a resource.<p>
369     *
370     * @param resource the resource to unlock
371     */
372    protected void tryUnlock(CmsResource resource) {
373
374        try {
375            getCmsObject().unlockResource(resource);
376        } catch (CmsException e) {
377            LOG.debug("Unable to unlock " + resource.getRootPath(), e);
378        }
379    }
380}