001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package com.sun.servicetag;
043:
044: import java.io.*;
045: import java.util.ArrayList;
046: import java.util.Date;
047: import java.util.HashSet;
048: import java.util.List;
049: import java.util.Properties;
050: import java.util.Set;
051:
052: import static com.sun.servicetag.Util.*;
053: import static com.sun.servicetag.RegistrationDocument.*;
054:
055: /**
056: * A service tag registry is a XML-based registry containing
057: * the list of {@link ServiceTag service tags} installed in the system.
058: * The {@code Registry} class provides interfaces
059: * to add, remove, update, and get a service tag from a service tag
060: * registry.
061: * This {@code Registry} class may not be supported
062: * on all systems. The {@link #isSupported} method
063: * can be called to determine if it is supported.
064: * <p>
065: * A registry may implement restrictions to only allow certain users
066: * to {@link #updateServiceTag update} and
067: * to {@link #removeServiceTag remove} a service tag record. Typically,
068: * only the owner of the service tag, the owner of the registry
069: * and superuser are authorized to update or remove a service tag in
070: * the registry.
071: *
072: * @see <a href="https://sn-tools.central.sun.com/twiki/bin/view/ServiceTags/ServiceTagDevGuideHelper">
073: * Service Tag User Guide</a>
074: */
075: public class Registry {
076:
077: private static final String STCLIENT_SOLARIS = "/usr/bin/stclient";
078: private static final String STCLIENT_LINUX = "/opt/sun/servicetag/bin/stclient";
079: // stclient exit value (see sthelper.h)
080: private static final int ST_ERR_NOT_AUTH = 245;
081: private static final int ST_ERR_REC_NOT_FOUND = 225;
082:
083: // The stclient output has to be an exported interface
084: private static final String INSTANCE_URN_DESC = "Product instance URN=";
085: private static boolean initialized = false;
086: private static boolean supportsHelperClass = true; // default
087: private static File stclient = null;
088: private static String stclientPath = null;
089: private static Registry registry = new Registry();
090:
091: // System properties for testing
092: private static String SVCTAG_STCLIENT_CMD = "servicetag.stclient.cmd";
093: private static String SVCTAG_STHELPER_SUPPORTED = "servicetag.sthelper.supported";
094:
095: private Registry() {
096: }
097:
098: private synchronized static String getSTclient() {
099: if (!initialized) {
100: // the system property always overrides the default setting
101: if (System.getProperty(SVCTAG_STHELPER_SUPPORTED) != null) {
102: supportsHelperClass = Boolean
103: .getBoolean(SVCTAG_STHELPER_SUPPORTED);
104: }
105:
106: // This is only used for testing
107: stclientPath = System.getProperty(SVCTAG_STCLIENT_CMD);
108: if (stclientPath != null) {
109: return stclientPath;
110: }
111:
112: // Initialization to determine the platform's stclient pathname
113: String os = System.getProperty("os.name");
114: if (os.equals("SunOS")) {
115: stclient = new File(STCLIENT_SOLARIS);
116: } else if (os.equals("Linux")) {
117: stclient = new File(STCLIENT_LINUX);
118: } else if (os.startsWith("Windows")) {
119: stclient = getWindowsStClientFile();
120: } else {
121: if (isVerbose()) {
122: System.out.println("Running on non-Sun JDK");
123: }
124: }
125: initialized = true;
126: }
127:
128: // com.sun.servicetag package has to be compiled with JDK 5 as well
129: // JDK 5 doesn't support the File.canExecute() method.
130: // Risk not checking isExecute() for the stclient command is very low.
131:
132: if (stclientPath == null && stclient != null
133: && stclient.exists()) {
134: stclientPath = stclient.getAbsolutePath();
135: }
136: return stclientPath;
137: }
138:
139: /**
140: * Returns the system service tag registry. The {@code Registry} class
141: * may not be supported on some platforms; use the {@link #isSupported}
142: * method to determine if it is supported.
143: *
144: * @return the {@code Registry} object for the system service tag registry.
145: *
146: * @throws UnsupportedOperationException if the {@code Registry} class is
147: * not supported.
148: */
149: public static Registry getSystemRegistry() {
150: if (isSupported()) {
151: return registry;
152: } else {
153: throw new UnsupportedOperationException(
154: "Registry class is not supported");
155: }
156: }
157:
158: /**
159: * Returns {@code true} if the {@code Registry} class is supported on this system.
160: *
161: * @return {@code true} if the {@code Registry} class is supported;
162: * otherwise, return {@code false}.
163: */
164: public static boolean isSupported() {
165: return (getSTclient() != null && supportsHelperClass);
166: }
167:
168: private static List<String> getCommandList() {
169: // Set up the arguments to call stclient
170: List<String> command = new ArrayList<String>();
171: if (System.getProperty(SVCTAG_STCLIENT_CMD) != null) {
172: // This is for jtreg testing use. This will be set to something
173: // like:
174: // $JAVA_HOME/bin/java -cp $TEST_DIR \
175: // -Dstclient.registry.path=$TEST_DIR/registry.xml \
176: // SvcTagClient
177: //
178: // On Windows, the JAVA_HOME and TEST_DIR path could contain
179: // space e.g. c:\Program Files\Java\jdk1.6.0_05\bin\java.
180: // The SVCTAG_STCLIENT_CMD must be set with a list of
181: // space-separated parameters. If a parameter contains spaces,
182: // it must be quoted with '"'.
183:
184: String cmd = getSTclient();
185: int len = cmd.length();
186: int i = 0;
187: while (i < len) {
188: char separator = ' ';
189: if (cmd.charAt(i) == '"') {
190: separator = '"';
191: i++;
192: }
193: // look for the separator or matched the closing '"'
194: int j;
195: for (j = i + 1; j < len; j++) {
196: if (cmd.charAt(j) == separator) {
197: break;
198: }
199: }
200:
201: if (i == j - 1) {
202: // add an empty parameter
203: command.add("\"\"");
204: } else {
205: // double quotes and space are not included
206: command.add(cmd.substring(i, j));
207: }
208:
209: // skip spaces
210: for (i = j + 1; i < len; i++) {
211: if (!Character.isSpaceChar(cmd.charAt(i))) {
212: break;
213: }
214: }
215: }
216: if (isVerbose()) {
217: System.out.println("Command list:");
218: for (String s : command) {
219: System.out.println(s);
220: }
221: }
222: } else {
223: command.add(getSTclient());
224: }
225: return command;
226: }
227:
228: // Returns null if the service tag record not found;
229: // or throw UnauthorizedAccessException or IOException
230: // based on the exitValue.
231: private static ServiceTag checkReturnError(int exitValue,
232: String output, ServiceTag st) throws IOException {
233: switch (exitValue) {
234: case ST_ERR_REC_NOT_FOUND:
235: return null;
236: case ST_ERR_NOT_AUTH:
237: if (st != null) {
238: throw new UnauthorizedAccessException(
239: "Not authorized to access "
240: + st.getInstanceURN()
241: + " installer_uid="
242: + st.getInstallerUID());
243: } else {
244: throw new UnauthorizedAccessException("Not authorized:"
245: + output);
246: }
247: default:
248: throw new IOException("stclient exits with error" + " ("
249: + exitValue + ")\n" + output);
250: }
251: }
252:
253: /**
254: * Adds a service tag to this registry.
255: * If the given service tag has an empty <tt>instance_urn</tt>,
256: * this helper class will generate a URN and place it in the
257: * copy of the service tag in this registry.
258: * This method will return the {@code ServiceTag} representing
259: * the service tag entry to this registry.
260: *
261: * @param st {@code ServiceTag} object
262: * @return a {@code ServiceTag} object representing the service tag
263: * entry to this registry.
264: *
265: * @throws IllegalArgumentException if a service tag of the same
266: * <tt>instance_urn</tt> already exists in this registry.
267: *
268: * @throws java.io.IOException if an I/O error occurs in this operation.
269: */
270: public ServiceTag addServiceTag(ServiceTag st) throws IOException {
271: List<String> command = getCommandList();
272: command.add("-a");
273: if (st.getInstanceURN().length() > 0) {
274: ServiceTag sysSvcTag = getServiceTag(st.getInstanceURN());
275: if (sysSvcTag != null) {
276: throw new IllegalArgumentException("Instance_urn = "
277: + st.getInstanceURN() + " already exists");
278: }
279: command.add("-i");
280: command.add(st.getInstanceURN());
281: }
282: command.add("-p");
283: command.add(st.getProductName());
284: command.add("-e");
285: command.add(st.getProductVersion());
286: command.add("-t");
287: command.add(st.getProductURN());
288: if (st.getProductParentURN().length() > 0) {
289: command.add("-F");
290: command.add(st.getProductParentURN());
291: }
292: command.add("-P");
293: command.add(st.getProductParent());
294: if (st.getProductDefinedInstanceID().length() > 0) {
295: command.add("-I");
296: command.add(st.getProductDefinedInstanceID());
297: }
298: command.add("-m");
299: command.add(st.getProductVendor());
300: command.add("-A");
301: command.add(st.getPlatformArch());
302: command.add("-z");
303: command.add(st.getContainer());
304: command.add("-S");
305: command.add(st.getSource());
306:
307: BufferedReader in = null;
308: try {
309: ProcessBuilder pb = new ProcessBuilder(command);
310: Process p = pb.start();
311: String output = commandOutput(p);
312: if (isVerbose()) {
313: System.out.println("Output from stclient -a command:");
314: System.out.println(output);
315: }
316: String urn = "";
317: if (p.exitValue() == 0) {
318: // Obtain the instance urn from the stclient output
319: in = new BufferedReader(new StringReader(output));
320: String line = null;
321: while ((line = in.readLine()) != null) {
322: line = line.trim();
323: if (line.startsWith(INSTANCE_URN_DESC)) {
324: urn = line
325: .substring(INSTANCE_URN_DESC.length());
326: break;
327: }
328: }
329: if (urn.length() == 0) {
330: throw new IOException(
331: "Error in creating service tag:\n" + output);
332: }
333: return getServiceTag(urn);
334: } else {
335: return checkReturnError(p.exitValue(), output, st);
336: }
337: } finally {
338: if (in != null) {
339: in.close();
340: }
341: }
342: }
343:
344: /**
345: * Removes a service tag of the given <tt>instance_urn</tt> from this
346: * registry.
347: *
348: * @param instanceURN the <tt>instance_urn</tt> of the service tag
349: * to be removed.
350: *
351: * @return the {@code ServiceTag} object removed from this registry;
352: * or {@code null} if the service tag does not exist in this registry.
353: *
354: * @throws UnauthorizedAccessException if the user is not authorized to
355: * remove the service tag of the given <tt>instance_urn</tt>
356: * from this registry.
357: *
358: * @throws java.io.IOException if an I/O error occurs in this operation.
359: */
360: public ServiceTag removeServiceTag(String instanceURN)
361: throws IOException {
362: ServiceTag st = getServiceTag(instanceURN);
363: if (st == null) {
364: return null;
365: }
366:
367: List<String> command = getCommandList();
368: command.add("-d");
369: command.add("-i");
370: command.add(instanceURN);
371:
372: ProcessBuilder pb = new ProcessBuilder(command);
373: Process p = pb.start();
374: String output = commandOutput(p);
375: if (isVerbose()) {
376: System.out.println("Output from stclient -d command:");
377: System.out.println(output);
378: }
379: if (p.exitValue() == 0) {
380: return st;
381: } else {
382: return checkReturnError(p.exitValue(), output, st);
383: }
384: }
385:
386: /**
387: * Updates the <tt>product_defined_instance_id</tt> in the service tag
388: * of the specified <tt>instance_urn</tt> in this registry.
389: *
390: * @param instanceURN the <tt>instance_urn</tt> of the service tag to be updated.
391: * @param productDefinedInstanceID the value of the
392: * <tt>product_defined_instance_id</tt> to be set.
393: *
394: * @return the updated {@code ServiceTag} object;
395: * or {@code null} if the service tag does not exist in this
396: * registry.
397: *
398: * @throws UnauthorizedAccessException if the user is not authorized to
399: * update the service tag from this registry.
400: *
401: * @throws IOException if an I/O error occurs in this operation.
402: */
403: public ServiceTag updateServiceTag(String instanceURN,
404: String productDefinedInstanceID) throws IOException {
405: ServiceTag svcTag = getServiceTag(instanceURN);
406: if (svcTag == null) {
407: return null;
408: }
409:
410: List<String> command = getCommandList();
411: command.add("-u");
412: command.add("-i");
413: command.add(instanceURN);
414: command.add("-I");
415: if (productDefinedInstanceID.length() > 0) {
416: command.add(productDefinedInstanceID);
417: } else {
418: command.add("\"\"");
419: }
420:
421: ProcessBuilder pb = new ProcessBuilder(command);
422: Process p = pb.start();
423: String output = commandOutput(p);
424: if (isVerbose()) {
425: System.out.println("Output from stclient -u command:");
426: System.out.println(output);
427: }
428:
429: if (p.exitValue() == 0) {
430: return getServiceTag(instanceURN);
431: } else {
432: return checkReturnError(p.exitValue(), output, svcTag);
433: }
434: }
435:
436: /**
437: * Returns a {@code ServiceTag} object of the given <tt>instance_urn</tt>
438: * in this registry.
439: *
440: * @param instanceURN the <tt>instance_urn</tt> of the service tag
441: * @return a {@code ServiceTag} object of the given <tt>instance_urn</tt>
442: * in this registry; or {@code null} if not found.
443: *
444: * @throws java.io.IOException if an I/O error occurs in this operation.
445: */
446: public ServiceTag getServiceTag(String instanceURN)
447: throws IOException {
448: if (instanceURN == null) {
449: throw new NullPointerException("instanceURN is null");
450: }
451:
452: List<String> command = getCommandList();
453: command.add("-g");
454: command.add("-i");
455: command.add(instanceURN);
456:
457: ProcessBuilder pb = new ProcessBuilder(command);
458: Process p = pb.start();
459: String output = commandOutput(p);
460: if (isVerbose()) {
461: System.out.println("Output from stclient -g command:");
462: System.out.println(output);
463: }
464: if (p.exitValue() == 0) {
465: return parseServiceTag(output);
466: } else {
467: return checkReturnError(p.exitValue(), output, null);
468: }
469: }
470:
471: private ServiceTag parseServiceTag(String output)
472: throws IOException {
473: BufferedReader in = null;
474: try {
475: Properties props = new Properties();
476: // parse the service tag output from stclient
477: in = new BufferedReader(new StringReader(output));
478: String line = null;
479: while ((line = in.readLine()) != null) {
480: if ((line = line.trim()).length() > 0) {
481: String[] ss = line.trim().split("=", 2);
482: if (ss.length == 2) {
483: props.setProperty(ss[0].trim(), ss[1].trim());
484: } else {
485: props.setProperty(ss[0].trim(), "");
486: }
487: }
488: }
489:
490: String urn = props.getProperty(ST_NODE_INSTANCE_URN);
491: String productName = props
492: .getProperty(ST_NODE_PRODUCT_NAME);
493: String productVersion = props
494: .getProperty(ST_NODE_PRODUCT_VERSION);
495: String productURN = props.getProperty(ST_NODE_PRODUCT_URN);
496: String productParent = props
497: .getProperty(ST_NODE_PRODUCT_PARENT);
498: String productParentURN = props
499: .getProperty(ST_NODE_PRODUCT_PARENT_URN);
500: String productDefinedInstanceID = props
501: .getProperty(ST_NODE_PRODUCT_DEFINED_INST_ID);
502: String productVendor = props
503: .getProperty(ST_NODE_PRODUCT_VENDOR);
504: String platformArch = props
505: .getProperty(ST_NODE_PLATFORM_ARCH);
506: String container = props.getProperty(ST_NODE_CONTAINER);
507: String source = props.getProperty(ST_NODE_SOURCE);
508: int installerUID = Util.getIntValue(props
509: .getProperty(ST_NODE_INSTALLER_UID));
510: Date timestamp = Util.parseTimestamp(props
511: .getProperty(ST_NODE_TIMESTAMP));
512:
513: return new ServiceTag(urn, productName, productVersion,
514: productURN, productParent, productParentURN,
515: productDefinedInstanceID, productVendor,
516: platformArch, container, source, installerUID,
517: timestamp);
518: } finally {
519: if (in != null) {
520: in.close();
521: }
522: }
523:
524: }
525:
526: /**
527: * Returns the service tags of the specified
528: * <tt>product_urn</tt> in this registry.
529: *
530: * @param productURN the <tt>product_urn</tt> to look up
531: * @return a {@code Set} of {@code ServiceTag} objects
532: * of the specified <tt>product_urn</tt> in this registry.
533: *
534: * @throws java.io.IOException if an I/O error occurs in this operation.
535: */
536: public Set<ServiceTag> findServiceTags(String productURN)
537: throws IOException {
538: if (productURN == null) {
539: throw new NullPointerException("productURN is null");
540: }
541:
542: List<String> command = getCommandList();
543: command.add("-f");
544: command.add("-t");
545: command.add(productURN);
546:
547: BufferedReader in = null;
548: try {
549: ProcessBuilder pb = new ProcessBuilder(command);
550: Process p = pb.start();
551: String output = commandOutput(p);
552:
553: Set<ServiceTag> instances = new HashSet<ServiceTag>();
554: if (p.exitValue() == 0) {
555: // parse the service tag output from stclient
556: in = new BufferedReader(new StringReader(output));
557: String line = null;
558: while ((line = in.readLine()) != null) {
559: String s = line.trim();
560: if (s.startsWith("urn:st:")) {
561: instances.add(getServiceTag(s));
562: }
563: }
564: } else {
565: checkReturnError(p.exitValue(), output, null);
566: }
567: return instances;
568: } finally {
569: if (in != null) {
570: in.close();
571: }
572: }
573: }
574: }
|