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.gwt; 029 030import org.opencms.db.CmsDriverManager; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsResource; 033import org.opencms.main.CmsEvent; 034import org.opencms.main.CmsLog; 035import org.opencms.main.CmsStaticResourceHandler; 036import org.opencms.main.I_CmsEventListener; 037import org.opencms.main.OpenCms; 038import org.opencms.util.CmsCollectionsGenericWrapper; 039import org.opencms.util.CmsStringUtil; 040 041import java.io.ByteArrayInputStream; 042import java.io.File; 043import java.io.FileInputStream; 044import java.io.IOException; 045import java.io.InputStream; 046import java.net.MalformedURLException; 047import java.net.URL; 048import java.net.URLConnection; 049import java.text.ParseException; 050import java.util.List; 051 052import org.apache.commons.logging.Log; 053 054import com.google.gwt.user.server.rpc.SerializationPolicy; 055import com.google.gwt.user.server.rpc.SerializationPolicyLoader; 056 057/** 058 * This class contains the data that should be cached for a specific service class.<p> 059 * 060 * We cache instances of this class rather than caching instances of {@link CmsGwtService} directly because 061 * its superclass, {@link com.google.gwt.user.server.rpc.RemoteServiceServlet}, does some caching which we can't use because it doesn't 062 * take the distinction between online and offline requests into account. 063 * 064 * @since 8.0.0 065 * 066 */ 067public class CmsGwtServiceContext implements I_CmsEventListener { 068 069 /** The static log object for this class. */ 070 private static final Log LOG = CmsLog.getLog(CmsGwtServiceContext.class); 071 072 /** The name, which is used for debugging. */ 073 private String m_name; 074 075 /** The serialization policy path. */ 076 private String m_serializationPolicyPath; 077 078 /** The offline serialization policy. */ 079 private SerializationPolicy m_serPolicyOffline; 080 081 /** The online serialization policy. */ 082 private SerializationPolicy m_serPolicyOnline; 083 084 /** 085 * Creates a new service context object.<p> 086 * 087 * @param name an identifier which is used for debugging 088 */ 089 public CmsGwtServiceContext(String name) { 090 091 m_name = name; 092 093 // listen on VFS changes for serialization policies 094 OpenCms.addCmsEventListener( 095 this, 096 new int[] { 097 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 098 I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED, 099 I_CmsEventListener.EVENT_RESOURCE_MODIFIED, 100 I_CmsEventListener.EVENT_RESOURCES_MODIFIED, 101 I_CmsEventListener.EVENT_RESOURCE_DELETED, 102 I_CmsEventListener.EVENT_PUBLISH_PROJECT, 103 I_CmsEventListener.EVENT_CLEAR_CACHES, 104 I_CmsEventListener.EVENT_CLEAR_ONLINE_CACHES, 105 I_CmsEventListener.EVENT_CLEAR_OFFLINE_CACHES}); 106 107 } 108 109 /** 110 * @see org.opencms.main.I_CmsEventListener#cmsEvent(org.opencms.main.CmsEvent) 111 */ 112 public void cmsEvent(CmsEvent event) { 113 114 CmsResource resource = null; 115 List<CmsResource> resources = null; 116 117 switch (event.getType()) { 118 case I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED: 119 case I_CmsEventListener.EVENT_RESOURCE_MODIFIED: 120 Object change = event.getData().get(I_CmsEventListener.KEY_CHANGE); 121 if ((change != null) && change.equals(Integer.valueOf(CmsDriverManager.NOTHING_CHANGED))) { 122 // skip lock & unlock 123 return; 124 } 125 // a resource has been modified in a way that it *IS NOT* necessary also to clear 126 // lists of cached sub-resources where the specified resource might be contained inside. 127 resource = (CmsResource)event.getData().get(I_CmsEventListener.KEY_RESOURCE); 128 uncacheResource(resource); 129 break; 130 131 case I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED: 132 // a list of resources and all of their properties have been modified 133 resources = CmsCollectionsGenericWrapper.list(event.getData().get(I_CmsEventListener.KEY_RESOURCES)); 134 uncacheResources(resources); 135 break; 136 137 case I_CmsEventListener.EVENT_RESOURCE_MOVED: 138 case I_CmsEventListener.EVENT_RESOURCE_DELETED: 139 case I_CmsEventListener.EVENT_RESOURCES_MODIFIED: 140 // a list of resources has been modified 141 resources = CmsCollectionsGenericWrapper.list(event.getData().get(I_CmsEventListener.KEY_RESOURCES)); 142 uncacheResources(resources); 143 break; 144 145 case I_CmsEventListener.EVENT_CLEAR_ONLINE_CACHES: 146 case I_CmsEventListener.EVENT_PUBLISH_PROJECT: 147 m_serPolicyOnline = null; 148 break; 149 150 case I_CmsEventListener.EVENT_CLEAR_CACHES: 151 m_serPolicyOnline = null; 152 m_serPolicyOffline = null; 153 break; 154 155 case I_CmsEventListener.EVENT_CLEAR_OFFLINE_CACHES: 156 m_serPolicyOffline = null; 157 break; 158 159 default: 160 // noop 161 break; 162 } 163 } 164 165 /** 166 * @see java.lang.Object#toString() 167 */ 168 @Override 169 public String toString() { 170 171 return super.toString() + "(" + m_name + ")"; 172 } 173 174 /** 175 * Returns the serialization policy for the service.<p> 176 * 177 * @param cms the current CMS context 178 * @param moduleBaseURL the module's base URL 179 * @param strongName the strong name of the service 180 * 181 * @return the serialization policy for the given service 182 */ 183 protected SerializationPolicy getSerializationPolicy(CmsObject cms, String moduleBaseURL, String strongName) { 184 185 if (m_serializationPolicyPath == null) { 186 m_serializationPolicyPath = getSerializationPolicyPath(moduleBaseURL, strongName); 187 } 188 return getSerializationPolicy(cms); 189 } 190 191 /** 192 * Finds the path of the serialization policy file.<p> 193 * 194 * @param moduleBaseURL the GWT module's base url 195 * @param strongName the strong name of the service 196 * 197 * @return the serialization policy path 198 */ 199 protected String getSerializationPolicyPath(String moduleBaseURL, String strongName) { 200 201 // locate the serialization policy file in OpenCms 202 String modulePath = null; 203 try { 204 modulePath = new URL(moduleBaseURL).getPath(); 205 } catch (MalformedURLException ex) { 206 // moduleBaseUrl is bad 207 LOG.error(ex.getLocalizedMessage(), ex); 208 return null; 209 } catch (NullPointerException ex) { 210 // moduleBaseUrl is null 211 LOG.error(ex.getLocalizedMessage(), ex); 212 return null; 213 } 214 return SerializationPolicyLoader.getSerializationPolicyFileName(modulePath + strongName); 215 } 216 217 /** 218 * Returns the serialization policy, using lazy initialization.<p> 219 * 220 * @param cms the current cms context 221 * 222 * @return the serialization policy 223 */ 224 private SerializationPolicy getSerializationPolicy(CmsObject cms) { 225 226 boolean online = cms.getRequestContext().getCurrentProject().isOnlineProject(); 227 if (online && (m_serPolicyOnline != null)) { 228 return m_serPolicyOnline; 229 } else if (!online && (m_serPolicyOffline != null)) { 230 return m_serPolicyOffline; 231 } 232 233 SerializationPolicy serializationPolicy = null; 234 235 // Open the RPC resource file and read its contents 236 InputStream is = null; 237 try { 238 // check if this is a static resource request 239 if (m_serializationPolicyPath.startsWith(OpenCms.getSystemInfo().getStaticResourceContext())) { 240 URL resourceURL = CmsStaticResourceHandler.getStaticResourceURL(m_serializationPolicyPath); 241 URLConnection connection; 242 connection = resourceURL.openConnection(); 243 is = connection.getInputStream(); 244 } else { 245 // try reading from the RFS 246 String rfsPath = m_serializationPolicyPath; 247 if (rfsPath.startsWith(OpenCms.getSystemInfo().getContextPath())) { 248 rfsPath = rfsPath.substring(OpenCms.getSystemInfo().getContextPath().length()); 249 } 250 rfsPath = CmsStringUtil.joinPaths(OpenCms.getSystemInfo().getWebApplicationRfsPath(), rfsPath); 251 File policyFile = new File(rfsPath); 252 if (policyFile.exists() && policyFile.canRead()) { 253 is = new FileInputStream(policyFile); 254 } else { 255 // the file does not exist in the RFS, try the VFS 256 String policyPath = OpenCms.getLinkManager().getRootPath(cms, m_serializationPolicyPath); 257 is = new ByteArrayInputStream(cms.readFile(policyPath).getContents()); 258 } 259 } 260 } catch (Exception ex) { 261 // most likely file not found 262 String message = "ERROR: The serialization policy file '" 263 + m_serializationPolicyPath 264 + "' was not found; did you forget to include it in this deployment?"; 265 LOG.warn(message); 266 LOG.warn(ex.getLocalizedMessage(), ex); 267 268 } 269 if (is == null) { 270 return new CmsDummySerializationPolicy(); 271 } 272 273 // read the policy 274 try { 275 serializationPolicy = SerializationPolicyLoader.loadFromStream(is, null); 276 } catch (ParseException e) { 277 LOG.error("ERROR: Failed to parse the policy file '" + m_serializationPolicyPath + "'", e); 278 } catch (IOException e) { 279 LOG.error("ERROR: Could not read the policy file '" + m_serializationPolicyPath + "'", e); 280 } finally { 281 try { 282 is.close(); 283 } catch (@SuppressWarnings("unused") IOException e) { 284 // Ignore this error 285 } 286 } 287 288 if (online) { 289 m_serPolicyOnline = serializationPolicy; 290 } else { 291 m_serPolicyOffline = serializationPolicy; 292 } 293 return serializationPolicy; 294 } 295 296 /** 297 * Removes a cached resource from the cache.<p> 298 * 299 * @param resource the resource 300 */ 301 private void uncacheResource(CmsResource resource) { 302 303 if (resource == null) { 304 return; 305 } 306 if ((m_serializationPolicyPath != null) && resource.getRootPath().equals(m_serializationPolicyPath)) { 307 m_serPolicyOffline = null; 308 } 309 } 310 311 /** 312 * Removes a bunch of cached resources from the cache.<p> 313 * 314 * @param resources a list of resources 315 * 316 * @see #uncacheResource(CmsResource) 317 */ 318 private void uncacheResources(List<CmsResource> resources) { 319 320 if (resources == null) { 321 return; 322 } 323 for (int i = 0, n = resources.size(); i < n; i++) { 324 // remove the resource 325 uncacheResource(resources.get(i)); 326 } 327 } 328 329}