Techs with my experience




Thursday, December 23, 2010

VMware VI (vSphere) Java API

VMware VI (vSphere) Java API


To Design the Applications for VMware Infrastructure



VMware VI (vSphere) Java API


VI Java is a set of client-side libraries designed to ease the burden of developing applications for


VMware Infrastructure (VI) using the Java language. VI Java brings true OOP design to the VI SDK.


VI java provides us the required API to manage the vCenter Server Infrastructure management.


To implement VIJava we need to add the respective libraries in the Project.


VI java is hosted by sourceforge.net with BSD license and readily available



Guide for Implementing VI Java:


Go to the VI Java API web site( http://vijava.sf.net ) and click on the download link at the top.



On the download page, select the latest release and click the download link as follows:


http://sourceforge.net/projects/vijava/files/vijava/VI%20Java%20API%202.1/


Choose the binary vijava**********.zip (Note: the name can be different for future releases. Just pick up latest one) and start to download it. Optionally, you can download the package with source code -- vijava*src.jar



Unzip the vijava*********.zip file to a folder of your choice and you will get two jar files, one for VI Java API itself, and the other is dom4j-1.6.1.jar.



Include both the files in the library.



Make sure that you do not include the rest of Library files offered by the vSphere WebService SDK as it will lead to confusion for system from where to fetch the library files.

Error:

Exception in thread "main" java.lang.InstantiationError: com.vmware.vim25.VimPortType

    at com.vmware.vim25.mo.ServiceInstance.(ServiceInstance.java:82)
    at com.vmware.vim25.mo.ServiceInstance.(ServiceInstance.java:69)
    at com.wipro.dummy.CreateVM.main(CreateVM.java:97)



Check the library Files make sure only viJava***.jar along with dom4j***.jar is included as shown below.




Writing First Program with VIJava


Kindly go through the tutorials/samples In case you are unable to use any of the feature


Inorder to setup a connection with vCenter Server



public static String serverip = "10.XXX.XXX.XX";

public static String userName = "administrator";

public static String password = "secret";

public static String centerServer = "https://”+ serverip + “/sdk/vimService";

public static String[] details = {centerServer,userName,password};


Create a service instance to a server


ServiceInstance si = new ServiceInstance(

new URL(centerServer), userName, password, true);

Folder rootFolder = si.getRootFolder();



To create a instance of HostSystem


HostSystem hostSystem= (HostSystem) new InventoryNavigator(rootFolder).searchManagedEntity("HostSystem", "hostName");



System.out.println("Max Vm Supported: "+hostSystem.getCapability().maxSupportedVcpus);

System.out.println("Max Vm Supported:"+hostSystem.getCapability().maxSupportedVcpus +" ");


Explore all the functions which are required to obtain respective information of HostSystem


Using this you can also explore the various configspec of the Host System


HostVirtualSwitch[] vSwitch = hostSystem.getConfig().getNetwork().getVswitch();

if(vSwitch!=null)

{

System.out.println(vSwitch.length);

System.out.println(vSwitch[1].getPnic()[1]);

}


In Order to obtain the Details of all the VM present in the HostSystem


VirtualMachine[] vMachine = hostSystem.getVms();


To obtain the details of all the VM Machines


for(VirtualMachine vm:vMachine)

{

System.out.println("Virtual Machine Name: " + vm.getName());

System.out.println("Virtual Machine Address: " + vm.getGuest().ipAddress );

System.out.println("Virtual Machine ID: " + vm.getGuest().guestId );

System.out.println("" + vm.getSummary().overallStatus );

System.out.println("Vm Power State: " + vm.getRuntime().getPowerState());


}



VM Creation:



To Create new VM provide the following parameters such as


String dcName = "DataCenter01"; //Name of data center

String vmName = "VjavaAPIVM"; //Name of VM

long memorySizeMB = 500; //Size of RAM

int cupCount = 1; //Number of Virtual CPU

String guestOsId = "sles10Guest"; //Guest Os IS

long diskSizeKB = 1000000; //Size of Hard Disk

String diskMode = "persistent"; //Disk Mode

String datastoreName = "software"; //DataStore for the VM

String netName = "VM Network"; //Network to be used

String nicName = "Network Adapter 1"; //Name of network Adapter

int cKey = 1000;


Create a service Instance as stated above


Folder rootFolder = si.getRootFolder();

Datacenter dc = (Datacenter) new InventoryNavigator( rootFolder).searchManagedEntity("Datacenter", dcName);

ResourcePool rp = (ResourcePool) new InventoryNavigator(

dc).searchManagedEntities("ResourcePool")[0];

Folder vmFolder = dc.getVmFolder();


Create a VM Config Spec

VirtualMachineConfigSpec vmSpec =

new VirtualMachineConfigSpec();

vmSpec.setName(vmName);

vmSpec.setAnnotation("VirtualMachine Annotation");

vmSpec.setMemoryMB(memorySizeMB);

vmSpec.setNumCPUs(cupCount);

vmSpec.setGuestId(guestOsId);


Adding Virtual Devices to VM:


Create a Scsi spec for the Storage of VM:


/**

Create a DeviceConfigSpec for the SCSI

Perform operation in order to add the Virtaul Device in VirtualDeviceConfigSpec */


/**

Create the type of SCSI you would require to add in your Virtual Machine such as LSILogicController Parallel, LSILogicController SAS, Bus Logic Controller , VM ware Parairtual

for every operating system there are recommended Scsi type Make sure you use the same

VirtualLsiLogicController scsiCtrl =

new VirtualLsiLogicController();


*/

static VirtualDeviceConfigSpec createScsiSpec(int cKey)

{

VirtualDeviceConfigSpec scsiSpec =

new VirtualDeviceConfigSpec();

scsiSpec.setOperation(VirtualDeviceConfigSpecOperation.add);



VirtualLsiLogicController scsiCtrl =

new VirtualLsiLogicController();

scsiCtrl.setKey(cKey);

scsiCtrl.setBusNumber(0);

scsiCtrl.setSharedBus(VirtualSCSISharing.noSharing);


//Add the scsi controller to SCSi device


scsiSpec.setDevice(scsiCtrl);

return scsiSpec;

}

/**

Create the HARDDISK you would require to add in your Virtual Machine

Define the dataStore Name

DataStore : dsName

Size of Hard disk: diskSizeKB

VirtualDeviceConfigSpecFileOperation.create); to create A .vmdk file

Adding the Virtaul Disk

diskSpec.setDevice(vd);


*/

static VirtualDeviceConfigSpec createDiskSpec(String dsName,

int cKey, long diskSizeKB, String diskMode)

{

VirtualDeviceConfigSpec diskSpec =

new VirtualDeviceConfigSpec();

diskSpec.setOperation(VirtualDeviceConfigSpecOperation.add);

diskSpec.setFileOperation(

VirtualDeviceConfigSpecFileOperation.create);

VirtualDisk vd = new VirtualDisk();

vd.setCapacityInKB(diskSizeKB);

diskSpec.setDevice(vd);

vd.setKey(0);

vd.setUnitNumber(0);

vd.setControllerKey(cKey);

/**

Setting up vmdk file for VM, Setting the various parameters for the Storage file


*/

VirtualDiskFlatVer2BackingInfo diskfileBacking =

new VirtualDiskFlatVer2BackingInfo();

String fileName = "["+ dsName +"]";

diskfileBacking.setFileName(fileName);

diskfileBacking.setDiskMode(diskMode);

diskfileBacking.setThinProvisioned(true);

vd.setBacking(diskfileBacking);

/**

Will return the Diskspec which will be used to add in the virtual machine Virtual Device

*/

return diskSpec;

}



/**

Creation of NIC

Parameters to passed : Network Name, Network Card Name


To add the Network we need to create a Instance of Virtual Device Config Spec.

than we have to create Specification for the NIC such as

NIC Name for that purpose we will use a NIC predefined

After Creating instance of “VirtualEthernetCardNetworkBackingInfo”


Set the various parameters as:

setDeviceName

Create the basic description of the Nic such as label Description info = new Description();

and add the description to Nic.setDeviceInfo(info)

*/

static VirtualDeviceConfigSpec createNicSpec(String netName,

String nicName) throws Exception

{

VirtualDeviceConfigSpec nicSpec =

new VirtualDeviceConfigSpec();

nicSpec.setOperation(VirtualDeviceConfigSpecOperation.add);


VirtualEthernetCard nic = new VirtualPCNet32();

VirtualEthernetCardNetworkBackingInfo nicBacking =

new VirtualEthernetCardNetworkBackingInfo();

nicBacking.setDeviceName(netName);


Description info = new Description();

info.setLabel(nicName);

info.setSummary(netName);

nic.setDeviceInfo(info);

// type: "generated", "manual", "assigned" by VC

nic.setAddressType("generated");

/**Set the backing for NIC*/

nic.setBacking(nicBacking);

nic.setKey(0);

/**

Add the NIC spec in Virtual Device


*/

nicSpec.setDevice(nic);

return nicSpec;

}



Add the respective function in order to populate the VM virtual Devices


VirtualDeviceConfigSpec scsiSpec = createScsiSpec(cKey);

VirtualDeviceConfigSpec diskSpec = createDiskSpec(datastoreName, cKey, diskSizeKB, diskMode);

VirtualDeviceConfigSpec nicSpec = createNicSpec(

netName, nicName);


After calling the respective function in the and obtaining all the VirtualDeviceConfigSpec


set the VirtualDeviceConfigSpec for the VM.



vmSpec.setDeviceChange(new VirtualDeviceConfigSpec[]

{scsiSpec, diskSpec, nicSpec});




// Create vm file info for the vmx file

VirtualMachineFileInfo vmfi = new VirtualMachineFileInfo();

vmfi.setVmPathName("["+ datastoreName +"]");

vmSpec.setFiles(vmfi);


Create a task to in-order to create a VM

// call the createVM_Task method on the vm folder


Task task = vmFolder.createVM_Task(vmSpec, rp, null);

String result = task.waitForMe();

if(result == Task.SUCCESS)

{

System.out.println("VM Created Sucessfully");

}

else

{

System.out.println("VM could not be created. ");

}

}



