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