/* * Copyright 2008 Perforce Software Inc., All Rights Reserved. */ package com.perforce.p4java.server; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import com.perforce.p4java.CharsetDefs; import com.perforce.p4java.Log; import com.perforce.p4java.Metadata; import com.perforce.p4java.impl.generic.sys.ISystemFileCommandsHelper; import com.perforce.p4java.exception.ConfigException; import com.perforce.p4java.exception.ConnectionException; import com.perforce.p4java.exception.NoSuchObjectException; import com.perforce.p4java.exception.NullPointerError; import com.perforce.p4java.exception.ResourceException; import com.perforce.p4java.impl.mapbased.rpc.NtsServerImpl; import com.perforce.p4java.impl.mapbased.rpc.OneShotServerImpl; import com.perforce.p4java.impl.mapbased.rpc.sys.helper.RpcSystemFileCommandsHelper; import com.perforce.p4java.impl.mapbased.server.Server; import com.perforce.p4java.impl.mapbased.server.IServerControl; import com.perforce.p4java.impl.mapbased.server.ServerAddressBuilder; import com.perforce.p4java.option.UsageOptions; import com.perforce.p4java.server.IServerAddress.Protocol; /** * The main P4Java server factory class. This class is used to obtain an IServer interface * onto a particular Perforce server using a specific (or default) protocol. Usage * is described below with the getServer method. */ public class ServerFactory { public static final String TRACE_PREFIX = "ServerFactory"; /** * The pseudo-protocol used in URIs to signal that the factory should make its own * decision about what protocol and server to use. */ public static final String DEFAULT_PROTOCOL_SPEC = IServerAddress.Protocol.P4JAVA.toString(); /** * Default protocol name, i.e. the name used when the protocol name is * specified as "p4java" in a suitable URL. Currently it always maps to * the native RPC implementation, but this is not guaranteed. */ public static final String DEFAULT_PROTOCOL_NAME = OneShotServerImpl.PROTOCOL_NAME; /** * Default SSL protocol name, i.e. the name used when the protocol name is * specified as "p4javassl" in a suitable URL. Currently it always maps to * the native SSL RPC implementation, but this is not guaranteed. */ public static final String DEFAULT_SSL_PROTOCOL_NAME = OneShotServerImpl.SSL_PROTOCOL_NAME; public static final String ZEROCONF_CLASS_NAME = "javax.jmdns.JmDNS"; /** * The factory's protocol scheme / implementation map. Not intended * for public consumption; also not really intended for large numbers of * implementation classes. */ @SuppressWarnings("rawtypes") private static Map<Protocol, Class> implMap = new HashMap<Protocol, Class>(); private static ISystemFileCommandsHelper rpcFileCommandsHelper = null; /** * The JmDNS zeroconf helper. */ @SuppressWarnings("deprecation") private static ZeroconfHelper zcHelper = null; /** * Initialize the factory. This includes setting the rpcFileCommandsHelper * to a default JDK 6 implementation of the ISystemFileCommandsHelper interface; * this can be replaced by an explicit call to setRpcFileSystemHelper later if * this is inappropriate for a specific context. */ static { // Set up server protocol name to implementation class mapping: implMap.put(Protocol.P4JAVA, OneShotServerImpl.class); implMap.put(Protocol.P4JAVASSL, OneShotServerImpl.class); implMap.put(Protocol.P4JRPC, OneShotServerImpl.class); implMap.put(Protocol.P4JRPCSSL, OneShotServerImpl.class); implMap.put(Protocol.P4JRPCNTS, NtsServerImpl.class); implMap.put(Protocol.P4JRPCNTSSSL, NtsServerImpl.class); Log.info("P4Java server factory loaded; version: " + Metadata.getP4JVersionString() + "; date: " + Metadata.getP4JDateString()); Log.info("Using default charset: " + CharsetDefs.DEFAULT + "; JVM charset: " + CharsetDefs.LOCAL); rpcFileCommandsHelper = new RpcSystemFileCommandsHelper(); Log.info("Using default RPC system file command helper: " + rpcFileCommandsHelper.getClass().getCanonicalName()); } /** * Return a non-null list of implementation metadata about available IServer * implementations. This can be useful for presenting implementation choices to * end users, or for debugging, etc. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static List<IServerImplMetadata> getAvailableImplementationMetadata() { Map<Class, IServerImplMetadata> implMetadataMap = new HashMap<Class, IServerImplMetadata>(); for (Protocol key : implMap.keySet()) { Class implClass = (Class) implMap.get(key); IServerImplMetadata md = getImplMetadata(implClass); if (md != null) { implMetadataMap.put(implClass, md); } } return new ArrayList<IServerImplMetadata>(implMetadataMap.values()); } /** * Return an IServer interface onto an underlying Perforce server at the host * address specified by serverUriString using the protocol implementation and * passed-in properties. Note that we use the term "URL" here a lot when we typically * mean "URI"; this is mostly done to pre-empt confusion, but we may tighten * usage up a lot over time.<p> * * The format of the server URI string is protocol + "://" + hostaddr [+ ":" + port] [+ queryString], * e.g. "p4java://server.perforce.com:1666" or "p4java://192.168.1.12:999" or * "p4jrpc://play.perforce.com:1199?progName=p4javaTest21&progVersion=Alpha203B". * The protocol, port, and hostaddr fields can't be missing, but the port and hostaddr fields * can be anything acceptable to the protocol, e.g. typically something like an IP address or * hostname coupled with a port number.<p> * * The protocol part specifies which network implementation to use; the value "p4java" * (DEFAULT_PROTOCOL_NAME) will tell the factory to use the default protocol, the details * of which are not spelled out here, and which should always be used unless you have * good reason to use an alternative (other protocol part values are possible, but should * not generally be used unless suggested by Perforce support staff).<p> * * To connect to an SSL-enabled Perforce server, use one of the SSL protocols defined in * the IServerAddress.Protocol enum (i.e. "p4javassl", "p4rpcssl" or "p4jrpcntsssl").<p> * * For advanced users, the optional queryString part can be used to set P4Java properties * from the URI; these override any correspondingly-named properties sent programmatically through * the props parameter (see next paragraph). These URI query strings are not interpreted, * but are simply put as-is into the properties passed to the individual server implementation * class. Note: this method does not do any query string replacement of things like * %20 to spaces, etc. (this really isn't needed here as the query parts are passed * as-is to the underlying implementation(s)).<p> * * The props argument can be used to pass in any protocol-specific properties; these * are typically described in usage or implementation notes supplied elsewhere, and are * not typically used by end-users.<p> * * IServer objects returned by this method may have been retrieved from a cache of * previously-used objects, but they are guaranteed to be ready for use by the consumer, * and to be for that consumer's exclusive use only. * * @param serverUriString non-null server address in URI form. * @param props protocol-specific properties; may be null * @return a non-null IServer object ready for use by the consumer. * @throws URISyntaxException if the passed-in URI string does not conform to * the Perforce URI specs detailed above. * @throws ConnectionException if the factory is unable to connect to the server * named by serverUrl * @throws NoSuchObjectException if no implementation class can be found for * the protocol specified in the passed-in URI protocol (scheme part); * @throws ConfigException if the underlying protocol supplier detects a misconfiguration * @throws ResourceException if the factory does not have the resources available to * service the request */ public static IServer getServer(String serverUriString, Properties props) throws URISyntaxException, ConnectionException, NoSuchObjectException, ConfigException, ResourceException{ return getOptionsServer(serverUriString, props); } /** * Return an IOptionsServer onto an underlying Perforce server at the host * address specified by serverUriString using the protocol implementation and * passed-in properties and a default UsageOptions object.<p> * * Basically a convenience wrapper for calling the main getOptionsServer method * with a null UsageOptions argument -- see that method's Javadoc for full * documentation. * * @param serverUriString non-null server address in URI form. * @param props protocol-specific properties; may be null * @return a non-null IOptionsServer object ready for use by the consumer. * @throws URISyntaxException if the passed-in URI string does not conform to * the Perforce URI specs detailed above. * @throws ConnectionException if the factory is unable to connect to the server * named by serverUrl * @throws NoSuchObjectException if no implementation class can be found for * the protocol specified in the passed-in URI protocol (scheme part); * @throws ConfigException if the underlying protocol supplier detects a misconfiguration * @throws ResourceException if the factory does not have the resources available to * service the request */ public static IOptionsServer getOptionsServer(String serverUriString, Properties props) throws URISyntaxException, ConnectionException, NoSuchObjectException, ConfigException, ResourceException { return getOptionsServer(serverUriString, props, null); } /** * Return an IOptionsServer interface onto an underlying Perforce server at the host * address specified by serverUriString using the protocol implementation and * passed-in properties and usage options. Note that we use the term "URL" here a lot * when we typically mean "URI"; this is mostly done to pre-empt confusion, but we may * tighten usage up a lot over time.<p> * * The format of the server URI string is protocol + "://" + hostaddr [+ ":" + port] [+ queryString], * e.g. "p4java://server.perforce.com:1666" or "p4java://192.168.1.12:999" or * "p4java://play.perforce.com:1199?progName=p4javaTest21&progVersion=Alpha203B". * The protocol, port, and hostaddr fields can't be missing, but the port and hostaddr fields * can be anything acceptable to the protocol, e.g. typically something like an IP address or * hostname coupled with a port number.<p> * * The protocol part specifies which network implementation to use; the value "p4java" * (DEFAULT_PROTOCOL_NAME) will tell the factory to use the default protocol, the details * of which are not spelled out here, and which should always be used unless you have * good reason to use an alternative (other protocol part values are possible, but should * not generally be used unless suggested by Perforce support staff).<p> * * To connect to an SSL-enabled Perforce server, use one of the SSL protocols defined in * the IServerAddress.Protocol enum (i.e. "p4javassl", "p4rpcssl" or "p4jrpcntsssl").<p> * * For advanced users, the optional queryString part can be used to set P4Java properties * from the URI; these override any correspondingly-named properties sent programmatically through * the props parameter (see next paragraph). These URI query strings are not interpreted, * but are simply put as-is into the properties passed to the individual server implementation * class. Note: this method does not do any query string replacement of things like * %20 to spaces, etc. (this really isn't needed here as the query parts are passed * as-is to the underlying implementation(s)).<p> * * The props argument can be used to pass in any protocol-specific properties; these * are typically described in usage or implementation notes supplied elsewhere, and are * not typically used by end-users.<p> * * IServer objects returned by this method may have been retrieved from a cache of * previously-used objects, but they are guaranteed to be ready for use by the consumer, * and to be for that consumer's exclusive use only. * * @param serverUriString non-null server address in URI form. * @param props protocol-specific properties; may be null. * @param opts UsageOptions object to be associated with the new server object * specifying the server's usage options; if null, a new usage options object * is constructed using the default UsageOptions constructor and associated * default values using the passed-in properties object (if not null); this is * then associated with the new server object. * @return a non-null IOptionsServer object ready for use by the consumer. * @throws URISyntaxException if the passed-in URI string does not conform to * the Perforce URI specs detailed above. * @throws ConnectionException if the factory is unable to connect to the server * named by serverUrl * @throws NoSuchObjectException if no implementation class can be found for * the protocol specified in the passed-in URI protocol (scheme part); * @throws ConfigException if the underlying protocol supplier detects a misconfiguration * @throws ResourceException if the factory does not have the resources available to * service the request */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static IOptionsServer getOptionsServer(String serverUriString, Properties props, UsageOptions opts) throws URISyntaxException, ConnectionException, NoSuchObjectException, ConfigException, ResourceException { if (serverUriString == null) { throw new NullPointerError("Null server serverUriString passed to server factory"); } Log.info("P4Java server factory called for Perforce server URI: " + serverUriString); ServerAddressBuilder addressBuilder = new ServerAddressBuilder(serverUriString); IServerAddress serverAddress = addressBuilder.build(); if (serverAddress == null) { throw new NullPointerError("Null server address constructed with Perforce server URI:" + serverUriString); } if (serverAddress.getProtocol() == null) { throw new URISyntaxException(serverUriString, "unknown protocol"); } if (serverAddress.getHost() == null) { throw new URISyntaxException(serverUriString, "missing or malformed Perforce server hostname"); } if (serverAddress.getPort() < 0) { throw new URISyntaxException(serverUriString, "missing or malformed Perforce server port specifier"); } IServerImplMetadata implMetadata = getImplMetadata(implMap.get(serverAddress.getProtocol())); if (implMetadata == null) { throw new NoSuchObjectException("No such server implementation found for protocol '" + serverAddress.getProtocol().toString() + "'"); } // Combine the properties if (serverAddress.getProperties() != null) { if (props == null) { props = new Properties(); } props.putAll(serverAddress.getProperties()); } try { Class serverImplClass = Class.forName(implMetadata.getImplClassName()); Log.info("Using Server implementation class: " + serverImplClass.getCanonicalName()); IServerControl serverImpl = (IServerControl) serverImplClass.newInstance(); serverImpl.init(serverAddress.getHost(), serverAddress.getPort(), props, opts, serverAddress.getProtocol().isSecure()); return (IOptionsServer) serverImpl; // This part may also cause a cast exception... } catch (ClassNotFoundException cnfe) { Log.error("Unable to instantiate Perforce server implementation class '" + implMetadata.getImplClassName() + "' (class not found)"); Log.exception(cnfe); throw new NoSuchObjectException( "No such p4j server implementation class found for protocol", cnfe); } catch(ClassCastException cce) { Log.error("Unable to instantiate Perforce server implementation class '" + implMetadata.getImplClassName() + "' (class cast error)"); Log.exception(cce); throw new ConfigException( "Specified Perforce server implementation class does not implement required interface(s)", cce); } catch (InstantiationException ie) { Log.error("Unable to instantiate Perforce server implementation class '" + implMetadata.getImplClassName() + "' (instantiation failed)"); Log.exception(ie); throw new ConfigException( "Unable to instantiate Perforce server implementation class '" + implMetadata.getImplClassName() + "'", ie); } catch (IllegalAccessException iae) { Log.error("Unable to instantiate Perforce server implementation class '" + implMetadata.getImplClassName() + "' (illegal access exception)"); Log.exception(iae); throw new ConfigException( "Unable to instantiate Perforce server class '" + implMetadata.getImplClassName() + "'", iae); } } /** * Deprecated way to get an IServer object -- see getServer(String, Properties) for * the correct way to get a server. * * @deprecated as of the 2009.2 release, use the getServer(String, Properties) method * due to that Java's URI class does not accept hostnames with anything * other than alphanumeric characters: even common hostnames like * "perforce_p" will fail, often silently. * @param serverUrl non-null server URI in the format described above * @param props protocol-specific properties; may be null * * @return a non-null IServer object ready for use by the consumer. * * @throws ConnectionException if the factory is unable to connect to the server * named by serverUrl * @throws NoSuchObjectException if no implementation class can be found for * the protocol specified in the passed-in URI protocol (scheme part); * @throws ConfigException if the underlying protocol supplier detects a misconfiguration * @throws ResourceException if the factory does not have the resources available to * service the request * @throws URISyntaxException if the passed-in URI is malformed. */ @Deprecated public static IServer getServer(URI serverUrl, Properties props) throws ConnectionException, NoSuchObjectException, ConfigException, ResourceException, URISyntaxException { if (serverUrl == null) { throw new NullPointerError("Null server URI passed to server factory"); } return getServer(serverUrl.toString(), props); } private static IServerImplMetadata getImplMetadata( final Class<Server> implClass) { if (implClass == null) { return null; } return new IServerImplMetadata() { public String getScreenName() { try { return (String) implClass.getField(Server.SCREEN_NAME_FIELD_NAME).get(null); } catch (Exception exc) { Log.error("Unexpected exception in ServerFactory.getImplMetadata: " + exc.getMessage()); Log.exception(exc); } return "Unknown"; } public String getComments() { try { return (String) implClass.getField(Server.IMPL_COMMENTS_FIELD_NAME).get(null); } catch (Exception exc) { Log.error("Unexpected exception in ServerFactory.getImplMetadata: " + exc.getMessage()); Log.exception(exc); } return "No comments available"; } public ImplType getImplType() { try { return (ImplType) implClass.getField(Server.IMPL_TYPE_FIELD_NAME).get(null); } catch (Exception exc) { Log.error("Unexpected exception in ServerFactory.getImplMetadata: " + exc.getMessage()); Log.exception(exc); } return ImplType.UNKNOWN; } public int getMinimumServerLevel() { try { return (Integer) implClass.getField( Server.MINIMUM_SUPPORTED_SERVER_LEVEL_FIELD_NAME).get(null); } catch (Exception exc) { Log.error("Unexpected exception in ServerFactory.getImplMetadata: " + exc.getMessage()); Log.exception(exc); } return -1; } public String getUriScheme() { try { return (String) implClass.getField(Server.PROTOCOL_NAME_FIELD_NAME).get(null); } catch (Exception exc) { Log.error("Unexpected exception in ServerFactory.getImplMetadata: " + exc.getMessage()); Log.exception(exc); } return "p4j"; } public boolean isDefault() { try { return (Boolean) implClass.getField(Server.DEFAULT_STATUS_FIELD_NAME).get(null); } catch (Exception exc) { Log.error("Unexpected exception in ServerFactory.getImplMetadata: " + exc.getMessage()); Log.exception(exc); } return false; } public String getImplClassName() { try { return implClass.getCanonicalName(); } catch (Exception exc) { Log.error("Unexpected exception in ServerFactory.getImplMetadata: " + exc.getMessage()); Log.exception(exc); } return "com.perforce.p4java.NoSuchImplClass"; } }; } /** * Register an ISystemFileCommandsHelper for the RPC implementations. See * the documentation for the ISystemFileCommandsHelper interface for more * semantic and usage details.<p> * * Helper classes are needed for certain hosted implementations and for some * JDK 5 installations, and are shared across an entire instance of P4Java. * Please do not register a helper class unless you know what you're doing and * you're OK with the dire consequences of getting a helper class implementation wrong.<p> * * Note that if the fsCmdHelper parameter is null, the default internal implementation * will be used, which is usually fine for JDK 6 systems. * * @see com.perforce.p4java.impl.generic.sys.ISystemFileCommandsHelper * @param fsCmdHelper file helper interface implementation; if null, use the * JVM default implementation(s). */ public static void setRpcFileSystemHelper(ISystemFileCommandsHelper fsCmdHelper) { rpcFileCommandsHelper = fsCmdHelper; Log.info("setting RPC system file helper to " + (fsCmdHelper == null ? "null" : "class " + fsCmdHelper.getClass().getCanonicalName())); } /** * Return the current SystemFileCommands helper, if any. */ public static ISystemFileCommandsHelper getRpcFileSystemHelper() { return rpcFileCommandsHelper; } /** * Return a list of Perforce servers registered locally with zeroconf at * the time the method was called. See the Perforce knowledge base * articles at http://kb.perforce.com/AdminTasks/Zeroconf for an * introduction to Perforce zeroconf usage, and zeroconf.org for * zeroconf in general.<p> * * This method uses the javax.jmdns JmDNS package (available through * Sourceforge, etc.), and that package must be visible to the current * class loader for this method to work. If the server factory is unable * to find a suitable JmDNS package using the current class loader, * this method will throw a ConfigException. The JmDNS package is <i>not</i> * supplied with P4Java and must be downloaded and installed separately.<p> * * Note that zeroconf discovery can take some time (in the order of tens * of seconds in some cases), and only works for local subnets, so the * first call to this method may return nothing even though there's a * suitable server out there on the same subnet. Subsequent calls are * usually more successful.<p> * * Note also that we recommend you call the associated isZeroconfAvailable * method first to check whether zeroconf is even available for this P4Java * instance -- this can save a lot of overhead and / or annoying log * messages. * * @return non-null (but possibly-empty) list of ZeroconfServerInfo objects * for Perforce servers registered when this method is called. * @throws ConfigException if the server factory can't load and use a suitable * JmDNS zeroconf package using the current class loader. * * @deprecated As of release 2013.1, ZeroConf is no longer supported by the * Perforce server 2013.1. */ @Deprecated public static List<ZeroconfServerInfo> getZeroconfServers() throws ConfigException { if (zcHelper == null) { zcHelper = new ZeroconfHelper(); } return zcHelper.getZeroconfServers(); } /** * Returns true if the server factory has a suitable zeroconf service browsing * implementation available to it. Should probably be used at least once before * calling the getZeroConfServers method to avoid unnecessary overhead. * * @return true iff zeroconf browsing services are available to the server factory. * * @deprecated As of release 2013.1, ZeroConf is no longer supported by the * Perforce server 2013.1. */ @Deprecated public static boolean isZeroConfAvailable() { if (zcHelper == null) { zcHelper = new ZeroconfHelper(); } return zcHelper.isZeroConfAvailable(); } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 19903 | stuartrowe |
Branching //guest/perforce_software/p4java/... to //guest/stuartrowe/p4java/... |
||
//guest/perforce_software/p4java/r14.1/src/main/java/com/perforce/p4java/server/ServerFactory.java | |||||
#1 | 12541 | Matt Attaway | Initial add of the 14.1 p4java source code |