Instance to Control the DataStore:


try

{

dc = (Datacenter) new InventoryNavigator(

rootFolder).searchManagedEntity("Datacenter", dcName);

}

catch (Exception e)

{

e.printStackTrace();

throw e;

}

Datastore[] dStore = dc.getDatastores();

if(dStore!=null)

{

for(int i=0;ilength;i++)

{

System.out.println("Data Store Name: " + dStore[i].getSummary().name);

System.out.println("Data Store Capacity: " + dStore[i].getSummary().capacity + "KB");

System.out.println("Data Store Free Space: " + dStore[i].getSummary().freeSpace+ "KB");

System.out.println("Data Store Type: " + dStore[i].getSummary().type);

System.out.println("Data Store URL: " + dStore[i].getSummary().url);

}

}



For better results on Controlling the VM using VIJavaAPI its better to install Vmware tools in the VM created. This will provide much better controls on VM.


Java Program to Power on or off the VM


public static String vmName="ViJAVAAPI";

public static String op="poweron"; //Operation to be performed





ServiceInstance si = new ServiceInstance(

new URL(centerServer), userName, password, true);


if("reboot".equalsIgnoreCase(op))

{

vm.rebootGuest();

System.out.println(vmname + " guest OS rebooted");

}

else if("poweron".equalsIgnoreCase(op))

