Convert Java Web applications to ASP. NET

Source: Internet
Author: User
Tags types of filters uppercase letter

Java Web ApplicationsProgramConvert to ASP. NET

Brian jimerson

 

This article discusses:

· Resource Positioning

· I/O Stream

· Logging and collection

· Reconstruction

This article uses the following technologies:
ASP. NET, JLCA, and C #

DownloadCode: Jlca2007_05.exe (157 KB)
Browse online code

Directory

About JLCA
Locate resources
Process input/output API
Log records
Set
Filters and HTTP handlers
Source tree and naming conventions
When to refactor the Convention
Directory layout and namespace
Attribute
Pascal case-sensitive method name
Summary

Typical software development cycles follow a simple model: collection requirements, designing applications, writing code, testing software, and deploying software. However, sometimes new development projects are started based on the platform the customer wants to deploy the application. In this case, the basic code of the existing application can be converted or transplanted to the expected platform.

In this article, I will fully introduce how to convert Java Web applications to ASP. NET applications implemented in C. This article is based on the actual project I participated in. In this project, we have a ready-made Java-based application, and the customer wants to use its ASP. NET version. First, I will introduce Microsoft Java language conversion Assistant (JLCA) and demonstrate common development examples that do not directly correspond to items on the two platforms. For example:

· Input/output

· Resource Parsing

· Source tree layout and naming conventions

· Use the execution environment

This application is implemented as a soap-compliant Web Service and uses the traditional relational database for permanent storage. I will not discuss the actual web service presentation layer and soap interface, but will introduce the applications that support it. The sample code in this article can be downloaded.

 

About JLCA

The JLCA tool is used to convert Java applications into C # applications. This tool is provided with Visual Studio since Visual Studio. NET 2003. The current version of JLCA 3.0 is included in Visual Studio 2005. You can also download it from the JLCA homepage for free.

Version 3.0 includes enhancements to conversions in the following aspects: Java projects such as servlets and Java Server Pages (JSP), and the use of swing or Abstract Window Toolkit (AWT) fat client application. In fact, JLCA is a good tool for starting the conversion, but it will not complete the entire process successfully. Therefore, do not expect this process to be completely automatic. After using this tool, you still need to manually parse the converted project.

To start JLCA learning in Visual Studio 2005, click "file", "open", and "convert" to start the wizard. The wizard screen is clear at a glance. The key information you need to enter (1) is the root directory of an existing Java project.

 

Figure 1 enter the Java root directory in the JLCA wizard (click the image to get a larger view)

Then, JLCA startsSource codeConvert to C #. The process will not take too long. The basic code that contains about 100 class files took less than 10 minutes to complete the conversion, just enough time to have a cup of coffee. Of course, for different projects and systems, this number will be different.

After the conversion process is complete, JLCA creates an HTML report for errors and warnings. These items will also be generated as comments for problematic members in the C # code. To help you, each item also contains a hyperlink to detailed information about troubleshooting.

Many warnings can be safely ignored. They are only used to record the differences between Java and C # behaviors, for example, warning: "forced type conversion between primitive types may have different behaviors ". However, you should still view each warning to ensure that these different actions do not affect the application.

During the first viewing of the conversion report, too many problems may be reported. In this example, there are 816 errors and 16 warnings. However, most errors can be categorized into three categories and can be easily solved. They belong to the following three categories:

· There is no member of the C # or Microsoft. NET Framework equivalent.

· There is no popular third-party Java library with direct equivalent items (such as Hibernate and log4j) in. NET Framework.

· Processing class loading or resource parsing items.

In addition, JLCA does not seem to try to parse the imported packages that it cannot find (or use namespace statements), but directly passes them to the generated C # code. If you try to compile a new C # application, you may get more compiler errors than in the conversion report.

But don't worry. As mentioned above, most of these errors belong to the same repetition mode and can be easily solved together. I will introduce solutions to these common errors in the following sections and discuss other tasks that need to be executed to make the converted application a true C # application.

 

Locate resources

The resource locating process in Java (specifically, finding and loading file resources) is very different from that in C. Java uses a class loader to load and analyze class definitions at runtime. Part of the responsibility of a class loader is to manage the context and provide the environment convenience for the classes it loads. In Java, a Class Loader uses a special type of environment variable called classpath to locate resources. Similar to the PATH environment variable, classpath defines where the class loader should look for other classes and resources. Applications wishing to load another class or resource can load by telling the Class Loader about the file location of the classpath.

