📜 ⬆️ ⬇️

Task: Objects and Access

Task : it is necessary to organize a quick and convenient way to get objects and their parameters from a Java Web application.

A little about the task itself.

I am writing browser RPG engine. All game logic (transitions between locations, purchase of things, etc.) serves Tomcat 6. The page requests a JSON object, the server processes the request, and returns the result. In the beginning, I had a separate servlet for each group of actions, there was a lot of code and duplications. In general, I did not like.


All calls to the server are divided into two types:
1. obtaining the state of the object (player's name, his level, the list of players in the location)
2. changing the state of the object (buying things, assembling the subject, sending a message)
')
I also simplified the processing of the second type, but about it next time.

The idea is to generalize all the objects in order to build a simple algorithm for obtaining the result.
For this, I used the interface, and the objects implemented it.

package engine.core.obj;

import org.json.JSONObject;

/**
*
* @author chernser
* Interface used to work with core objects
*
*/
public interface ICoreObject {

/**
* Should implement encoding into
* JSON notation
* @param context TODO
*
* @return returns object in JSON notation
* @return null if error
*/
public JSONObject toJSON(Context context);


/**
*
* Should implement getting object's class
* name
*
* @return returns object class name
* @return null if error
*/
public String getClassName();


/**
* Should find child object by name
* and return it
* @param context TODO
*
* @return returns child object
* @return null if error
*/
public ICoreObject getChild(String name, Context context);


/**
* Should find child object
* by name and index then return it
* @param context TODO
*
* @return returns child object
* @return null if error
*/
public ICoreObject getChild(String name, Integer index, Context context);


/**
* Should return parameter's value
*
* @param name - parameter name
* @param context - current context
* @return returns value of parameter
* @return null if no such parameter
*
*/
public Object getParameter(String name, Context context);
}


* This source code was highlighted with Source Code Highlighter .


The main methods are:


Some examples of the implementation of these methods:

@Override
public ICoreObject getChild(String name, Context context) {
if (name.equals(AvatarBaseParams.CORE_OBJ_NAME)) {
return itsBaseParams;
} else if (name.equals(Location.CORE_OBJ_NAME)) {
return itsLocation;
} else if (name.equals(AvatarInventory.CORE_OBJ_NAME))
{
return itsInventory;
} else if (name.equals(AvatarSkills.CORE_OBJ_NAME))
{
return itsSkills;
} else if (name.equals(AvatarModifications.CORE_OBJ_NAME))
{
return itsModifications;
}

return null ;
}


* This source code was highlighted with Source Code Highlighter .
@Override
public ICoreObject getChild(String name, Context context) {
if (name.equals(AvatarBaseParams.CORE_OBJ_NAME)) {
return itsBaseParams;
} else if (name.equals(Location.CORE_OBJ_NAME)) {
return itsLocation;
} else if (name.equals(AvatarInventory.CORE_OBJ_NAME))
{
return itsInventory;
} else if (name.equals(AvatarSkills.CORE_OBJ_NAME))
{
return itsSkills;
} else if (name.equals(AvatarModifications.CORE_OBJ_NAME))
{
return itsModifications;
}

return null ;
}


* This source code was highlighted with Source Code Highlighter .

This method is usually a simple dispatcher. Thanks to this method, you can build any hierarchy.

@Override
public JSONObject toJSON(Context context) {

JSONObject result = new JSONObject();
try {

result.put( "name" , itsName);
result.put( "bparams" , itsBaseParams.toJSON(context));

} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}

return result;
}


* This source code was highlighted with Source Code Highlighter .
@Override
public JSONObject toJSON(Context context) {

JSONObject result = new JSONObject();
try {

result.put( "name" , itsName);
result.put( "bparams" , itsBaseParams.toJSON(context));

} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}

return result;
}


* This source code was highlighted with Source Code Highlighter .
@Override
public JSONObject toJSON(Context context) {

JSONObject result = new JSONObject();
try {

result.put( "name" , itsName);
result.put( "bparams" , itsBaseParams.toJSON(context));

} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}

return result;
}


* This source code was highlighted with Source Code Highlighter .


Here we create a JSONObject with the fields we need.

So we have objects subject to the general rules. We need an algorithm for fast passing through all the chain and return the result. Here, perhaps, the most difficult moment :-)

The query string looks like this:
user.avatar: name - returns the name of the avatar object. Use colon to access parameters.
user.avatar.location - returns a JSON representation of the location object.
user.avatar.inventory.slot [4] - returns a JSON representation of the 4th slot in the player’s inventory.

Requests fall into a separate servlet and this is how they are processed:

1. Initial request validation and return value


protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

/* Set encoding for request to properly parse parameters */
request.setCharacterEncoding( "UTF-8" );

// get user from session
User user = (User) request.getSession().getAttribute(
CommonConstants.USER);

// save query and prelimenary result
String query = request.getParameter( "query" );
String result = "{error: -1}" ;

