001/* 002 * Copyright 2010 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.web.site; 017 018import java.io.IOException; 019import java.util.HashMap; 020import java.util.Map; 021 022import javax.jcr.Node; 023import javax.jcr.Property; 024import javax.jcr.PropertyIterator; 025import javax.jcr.RepositoryException; 026 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.cocoon.ProcessingException; 030import org.apache.cocoon.environment.ObjectModelHelper; 031import org.apache.cocoon.environment.Request; 032import org.apache.cocoon.generation.ServiceableGenerator; 033import org.apache.cocoon.xml.AttributesImpl; 034import org.apache.cocoon.xml.XMLUtils; 035import org.xml.sax.SAXException; 036 037import org.ametys.cms.repository.Content; 038import org.ametys.plugins.explorer.resources.Resource; 039import org.ametys.plugins.explorer.resources.ResourceCollection; 040import org.ametys.plugins.repository.AmetysObject; 041import org.ametys.plugins.repository.AmetysObjectIterable; 042import org.ametys.plugins.repository.TraversableAmetysObject; 043import org.ametys.plugins.repository.jcr.JCRAmetysObject; 044import org.ametys.web.cache.PageHelper; 045import org.ametys.web.repository.page.Page; 046import org.ametys.web.repository.page.PagesContainer; 047import org.ametys.web.repository.page.Zone; 048import org.ametys.web.repository.page.ZoneItem; 049import org.ametys.web.repository.site.Site; 050import org.ametys.web.repository.site.SiteManager; 051import org.ametys.web.repository.sitemap.Sitemap; 052import org.ametys.web.service.Service; 053import org.ametys.web.service.ServiceExtensionPoint; 054 055/** 056 * Provides some statistics about a {@link Site}, such as :<br> 057 * <ul> 058 * <li>Number of pages 059 * <li>Number of live contents 060 * <li>Number of orphaned contents 061 * <li>Number of external contents 062 * </ul> 063 */ 064public class SiteStatisticsGenerator extends ServiceableGenerator 065{ 066 /** The site manager. */ 067 protected SiteManager _siteManager; 068 069 /** The service extension point. */ 070 protected ServiceExtensionPoint _serviceExtPt; 071 072 /** The page cache helper. */ 073 protected PageHelper _pageCacheHelper; 074 075 @Override 076 public void service(ServiceManager sManager) throws ServiceException 077 { 078 super.service(sManager); 079 _siteManager = (SiteManager) sManager.lookup(SiteManager.ROLE); 080 _serviceExtPt = (ServiceExtensionPoint) sManager.lookup(ServiceExtensionPoint.ROLE); 081 _pageCacheHelper = (PageHelper) sManager.lookup(PageHelper.ROLE); 082 } 083 084 @Override 085 public void generate() throws IOException, SAXException, ProcessingException 086 { 087 Request request = ObjectModelHelper.getRequest(objectModel); 088 String siteName = parameters.getParameter("siteName", request.getParameter("siteName")); 089 Site site = _siteManager.getSite(siteName); 090 091 contentHandler.startDocument(); 092 093 saxSite(site); 094 095 contentHandler.endDocument(); 096 } 097 098 /** 099 * SAX statistics on a site. 100 * @param site the site to SAX statistics on. 101 * @throws SAXException if an error occurs while saxing 102 * @throws ProcessingException if an error occurs 103 */ 104 protected void saxSite(Site site) throws SAXException, ProcessingException 105 { 106 AttributesImpl atts = new AttributesImpl(); 107 atts.addCDATAAttribute("name", site.getName()); 108 atts.addCDATAAttribute("title", site.getTitle()); 109 110 XMLUtils.startElement(contentHandler, "site", atts); 111 112 for (Sitemap sitemap : site.getSitemaps()) 113 { 114 _saxSitemap(sitemap); 115 } 116 117 try 118 { 119 _saxContents(site); 120 } 121 catch (RepositoryException e) 122 { 123 throw new ProcessingException("Unable to process contents for site " + site.getName(), e); 124 } 125 126 _saxResources(site); 127 128 XMLUtils.endElement(contentHandler, "site"); 129 } 130 131 /** 132 * SAX statistics on a sitemap. 133 * @param sitemap the sitemap to SAX statistics on. 134 * @throws SAXException if an error occurs while saxing 135 */ 136 protected void _saxSitemap(Sitemap sitemap) throws SAXException 137 { 138 HashMap<String, Long> values = new HashMap<>(); 139 values.put("nbPages", 0L); 140 values.put("nbCacheable", 0L); 141 values.put("services", 0L); 142 values.put("contents", 0L); 143 144 _processPages(sitemap, values); 145 146 long nbPages = values.get("nbPages"); 147 long nbCacheable = values.get("nbCacheable"); 148 long services = values.get("services"); 149 long contents = values.get("contents"); 150 151 AttributesImpl atts = new AttributesImpl(); 152 atts.addCDATAAttribute("name", sitemap.getName()); 153 atts.addCDATAAttribute("nbPages", String.valueOf(nbPages)); 154 atts.addCDATAAttribute("nbCacheable", Long.toString(nbCacheable)); 155 atts.addCDATAAttribute("services", String.valueOf(services)); 156 atts.addCDATAAttribute("contents", String.valueOf(contents)); 157 158 XMLUtils.createElement(contentHandler, "sitemap", atts); 159 } 160 161 private void _processPages(PagesContainer pages, Map<String, Long> values) 162 { 163 AmetysObjectIterable<? extends Page> childPages = pages.getChildrenPages(); 164 165 values.put("nbPages", values.get("nbPages") + childPages.getSize()); 166 167 int cacheableCount = 0; 168 169 for (Page page : childPages) 170 { 171 boolean isCacheable = _pageCacheHelper.isCacheable(page); 172 for (Zone zone : page.getZones()) 173 { 174 for (ZoneItem item : zone.getZoneItems()) 175 { 176 switch (item.getType()) 177 { 178 case CONTENT: 179 values.put("contents", values.get("contents") + 1); 180 break; 181 case SERVICE: 182 values.put("services", values.get("services") + 1); 183 184 // Test if the service is cacheable, only if the page is cacheable since then. 185 if (isCacheable) 186 { 187 Service service = _serviceExtPt.getExtension(item.getServiceId()); 188 if (service != null && !service.isCacheable(page, item)) 189 { 190 isCacheable = false; 191 } 192 } 193 break; 194 default: 195 break; 196 } 197 } 198 } 199 200 if (isCacheable) 201 { 202 cacheableCount++; 203 } 204 205 _processPages(page, values); 206 } 207 208 values.put("nbCacheable", values.get("nbCacheable") + cacheableCount); 209 } 210 211 private void _saxContents(Site site) throws SAXException, RepositoryException 212 { 213 AmetysObjectIterable<Content> contents = site.getContents(); 214 215 AttributesImpl atts = new AttributesImpl(); 216 atts.addCDATAAttribute("count", String.valueOf(contents.getSize())); 217 218 int orphaned = 0; 219 int external = 0; 220 for (Content content : contents) 221 { 222 int referers = 0; 223 int ext = 0; 224 225 // FIXME API getNode 226 PropertyIterator it = ((JCRAmetysObject) content).getNode().getReferences(); 227 while (it.hasNext()) 228 { 229 Property property = it.nextProperty(); 230 Node node = property.getParent(); 231 if (!node.isNodeType("oswf:entry")) 232 { 233 referers++; 234 235 if (node.isNodeType("ametys:zoneItem")) 236 { 237 Node parent = node.getParent(); 238 while (!parent.isNodeType("ametys:site")) 239 { 240 parent = parent.getParent(); 241 } 242 243 if (!parent.getName().equals(site.getName())) 244 { 245 ext++; 246 } 247 } 248 } 249 } 250 251 if (referers == 0) 252 { 253 orphaned++; 254 } 255 256 if (ext > 0) 257 { 258 external++; 259 } 260 } 261 262 atts.addCDATAAttribute("orphaned", String.valueOf(orphaned)); 263 atts.addCDATAAttribute("external", String.valueOf(external)); 264 265 XMLUtils.createElement(contentHandler, "contents", atts); 266 } 267 268 private void _saxResources(Site site) throws SAXException 269 { 270 HashMap<String, Long> values = new HashMap<>(); 271 values.put("folderCount", 0L); 272 values.put("resourceCount", 0L); 273 values.put("totalSize", 0L); 274 275 _processResources(site.getRootResources(), values); 276 277 long folderCount = values.get("folderCount"); 278 long resourceCount = values.get("resourceCount"); 279 long totalSize = values.get("totalSize"); 280 281 AttributesImpl atts = new AttributesImpl(); 282 atts.addCDATAAttribute("folderCount", Long.toString(folderCount)); 283 atts.addCDATAAttribute("resourceCount", Long.toString(resourceCount)); 284 atts.addCDATAAttribute("totalSize", Long.toString(totalSize)); 285 286 XMLUtils.createElement(contentHandler, "resources", atts); 287 } 288 289 private void _processResources(TraversableAmetysObject resourceContainer, HashMap<String, Long> values) 290 { 291 AmetysObjectIterable<? extends AmetysObject> objects = resourceContainer.getChildren(); 292 293 for (AmetysObject object : objects) 294 { 295 // Traverse the child nodes if depth < 0 (generate all) or depth > 0 (we're not in the last level). 296 if (object instanceof ResourceCollection) 297 { 298 values.put("folderCount", values.get("folderCount") + 1); 299 300 _processResources((ResourceCollection) object, values); 301 } 302 else if (object instanceof Resource) 303 { 304 Resource resource = (Resource) object; 305 306 values.put("resourceCount", values.get("resourceCount") + 1); 307 values.put("totalSize", values.get("totalSize") + resource.getLength()); 308 } 309 } 310 } 311 312}