docs: private CmsObject initCmsObject(
          HttpServletRequest request,
          CmsUser user,
          String siteRoot,
          CmsUUID projectId,
          String ouFqn)
      throws CmsException {
  
          CmsProject project = null;
          try {
              project = m_securityManager.readProject(projectId);
          } catch (CmsDbEntryNotFoundException e) {
              // project not found, switch to online project
              project = m_securityManager.readProject(CmsProject.ONLINE_PROJECT_ID);
              LOG.debug("Project '" + projectId + "' was not found, switch to online project.", e);
          }
  
          // get requested resource uri and remote IP address, as well as time for "time warp" browsing
          String requestedResource = null;
          Long requestTimeAttr = null;
          String remoteAddr;
          CmsSiteMatcher requestMatcher;
  
          boolean isSecureRequest = false;
  
          if (request != null) {
              // get path info from request
              requestedResource = getPathInfo(request);
  
              // check for special header for remote address
              remoteAddr = request.getHeader(CmsRequestUtil.HEADER_X_FORWARDED_FOR);
              if (remoteAddr == null) {
                  // if header is not available, use default remote address
                  remoteAddr = request.getRemoteAddr();
              }
  
              // check for special "time warp" browsing
              HttpSession session = request.getSession(false);
              if (session != null) {
                  // no new session must be created here
                  requestTimeAttr = (Long)session.getAttribute(CmsContextInfo.ATTRIBUTE_REQUEST_TIME);
              }
              isSecureRequest = OpenCms.getSiteManager().usesSecureSite(request);
  
              // create the request matcher
              requestMatcher = new CmsSiteMatcher(request.getRequestURL().toString());
          } else {
              // if no request is available, the IP is always set to localhost
              remoteAddr = CmsContextInfo.LOCALHOST;
              // also the request matcher is always the workplace server
              requestMatcher = OpenCms.getSiteManager().getWorkplaceSiteMatcher();
          }
          if (requestedResource == null) {
              // path info can still be null
              requestedResource = "/";
          }
  
          // calculate the request time
          long requestTime;
          if (requestTimeAttr == null) {
              requestTime = System.currentTimeMillis();
          } else {
              requestTime = requestTimeAttr.longValue();
          }
  
          // get locale and encoding
          CmsI18nInfo i18nInfo;
          if (m_localeManager.isInitialized()) {
              // locale manager is initialized
              // resolve locale and encoding
              if ((request != null)
                  && (requestedResource.endsWith(OpenCmsServlet.HANDLE_GWT) || isWorkplaceServletRequest(request))) {
                  // GWT RPC or workplace servlet call, always keep the request encoding and use the default locale
                  i18nInfo = new CmsI18nInfo(CmsLocaleManager.getDefaultLocale(), request.getCharacterEncoding());
              } else {
                  String resourceName;
                  if (requestedResource.startsWith(CmsWorkplace.VFS_PATH_SYSTEM)) {
                      // add site root only if resource name does not start with "/system"
                      resourceName = requestedResource;
                  } else if (OpenCms.getSiteManager().startsWithShared(requestedResource)) {
                      resourceName = requestedResource;
                  } else {
                      resourceName = siteRoot.concat(requestedResource);
                  }
                  i18nInfo = m_localeManager.getI18nInfo(request, user, project, resourceName);
              }
          } else {
              // locale manager not initialized, this will be true _only_ during system startup
              // the values set does not matter, no locale information form VFS is used on system startup
              // this is just to protect against null pointer exceptions
              i18nInfo = new CmsI18nInfo(Locale.ENGLISH, getSystemInfo().getDefaultEncoding());
          }
  
          // decode the requested resource, always using UTF-8
          requestedResource = CmsEncoder.decode(requestedResource);
  
          // in case the current site could be configured for single tree localization, if so, remove the locale prefix if present
          CmsSite site = OpenCms.getSiteManager().getSiteForSiteRoot(siteRoot);
          if ((site != null) && CmsSite.LocalizationMode.singleTree.equals(site.getLocalizationMode())) {
              Locale locale = CmsSingleTreeLocaleHandler.getLocaleFromPath(requestedResource);
              if (locale != null) {
                  requestedResource = requestedResource.substring(
                      requestedResource.indexOf(locale.toString()) + locale.toString().length());
              }
          }
  
          // initialize the context info
          CmsContextInfo contextInfo = new CmsContextInfo(
              user,
              project,
              requestedResource,
              requestMatcher,
              siteRoot,
              isSecureRequest,
              i18nInfo.getLocale(),
              i18nInfo.getEncoding(),
              remoteAddr,
              requestTime,
              ouFqn);
  
          // now generate and return the CmsObject
          return initCmsObject(contextInfo);
      }