// check if we have user
if (user != null )
{
Context context = user.itsContext;

// Check parameters
if (query != null )
{
// process query
result = ParseAndExecuteQuery(query, request.getSession(), context);
}
}

/* tune response to avoid problem with Russian */
response.setContentType( "text/javascript; charset=UTF-8" );
response.setHeader( "Cache-Control" , "no-cache" ); //HTTP 1.1
response.setHeader( "Pragma" , "no-cache" ); //HTTP 1.0
response.setDateHeader( "Expires" , 0); //prevents caching at the proxy server

response.getWriter().write(result);

}


* This source code was highlighted with Source Code Highlighter .
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

/* Set encoding for request to properly parse parameters */
request.setCharacterEncoding( "UTF-8" );

// get user from session
User user = (User) request.getSession().getAttribute(
CommonConstants.USER);

// save query and prelimenary result
String query = request.getParameter( "query" );
String result = "{error: -1}" ;

// check if we have user
if (user != null )
{
Context context = user.itsContext;

// Check parameters
if (query != null )
{
// process query
result = ParseAndExecuteQuery(query, request.getSession(), context);
}
}

/* tune response to avoid problem with Russian */
response.setContentType( "text/javascript; charset=UTF-8" );
response.setHeader( "Cache-Control" , "no-cache" ); //HTTP 1.1
response.setHeader( "Pragma" , "no-cache" ); //HTTP 1.0
response.setDateHeader( "Expires" , 0); //prevents caching at the proxy server

response.getWriter().write(result);

}


* This source code was highlighted with Source Code Highlighter .


2. Preparing the request and determining the initial object (I have two of them - the user and the world)


private String ParseAndExecuteQuery(String query, HttpSession session, Context context)
{
ICoreObject rootObject = null ;

// if query starts from "user." Also check syntax
if (query.matches( "user\\.([a-zA-Z_]+(\\[[0-9]+\\])*(:[a-zA-Z_]+)*\\.{0,1})+" ))
{
rootObject = (User) session.getAttribute(CommonConstants.USER);
}
// if query starts from "world." Also check syntax
else if (query.matches( "world\\.([a-zA-Z_]+(\\[[0-9]+\\])*(:[a-zA-Z_]+)*\\.{0,1})+" ))
{
rootObject = (World)session.getServletContext().
getAttribute(CommonConstants.WORLD);
}

// Check if we have root object
if (rootObject != null )
{
// Start processing
ICoreObject result = getObject(rootObject, query, context);
if (result != null )
{
// Here we got result
JSONObject jsonObj = result.toJSON(context);
if (jsonObj != null )
{
// return it
return jsonObj.toString();
}
}
}


return "" ;
}


* This source code was highlighted with Source Code Highlighter .
private String ParseAndExecuteQuery(String query, HttpSession session, Context context)
{
ICoreObject rootObject = null ;

// if query starts from "user." Also check syntax
if (query.matches( "user\\.([a-zA-Z_]+(\\[[0-9]+\\])*(:[a-zA-Z_]+)*\\.{0,1})+" ))
{
rootObject = (User) session.getAttribute(CommonConstants.USER);
}
// if query starts from "world." Also check syntax
else if (query.matches( "world\\.([a-zA-Z_]+(\\[[0-9]+\\])*(:[a-zA-Z_]+)*\\.{0,1})+" ))
{
rootObject = (World)session.getServletContext().
getAttribute(CommonConstants.WORLD);
}

// Check if we have root object
if (rootObject != null )
{
// Start processing
ICoreObject result = getObject(rootObject, query, context);
if (result != null )
{
// Here we got result
JSONObject jsonObj = result.toJSON(context);
if (jsonObj != null )
{
// return it
return jsonObj.toString();
}
}
}


return "" ;
}


* This source code was highlighted with Source Code Highlighter .
private String ParseAndExecuteQuery(String query, HttpSession session, Context context)
{
ICoreObject rootObject = null ;

// if query starts from "user." Also check syntax
if (query.matches( "user\\.([a-zA-Z_]+(\\[[0-9]+\\])*(:[a-zA-Z_]+)*\\.{0,1})+" ))
{
rootObject = (User) session.getAttribute(CommonConstants.USER);
}
// if query starts from "world." Also check syntax
else if (query.matches( "world\\.([a-zA-Z_]+(\\[[0-9]+\\])*(:[a-zA-Z_]+)*\\.{0,1})+" ))
{
rootObject = (World)session.getServletContext().
getAttribute(CommonConstants.WORLD);
}

// Check if we have root object
if (rootObject != null )
{
// Start processing
ICoreObject result = getObject(rootObject, query, context);
if (result != null )
{
// Here we got result
JSONObject jsonObj = result.toJSON(context);
if (jsonObj != null )
{
// return it
return jsonObj.toString();
}
}
}


return "" ;
}


* This source code was highlighted with Source Code Highlighter .



3. Finding the desired object


