usr/src/java/rad/org/opensolaris/os/rad/Client.java
author Stephen Talley <stephen.talley@oracle.com>
Wed, 14 Mar 2012 10:45:15 -0400
changeset 809 8a6fba4105d7
parent 749 dd22ec9bff4a
permissions -rw-r--r--
7150175 radadrgen should generate rad module man pages 7150179 radadrgen should validate against original rng schema 7150184 radadrgen's output doesn't conform to docbook schema 7150189 adr schema should support documentation markup for union arms 7150226 radadrgen transforms should be internationalized 7150292 radadrgen -c should take a directory name, like -j 7150294 radadrgen command line usage should be broken into multiple synopses 7150352 radadrgen -o text should be revisited 7106700 radadrgen man page lacks documentation, -N, -m options

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
 */

package org.opensolaris.os.rad;

import java.io.*;
import java.net.InetAddress;
import java.util.*;
import java.util.logging.Logger;
import javax.management.openmbean.*;
import org.opensolaris.os.adr.*;
import org.opensolaris.os.rad.errors.target;
import org.opensolaris.os.xdr.*;

public class Client implements Closeable {

    private static final Logger log = Logger.getLogger(Client.class.getName());

    private static final int INVOKE = 0;
    private static final int GETATTR = 1;
    private static final int SETATTR = 2;
    private static final int LOOKUP = 3;
    private static final int DEFINE = 4;
    private static final int LIST = 5;
    private static final int SUB = 6;
    private static final int UNSUB = 7;

    private static final Map<String, Property> emptyPmap_ =
	Collections.emptyMap();
    private static final Map<String, Method> emptyMmap_ =
	Collections.emptyMap();
    private static final Map<String, Event> emptyEmap_ =
	Collections.emptyMap();

    static private Property findProperty(Instance inst, String pname)
	throws ContainerException {

	Property p = inst.getAPI().getProp(pname);
	if (p != null)
	    return p;
	throw new TargetedException(RadError.NOTFOUND, target.feature, pname,
	    "Unknown property: " + pname);
    }

    static private Method findMethod(Instance inst, String mname)
	throws ContainerException {

	Method m = inst.getAPI().getMethod(mname);
	if (m != null)
	    return m;
	throw new TargetedException(RadError.NOTFOUND, target.feature, mname,
	    "Unknown operation: " + mname);
    }


    private ADRXDR adrxdr_;
    private ClientFramer framer_;
    private final Map<Long, API> apimap_ = new HashMap<Long, API>();
    private final Map<Long, Instance> idmap_ = new HashMap<Long, Instance>();
    private final Map<RadError, Type> errorTypes_ =
	new HashMap<RadError, Type>();

    public Client(Transport xport, EnumMapper mapper, String locale)
	throws IOException {

	if (mapper == null)
	    mapper = new PlainEnumMapper();
	mapper.setClient(this);
	adrxdr_ = new ADRXDR(mapper);
	framer_ = new ClientFramer(xport, adrxdr_, locale);
	for (RadError e : RadError.values()) {
	    int i = e.toCode() - 2;
	    if (i < 0)
		    continue;

	    Type t = null;
	    if (i >= framer_.errors_.length ||
		!((t = framer_.errors_[i]) instanceof Type.ComplexType)) {
		if (t != Type.getType(StdType.VOID))
		    System.err.println(
			"Client.<init>: missing or bad error type: " + e);
		errorTypes_.put(e, Type.getType(StdType.VOID));
	    } else {
		errorTypes_.put(e, t);
	    }
	}
    }

    private ContainerException toException(Response r, String name, Type t) {
	assert (!r.isSuccess());
	RadError e = r.getError();

	CompositeData payload = null;
	try {
	    if (e == RadError.OBJECT) {
		if (t == null) {
		    /* Violation of interface; map to system error */
		    e = RadError.SYSTEM;
		} else if (t instanceof Type.ComplexType) {
		    XDRInputStream is = r.getPayload();
		    payload = (CompositeData)adrxdr_.readOptional(t, is);
		}
	    } else {
		XDRInputStream is = r.getPayload();
		t = errorTypes_.get(e);
		payload = (CompositeData)adrxdr_.readOptional(t, is);
	    }
	} catch (ClassCastException ex) {
	} catch (IOException ex) {
	} catch (OpenDataException ex) {
	}

	switch (e) {
	case OBJECT:
	    return new ObjectException(name, payload, null);
	case MISMATCH:
	case EXISTS:
	case NOTFOUND:
	    /* Just a helpful special case of ContainerException */
	    return new TargetedException(e, payload, null);
	default:
	    return new ContainerException(e, payload, null);
	}
    }