{

Task task = vm.powerOnVM_Task(null); //to Power On

if(task.waitForMe()==Task.SUCCESS)

{

System.out.println(vmname + " powered on");

}

}

else if("poweroff".equalsIgnoreCase(op))

{

Task task = vm.powerOffVM_Task();

if(task.waitForMe()==Task.SUCCESS)

{

System.out.println(vmname + " powered off");

}

}

else if("reset".equalsIgnoreCase(op))

{

Task task = vm.resetVM_Task();

if(task.waitForMe()==Task.SUCCESS)

{

System.out.println(vmname + " reset");

}

}

else if("standby".equalsIgnoreCase(op))

{

vm.standbyGuest();

System.out.println(vmname + " guest OS stoodby");

}

else if("suspend".equalsIgnoreCase(op))

{

Task task = vm.suspendVM_Task();

if(task.waitForMe()==Task.SUCCESS)

{

System.out.println(vmname + " suspended");

}

}

else if("shutdown".equalsIgnoreCase(op))

{

Task task = vm.suspendVM_Task();

if(task.waitForMe()==Task.SUCCESS)

{

System.out.println(vmname + " suspended");

}

}


The above code can be used to Start and Stop the VM from remote.

Thursday, December 9, 2010

RestFul Web Services Using Jersey