private ICoreObject getObject(ICoreObject rootObject, String query, Context context)
{
ICoreObject curObject = rootObject; // current object
query = query.substring(query.indexOf( "." ) + 1) + "." ; // query

Integer dotIndex = query.indexOf( "." );
String tmpObjName = "" ; // object name
do {
String objName = query.substring(0, dotIndex); // get first object in chain

// if by index
if (objName.matches( "[a-zA-Z_]+\\[[0-9]+\\](:[a-zA-Z0-9_]+)*" ))
{
Integer index = new Integer(objName.substring(objName.indexOf( "[" ) + 1,
objName.indexOf( "]" )));
tmpObjName = objName.substring(0, objName.indexOf( "[" ));
curObject = curObject.getChild(tmpObjName, index, context);
}
else // if not by index
{
Integer semiIndex = 0;
if ((semiIndex = objName.indexOf( ":" )) > -1)
curObject = curObject.getChild(objName.substring(0, semiIndex), context);
else
curObject = curObject.getChild(objName, context);
}

// If current object in chain contains ':' - then proccess it as parameter
if (objName.matches( "[a-zA-Z_]+(\\[[0-9]+\\])*:[a-zA-Z0-9_]+" ))
{
// get parameter name
String param = objName.substring(objName.indexOf( ":" ) + 1);

return new CoreObjectParameter(param, curObject.getParameter(param, context));
}

// cut off current object name and step forward
query = query.substring(dotIndex + 1);
dotIndex = query.indexOf( "." );
} while ((dotIndex > -1) && (curObject != null ));


return curObject;
}


* This source code was highlighted with Source Code Highlighter .
private ICoreObject getObject(ICoreObject rootObject, String query, Context context)
{
ICoreObject curObject = rootObject; // current object
query = query.substring(query.indexOf( "." ) + 1) + "." ; // query

Integer dotIndex = query.indexOf( "." );
String tmpObjName = "" ; // object name
do {
String objName = query.substring(0, dotIndex); // get first object in chain

// if by index
if (objName.matches( "[a-zA-Z_]+\\[[0-9]+\\](:[a-zA-Z0-9_]+)*" ))
{
Integer index = new Integer(objName.substring(objName.indexOf( "[" ) + 1,
objName.indexOf( "]" )));
tmpObjName = objName.substring(0, objName.indexOf( "[" ));
curObject = curObject.getChild(tmpObjName, index, context);
}
else // if not by index
{
Integer semiIndex = 0;
if ((semiIndex = objName.indexOf( ":" )) > -1)
curObject = curObject.getChild(objName.substring(0, semiIndex), context);
else
curObject = curObject.getChild(objName, context);
}

// If current object in chain contains ':' - then proccess it as parameter
if (objName.matches( "[a-zA-Z_]+(\\[[0-9]+\\])*:[a-zA-Z0-9_]+" ))
{
// get parameter name
String param = objName.substring(objName.indexOf( ":" ) + 1);

return new CoreObjectParameter(param, curObject.getParameter(param, context));
}

// cut off current object name and step forward
query = query.substring(dotIndex + 1);
dotIndex = query.indexOf( "." );
} while ((dotIndex > -1) && (curObject != null ));


return curObject;
}


* This source code was highlighted with Source Code Highlighter .
private ICoreObject getObject(ICoreObject rootObject, String query, Context context)
{
ICoreObject curObject = rootObject; // current object
query = query.substring(query.indexOf( "." ) + 1) + "." ; // query

Integer dotIndex = query.indexOf( "." );
String tmpObjName = "" ; // object name
do {
String objName = query.substring(0, dotIndex); // get first object in chain

// if by index
if (objName.matches( "[a-zA-Z_]+\\[[0-9]+\\](:[a-zA-Z0-9_]+)*" ))
{
Integer index = new Integer(objName.substring(objName.indexOf( "[" ) + 1,
objName.indexOf( "]" )));
tmpObjName = objName.substring(0, objName.indexOf( "[" ));
curObject = curObject.getChild(tmpObjName, index, context);
}
else // if not by index
{
Integer semiIndex = 0;
if ((semiIndex = objName.indexOf( ":" )) > -1)
curObject = curObject.getChild(objName.substring(0, semiIndex), context);
else
curObject = curObject.getChild(objName, context);
}

// If current object in chain contains ':' - then proccess it as parameter
if (objName.matches( "[a-zA-Z_]+(\\[[0-9]+\\])*:[a-zA-Z0-9_]+" ))
{
// get parameter name
String param = objName.substring(objName.indexOf( ":" ) + 1);

return new CoreObjectParameter(param, curObject.getParameter(param, context));
}

// cut off current object name and step forward
query = query.substring(dotIndex + 1);
dotIndex = query.indexOf( "." );
} while ((dotIndex > -1) && (curObject != null ));


return curObject;
}


* This source code was highlighted with Source Code Highlighter .



If the object is found, it is transferred as JSON to the script on the page; if not, an empty object is returned.

The mechanism is still in beta :-) So far, however, it has not shown any bugs.

Source: https://habr.com/ru/post/50115/


All Articles