    private ContainerException toException(Response r) {
	return toException(r, null, null);
    }

    private void checkInstance(Instance inst) {
	if (inst.getClient() != this)
	    throw new IllegalArgumentException(
		"Given Instance from different Client");
    }

    public List<ADRName> list() throws IOException {
	/*
	 * Request
	 */
	ByteArrayOutputStream listop = new ByteArrayOutputStream(4);
	new XDROutputStream(listop).putString("");
	Response r = framer_.syncRequest(LIST, listop);

	/*
	 * Response
	 */
	if (r.isSuccess()) {
	    XDRInputStream listresp = r.getPayload();
	    int count = listresp.getInteger();
	    List<ADRName> result = new LinkedList<ADRName>();
	    for (int i = 0; i < count; i++)
		result.add(new ADRName(listresp.getString()));
	    return result;
	} else {
	    throw new IOException(toException(r));
	}
    }

    public void setattr(Instance inst, String aname, Object data)
	throws ContainerException, IOException {

	checkInstance(inst);
	Property p = findProperty(inst, aname);

	if (!p.isWritable())
	    throw new ContainerException(RadError.ILLEGAL, null,
		"Attribute not writable");

	/*
	 * Request
	 */
	ByteArrayOutputStream value = new ByteArrayOutputStream();
	try {
	    adrxdr_.writeOptional(new XDROutputStream(value), p.getType(),
		data);
	} catch (Exception e) {
	    throw new TargetedException(RadError.MISMATCH, target.feature,
		aname, "Invalid type");
	}
	ByteArrayOutputStream descop = new ByteArrayOutputStream();
	new XDROutputStream(descop).putHyper(inst.getID()).putString(aname).
	    putOpaque(value);
	Response r = framer_.syncRequest(SETATTR, descop);

	if (!r.isSuccess())
	    throw toException(r, inst.getName().toString(),
		p.getWriteErrorType());
    }

    public Object getattr(Instance inst, String aname)
	throws IOException, ContainerException {

	checkInstance(inst);
	Property p = findProperty(inst, aname);
	if (!p.isReadable())
	    throw new ContainerException(RadError.ILLEGAL, null);

	/*
	 * Request
	 */
	ByteArrayOutputStream descop = new ByteArrayOutputStream();
	new XDROutputStream(descop).putHyper(inst.getID()).putString(aname);
	Response r = framer_.syncRequest(GETATTR, descop);

	/*
	 * Response
	 */
	if (r.isSuccess()) {
	    XDRInputStream descresp = r.getPayload();
	    try {
		return adrxdr_.readOptional(p.getType(), descresp);
	    } catch (OpenDataException ex) {
		throw new ContainerException(RadError.SYSTEM, null,
		    "result: ODE error");
	    }
	} else {
	    throw toException(r, inst.getName().toString(),
		p.getReadErrorType());
	}
    }

    public Object invoke(Instance inst, String mname, Object params[])
	throws IOException, ContainerException {

	checkInstance(inst);
	Method m = findMethod(inst, mname);

	int nparam = params == null ? 0 : params.length;
	if (nparam != m.getArguments().size()) {
	    throw new TargetedException(RadError.MISMATCH, target.feature,
		mname, String.format(
		"Wrong parameter count.  Expected %d, got %d.",
		m.getArguments().size(), params.length));
	}

	/*
	 * Request
	 */
	ByteArrayOutputStream descop = new ByteArrayOutputStream();
	XDROutputStream reqstream =
	    new XDROutputStream(descop).putHyper(inst.getID()).putString(mname);
	reqstream.putInteger(nparam);
	for (int i = 0; i < nparam; i++) {
	    ByteArrayOutputStream paramstr = new ByteArrayOutputStream();
	    Argument a = m.getArguments().get(i);
	    Type argtype = a.getType();
	    try {
		adrxdr_.writeOptional(new XDROutputStream(paramstr), argtype,
		    params[i]);
	    } catch (Exception e) {
		throw new TargetedException(RadError.MISMATCH, target.argument,
		    a.getName(),
		    String.format("parameter %d has the wrong type", i));
	    }
	    reqstream.putOpaque(paramstr);
	}
	Response r = framer_.syncRequest(INVOKE, descop);

	/*
	 * Response
	 */
	if (r.isSuccess()) {
	    XDRInputStream descresp = r.getPayload();
	    try {
		return adrxdr_.readOptional(m.getType(), descresp);
	    } catch (OpenDataException ex) {
		throw new ContainerException(RadError.SYSTEM, null,
		    "result: ODE error");
	    }
	} else {
	    throw toException(r, inst.getName().toString(), m.getErrorType());
	}
    }