RESTful Web Services


What Are RESTful Web Services?

RESTful web services are services that are built to work best on the web. Representational State
Transfer (REST) is an architectural style that specifies constraints, such as the uniform
interface, that if applied to a web service induce desirable properties, such as performance,
scalability, and modifiability, that enable services to work best on the Web. In the REST
architectural style, data and functionality are considered resources, and these resources are
accessed using Uniform Resource Identifiers (URIs), typically links on the web. The resources
are acted upon by using a set of simple, well-defined operations.
In the REST architecture style, clients and servers
exchange representations of resources using a standardized interface and protocol.

HTTP Methods to Operations Performed
HTTP Method Operations Performed
GET Get a resource
POST Create a resource and other operations, as it has no defined semantics
PUT Create or update a resource
DELETE Delete a resource

Installing Jersey?
Installing Jersey on GlassFish
These steps assume that GlassFish is installed on your system. Jersey is installed as an add-on to GlassFish using the Update Tool that ships with GlassFish.
▼ Adding Jersey to GlassFish
This task describes how to download and add Jersey technology to the GlassFish container.
Start the Update Tool.
1 There are several ways to start the Update Tool. Here a few of the options:
From the Windows Start menu, select GlassFish v3 Prelude, then select Start Update Tool.
From a Windows file browser or command prompt, or from a Unix terminal prompt,
change to the directory where GlassFish was installed, then the bin directory, and run
updatetool.exe (Windows) or updatetool (Unix).
From a web browser, open the Admin Console by going to http://localhost:4848, then
select Update Tool from the left pane.
Click Available Add-ons.
Select Jersey RESTful Web Services for GlassFish.
Installing Jersey in NetBeans
Click Install.
Accept the license.
Jersey-Annotated Application
The sample shown here is from the samples that ship with Jersey, and which can be
found in the following directory of that installation:
jersey/samples/helloworld/src/main/java/com/sun/jersey/samples/helloworld/resources/H
package com.sun.jersey.samples.helloworld.resources;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.Path;
// The Java class will be hosted at the URI path "/helloworld"
@Path("/helloworld")
public class HelloWorldResource {
// The Java method will process HTTP GET requests
@GET
// The Java method will produce content identified by the MIME Media
// type "text/plain"
@Produces("text/plain")
public String getClichedMessage() {
// Return some cliched textual content
return "Hello World";
}
}
The following annotations are used in this example:
The @Path annotation's value is a relative URI path. In the example above, the Java class will be hosted at the URI path /helloworld. This is an extremely simple use of the @Path
annotation. What makes JAX-RS so useful is that you can embed variables in the URIs. URI path templates are URIs with variables embedded within the URI syntax.
The @GET annotation is a request method designator, along with@POST, @PUT, @DELETE, and @HEAD, that is defined by JAX-RS, and which correspond to the similarly named HTTP methods. In the example above, the annotated Java method will process HTTP GET requests. The behavior of a resource is determined by the HTTP method to which the resource is responding.
The @Produces annotation is used to specify the MIME media types of representations a resource can produce and send back to the client. In this example, the Java method will produce representations identified by the MIME media type "text/plain".
The @Consumes annotation is used to specify the MIME media types of representations a resource can consume that were sent by the client. The above example could be modified to set the cliched message as shown here:\

@POST
@Consumes("text/plain")
public void postClichedMessage(String message) {
// Store the message
}

