Pull to refresh

Задача: Объекты и доступ

Reading time9 min
Views648
Задача: необходимо организовать быстрый и удобный способ получения объектов и их параметров от Java Web приложения.

Немного о самой задачи.

Я пишу движок браузерной РПГ. Вся игровая логика (переходы между локациями, покупка вещей и т.п.) обслуживает Tomcat 6. Страничка запрашивает JSON объект, сервер обрабатывает запрос, и отдает результат. В начале у меня на каждую группу действий был отдельный сервлет, было много кода и дупликаций. Вобщем, мне не нравилось.


Все обращения к серверу деляться на два типа:
1. получение состояния объекта (имя игрока, его уровень, список игроков на локации)
2. изменение состояния объекта (покупка вещи, сборка предмета, отправка сообщения)

Обработку второго типа я тоже упростил, но о нем в следующий раз.

Идея заключается в обобщении всех объектов, чтобы построить простой алгоритм получения результата.
Для этого я ипользовал интерфес, а объекты его реализовали.

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.


основными методами являютяс:
  • toJSON() — собственно формирование объекта
  • getChild() — возвращает потомка текущего объекта
  • getParameter() — возвращает один из параметров объекта, например, имя игрока


Несколько примеров реализации этих методов:

@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 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.


Здесь создаем JSONObject с нужными нам полями.

Так у нас есть объекты подчиненные общим правилам. Нам нужен алгоритм быстрого прохождения по все цепочке и возвращения результата. Тут, пожалуй, самый сложный момент :-)

Строка запроса выглядит примерно так:
user.avatar:name — возвращает name объекта avatar. Для доступа к параметрам используется двоеточие
user.avatar.location — возвращает JSON представление объекта location.
user.avatar.inventory.slot[4] — возвращает JSON представление 4го слота в инвентаре игрока.

Запросы попадают в отдельный сервлет и вот как обрабатываются:

1. Начальная проверка запроса и возврат значения


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. Подготовка запроса и определение начального объекта ( у меня их два — пользователь и мир)


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. Нахождение нужного объекта


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.



Если объект найден, он передается в виде JSON скрипту на странице, если нет — возвращается пустой объект.

Механизм пока на стадии бета :-) Пока, правда, багов не проявил.
Tags:
Hubs:
Total votes 3: ↑3 and ↓0+3
Comments4

Articles