Java tutorial
/* ======================================================================== * Copyright (c) 2005-2013 The OPC Foundation, Inc. All rights reserved. * * OPC Reciprocal Community License ("RCL") Version 1.00 * * Unless explicitly acquired and licensed from Licensor under another * license, the contents of this file are subject to the Reciprocal * Community License ("RCL") Version 1.00, or subsequent versions as * allowed by the RCL, and You may not copy or use this file in either * source code or executable form, except in compliance with the terms and * conditions of the RCL. * * All software distributed under the RCL is provided strictly on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, * AND LICENSOR HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, QUIET ENJOYMENT, OR NON-INFRINGEMENT. See the RCL for specific * language governing rights and limitations under the RCL. * * The complete license agreement can be found here: * http://opcfoundation.org/License/RCL/1.00/ * ======================================================================*/ package org.opcfoundation.ua.application; import java.net.SocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.log4j.Logger; import org.opcfoundation.ua.builtintypes.ServiceRequest; import org.opcfoundation.ua.builtintypes.UnsignedByte; import org.opcfoundation.ua.common.ServiceResultException; import org.opcfoundation.ua.core.ApplicationDescription; import org.opcfoundation.ua.core.ApplicationType; import org.opcfoundation.ua.core.AttributeServiceSetHandler; import org.opcfoundation.ua.core.DiscoveryServiceSetHandler; import org.opcfoundation.ua.core.EndpointDescription; import org.opcfoundation.ua.core.MessageSecurityMode; import org.opcfoundation.ua.core.MethodServiceSetHandler; import org.opcfoundation.ua.core.MonitoredItemServiceSetHandler; import org.opcfoundation.ua.core.NodeManagementServiceSetHandler; import org.opcfoundation.ua.core.ServiceFault; import org.opcfoundation.ua.core.SessionServiceSetHandler; import org.opcfoundation.ua.core.SubscriptionServiceSetHandler; import org.opcfoundation.ua.core.TestServiceSetHandler; import org.opcfoundation.ua.core.UserTokenPolicy; import org.opcfoundation.ua.encoding.IEncodeable; import org.opcfoundation.ua.transport.Endpoint; import org.opcfoundation.ua.transport.EndpointBinding; import org.opcfoundation.ua.transport.EndpointServer; import org.opcfoundation.ua.transport.EndpointServer.EndpointHandle; import org.opcfoundation.ua.transport.UriUtil; import org.opcfoundation.ua.transport.endpoint.EndpointBindingCollection; import org.opcfoundation.ua.transport.https.HttpsServer; import org.opcfoundation.ua.transport.security.CertificateValidator; import org.opcfoundation.ua.transport.security.KeyPair; import org.opcfoundation.ua.transport.security.SecurityMode; import org.opcfoundation.ua.transport.security.SecurityPolicy; import org.opcfoundation.ua.transport.tcp.nio.OpcTcpServer; import org.opcfoundation.ua.utils.EndpointUtil; /** * This object represents a service server. It is an application that responds * to {@link ServiceRequest}s queries. * <p> * Server is assigned with at least one application instance certificate. * <p> * The initial server contains {@link EndpointDiscoveryService} by default. * * @see Application * @see ServiceHandler service handler * @see BindingFactory#bind(Endpoint, Server) to bind an endpoint to a server */ public class Server { public final static String SOAP_XML_TRANSPORT_PROFILE_URI = "http://opcfoundation.org/UA-Profile/Transport/soaphttp-wssc-uaxml-uabinary"; public final static String UATCP_BINARY_TRANSPORT_PROFILE_URI = "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"; public final static String HTTPS_BINARY_TRANSPORT_PROFILE_URI = "http://opcfoundation.org/UA-Profile/Transport/https-uabinary"; /** Logger */ static Logger logger = Logger.getLogger(Server.class); /** Service Handler */ protected ServiceHandlerComposition serviceHandlers = new ServiceHandlerComposition(); /** User Token Policies */ protected List<UserTokenPolicy> userTokenPolicies = new CopyOnWriteArrayList<UserTokenPolicy>(); /** Endpoints */ protected EndpointBindingCollection endpointBindings; /** Endpoint discovery service */ protected EndpointDiscoveryService endpointDiscoveryService; /** The application */ protected Application application; /** Bound handles */ protected List<EndpointHandle> boundHandles = new CopyOnWriteArrayList<EndpointHandle>(); public static Server createServerApplication() { Application application = new Application(); Server server = new Server(application); application.getOpctcpSettings().setCertificateValidator(CertificateValidator.ALLOW_ALL); application.getHttpsSettings().setCertificateValidator(CertificateValidator.ALLOW_ALL); application.getHttpsSettings().setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); return server; } public Server(Application application) { this.application = application; endpointBindings = new EndpointBindingCollection(); endpointDiscoveryService = new EndpointDiscoveryService(endpointBindings); addServiceHandler(endpointDiscoveryService); } public Application getApplication() { return application; } public ApplicationDescription createApplicationDescription() { ApplicationDescription result = application.applicationDescription.clone(); result.setApplicationType(ApplicationType.Server); return result; } public synchronized void setEndpointBindings(EndpointBindingCollection newBindings) { this.endpointBindings = newBindings; endpointDiscoveryService.endpointBindings = newBindings; } public EndpointBindingCollection getEndpointBindings() { return endpointBindings; } /** * Add Service Handler. Service handler handles one or more service methods. * Note, the server may not have more than one service handler for each method. * <p> * The <tt>serviceHandler</tt> is either: * (a) an implementation of {@link ServiceHandler} * (b) an object that contains methods that implement service requests. * These methods are discovered using Java Reflection. * <p> * The following list contains service methods grouped by service sets: * @see AttributeServiceSetHandler * @see DiscoveryServiceSetHandler * @see MethodServiceSetHandler * @see MonitoredItemServiceSetHandler * @see NodeManagementServiceSetHandler * @see SessionServiceSetHandler * @see SubscriptionServiceSetHandler * @see TestServiceSetHandler * <P> * The <tt>serviceHandler</tt> may implement one or more methods. * In typical case service handler implements one service set, e.g. * {@link SessionServiceSetHandler}. * <p> * A {@link ServiceFault} is returned to the client in case the server doesn't * the requested service method. * <p> * Example: * addServiceHandler( new TestServiceSetHandler() { * void onTestStack(EndpointServiceRequest<TestStackRequest, TestStackResponse> req) { * req.sendResponse( new ServiceFault() ); * } * void onTestStackEx(EndpointServiceRequest<TestStackExRequest, TestStackExResponse> req) { * req.sendFault(new ServiceFault()); * } * } ); * * @param serviceHandler instanceof {@link ServiceHandler} or Object implementing service requests */ public void addServiceHandler(Object serviceHandler) { if (serviceHandler == null) throw new IllegalArgumentException("null arg"); if (logger.isDebugEnabled()) logger.debug("addServiceHandler: " + serviceHandler); if (logger.isTraceEnabled()) logger.trace("addServiceHandler: from " + Thread.currentThread().getStackTrace()[2].toString()); serviceHandlers.add(serviceHandler); } public ServiceHandler[] getServiceHandlers() { return serviceHandlers.getServiceHandlers(); } /** * Get Service Handler object by service. * <p> * For example, to acquire session manager: * SessionManager sessionManager = x.getServiceHandlerByService( CreateSessionRequest.class ); * * @param <T> * @param requestClass Service request class * @return Service handler that serves the given request class in this server */ @SuppressWarnings("unchecked") public <T> T getServiceHandlerByService(Class<? extends ServiceRequest> requestClass) { return (T) serviceHandlers.getServiceHandlerByService(requestClass); } /** * Query whether the server can handle a service. * * @param requestClass request class of the service, e.g. ReadRequest * @return true if server can handle the service */ public boolean handlesService(Class<? extends IEncodeable> requestClass) { return serviceHandlers.supportsService(requestClass); } public ServiceHandlerComposition getServiceHandlerComposition() { return serviceHandlers; } public void addUserTokenPolicy(UserTokenPolicy policy) { this.userTokenPolicies.add(policy); } public void removeUserTokenPolicy(UserTokenPolicy policy) { this.userTokenPolicies.remove(policy); } public UserTokenPolicy[] getUserTokenPolicies() { return userTokenPolicies.toArray(new UserTokenPolicy[0]); } /** * Bind an endpoint to the server. * * bindAddress is resolved into ip-addresses. For instance, if "localhost" * is used, then all local interface ips are bound. If port is not explicitly * specified, the default port for the protocol is used. * * There must be protocol: "opc.tcp", "http", or "https". * * EndpointUri is the identifier of the endpoint. * * @param bindAddress * @param endpointUri * @param modes * @return list of handles * @throws ServiceResultException */ public List<EndpointHandle> bind(String bindAddress, Endpoint endpointAddress) throws ServiceResultException { List<EndpointHandle> result = new ArrayList<EndpointHandle>(); String scheme = UriUtil.getTransportProtocol(bindAddress); List<SocketAddress> socketAddresses = EndpointUtil.toSocketAddresses(bindAddress); if (socketAddresses.isEmpty()) return Collections.emptyList(); EndpointServer endpointServer = application.getOrCreateEndpointServer(scheme); for (SocketAddress socketAddress : socketAddresses) { EndpointBinding eb = new EndpointBinding(endpointServer, endpointAddress, this); EndpointHandle handle = endpointServer.bind(socketAddress, eb); boundHandles.add(handle); } return result; } public List<EndpointHandle> bind(String bindAddress, String endpointUri, SecurityMode... modes) throws ServiceResultException { Endpoint endpointAddress = new Endpoint(endpointUri, modes); return bind(bindAddress, endpointAddress); } /** * Close the server. */ public void close() { for (EndpointHandle handle : boundHandles) { logger.debug("unbind: " + handle); handle.close(); } boundHandles.clear(); logger.info("Server " + this + " closed"); OpcTcpServer opctcpServer = getApplication().opctcpServer; HttpsServer httpsServer = getApplication().httpsServer; if ((opctcpServer == null || opctcpServer.getEndpointBindings().isEmpty()) && (httpsServer == null || httpsServer.getEndpointBindings().isEmpty())) getApplication().close(); } public Endpoint[] getEndpoints() { List<EndpointBinding> bindings = endpointBindings.get(this); List<Endpoint> endpoints = EndpointBindingCollection.getEndpointAddresses(bindings); return endpoints.toArray(new Endpoint[endpoints.size()]); } public boolean hasEndpoint(String uri) { List<EndpointBinding> bindings = endpointBindings.get(uri); return !bindings.isEmpty(); } public Endpoint getEndpointByUri(String uri) { List<EndpointBinding> bindings = endpointBindings.get(uri); if (bindings.isEmpty()) return null; return bindings.get(0).endpointAddress; } public EndpointDescription[] getEndpointDescriptions() { List<EndpointDescription> result = new ArrayList<EndpointDescription>(endpointBindings.size()); UserTokenPolicy[] userTokenPolicies = getUserTokenPolicies(); // opc.tcp endpoint descriptions ApplicationDescription ap = createApplicationDescription(); for (Endpoint ep : endpointBindings.getEndpointAddresses()) { String endpointUrl = ep.getEndpointUrl(); String proto = UriUtil.getTransportProtocol(endpointUrl).toLowerCase(); for (KeyPair keypair : application.getApplicationInstanceCertificates()) { SecurityMode[] securityModes = ep.getSecurityModes(); if (logger.isTraceEnabled()) logger.trace("getEndpointDescriptions: securityModes=" + Arrays.toString(securityModes)); if (UriUtil.SCHEME_HTTPS.equals(proto)) { securityModes = SecurityMode.NON_SECURE; } else if (UriUtil.SCHEME_HTTP.equals(proto)) { securityModes = SecurityMode.NON_SECURE; } for (SecurityMode conf : securityModes) { MessageSecurityMode msm = MessageSecurityMode.None; String securityPolicyUri = ""; String transportProfileUri = UATCP_BINARY_TRANSPORT_PROFILE_URI; int securityLevel = 0; if (UriUtil.SCHEME_HTTPS.equals(proto)) { securityLevel = 2; securityPolicyUri = SecurityPolicy.NONE.getPolicyUri(); transportProfileUri = HTTPS_BINARY_TRANSPORT_PROFILE_URI; } else if (UriUtil.SCHEME_HTTP.equals(proto)) { securityLevel = 0; securityPolicyUri = SecurityPolicy.NONE.getPolicyUri(); transportProfileUri = HTTPS_BINARY_TRANSPORT_PROFILE_URI; } else if (UriUtil.SCHEME_OPCTCP.equals(proto)) { msm = conf.getMessageSecurityMode(); securityLevel = msm == MessageSecurityMode.None ? 0 : msm == MessageSecurityMode.Sign ? 1 : msm == MessageSecurityMode.SignAndEncrypt ? 2 : -1; securityPolicyUri = conf.getSecurityPolicy().getPolicyUri(); } // Ensure keysize matches security policy if (msm.hasEncryption() || msm.hasSigning()) { if (!conf.getSecurityPolicy().isUsableWith(keypair.certificate)) continue; } EndpointDescription desc = new EndpointDescription(); desc.setEndpointUrl(ep.getEndpointUrl()); desc.setSecurityMode(msm); desc.setSecurityLevel(UnsignedByte.valueOf(securityLevel)); desc.setSecurityPolicyUri(securityPolicyUri); desc.setServer(ap); desc.setServerCertificate(keypair.getCertificate().getEncoded()); desc.setTransportProfileUri(transportProfileUri); desc.setUserIdentityTokens(userTokenPolicies); result.add(desc); } } } if (logger.isTraceEnabled()) logger.trace("getEndpointDescriptions: result=" + result.toString()); return result.toArray(new EndpointDescription[0]); } public EndpointServer[] getBindings() { List<EndpointServer> result = new ArrayList<EndpointServer>(); for (EndpointBinding binding : endpointBindings.get(this)) { if (!result.contains(binding.endpointServer)) result.add(binding.endpointServer); } return result.toArray(new EndpointServer[result.size()]); } @Override public String toString() { return "Server " + application.getApplicationUri(); } }