Annotations Defined by JAX-RS?
Annotation Description
@Path The @Path annotation's value is a relative URI path indicating where the Java class will
be hosted, for example, /helloworld. You can also embed variables in the URIs to
make a URI path template. For example, you could ask for the name of a user, and pass
it to the application as a variable in the URI, like this, /helloworld/{username}.
@GET The @GET annotation is a request method designator and corresponds to the similarly
named HTTP method. The Java method annotated with this request method
designator will process HTTP GET requests. The behavior of a resource is determined
by the HTTP method to which the resource is responding.
@POST The @POST annotation is a request method designator and corresponds to the similarly
named HTTP method. The Java method annotated with this request method
designator will process HTTP POST requests. The behavior of a resource is
determined by the HTTP method to which the resource is responding.
@PUT The @PUT annotation is a request method designator and corresponds to the similarly
named HTTP method. The Java method annotated with this request method
designator will process HTTP PUT requests. The behavior of a resource is determined
by the HTTP method to which the resource is responding.
@DELETE The @DELETE annotation is a request method designator and corresponds to the
similarly named HTTP method. The Java method annotated with this request method
designator will process HTTP DELETE requests. The behavior of a resource is
determined by the HTTP method to which the resource is responding.
@HEAD The @HEAD annotation is a request method designator and corresponds to the similarly
named HTTP method. The Java method annotated with this request method
designator will process HTTP HEAD requests. The behavior of a resource is
determined by the HTTP method to which the resource is responding.
@PathParam The @PathParam annotation is a type of parameter that you can extract for use in your
resource class. URI path parameters are extracted from the request URI, and the
parameter names correspond to the URI path template variable names specified in the
@Path class-level annotation.
@QueryParam The @QueryParam annotation is a type of parameter that you can extract for use in your
resource class. Query parameters are extracted from the request URI query
parameters.
@Consumes The @Consumes annotation is used to specify the MIME media types of representations
a resource can consume that were sent by the client.
@Produces The @Produces annotation is used to specify the MIME media types of representations
a resource can produce and send back to the client, for example, "text/plain".
@Provider The @Provider annotation is used for anything that is of interest to the JAX-RS
runtime, such as MessageBodyReader and MessageBodyWriter. For HTTP requests,
the MessageBodyReader is used to map an HTTP request entity body to method
parameters. On the response side, a return value is mapped to an HTTP response
entity body using a MessageBodyWriter. If the application needs to supply additional
metadata, such as HTTP headers or a different status code, a method can return a
Response that wraps the entity, and which can be built using
Response.ResponseBuilder.
@Singleton The @Singleton annotation is used so that only one instance of this class will be instantiated per web application.

@Path Annotation and URI Path Templates
The @Path annotation identifies the URI path template to which the resource responds
For example, look at the following
@Path annotation:
@Path("/users/{username}")
URL will be
Function will be
@Path("/users/{username}")
public class UserResource {
@GET
@Produces("text/xml")
public String getUser(@PathParam("username") String userName) {
...
}
}

@Produces Annotation
The @Produces annotation is used to specify the MIME media types or representations a
resource can produce and send back to the client.
The value of @Produces is an array of String of MIME types. For example:
@Produces({"image/jpeg,image/png"})
The following example shows how to apply @Produces at both the class and method levels:
@Path("/myResource")
@Produces("text/plain")
If a resource class is capable of producing more that one MIME media type, the resource
method chosen will correspond to the most acceptable media type as declared by the client.
@Produces({"application/xml", "application/json"})
public String doGetAsXmlOrJson() {
...
}
The doGetAsXmlOrJson method will get invoked if either of the media types application/xml
and application/json are acceptable

Mediatypes in Produces and Consumes

java.lang.Object
  javax.ws.rs.core.MediaType

