1 /*
   2  * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.tools.serialver;
  27 
  28 import java.io.*;
  29 import java.io.ObjectStreamClass;
  30 import java.nio.file.Paths;
  31 import java.text.MessageFormat;
  32 import java.util.ResourceBundle;
  33 import java.util.MissingResourceException;
  34 import java.net.URLClassLoader;
  35 import java.net.URL;
  36 import java.net.MalformedURLException;
  37 
  38 /**
  39  * Supporting class for the serialver tool.
  40  */
  41 public class SerialVer {
  42 
  43     /*
  44      * A class loader that will load from the CLASSPATH environment
  45      * variable set by the user.
  46      */
  47     static URLClassLoader loader = null;
  48 
  49     /*
  50      * Create a URL class loader that will load classes from the
  51      * specified classpath.
  52      */
  53     static void initializeLoader(String cp) throws IOException {
  54         String[] paths = cp.split(File.pathSeparator);
  55         int count = paths.length;
  56         URL[] urls = new URL[count];
  57         for (int i = 0; i < count; i++) {
  58             urls[i] = Paths.get(paths[i]).toUri().toURL();
  59         }
  60         loader = new URLClassLoader(urls);
  61     }
  62 
  63     /*
  64      * From the classname find the serialVersionUID string formatted
  65      * for to be copied to a java class.
  66      */
  67     static String serialSyntax(String classname) throws ClassNotFoundException {
  68         String ret = null;
  69         boolean classFound = false;
  70 
  71         // If using old style of qualifying inner classes with '$'s.
  72         if (classname.indexOf('$') != -1) {
  73             ret = resolveClass(classname);
  74         } else {
  75             /* Try to resolve the fully qualified name and if that fails, start
  76              * replacing the '.'s with '$'s starting from the last '.', until
  77              * the class is resolved.
  78              */
  79             try {
  80                 ret = resolveClass(classname);
  81                 classFound = true;
  82             } catch (ClassNotFoundException e) {
  83                 /* Class not found so far */
  84             }
  85             if (!classFound) {
  86                 StringBuilder workBuffer = new StringBuilder(classname);
  87                 String workName = workBuffer.toString();
  88                 int i;
  89                 while ((i = workName.lastIndexOf('.')) != -1 && !classFound) {
  90                     workBuffer.setCharAt(i, '$');
  91                     try {
  92                         workName = workBuffer.toString();
  93                         ret = resolveClass(workName);
  94                         classFound = true;
  95                     } catch (ClassNotFoundException e) {
  96                         /* Continue searching */
  97                     }
  98                 }
  99             }
 100             if (!classFound) {
 101                 throw new ClassNotFoundException();
 102             }
 103         }
 104         return ret;
 105     }
 106 
 107     static String resolveClass(String classname) throws ClassNotFoundException {
 108         Class<?> cl = Class.forName(classname, false, loader);
 109         ObjectStreamClass desc = ObjectStreamClass.lookup(cl);
 110         if (desc != null) {
 111             return "    private static final long serialVersionUID = " +
 112                 desc.getSerialVersionUID() + "L;";
 113         } else {
 114             return null;
 115         }
 116     }
 117 
 118     /**
 119      * Entry point for serialver tool.
 120      * @param args the arguments
 121      */
 122     public static void main(String[] args) {
 123         String envcp = null;
 124         int i = 0;
 125 
 126         if (args.length == 0) {
 127             usage();
 128             System.exit(1);
 129         }
 130 
 131         for (i = 0; i < args.length; i++) {
 132             if (args[i].equals("-classpath")) {
 133                 if ((i+1 == args.length) || args[i+1].startsWith("-")) {
 134                     System.err.println(Res.getText("error.missing.classpath"));
 135                     usage();
 136                     System.exit(1);
 137                 }
 138                 envcp = new String(args[i+1]);
 139                 i++;
 140             }  else if (args[i].startsWith("-")) {
 141                 System.err.println(Res.getText("invalid.flag", args[i]));
 142                 usage();
 143                 System.exit(1);
 144             } else {
 145                 break;          // drop into processing class names
 146             }
 147         }
 148 
 149 
 150         /*
 151          * Get user's CLASSPATH environment variable, if the -classpath option
 152          * is not defined, and make a loader that can read from that path.
 153          */
 154         if (envcp == null) {
 155             envcp = System.getProperty("env.class.path");
 156             /*
 157              * If environment variable not set, add current directory to path.
 158              */
 159             if (envcp == null) {
 160                 envcp = ".";
 161             }
 162         }
 163 
 164         try {
 165             initializeLoader(envcp);
 166         } catch (MalformedURLException mue) {
 167             System.err.println(Res.getText("error.parsing.classpath", envcp));
 168             System.exit(2);
 169         } catch (IOException ioe) {
 170             System.err.println(Res.getText("error.parsing.classpath", envcp));
 171             System.exit(3);
 172         }
 173 
 174         /*
 175          * Check if there are any class names specified
 176          */
 177         if (i == args.length) {
 178             usage();
 179             System.exit(1);
 180         }
 181 
 182         /*
 183          * The rest of the parameters are classnames.
 184          */
 185         boolean exitFlag = false;
 186         for (i = i; i < args.length; i++ ) {
 187             try {
 188                 String syntax = serialSyntax(args[i]);
 189                 if (syntax != null)
 190                     System.out.println(args[i] + ":" + syntax);
 191                 else {
 192                     System.err.println(Res.getText("NotSerializable",
 193                         args[i]));
 194                     exitFlag = true;
 195                 }
 196             } catch (ClassNotFoundException cnf) {
 197                 System.err.println(Res.getText("ClassNotFound", args[i]));
 198                 exitFlag = true;
 199             }
 200         }
 201         if (exitFlag) {
 202             System.exit(1);
 203         }
 204     }
 205 
 206 
 207     /**
 208      * Usage
 209      */
 210     public static void usage() {
 211         System.err.println(Res.getText("usage"));
 212     }
 213 
 214 }
 215 
 216 /**
 217  * Utility for integrating with serialver and for localization.
 218  * Handle Resources. Access to error and warning counts.
 219  * Message formatting.
 220  *
 221  * @see java.util.ResourceBundle
 222  * @see java.text.MessageFormat
 223  */
 224 class Res {
 225 
 226     private static ResourceBundle messageRB;
 227 
 228     /**
 229      * Initialize ResourceBundle
 230      */
 231     static void initResource() {
 232         try {
 233             messageRB =
 234                 ResourceBundle.getBundle("sun.tools.serialver.resources.serialver");
 235         } catch (MissingResourceException e) {
 236             throw new Error("Fatal: Resource for serialver is missing");
 237         }
 238     }
 239 
 240     /**
 241      * get and format message string from resource
 242      *
 243      * @param key selects message from resource
 244      */
 245     static String getText(String key) {
 246         return getText(key, (String)null);
 247     }
 248 
 249     /**
 250      * get and format message string from resource
 251      *
 252      * @param key selects message from resource
 253      * @param a1 first argument
 254      */
 255     static String getText(String key, String a1) {
 256         return getText(key, a1, null);
 257     }
 258 
 259     /**
 260      * get and format message string from resource
 261      *
 262      * @param key selects message from resource
 263      * @param a1 first argument
 264      * @param a2 second argument
 265      */
 266     static String getText(String key, String a1, String a2) {
 267         return getText(key, a1, a2, null);
 268     }
 269 
 270     /**
 271      * get and format message string from resource
 272      *
 273      * @param key selects message from resource
 274      * @param a1 first argument
 275      * @param a2 second argument
 276      * @param a3 third argument
 277      */
 278     static String getText(String key, String a1, String a2, String a3) {
 279         if (messageRB == null) {
 280             initResource();
 281         }
 282         try {
 283             String message = messageRB.getString(key);
 284             return MessageFormat.format(message, a1, a2, a3);
 285         } catch (MissingResourceException e) {
 286             throw new Error("Fatal: Resource for serialver is broken. There is no " + key + " key in resource.");
 287         }
 288     }
 289 }