A very common method for parsing resources in Java is to use special types of files called attribute files to obtain configuration information. For example, connection or host information, path of other resources, localization string, and creden。 for authentication. Attribute files include name = value pairs, which are separated by line breaks. This format is very similar to the INI file, but there is no section.

In addition, resource locating is always performed using File System notation regardless of whether the resource is actually located in the file system. This will ease the burden that developers must know how to deploy applications. Other types of resources (such as images and binary files) are located and loaded in the same way. The following is an example of locating and using attribute files in Java:

Copy code

Inputstream is = This. getclass (). getresourceasstream (

"/Application. properties ");

Properties Properties = new properties ();

Properties. Load (is );

String myvalue = properties. getproperty ("mykey ");

On the contrary, you can deploy and load resources in. NET Framework in two different ways: embedding binary resources into a set of programs, or serving as files in a local file system.

The proper skill for accessing resources depends on the resource location. For example, if it is a file system resource, you can do the following:

Copy code

String filename = path. Combine (path. getfullpath (

@ ".. \ Config \"), "properties. xml ");

Stream filestream = file. Open (filename, filemode. Open );

// Do something with the stream

However, if the resources are embedded into the Assembly, it is more likely to do this:

Copy code

Assembly = assembly. getexecutingassembly ();

Stream filestream = assembly. getmanifestresourcestream (

GetType (), "properties. xml ");

// Do something with the stream

An assumption for applications compiled by my team is that you do not need to know how to deploy resources for parsing. Because many resources need to be loaded in the entire application, it takes a lot of effort to analyze each loaded resource, determine how the resource is deployed, and modify the code to load it correctly. Therefore, we have created a utility class called resourcelocator.

Resourcelocator is designed to simulate the ability of a Java class loader to parse resources based on classpath. Since this method is used to write all calls to load resources, it seems to be the least invasive conversion method. After compiling resourcelocator, all we need to do is change the call from class. getresourceasstream to resourcelocator. locateresource. Use simple search and replacement in Visual Studio.

Basically, resourcelocator gets the name and relative path of the resource to be searched, and then tries to find the resource by traversing the available Assembly and local file system. You can also provide more fine-grained overload methods, such as specifying the order of search regions and selecting to only search for an assembly or file system. The sample code on the msdn magazine website contains the source code of resourcelocator.

You may think that it is very costly to find resources by finding all available locations. However, all resources located and loaded in the application will be cached later. This means that each resource is loaded only once during application execution, which reduces the sales (but it does increase memory usage. If you want to load a large amount of resources, this will be a problem ). Therefore, considering that it reduces a lot of code changes, we think such a trade-off is acceptable. You can also modify these types of changes over time. First, you can use a simple solution to make the port run quickly, and then slowly and reliably change the implementation to better comply with the based on.. net.

 

Process input/output API

There are several differences between the I/o api in Java and the I/O API to be processed after conversion in. net. An important difference is that. net I/O streams are bidirectional, while Java I/O streams are unidirectional. This means that in. NET programming, the same stream can be read and written theoretically. However, in Java, you can only read or write data to a stream, but you cannot perform both operations on the same stream at the same time. This difference does not cause great difficulties during the conversion, because it is an extended difference, and the. Net stream provides at least the same functions as its Java counterparts.

To ensure the security of a one-way stream, you can use the. net I/O reader and writer. They package the basic stream to provide the read or write functions. The result will make the I/O operation program similar to the Java I/O operation.

For our applications, direct access to the stream is enough. JLCA can correctly convert I/O operations, so there is no need to make any changes to execute the compilation. However, we carefully checked the converted code because logical errors may easily occur in these low-level operations.

 

Log records

Logging focuses on messages (for example, caught exceptions, logic aspects that may need to use debugging information, and loaded configuration information) at specific execution points in the code) write target capability. Both Java and. NET Framework provide a powerful framework for recording information, but their design and implementation are quite different.

In Java, Apache Software Foundation (ASF) is usually distributed together with the latest Sun Microsystems Java Development Kit (JDK) log4j framework or Java log API to complete the application logging function. Java log API is very similar to log4j in execution. Therefore, the two frameworks can be applied alternately for the purpose of this discussion. For. Net-based applications, Microsoft Enterprise Library provides powerful application blocks for logging, known as log application blocks (see Microsoft logging Application Block homepage ).

The standard log framework in Java and. NET provides powerful functions and supports design-time configuration and log target hosts (such as databases, files, and Email recipients. However, note that the API design varies, so manual intervention is required during the conversion process.

To better understand the differences between APIs, let's take the two code segments shown in Figure 2 as an example. They demonstrate common log solutions executed in Java and C. Use the code segment of log4j to create a static instance of the log object through the getlog factory method, and assign it to the myclass class. Then, the Code prints some information to the log at the debugging level. C # code segment (using the log Application Block) creates a new logentry class instance. It indicates an entry in the target log. Then, the Code assigns priority 2 to log entries, sets the category to debug, and then provides a message. Finally, it is written into the logger class.

Log records in Figure 2 Java and C #

Use the Java

Copy code

Private Static final log = logger. getlog (myclass. Class );

...

// Somewhere else in the class

Log. debug ("printing some debug information .");

Use C # Of the log Application Block #

Copy code

Logger. Write ("printing some debug information.", "DEBUG ");

Note that log records on both platforms are controlled by external configurations. Information such as logging target, selective Log message filtering, and log entry format settings are included in this configuration. I intentionally deleted the configuration in this example because it has nothing to do with our discussion.

The two examples show that they perform very similar functions but are implemented in different ways. The log4j example uses a category to determine whether a message is recorded (based on its level) and the target location it records, the log Application Block uses a combination of priority and category filters to determine the content to be recorded.

The logger class provides several overloaded write methods, each of which has different levels of flexibility (including one overload, which allows you to provide a logentry instance, there are a large number of control settings used to adjust how to record information ). However, if the simple overload is used in Figure 2, we can combine it with a regular expression and use search and replacement to complete the batch conversion process.

Logging is a resource-consuming process and requires careful use to ensure that it does not affect the performance of applications. Finally, it takes only a few hours to search for and replace logs in the application.

 

Set

Both. NET Framework and Java provide powerful collection APIs. Both of them have excellent scalability and cover most of the situations that may occur when a set is used. The following describes two common collection types: List and dictionary.

A list is a set that can be accessed by index. They are ordered sets and can be considered as a one-dimensional array. On the other hand, a dictionary is a set of name and value pairs. The name is the key used to access the values in the set. Note that the content in the dictionary cannot be ordered.

Figure 3 lists the equivalent C # and Java implementations of the list and dictionary. JLCA can effectively convert these Java Collection classes into their. Net equivalent items, and little processing is required after the conversion. A warning will be generated here, indicating that the behavior of these common sets is different on two platforms, so the conversion should be verified. However, the vast majority of the conversion process should be successful and effective.

Equivalent collection classes of Figure 3 in. NET and Java

 

 

. Net

Java

List Interface

Ilist, ilist <t>

List

Common List classes

Arraylist, list <t>

Arraylist, Vector

Dictionary Interface

Idictionary, idictionary <tkey, tvalue>

Map

Common tokens

Hashtable, Dictionary <tkey, tvalue>

Hashmap, hashtable

In fact, we encountered a problem when using a special set in the original Java application when converting the set. For example, this problem occurs when you use the Java javashashset class. According to the Java API documentation, the javashashset class ensures that entries in the hashset (which is a dictionary type) are in the same order, while avoiding the overhead of other sort dictionaries. Although the definition of linkedhashset is easy to understand, its usage in applications is not clear. (The person who wrote the code has left the team and has not left any documentation on why it was used .) In addition, the context using it cannot provide us with any information, so it is unclear whether the purpose of using this class is to fix the problem or for another reason.

When we look up the code, we find that there is no legitimate reason to use it, so we have three options: assuming that it is not actually needed in the original application,.. NET application programming our own implementation, in.. NET Framework. We assume that the dedicated linkedhashset implementation is unnecessary, because there is no indication that the sort name value pair is used anywhere else. Similarly, we replaced the basic. Net hashtable class and used the existing unit and integration test to verify the correct behavior. However, if we find performance or functional problems, it may have been replaced.. NET Framework 2.0 sorteddictionary <tkey, tvalue> class, which indicates the set of key/value pairs for key-press sorting. Internally, its implementation uses a set based on the data structure of the Red-black tree.

In our project, only four special Java Collection classes are used, and all of them present similar environments. They provide other functions that are useless, and use a more general. Net corresponding item to complete the task at hand.

I should note that our Java applications are written in Java 1.4. Java is not introduced to generic collections until version 1.5. Generic collections provide powerful typing functions for set members. Therefore, we do not need to delve into the conversion of generic sets. Since not only the set needs to be converted but also the entries it typed, it is expected that the conversion process will be more complex in Java 1.5.

 

Filters and HTTP handlers

Filters are common examples used in J2EE Web applications to selectively intercept requests and responses for pre-and post-processing operations. Filters are commonly used for logging, usage review, and security.

Java filters implement the filter interface, which is used to define specific lifecycle events. The filter class is mapped to a complete or partial URL by using URL ing. The filter is called by the application server. After a match is established, the application server will implement the lifecycle events of the mapped filter, and then pass the handle to the request and response.

ASP. NET provides similar functions through the ihttphandler interface. Filters and HTTP handlers have very simple interfaces, but they have very powerful functions. In our applications, we have two different types of filters: one uses gzip compression to compress the response, and the other intercepts the request to determine whether the request comes from a known user proxy.

Compression filters are implemented in Java applications to improve performance. It obtains each response stream and checks whether the client supports gzip compression. If yes, It compresses the response. Generally, this is not necessary, because most modern HTTP servers provide this function without the need for custom code. However, our application is included as a servlet application, which does not necessarily require the use of HTTP servers. Therefore, the compression function is a value-added module.

The second filter rejects requests based on the value of the user proxy header, which deserves more attention. Many of our customers want to implement a certain level of authentication, that is, they can compare the HTTP user proxy header with the allowed proxy list. If the user agent value of the incoming request is not the permitted user agent, the request should be rejected (the HTTP return value is usually returned ).

This type of request/response filter can be completed in many ways. However, most solutions require a lot of code or attributes injection. Fortunately, J2EE and. Net provide a very simple mechanism to intercept, modify, and adjust requests and responses at the application level. In addition, HTTP listeners like this are not widely used in code, that is, they are not the code lines in each class. Instead, they are individual classes managed and injected through the application server, so it is easier to modify the function than to execute global search and replacement.

JLCA 3.0 also provides helper classes to help you migrate filters from Java to ASP. NET applications. Figure 4 shows the sample filter implemented in Java (which is used to time server processing) and how JLCA tries to convert this filter against ASP. NET. In ideal cases. net implementation, you may need to re-compile the filter from the beginning as an HTTP processing program, but the support class supportclass provided by JLCA. servetfilter helps you complete most of the work by implementing it again. Servletfilter can simulate the lifecycle provided by Java. Although it does not solve all the problems, it can make porting implementation easy.

Figure 4 Use servletfilter for JLCA Conversion

Java

Copy code

Import javax. servlet .*;

Import java. Io .*;

 

Public final class timerfilter implements Filter

{

 

Public void dofilter (servletrequest request,

Servletresponse response,

Filterchain chain)

Throws ioexception, servletexception

{

 

Long starttime = system. currenttimemillis ();

Chain. dofilter (request, response );

Long stoptime = system. currenttimemillis ();

System. Out. println ("time to execute request:" +

(Stoptime-starttime) + "milliseconds ");

}

 

Public void destroy (){}

 

Public void Init (filterconfig FC ){}

}

C #

Copy code

Using system;

Using system. Web;

 

// Upgrade_todo: Verify List of Registered servlet filters.

Public sealed class timerfilter: supportclass. servletfilter

{

Public override void dofilter (httprequest request,

Httpresponse response, supportclass. servletfilterchain chain)

{

Long starttime =

(System. datetime. Now. ticks-621355968000000000)/10000;

Chain. dofilter (request, response );

Long stoptime =

(System. datetime. Now. ticks-621355968000000000)/10000;

Console. Out. writeline ("time to execute request:" +

(Stoptime-starttime) + "milliseconds ");

}

 

Public void destroy (){}

 

// Upgrade_issue: interface 'javax. servlet. filterconfig'

// Was not converted.

Public void Init (){}

}

In fact, our team adopted the method of manually converting the Java filter function into an HTTP handler. To guide you through this process, I should compare two interfaces. J2EE filters have three methods: init, destroy, and dofilter. The init and destroy methods are important life cycle methods, but they are beyond the scope of this article. The dofilter method performs most of the work in the filter.

In our solution, the dofilter method obtains the user proxy header variable from the input request and checks the variable against the configurable list of known user proxies. In conversion, the most difficult aspect is that there are differences between objects passed as parameters to operable methods.

In the Java filter. dofilter method, three parameters are passed: request object, response object, and filter chain object. On the contrary, the ihttphandler class's processrequest method has only one parameter: The httpcontext variable. The httpcontext variable references httprequest and httpresponse objects. In addition, you can access members of httpcontext to perform most of the same functions.

Java filterchain objects are interesting. It indicates the filter chain in the HTTP request. The filter may pass the request to the next filter in the chain to delegate the responsibility in a continuous manner. Filterchain is not often used, but it can be implemented using ihttpmodule to obtain similar behaviors.

With filterchain, it is very easy to convert Java filter to. Net ihttphandler. First, you must create an ihttphandler implementation. Then, you need to re-construct the logic in the dofilter method of the filter to the processrequest method of the implementation, so as to use the members of httpcontext, rather than the passed request and response object. Finally, you need to define the isreusable method in the implementation; for simplicity, it can only return false (this is another case, you can write more code later, to determine whether the same handler instance can be reused by subsequent requests to improve performance ).

 

Source tree and naming conventions

Although Java and C # are similar, they differ in terms of dictionaries, source tree layout, and naming conventions. Here, I would like to explain in detail some of these differences.

The Java package (equivalent to the namespace in C #) complies with the Reverse Domain Name conventions, followed by the application or module name, followed by the function. These packages are usually nested deep (usually four or five levels of depth ). On the contrary, C # namespaces are usually grouped by descriptive names of functions, and usually have a very low degree of nesting (usually level 1 to level 4 ). In addition, Java packages are usually in lower case or camel case, while C # namespaces are usually in Pascal case. Similarly, the Java method usually uses camel case sensitivity, while the C # method and attributes usually use Pascal case sensitivity.

C # An interface generally starts with an uppercase I, indicating an interface (this is a convention and is not required for a correct function ). According to the old Java conventions, the interface name should end with "able" to indicate that something can be done. This Convention is almost no longer used. Currently, the interface name and class name are usually no different.

C # use attributes to access and mutate private members. The property is the metadata wrapper used to obtain and set the accessors method. However, Java has no attributes. Generally, the accessors and mutators of Private Members are implemented as getter or setter methods. In other words, the accessor of the private field named "name" will be getname.

Finally, the Java class must be located in the directory that matches the package it declares (relative to the root of the source file (that is, classpath ). C # does not have this restriction.

 

When to refactor the Convention

Although the C # application that resolves these differences can be compiled and run without being converted, compliance with conventions is always the best practice. It is difficult to decide when to refactor the converted code to comply with the conventions. However, there are two factors that make it appropriate to finish the job early.

Since refactoring code to comply with conventions is not absolutely important for the normal operation of the application (not to mention sometimes it is boring), people may think that this step is not needed in the project. There are many things that make it a low-priority task, so it is possible to never do this job. However, if a development team is responsible for the project, refactoring the Code to make it look familiar will help the team improve efficiency and productivity. Do not underestimate the importance of these tasks. This refactoring is as important as other tasks involved in ensuring successful results during the conversion process.

 

Directory layout and namespace

Naming and directory conventions are subjective, but there are still general rules. For our project, assume there is a class for custom XML analysis, and it is located in the com. mycompany. myapplication. xml. util namespace (and directory, after conversion. C # It is recommended that the class be in the XML. util namespace. Before refactoring, our directory tree looks like figure 5. The file drag-and-drop feature in Visual Studio allows you to physically move files, making the directory tree look like figure 6.

 

Figure 5 Java source tree

 

Figure 6 C # source tree

However, C # does not specify that the directory location of the file should match the declared namespace. Therefore, the system does not update the namespace of the class to match the file system location. In Visual Studio, there is no automated way to move multiple classes to different namespaces. The best way to do this is to perform search and replacement in the entire solution, 7. Of course, there is a premise assumption that all classes in a namespace should be moved to the same target.

 

Figure 7 find and replace the namespace Declaration (click the image to get a larger view)

 

Attribute

The attribute construction in C # is very different from that in Java. Attributes can be considered as common fields for storing class States (or in UML terminology, class attributes. However, Java does not have attribute construction, but uses methods called getter and setter to represent attributes.

Suppose there is a class that has an instance variable called "name. You do not want to set this variable as a public variable, because it will lose all control of modifying this variable. In Java, the standard method for accessing this variable is to use getter and setter, so that the name is obtained by adding the prefix "get" or "set" for the variable name as agreed. Therefore, if the name variable is a string, Java getter and setter may look like the following:

Copy code

Public String getname (){

Return this. Name;

}

 

Protected void setname (string name ){

This. Name = Name;

}

C # provides attribute construction to complete the same function. Although they can be used in application logic, they are initially designed to provide protected access to private implementation details, as described above. Therefore, the C # Implementation of the same access will be as follows:

Copy code

Public string name

{

Get {return this. Name ;}

Protected set {This. Name = value ;}

}

When the C # attributes fulfill the same goal, I find them clearer-in Java, methods that do not modify the state of classes may start with get or set, which leads to confusion. Therefore, we recommend that you refactor Java getter and setter to make them C # attributes.

In fact, there is no simple way to convert Java getter and setter to C # attributes. Regular Expression search and replacement will be complicated, and JLCA only migrates Java getter and setter methods as they are, because it cannot tell whether the method is modifying the class status or executing some other functions. Our solution to this problem is to use a more practical solution. Visual Studio provides a wizard for encapsulating Private Members with properties. This will generate the expected attributes without deleting the converted Java getter and setter.

When processing code for other reasons, our team also generated the C # Attribute in Visual Studio and deleted the corresponding getter and setter methods. After this step, developers will use the Visual Studio 2005 lookup reference function to provide a list of all call sites for a specific method. This allows them to reference the old getter and setter methods, so that they can easily change the reference to the new property. This is not the best solution, but it is surprisingly good.

It should be emphasized that this is a very important step in the porting process, because attributes are inherent core features of C # And. net, and our goal is to create a C # application. (Remember, this application is finally supported by another team, and they want a C # application .)

 

Pascal case-sensitive method name

As mentioned above, the names of Java methods are in camel case. In other words, these names start with lowercase letters, and each subsequent word boundary uses uppercase letters. C # Conventions require that the method and other members use Pascal case. Pascal is similar to camel in case, but the first letter in the name is also in case.

What does this mean to you? You may need to rename all methods to start with an uppercase letter instead of a lowercase letter. For example, Java method

Copy code

Public void getresponsecode ()

It should be restructured into C # method:

Copy code

Public String getresponsecode ()

Similar to converting getter and setter into attributes, there is no simple method to traverse all converted code and update Members to comply with the C # naming conventions. However, we think this is a very important task, because the converted member is different from the C # member, and again stressed that, our team and our customers want to get the correct C # application.

Theoretically, you can write a code converter to execute this task, and we will consider doing it ourselves. However, we decided to adopt the same way as processing attributes, that is, to update the member name section. There are several reasons for using the manual method.

For beginners, our developers have analyzed all the code to update other elements, such as getter and setter. This process may increase gradually because the compilation capability or function will not be affected if the member name is not changed. Moreover, if one or two Members are lost for some reason, the application will not be interrupted. This process takes less time than you think-the main problem is that the task is boring.

Visual Studio 2005 supports built-in refactoring. Some tasks supported by refactoring include securely renaming a member, determining how to rename a member, and updating all references to the member. Traversing all the Members to be updated takes some time, but this is an effective solution.

 

Summary

This article describes a real case of converting a Java Web application to ASP. NET. For us, most of the heavy work is done by JLCA, but several tasks require manual intervention. However, JLCA is a powerful tool that enables our applications to be quickly transplanted to actual. NET applications.

This article aims to demonstrate the feasibility of converting a Java application to. net, and points out some problems that need to be solved and exceed the JLCA capability. Of course, each specific application porting will encounter specific problems, which cannot be solved in this article. However, some tips and methods provided in this article should help you solve any possible problems.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.