    private API readAPI(XDRInputStream descresp) throws IOException,
	ContainerException {

	Interface iface = new Interface(descresp.getString());
	int nAPIs = descresp.getInteger();
	if (nAPIs < 1)
	    throw new IOException("invalid API count");

	String aname = null;
	Map<Stability, APIVersion> vers = null;
	for (int a = 0; a < nAPIs; a++) {
	    vers = new EnumMap<Stability, APIVersion>(Stability.class);
	    aname = descresp.getString();
	    for (int i = 0; i < 3; i++) {
		Stability stability = ADRXDR.readStability(descresp);
		int major = descresp.getInteger();
		int minor = descresp.getInteger();
		if (APIVersion.isValid(stability, major, minor))
		    vers.put(stability,
			new APIVersion(stability, major, minor));
	    }
	    /* The last API is the real one */
	    if (a < nAPIs - 1)
		iface.addAPI(new API(aname, vers, emptyPmap_,
		    emptyMmap_, emptyEmap_, null));
	}

	/*
	 * Types
	 */
	List<Type> types = adrxdr_.readTypes(descresp);

	/*
	 * Attributes
	 */
	int count = descresp.getInteger();
	Map<String, Property> props = new TreeMap<String, Property>();
	for (int i = 0; i < count; i++) {
	    String attname = descresp.getString();
	    Stability stability = ADRXDR.readStability(descresp);
	    boolean readable = descresp.getBoolean();
	    boolean writable = descresp.getBoolean();
	    boolean optional = descresp.getBoolean();
	    Type type = adrxdr_.readRef(descresp, types, false);
	    Type readErrorType = adrxdr_.readRef(descresp, types, true);
	    Type writeErrorType = adrxdr_.readRef(descresp, types, true);
	    ADRError rerr = readErrorType != null ?
		new ADRError(readErrorType) : null;
	    ADRError werr = writeErrorType != null ?
		new ADRError(writeErrorType) : null;
	    props.put(attname, new Property(attname, stability, type,
		optional, readable, writable, rerr, werr));
	}

	/*
	 * Methods
	 */
	count = descresp.getInteger();
	Map<String, Method> meths = new TreeMap<String, Method>();
	for (int i = 0; i < count; i++) {
	    String mname = descresp.getString();
	    Stability stability = ADRXDR.readStability(descresp);
	    boolean optional = descresp.getBoolean();
	    Type type = adrxdr_.readRef(descresp, types, false);
	    Result result = new Result(type, optional);
	    Type terr = adrxdr_.readRef(descresp, types, true);
	    ADRError errortype = terr != null ? new ADRError(terr) : null;
	    int acount = descresp.getInteger();
	    List<Argument> args = new ArrayList<Argument>(acount);
	    for (int j = 0; j < acount; j++) {
		String pname = descresp.getString();
		boolean poptional = descresp.getBoolean();
		Type ptype = adrxdr_.readRef(descresp, types, false);
		args.add(new Argument(pname, ptype, poptional));
	    }
	    meths.put(mname, new Method(mname, stability, result,
		args, errortype));
	}

	/*
	 * Events
	 */
	count = descresp.getInteger();
	Map<String, Event> events = new TreeMap<String, Event>();
	for (int i = 0; i < count; i++) {
	    String nname = descresp.getString();
	    Stability stability = ADRXDR.readStability(descresp);
	    Type ntype = adrxdr_.readRef(descresp, types, false);
	    events.put(nname, new Event(nname, stability, ntype));
	}

	API api = new API(aname, vers, props, meths, events,
	    new ArrayList<API>(iface.getAPIs().values()));
	iface.addAPI(api);

	return api;
    }

