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.SitemapElement; 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(SitemapElement 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 AmetysObjectIterable<? extends ZoneItem> zoneItems = zone.getZoneItems(); 175 for (ZoneItem item : zoneItems) 176 { 177 switch (item.getType()) 178 { 179 case CONTENT: 180 values.put("contents", values.get("contents") + 1); 181 break; 182 case SERVICE: 183 values.put("services", values.get("services") + 1); 184 185 // Test if the service is cacheable, only if the page is cacheable since then. 186 if (isCacheable) 187 { 188 Service service = _serviceExtPt.getExtension(item.getServiceId()); 189 if (service != null && !service.isCacheable(page, item)) 190 { 191 isCacheable = false; 192 } 193 } 194 break; 195 default: 196 break; 197 } 198 } 199 } 200 201 if (isCacheable) 202 { 203 cacheableCount++; 204 } 205 206 _processPages(page, values); 207 } 208 209 values.put("nbCacheable", values.get("nbCacheable") + cacheableCount); 210 } 211 212 private void _saxContents(Site site) throws SAXException, RepositoryException 213 { 214 AmetysObjectIterable<Content> contents = site.getContents(); 215 216 AttributesImpl atts = new AttributesImpl(); 217 atts.addCDATAAttribute("count", String.valueOf(contents.getSize())); 218 219 int orphaned = 0; 220 int external = 0; 221 for (Content content : contents) 222 { 223 int referers = 0; 224 int ext = 0; 225 226 // FIXME API getNode 227 PropertyIterator it = ((JCRAmetysObject) content).getNode().getReferences(); 228 while (it.hasNext()) 229 { 230 Property property = it.nextProperty(); 231 Node node = property.getParent(); 232 if (!node.isNodeType("oswf:entry")) 233 { 234 referers++; 235 236 if (node.isNodeType("ametys:zoneItem")) 237 { 238 Node parent = node.getParent(); 239 while (!parent.isNodeType("ametys:site")) 240 { 241 parent = parent.getParent(); 242 } 243 244 if (!parent.getName().equals(site.getName())) 245 { 246 ext++; 247 } 248 } 249 } 250 } 251 252 if (referers == 0) 253 { 254 orphaned++; 255 } 256 257 if (ext > 0) 258 { 259 external++; 260 } 261 } 262 263 atts.addCDATAAttribute("orphaned", String.valueOf(orphaned)); 264 atts.addCDATAAttribute("external", String.valueOf(external)); 265 266 XMLUtils.createElement(contentHandler, "contents", atts); 267 } 268 269 private void _saxResources(Site site) throws SAXException 270 { 271 HashMap<String, Long> values = new HashMap<>(); 272 values.put("folderCount", 0L); 273 values.put("resourceCount", 0L); 274 values.put("totalSize", 0L); 275 276 _processResources(site.getRootResources(), values); 277 278 long folderCount = values.get("folderCount"); 279 long resourceCount = values.get("resourceCount"); 280 long totalSize = values.get("totalSize"); 281 282 AttributesImpl atts = new AttributesImpl(); 283 atts.addCDATAAttribute("folderCount", Long.toString(folderCount)); 284 atts.addCDATAAttribute("resourceCount", Long.toString(resourceCount)); 285 atts.addCDATAAttribute("totalSize", Long.toString(totalSize)); 286 287 XMLUtils.createElement(contentHandler, "resources", atts); 288 } 289 290 private void _processResources(TraversableAmetysObject resourceContainer, HashMap<String, Long> values) 291 { 292 AmetysObjectIterable<? extends AmetysObject> objects = resourceContainer.getChildren(); 293 294 for (AmetysObject object : objects) 295 { 296 // Traverse the child nodes if depth < 0 (generate all) or depth > 0 (we're not in the last level). 297 if (object instanceof ResourceCollection) 298 { 299 values.put("folderCount", values.get("folderCount") + 1); 300 301 _processResources((ResourceCollection) object, values); 302 } 303 else if (object instanceof Resource) 304 { 305 Resource resource = (Resource) object; 306 307 values.put("resourceCount", values.get("resourceCount") + 1); 308 values.put("totalSize", values.get("totalSize") + resource.getLength()); 309 } 310 } 311 } 312 313}