Gather statistics about access to the web API.
authorJan Lahoda <jlahoda@netbeans.org>
Sat, 27 Oct 2012 14:43:13 +0200
changeset 897b9f28ee9f714
parent 896 1770c48c13c9
child 898 20a2751b3866
Gather statistics about access to the web API.
remoting/server/web/base.web.api/src/org/netbeans/modules/jackpot30/backend/base/AccessStatistics.java
remoting/server/web/base.web.api/src/org/netbeans/modules/jackpot30/backend/base/CategoryStorage.java
remoting/server/web/base.web.api/src/org/netbeans/modules/jackpot30/backend/base/api/API.java
remoting/server/web/web.main/src/web/main/WebMain.java
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/remoting/server/web/base.web.api/src/org/netbeans/modules/jackpot30/backend/base/AccessStatistics.java	Sat Oct 27 14:43:13 2012 +0200
     1.3 @@ -0,0 +1,187 @@
     1.4 +/*
     1.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     1.6 + *
     1.7 + * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
     1.8 + *
     1.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    1.10 + * Other names may be trademarks of their respective owners.
    1.11 + *
    1.12 + * The contents of this file are subject to the terms of either the GNU
    1.13 + * General Public License Version 2 only ("GPL") or the Common
    1.14 + * Development and Distribution License("CDDL") (collectively, the
    1.15 + * "License"). You may not use this file except in compliance with the
    1.16 + * License. You can obtain a copy of the License at
    1.17 + * http://www.netbeans.org/cddl-gplv2.html
    1.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    1.19 + * specific language governing permissions and limitations under the
    1.20 + * License.  When distributing the software, include this License Header
    1.21 + * Notice in each file and include the License file at
    1.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    1.23 + * particular file as subject to the "Classpath" exception as provided
    1.24 + * by Oracle in the GPL Version 2 section of the License file that
    1.25 + * accompanied this code. If applicable, add the following below the
    1.26 + * License Header, with the fields enclosed by brackets [] replaced by
    1.27 + * your own identifying information:
    1.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    1.29 + *
    1.30 + * If you wish your version of this file to be governed by only the CDDL
    1.31 + * or only the GPL Version 2, indicate your decision by adding
    1.32 + * "[Contributor] elects to include this software in this distribution
    1.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    1.34 + * single choice of license, a recipient has the option to distribute
    1.35 + * your version of this file under either the CDDL, the GPL Version 2 or
    1.36 + * to extend the choice of license to its licensees as provided above.
    1.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    1.38 + * Version 2 license, then the option applies only if the new code is
    1.39 + * made subject to such option by the copyright holder.
    1.40 + *
    1.41 + * Contributor(s):
    1.42 + *
    1.43 + * Portions Copyrighted 2012 Sun Microsystems, Inc.
    1.44 + */
    1.45 +package org.netbeans.modules.jackpot30.backend.base;
    1.46 +
    1.47 +import com.sun.jersey.spi.container.ContainerRequest;
    1.48 +import com.sun.jersey.spi.container.ContainerRequestFilter;
    1.49 +import java.io.BufferedInputStream;
    1.50 +import java.io.BufferedOutputStream;
    1.51 +import java.io.File;
    1.52 +import java.io.FileInputStream;
    1.53 +import java.io.FileOutputStream;
    1.54 +import java.io.IOException;
    1.55 +import java.io.InputStream;
    1.56 +import java.io.OutputStream;
    1.57 +import java.util.Collections;
    1.58 +import java.util.HashMap;
    1.59 +import java.util.List;
    1.60 +import java.util.Map;
    1.61 +import java.util.Map.Entry;
    1.62 +import java.util.Properties;
    1.63 +import java.util.concurrent.Executors;
    1.64 +import java.util.concurrent.ScheduledExecutorService;
    1.65 +import java.util.concurrent.TimeUnit;
    1.66 +import java.util.logging.Level;
    1.67 +import java.util.logging.Logger;
    1.68 +
    1.69 +/**
    1.70 + *
    1.71 + * @author lahvac
    1.72 + */
    1.73 +public class AccessStatistics implements ContainerRequestFilter {
    1.74 +
    1.75 +    private static Map<String, Long> statistics = null;
    1.76 +    private static long lastModifyStamp = 0;
    1.77 +    private static long lastSaveStamp = -1;
    1.78 +    private static ScheduledExecutorService store = Executors.newSingleThreadScheduledExecutor();
    1.79 +
    1.80 +    private static synchronized void incrementUsage(String key) {
    1.81 +        if (statistics == null) {
    1.82 +            statistics = new HashMap<String, Long>();
    1.83 +
    1.84 +            File accessStatistics = CategoryStorage.getAccessStatisticsFile();
    1.85 +
    1.86 +            if (accessStatistics.canRead()) {
    1.87 +                InputStream in = null;
    1.88 +
    1.89 +                try {
    1.90 +                    in = new BufferedInputStream(new FileInputStream(accessStatistics));
    1.91 +                    Properties p = new Properties();
    1.92 +
    1.93 +                    p.load(in);
    1.94 +
    1.95 +                    for (String propertyKey : p.stringPropertyNames()) {
    1.96 +                        try {
    1.97 +                            long count = Long.parseLong(p.getProperty(propertyKey));
    1.98 +
    1.99 +                            statistics.put(propertyKey, count);
   1.100 +                        } catch (NumberFormatException ex) {
   1.101 +                            //ignore...
   1.102 +                            Logger.getLogger(AccessStatistics.class.getName()).log(Level.SEVERE, null, ex);
   1.103 +                        }
   1.104 +                    }
   1.105 +                } catch (IOException ex) {
   1.106 +                    Logger.getLogger(AccessStatistics.class.getName()).log(Level.SEVERE, null, ex);
   1.107 +                } finally {
   1.108 +                    if (in != null) {
   1.109 +                        try {
   1.110 +                            in.close();
   1.111 +                        } catch (IOException ex) {
   1.112 +                            Logger.getLogger(AccessStatistics.class.getName()).log(Level.SEVERE, null, ex);
   1.113 +                        }
   1.114 +                    }
   1.115 +                }
   1.116 +            }
   1.117 +        }
   1.118 +
   1.119 +        Long l = statistics.get(key);
   1.120 +
   1.121 +        if (l == null) l = 0L;
   1.122 +
   1.123 +        statistics.put(key, l + 1);
   1.124 +
   1.125 +        lastModifyStamp++;
   1.126 +
   1.127 +        store.schedule(new Runnable() {
   1.128 +            @Override public void run() {
   1.129 +                storeStatistics();
   1.130 +            }
   1.131 +        }, 1, TimeUnit.SECONDS);
   1.132 +    }
   1.133 +
   1.134 +    private static void storeStatistics() {
   1.135 +        Properties p = new Properties();
   1.136 +
   1.137 +        synchronized (AccessStatistics.class) {
   1.138 +            if (lastSaveStamp == lastModifyStamp) return;
   1.139 +            lastSaveStamp = lastModifyStamp;
   1.140 +            for (Entry<String, Long> e : statistics.entrySet()) {
   1.141 +                p.setProperty(e.getKey(), Long.toString(e.getValue()));
   1.142 +            }
   1.143 +        }
   1.144 +
   1.145 +        File accessStatistics = CategoryStorage.getAccessStatisticsFile();
   1.146 +        File tempFile = new File(accessStatistics.getParentFile(), accessStatistics.getName() + ".new");
   1.147 +        OutputStream out = null;
   1.148 +
   1.149 +        try {
   1.150 +            out = new BufferedOutputStream(new FileOutputStream(tempFile));
   1.151 +
   1.152 +            p.store(out, "");
   1.153 +        } catch (IOException ex) {
   1.154 +            Logger.getLogger(AccessStatistics.class.getName()).log(Level.SEVERE, null, ex);
   1.155 +        } finally {
   1.156 +            if (out != null) {
   1.157 +                try {
   1.158 +                    out.close();
   1.159 +                } catch (IOException ex) {
   1.160 +                    Logger.getLogger(AccessStatistics.class.getName()).log(Level.SEVERE, null, ex);
   1.161 +                }
   1.162 +
   1.163 +                tempFile.renameTo(accessStatistics);
   1.164 +            }
   1.165 +        }
   1.166 +    }
   1.167 +
   1.168 +    public static synchronized Map<String, Long> getStatistics() {
   1.169 +        return Collections.unmodifiableMap(new HashMap<String, Long>(statistics));
   1.170 +    }
   1.171 +
   1.172 +    @Override
   1.173 +    public ContainerRequest filter(ContainerRequest request) {
   1.174 +        StringBuilder statisticsKey = new StringBuilder();
   1.175 +        List<String> paths = request.getQueryParameters().get("path");
   1.176 +
   1.177 +        statisticsKey.append(request.getPath());
   1.178 +
   1.179 +        if (paths != null) {
   1.180 +            for (String path : paths) {
   1.181 +                statisticsKey.append(":").append(path);
   1.182 +            }
   1.183 +        }
   1.184 +
   1.185 +        incrementUsage(statisticsKey.toString());
   1.186 +        
   1.187 +        return request;
   1.188 +    }
   1.189 +
   1.190 +}
     2.1 --- a/remoting/server/web/base.web.api/src/org/netbeans/modules/jackpot30/backend/base/CategoryStorage.java	Wed Oct 24 19:28:02 2012 +0200
     2.2 +++ b/remoting/server/web/base.web.api/src/org/netbeans/modules/jackpot30/backend/base/CategoryStorage.java	Sat Oct 27 14:43:13 2012 +0200
     2.3 @@ -80,6 +80,10 @@
     2.4          categoryCache = null;
     2.5      }
     2.6  
     2.7 +    public static synchronized File getAccessStatisticsFile() {
     2.8 +        return new File(cacheRoot, "accessStatistics");
     2.9 +    }
    2.10 +
    2.11      public static void internalReset() {
    2.12          setCacheRoot(cacheRoot);
    2.13      }
    2.14 @@ -95,6 +99,7 @@
    2.15          List<CategoryStorage> result = new ArrayList<CategoryStorage>();
    2.16  
    2.17          for (File cat : cacheRoot.listFiles()) {
    2.18 +            if (!cat.isDirectory()) continue;
    2.19              File info = new File(cat, "info");
    2.20              String displayName = cat.getName();
    2.21              if (info.canRead()) {
     3.1 --- a/remoting/server/web/base.web.api/src/org/netbeans/modules/jackpot30/backend/base/api/API.java	Wed Oct 24 19:28:02 2012 +0200
     3.2 +++ b/remoting/server/web/base.web.api/src/org/netbeans/modules/jackpot30/backend/base/api/API.java	Sat Oct 27 14:43:13 2012 +0200
     3.3 @@ -44,10 +44,9 @@
     3.4  import javax.ws.rs.Path;
     3.5  import javax.ws.rs.Produces;
     3.6  import javax.ws.rs.QueryParam;
     3.7 -import javax.ws.rs.core.Response;
     3.8 -import javax.ws.rs.core.Response.Status;
     3.9 +import org.codeviation.pojson.Pojson;
    3.10 +import org.netbeans.modules.jackpot30.backend.base.AccessStatistics;
    3.11  import org.netbeans.modules.jackpot30.backend.base.CategoryStorage;
    3.12 -import org.openide.filesystems.FileObject;
    3.13  
    3.14  /**
    3.15   *
    3.16 @@ -92,4 +91,11 @@
    3.17          return cat.getInfo();
    3.18      }
    3.19  
    3.20 +    @GET
    3.21 +    @Path("/accessStatistics")
    3.22 +    @Produces("text/plain")
    3.23 +    public String accessStatistics() throws IOException {
    3.24 +        return Pojson.save(AccessStatistics.getStatistics());
    3.25 +    }
    3.26 +
    3.27  }
     4.1 --- a/remoting/server/web/web.main/src/web/main/WebMain.java	Wed Oct 24 19:28:02 2012 +0200
     4.2 +++ b/remoting/server/web/web.main/src/web/main/WebMain.java	Sat Oct 27 14:43:13 2012 +0200
     4.3 @@ -56,6 +56,7 @@
     4.4  import java.util.ArrayList;
     4.5  import java.util.Arrays;
     4.6  import java.util.List;
     4.7 +import org.netbeans.modules.jackpot30.backend.base.AccessStatistics;
     4.8  import org.netbeans.modules.jackpot30.backend.base.CategoryStorage;
     4.9  import org.netbeans.modules.jackpot30.backend.base.RelStreamHandlerFactory;
    4.10  import org.openide.util.Exceptions;
    4.11 @@ -104,6 +105,7 @@
    4.12          // Jersey web resources
    4.13          ServletAdapter jerseyAdapter = new ServletAdapter();
    4.14          jerseyAdapter.addInitParameter("com.sun.jersey.config.property.packages", "org.netbeans.modules.jackpot30");
    4.15 +        jerseyAdapter.addInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters", AccessStatistics.class.getName());
    4.16  //        jerseyAdapter.setContextPath("/");
    4.17          jerseyAdapter.setServletInstance(new ServletContainer());
    4.18