    private API define(long id) throws IOException, ContainerException {
	/*
	 * Request
	 */
	ByteArrayOutputStream descop = new ByteArrayOutputStream();
	new XDROutputStream(descop).putHyper(id);
	Response r = framer_.syncRequest(DEFINE, descop);

	/*
	 * Response
	 */
	if (r.isSuccess()) {
	    XDRInputStream descresp = r.getPayload();
	    return readAPI(descresp);
	} else {
	    throw toException(r);
	}
    }

    public Instance describe(ADRName name) throws IOException,
	ContainerException {

	/*
	 * Request
	 */
	ByteArrayOutputStream descop = new ByteArrayOutputStream();
	new XDROutputStream(descop).putString(name.toString()).
	    putBoolean(false);
	Response r = framer_.syncRequest(LOOKUP, descop);

	/*
	 * Response
	 */
	if (r.isSuccess()) {
	    XDRInputStream descresp = r.getPayload();

	    long id = descresp.getHyper();
	    synchronized (idmap_) {
		/*
		 * Though we read-through, we will short-circuit processing
		 * the rest of the response if the object is the same.
		 */
		Instance i = idmap_.get(id);
		if (i != null)
		    return i;
	    }

	    long typeid = descresp.getHyper();
	    API a;

	    synchronized (apimap_) {
		a = apimap_.get(typeid);
	    }

	    if (a == null) {
		a = descresp.getBoolean() ? readAPI(descresp) : define(typeid);
		synchronized (apimap_) {
		    if (!apimap_.containsKey(typeid))
			apimap_.put(typeid, a);
		    else
			a = apimap_.get(typeid);
		}
	    }

	    Instance i = new Instance(this, a, id, name);
	    synchronized (idmap_) {
		idmap_.put(i.getID(), i);
	    }
	    return i;
	} else {
	    throw toException(r);
	}
    }

    public OpenType<?> getOpenType(Type t) {
	return adrxdr_.getOpenType(t);
    }

    public void subscribe(Instance inst, String event)
	throws IOException, ContainerException {

	checkInstance(inst);
	/*
	 * Request
	 */
	ByteArrayOutputStream descop = new ByteArrayOutputStream();
	new XDROutputStream(descop).putHyper(inst.getID()).putString(event);
	Response r = framer_.syncRequest(SUB, descop);

	/*
	 * Response
	 */
	if (!r.isSuccess())
	    throw toException(r);
    }

    public void unsubscribe(Instance inst, String event)
	throws IOException, ContainerException {

	checkInstance(inst);
	/*
	 * Request
	 */
	ByteArrayOutputStream descop = new ByteArrayOutputStream();
	new XDROutputStream(descop).putHyper(inst.getID()).putString(event);
	Response r = framer_.syncRequest(UNSUB, descop);

	/*
	 * Response
	 */
	if (!r.isSuccess())
	    throw toException(r);
    }

    public RadEvent readEvent() throws IOException, ContainerException,
	OpenDataException {

	Instance inst;
	RadEvent e;
	do {
	    e = framer_.readEvent();
	    synchronized (idmap_) {
		inst = idmap_.get(e.getSource());
	    }
	    if (inst == null)
		log.warning("Received event from unknown source: " +
		    e.getSource());
	} while (inst == null);

	e.instance_ = inst;
	Event ev = inst.getAPI().getEvent(e.getEvent());
	if (ev != null) {
	    XDRInputStream is = new XDRInputStream(e.getPayload());
	    e.data_ = adrxdr_.readOptional(ev.getType(), is);
	}
	return e;
    }

    public void close() throws IOException {
	framer_.close();
    }

    public static void main(String args[]) {
	try {
	    Client c = new Client(SocketTransport.newSocketTransport(
		InetAddress.getLocalHost(), 1234), null, "C");

	    List<ADRName> names = c.list();
	    System.out.format("%d objects:\n", names.size());
	    for (ADRName name : names)
		System.out.println("  " + name);
	} catch (IOException ex) {
	    System.out.println("Exception caught:" + ex);
	}
    }
}