public class MediaTypeextends java.lang.Object
An abstraction for a media type. Instances are immutable.
static java.lang.String APPLICATION_ATOM_XML "application/atom+xml"
static MediaType APPLICATION_ATOM_XML_TYPE "application/atom+xml"
static java.lang.String APPLICATION_FORM_URLENCODED "application/x-www-form-urlencoded"
static MediaType APPLICATION_FORM_URLENCODED_TYPE "application/x-www-form-urlencoded"
static java.lang.String APPLICATION_JSON "application/json"
static MediaType APPLICATION_JSON_TYPE "application/json"
static java.lang.String APPLICATION_OCTET_STREAM "application/octet-stream"
static MediaType APPLICATION_OCTET_STREAM_TYPE "application/octet-stream"
static java.lang.String APPLICATION_SVG_XML "application/svg+xml"
static MediaType APPLICATION_SVG_XML_TYPE "application/svg+xml"
static java.lang.String APPLICATION_XHTML_XML "application/xhtml+xml"
static MediaType APPLICATION_XHTML_XML_TYPE "application/xhtml+xml"
static java.lang.String APPLICATION_XML "application/xml"
static MediaType APPLICATION_XML_TYPE "application/xml"
static java.lang.String MEDIA_TYPE_WILDCARD The value of a type or subtype wildcard: "*"
static java.lang.String MULTIPART_FORM_DATA ` "multipart/form-data"
static MediaType MULTIPART_FORM_DATA_TYPE "multipart/form-data"
static java.lang.String TEXT_HTML "text/html"
static MediaType TEXT_HTML_TYPE "text/html"
static java.lang.String TEXT_PLAIN "text/plain"
static MediaType TEXT_PLAIN_TYPE "text/plain"
static java.lang.String TEXT_XML "text/xml"
static MediaType TEXT_XML_TYPE "text/xml"
static java.lang.String WILDCARD "*/*"
static MediaType WILDCARD_TYPE "*/*"




Creating a Demo project in Jersey using Tomcat

Library Files

Jars to be included

Keep the copy of Jars in Project/WebContent/Web-inf/lib as well as in Tomcat-Home/lib directory
along with the project library. Kindly add the library files in Project/WebContent/Web-inf/lib to build path by selecting and right click and add to buildpath

activation-1.1.jar
asm-3.1.jar
grizzly-servlet-webserver-1.9.18-i.jar
jaxb-api-2.1.jar
jaxb-impl-2.1.12.jar
jersey-archive-1.4.zip
jersey-bundle-1.4.jar
jersey-client-1.0.3.1.jar
jersey-core-1.4.jar
jersey-server-1.4.jar
jersey-view-client-1.5-SNAPSHOT.jar
jsr311-api-1.0.jar
jsr311-api-1.1.jar
stax-api-1.0-2.jar

Edit the Project/WebContent/Web-inf/web.xml

xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>RestTomcatdisplay-name>
<servlet>
<servlet-name>jerseyservlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainerservlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.resourceConfigClassparam-name>
<param-value>com.sun.jersey.api.core.PackagesResourceConfigparam-value>
init-param>
<init-param>
<param-name>com.sun.jersey.config.property.packagesparam-name>
<--/*Specify the package of the web application or Java Classes you want to deploy in Jserver or Tomcat Server*/ --!>
<param-value>com.sun.jersey.samples.helloworld.resourcesparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>jerseyservlet-name>
<url-pattern>/*url-pattern>
servlet-mapping>
<welcome-file-list>
<welcome-file>index.htmlwelcome-file>
welcome-file-list>
web-app>

Example of First Web Application

package com.sun.jersey.samples.helloworld.resources;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

@Path("/user/{number1}&{number2}")
public class HelloAddition
{
@GET
@Produces("text/plain")
public String getUser(@PathParam("number1") String number1,@PathParam("number2") String number2)
{
int i = Integer.valueOf(number1);
int j = Integer.valueOf(number2);
int sum = i+j;
String k = String.valueOf(sum);
HelloAddition addition=new HelloAddition();
addition.updateDB(sum);
return k;
}
public void updateDB(int k)
{
try
{
Class.forName("com.mysql.jdbc.Driver").newInstance();
Connection con=null;
ResultSet rst=null;
Statement stmt=null;
String url="jdbc:mysql://localhost:3306/testDB?";
con=DriverManager.getConnection(url,"root","secret");
stmt = con.createStatement();
stmt.execute("insert into testtable VALUES ('"+ k + "')");
con.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
}

After starting the server
Visit the respective link

http://localhost:8080/RestTomcat/user/1&2