Use openstack4j for OpenStack data model and rest client
Change-Id: I4eb52c3c82d847c442420d1287392fe9079bf699
Showing
8 changed files
with
163 additions
and
37 deletions
| ... | @@ -18,7 +18,7 @@ | ... | @@ -18,7 +18,7 @@ |
| 18 | category="Traffic Steering" url="http://onosproject.org" title="CORD Virtual Tenant Network" | 18 | category="Traffic Steering" url="http://onosproject.org" title="CORD Virtual Tenant Network" |
| 19 | featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features" | 19 | featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features" |
| 20 | features="${project.artifactId}" | 20 | features="${project.artifactId}" |
| 21 | - apps="org.onosproject.ovsdb-base,org.onosproject.openstackinterface,org.onosproject.dhcp"> | 21 | + apps="org.onosproject.ovsdb-base,org.onosproject.dhcp"> |
| 22 | <description>${project.description}</description> | 22 | <description>${project.description}</description> |
| 23 | <artifact>mvn:${project.groupId}/onos-app-cordvtn/${project.version}</artifact> | 23 | <artifact>mvn:${project.groupId}/onos-app-cordvtn/${project.version}</artifact> |
| 24 | </app> | 24 | </app> | ... | ... |
| ... | @@ -19,5 +19,6 @@ | ... | @@ -19,5 +19,6 @@ |
| 19 | <feature>onos-api</feature> | 19 | <feature>onos-api</feature> |
| 20 | <bundle>mvn:${project.groupId}/onos-app-cordvtn/${project.version}</bundle> | 20 | <bundle>mvn:${project.groupId}/onos-app-cordvtn/${project.version}</bundle> |
| 21 | <bundle>wrap:mvn:com.jcraft/jsch/0.1.53$Bundle-SymbolicName=jsch&Bundle-Version=0.1.53</bundle> | 21 | <bundle>wrap:mvn:com.jcraft/jsch/0.1.53$Bundle-SymbolicName=jsch&Bundle-Version=0.1.53</bundle> |
| 22 | + <bundle>wrap:mvn:org.pacesys/openstack4j/2.11/jar/withdeps$Bundle-SymbolicName=openstack4j&Bundle-Version=2.11</bundle> | ||
| 22 | </feature> | 23 | </feature> |
| 23 | </features> | 24 | </features> | ... | ... |
| ... | @@ -112,6 +112,21 @@ | ... | @@ -112,6 +112,21 @@ |
| 112 | <artifactId>jsch</artifactId> | 112 | <artifactId>jsch</artifactId> |
| 113 | <version>0.1.53</version> | 113 | <version>0.1.53</version> |
| 114 | </dependency> | 114 | </dependency> |
| 115 | + <dependency> | ||
| 116 | + <groupId>org.pacesys</groupId> | ||
| 117 | + <artifactId>openstack4j-core</artifactId> | ||
| 118 | + <version>2.11</version> | ||
| 119 | + </dependency> | ||
| 120 | + <dependency> | ||
| 121 | + <groupId>org.pacesys.openstack4j.connectors</groupId> | ||
| 122 | + <artifactId>openstack4j-http-connector</artifactId> | ||
| 123 | + <version>2.11</version> | ||
| 124 | + </dependency> | ||
| 125 | + <dependency> | ||
| 126 | + <groupId>org.pacesys.openstack4j.connectors</groupId> | ||
| 127 | + <artifactId>openstack4j-httpclient</artifactId> | ||
| 128 | + <version>2.11</version> | ||
| 129 | + </dependency> | ||
| 115 | </dependencies> | 130 | </dependencies> |
| 116 | 131 | ||
| 117 | <build> | 132 | <build> |
| ... | @@ -131,8 +146,14 @@ | ... | @@ -131,8 +146,14 @@ |
| 131 | ${project.groupId}.${project.artifactId} | 146 | ${project.groupId}.${project.artifactId} |
| 132 | </Bundle-SymbolicName> | 147 | </Bundle-SymbolicName> |
| 133 | <Import-Package> | 148 | <Import-Package> |
| 149 | + !org.apache.http.*, | ||
| 134 | *,org.glassfish.jersey.servlet | 150 | *,org.glassfish.jersey.servlet |
| 135 | </Import-Package> | 151 | </Import-Package> |
| 152 | + <Embed-Dependency> | ||
| 153 | + openstack4j-core, | ||
| 154 | + openstack4j-http-connector, | ||
| 155 | + openstack4j-httpclient | ||
| 156 | + </Embed-Dependency> | ||
| 136 | <Web-ContextPath>${web.context}</Web-ContextPath> | 157 | <Web-ContextPath>${web.context}</Web-ContextPath> |
| 137 | </instructions> | 158 | </instructions> |
| 138 | </configuration> | 159 | </configuration> | ... | ... |
| ... | @@ -19,8 +19,8 @@ import com.google.common.base.MoreObjects; | ... | @@ -19,8 +19,8 @@ import com.google.common.base.MoreObjects; |
| 19 | import org.onlab.packet.IpAddress; | 19 | import org.onlab.packet.IpAddress; |
| 20 | import org.onlab.packet.IpPrefix; | 20 | import org.onlab.packet.IpPrefix; |
| 21 | import org.onosproject.net.Host; | 21 | import org.onosproject.net.Host; |
| 22 | -import org.onosproject.openstackinterface.OpenstackNetwork; | 22 | +import org.openstack4j.model.network.Network; |
| 23 | -import org.onosproject.openstackinterface.OpenstackSubnet; | 23 | +import org.openstack4j.model.network.Subnet; |
| 24 | 24 | ||
| 25 | import java.util.Map; | 25 | import java.util.Map; |
| 26 | import java.util.Objects; | 26 | import java.util.Objects; |
| ... | @@ -47,18 +47,18 @@ public final class CordService { | ... | @@ -47,18 +47,18 @@ public final class CordService { |
| 47 | /** | 47 | /** |
| 48 | * Default constructor. | 48 | * Default constructor. |
| 49 | * | 49 | * |
| 50 | - * @param vNet OpenStack network | 50 | + * @param osNet OpenStack network |
| 51 | - * @param subnet OpenStack subnet | 51 | + * @param osSubnet OpenStack subnet |
| 52 | * @param hosts host and tunnel ip map | 52 | * @param hosts host and tunnel ip map |
| 53 | * @param tenantServices list of tenant service ids | 53 | * @param tenantServices list of tenant service ids |
| 54 | */ | 54 | */ |
| 55 | - public CordService(OpenstackNetwork vNet, OpenstackSubnet subnet, | 55 | + public CordService(Network osNet, Subnet osSubnet, |
| 56 | Map<Host, IpAddress> hosts, Set<CordServiceId> tenantServices) { | 56 | Map<Host, IpAddress> hosts, Set<CordServiceId> tenantServices) { |
| 57 | - this.id = CordServiceId.of(vNet.id()); | 57 | + this.id = CordServiceId.of(osNet.getId()); |
| 58 | - this.segmentationId = Long.parseLong(vNet.segmentId()); | 58 | + this.segmentationId = Long.parseLong(osNet.getProviderSegID()); |
| 59 | - this.serviceType = getServiceType(vNet.name()); | 59 | + this.serviceType = getServiceType(osNet.getName()); |
| 60 | - this.serviceIpRange = IpPrefix.valueOf(subnet.cidr()); | 60 | + this.serviceIpRange = IpPrefix.valueOf(osSubnet.getCidr()); |
| 61 | - this.serviceIp = IpAddress.valueOf(subnet.gatewayIp()); | 61 | + this.serviceIp = IpAddress.valueOf(osSubnet.getGateway()); |
| 62 | this.hosts = hosts; | 62 | this.hosts = hosts; |
| 63 | this.tenantServices = tenantServices; | 63 | this.tenantServices = tenantServices; |
| 64 | } | 64 | } | ... | ... |
| ... | @@ -46,10 +46,6 @@ public class CordVtnConfig extends Config<ApplicationId> { | ... | @@ -46,10 +46,6 @@ public class CordVtnConfig extends Config<ApplicationId> { |
| 46 | public static final String LOCAL_MANAGEMENT_IP = "localManagementIp"; | 46 | public static final String LOCAL_MANAGEMENT_IP = "localManagementIp"; |
| 47 | public static final String OVSDB_PORT = "ovsdbPort"; | 47 | public static final String OVSDB_PORT = "ovsdbPort"; |
| 48 | 48 | ||
| 49 | - public static final String SSH_PORT = "sshPort"; | ||
| 50 | - public static final String SSH_USER = "sshUser"; | ||
| 51 | - public static final String SSH_KEY_FILE = "sshKeyFile"; | ||
| 52 | - | ||
| 53 | public static final String CORDVTN_NODES = "nodes"; | 49 | public static final String CORDVTN_NODES = "nodes"; |
| 54 | public static final String HOSTNAME = "hostname"; | 50 | public static final String HOSTNAME = "hostname"; |
| 55 | public static final String HOST_MANAGEMENT_IP = "hostManagementIp"; | 51 | public static final String HOST_MANAGEMENT_IP = "hostManagementIp"; |
| ... | @@ -57,6 +53,17 @@ public class CordVtnConfig extends Config<ApplicationId> { | ... | @@ -57,6 +53,17 @@ public class CordVtnConfig extends Config<ApplicationId> { |
| 57 | public static final String DATA_PLANE_INTF = "dataPlaneIntf"; | 53 | public static final String DATA_PLANE_INTF = "dataPlaneIntf"; |
| 58 | public static final String BRIDGE_ID = "bridgeId"; | 54 | public static final String BRIDGE_ID = "bridgeId"; |
| 59 | 55 | ||
| 56 | + public static final String SSH = "ssh"; | ||
| 57 | + public static final String SSH_PORT = "sshPort"; | ||
| 58 | + public static final String SSH_USER = "sshUser"; | ||
| 59 | + public static final String SSH_KEY_FILE = "sshKeyFile"; | ||
| 60 | + | ||
| 61 | + public static final String OPENSTACK = "openstack"; | ||
| 62 | + public static final String OPENSTACK_ENDPOINT = "endpoint"; | ||
| 63 | + public static final String OPENSTACK_TENANT = "tenant"; | ||
| 64 | + public static final String OPENSTACK_USER = "user"; | ||
| 65 | + public static final String OPENSTACK_PASSWORD = "password"; | ||
| 66 | + | ||
| 60 | /** | 67 | /** |
| 61 | * Returns the set of nodes read from network config. | 68 | * Returns the set of nodes read from network config. |
| 62 | * | 69 | * |
| ... | @@ -65,15 +72,22 @@ public class CordVtnConfig extends Config<ApplicationId> { | ... | @@ -65,15 +72,22 @@ public class CordVtnConfig extends Config<ApplicationId> { |
| 65 | public Set<CordVtnNode> cordVtnNodes() { | 72 | public Set<CordVtnNode> cordVtnNodes() { |
| 66 | 73 | ||
| 67 | Set<CordVtnNode> nodes = Sets.newHashSet(); | 74 | Set<CordVtnNode> nodes = Sets.newHashSet(); |
| 68 | - JsonNode jsonNodes = object.get(CORDVTN_NODES); | 75 | + |
| 69 | - if (jsonNodes == null) { | 76 | + JsonNode cordvtnNodes = object.get(CORDVTN_NODES); |
| 77 | + if (cordvtnNodes == null) { | ||
| 70 | log.debug("No CORD VTN nodes found"); | 78 | log.debug("No CORD VTN nodes found"); |
| 71 | return nodes; | 79 | return nodes; |
| 72 | } | 80 | } |
| 73 | 81 | ||
| 74 | - for (JsonNode jsonNode : jsonNodes) { | 82 | + JsonNode sshNode = object.get(SSH); |
| 83 | + if (sshNode == null) { | ||
| 84 | + log.warn("SSH information not found"); | ||
| 85 | + return nodes; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + for (JsonNode cordvtnNode : cordvtnNodes) { | ||
| 75 | try { | 89 | try { |
| 76 | - NetworkAddress hostMgmt = NetworkAddress.valueOf(getConfig(jsonNode, HOST_MANAGEMENT_IP)); | 90 | + NetworkAddress hostMgmt = NetworkAddress.valueOf(getConfig(cordvtnNode, HOST_MANAGEMENT_IP)); |
| 77 | NetworkAddress localMgmt = NetworkAddress.valueOf(getConfig(object, LOCAL_MANAGEMENT_IP)); | 91 | NetworkAddress localMgmt = NetworkAddress.valueOf(getConfig(object, LOCAL_MANAGEMENT_IP)); |
| 78 | if (hostMgmt.prefix().contains(localMgmt.prefix()) || | 92 | if (hostMgmt.prefix().contains(localMgmt.prefix()) || |
| 79 | localMgmt.prefix().contains(hostMgmt.prefix())) { | 93 | localMgmt.prefix().contains(hostMgmt.prefix())) { |
| ... | @@ -84,22 +98,22 @@ public class CordVtnConfig extends Config<ApplicationId> { | ... | @@ -84,22 +98,22 @@ public class CordVtnConfig extends Config<ApplicationId> { |
| 84 | Ip4Address hostMgmtIp = hostMgmt.ip().getIp4Address(); | 98 | Ip4Address hostMgmtIp = hostMgmt.ip().getIp4Address(); |
| 85 | SshAccessInfo sshInfo = new SshAccessInfo( | 99 | SshAccessInfo sshInfo = new SshAccessInfo( |
| 86 | hostMgmtIp, | 100 | hostMgmtIp, |
| 87 | - TpPort.tpPort(Integer.parseInt(getConfig(object, SSH_PORT))), | 101 | + TpPort.tpPort(Integer.parseInt(getConfig(sshNode, SSH_PORT))), |
| 88 | - getConfig(object, SSH_USER), getConfig(object, SSH_KEY_FILE)); | 102 | + getConfig(sshNode, SSH_USER), getConfig(sshNode, SSH_KEY_FILE)); |
| 89 | 103 | ||
| 90 | - String hostname = getConfig(jsonNode, HOSTNAME); | 104 | + String hostname = getConfig(cordvtnNode, HOSTNAME); |
| 91 | CordVtnNode newNode = new CordVtnNode( | 105 | CordVtnNode newNode = new CordVtnNode( |
| 92 | hostname, hostMgmt, localMgmt, | 106 | hostname, hostMgmt, localMgmt, |
| 93 | - NetworkAddress.valueOf(getConfig(jsonNode, DATA_PLANE_IP)), | 107 | + NetworkAddress.valueOf(getConfig(cordvtnNode, DATA_PLANE_IP)), |
| 94 | TpPort.tpPort(Integer.parseInt(getConfig(object, OVSDB_PORT))), | 108 | TpPort.tpPort(Integer.parseInt(getConfig(object, OVSDB_PORT))), |
| 95 | sshInfo, | 109 | sshInfo, |
| 96 | - DeviceId.deviceId(getConfig(jsonNode, BRIDGE_ID)), | 110 | + DeviceId.deviceId(getConfig(cordvtnNode, BRIDGE_ID)), |
| 97 | - getConfig(jsonNode, DATA_PLANE_INTF), | 111 | + getConfig(cordvtnNode, DATA_PLANE_INTF), |
| 98 | CordVtnNodeState.noState()); | 112 | CordVtnNodeState.noState()); |
| 99 | 113 | ||
| 100 | nodes.add(newNode); | 114 | nodes.add(newNode); |
| 101 | } catch (IllegalArgumentException | NullPointerException e) { | 115 | } catch (IllegalArgumentException | NullPointerException e) { |
| 102 | - log.error("{}", e.toString()); | 116 | + log.error("{}", e); |
| 103 | } | 117 | } |
| 104 | } | 118 | } |
| 105 | 119 | ||
| ... | @@ -167,5 +181,90 @@ public class CordVtnConfig extends Config<ApplicationId> { | ... | @@ -167,5 +181,90 @@ public class CordVtnConfig extends Config<ApplicationId> { |
| 167 | 181 | ||
| 168 | return publicGateways; | 182 | return publicGateways; |
| 169 | } | 183 | } |
| 170 | -} | ||
| 171 | 184 | ||
| 185 | + /** | ||
| 186 | + * Returns OpenStack API access information. | ||
| 187 | + * | ||
| 188 | + * @return openstack config | ||
| 189 | + */ | ||
| 190 | + public OpenStackConfig openstackConfig() { | ||
| 191 | + JsonNode jsonNode = object.get(OPENSTACK); | ||
| 192 | + if (jsonNode == null) { | ||
| 193 | + log.error("Failed to get OpenStack configurations"); | ||
| 194 | + return null; | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + try { | ||
| 198 | + return new OpenStackConfig( | ||
| 199 | + jsonNode.path(OPENSTACK_ENDPOINT).asText(), | ||
| 200 | + jsonNode.path(OPENSTACK_TENANT).asText(), | ||
| 201 | + jsonNode.path(OPENSTACK_USER).asText(), | ||
| 202 | + jsonNode.path(OPENSTACK_PASSWORD).asText()); | ||
| 203 | + } catch (IllegalArgumentException | NullPointerException e) { | ||
| 204 | + log.error("Failed to get OpenStack configurations"); | ||
| 205 | + return null; | ||
| 206 | + } | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + /** | ||
| 210 | + * Configuration for OpenStack API access. | ||
| 211 | + */ | ||
| 212 | + public static class OpenStackConfig { | ||
| 213 | + | ||
| 214 | + private final String endpoint; | ||
| 215 | + private final String tenant; | ||
| 216 | + private final String user; | ||
| 217 | + private final String password; | ||
| 218 | + | ||
| 219 | + /** | ||
| 220 | + * Default constructor. | ||
| 221 | + * | ||
| 222 | + * @param endpoint Keystone endpoint | ||
| 223 | + * @param tenant tenant name | ||
| 224 | + * @param user user name | ||
| 225 | + * @param password passwowrd | ||
| 226 | + */ | ||
| 227 | + public OpenStackConfig(String endpoint, String tenant, String user, String password) { | ||
| 228 | + this.endpoint = endpoint; | ||
| 229 | + this.tenant = tenant; | ||
| 230 | + this.user = user; | ||
| 231 | + this.password = password; | ||
| 232 | + } | ||
| 233 | + | ||
| 234 | + /** | ||
| 235 | + * Returns OpenStack API endpoint. | ||
| 236 | + * | ||
| 237 | + * @return endpoint | ||
| 238 | + */ | ||
| 239 | + public String endpoint() { | ||
| 240 | + return this.endpoint; | ||
| 241 | + } | ||
| 242 | + | ||
| 243 | + /** | ||
| 244 | + * Returns OpenStack tenant name. | ||
| 245 | + * | ||
| 246 | + * @return tenant name | ||
| 247 | + */ | ||
| 248 | + public String tenant() { | ||
| 249 | + return this.tenant; | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + /** | ||
| 253 | + * Returns OpenStack user. | ||
| 254 | + * | ||
| 255 | + * @return user name | ||
| 256 | + */ | ||
| 257 | + public String user() { | ||
| 258 | + return this.user; | ||
| 259 | + } | ||
| 260 | + | ||
| 261 | + /** | ||
| 262 | + * Returns OpenStack password for the user. | ||
| 263 | + * | ||
| 264 | + * @return password | ||
| 265 | + */ | ||
| 266 | + public String password() { | ||
| 267 | + return this.password; | ||
| 268 | + } | ||
| 269 | + } | ||
| 270 | +} | ... | ... |
This diff is collapsed. Click to expand it.
| ... | @@ -75,8 +75,8 @@ import org.onosproject.net.group.GroupBuckets; | ... | @@ -75,8 +75,8 @@ import org.onosproject.net.group.GroupBuckets; |
| 75 | import org.onosproject.net.group.GroupDescription; | 75 | import org.onosproject.net.group.GroupDescription; |
| 76 | import org.onosproject.net.group.GroupKey; | 76 | import org.onosproject.net.group.GroupKey; |
| 77 | import org.onosproject.net.group.GroupService; | 77 | import org.onosproject.net.group.GroupService; |
| 78 | -import org.onosproject.openstackinterface.OpenstackNetwork; | 78 | +import org.openstack4j.model.network.Network; |
| 79 | -import org.onosproject.openstackinterface.OpenstackSubnet; | 79 | +import org.openstack4j.model.network.Subnet; |
| 80 | import org.slf4j.Logger; | 80 | import org.slf4j.Logger; |
| 81 | 81 | ||
| 82 | import java.util.ArrayList; | 82 | import java.util.ArrayList; |
| ... | @@ -190,30 +190,31 @@ public class CordVtnRuleInstaller { | ... | @@ -190,30 +190,31 @@ public class CordVtnRuleInstaller { |
| 190 | * | 190 | * |
| 191 | * @param host host | 191 | * @param host host |
| 192 | * @param tunnelIp tunnel ip | 192 | * @param tunnelIp tunnel ip |
| 193 | - * @param vNet openstack network | 193 | + * @param osNet openstack network |
| 194 | */ | 194 | */ |
| 195 | - public void populateBasicConnectionRules(Host host, IpAddress tunnelIp, OpenstackNetwork vNet) { | 195 | + public void populateBasicConnectionRules(Host host, IpAddress tunnelIp, Network osNet) { |
| 196 | checkNotNull(host); | 196 | checkNotNull(host); |
| 197 | - checkNotNull(vNet); | 197 | + checkNotNull(osNet); |
| 198 | 198 | ||
| 199 | DeviceId deviceId = host.location().deviceId(); | 199 | DeviceId deviceId = host.location().deviceId(); |
| 200 | PortNumber inPort = host.location().port(); | 200 | PortNumber inPort = host.location().port(); |
| 201 | MacAddress dstMac = host.mac(); | 201 | MacAddress dstMac = host.mac(); |
| 202 | IpAddress hostIp = host.ipAddresses().stream().findFirst().get(); | 202 | IpAddress hostIp = host.ipAddresses().stream().findFirst().get(); |
| 203 | - long tunnelId = Long.parseLong(vNet.segmentId()); | 203 | + long tunnelId = Long.parseLong(osNet.getProviderSegID()); |
| 204 | 204 | ||
| 205 | - OpenstackSubnet subnet = vNet.subnets().stream() | 205 | + Subnet osSubnet = osNet.getNeutronSubnets().stream() |
| 206 | .findFirst() | 206 | .findFirst() |
| 207 | .orElse(null); | 207 | .orElse(null); |
| 208 | 208 | ||
| 209 | - if (subnet == null) { | 209 | + if (osSubnet == null) { |
| 210 | log.error("Failed to get subnet for {}", host.id()); | 210 | log.error("Failed to get subnet for {}", host.id()); |
| 211 | return; | 211 | return; |
| 212 | } | 212 | } |
| 213 | + Ip4Prefix cidr = Ip4Prefix.valueOf(osSubnet.getCidr()); | ||
| 213 | 214 | ||
| 214 | populateLocalInPortRule(deviceId, inPort, hostIp); | 215 | populateLocalInPortRule(deviceId, inPort, hostIp); |
| 215 | - populateDirectAccessRule(Ip4Prefix.valueOf(subnet.cidr()), Ip4Prefix.valueOf(subnet.cidr())); | 216 | + populateDirectAccessRule(cidr, cidr); |
| 216 | - populateServiceIsolationRule(Ip4Prefix.valueOf(subnet.cidr())); | 217 | + populateServiceIsolationRule(cidr); |
| 217 | populateDstIpRule(deviceId, inPort, dstMac, hostIp, tunnelId, tunnelIp); | 218 | populateDstIpRule(deviceId, inPort, dstMac, hostIp, tunnelId, tunnelIp); |
| 218 | populateTunnelInRule(deviceId, inPort, dstMac, tunnelId); | 219 | populateTunnelInRule(deviceId, inPort, dstMac, tunnelId); |
| 219 | } | 220 | } | ... | ... |
| ... | @@ -47,6 +47,10 @@ | ... | @@ -47,6 +47,10 @@ |
| 47 | <param-name>javax.ws.rs.Application</param-name> | 47 | <param-name>javax.ws.rs.Application</param-name> |
| 48 | <param-value>org.onosproject.cordvtn.rest.CordVtnWebApplication</param-value> | 48 | <param-value>org.onosproject.cordvtn.rest.CordVtnWebApplication</param-value> |
| 49 | </init-param> | 49 | </init-param> |
| 50 | + <init-param> | ||
| 51 | + <param-name>jersey.config.server.disableAutoDiscovery</param-name> | ||
| 52 | + <param-value>true</param-value> | ||
| 53 | + </init-param> | ||
| 50 | <load-on-startup>1</load-on-startup> | 54 | <load-on-startup>1</load-on-startup> |
| 51 | </servlet> | 55 | </servlet> |
| 52 | 56 | ... | ... |
-
Please register or login to post a comment