  public boolean add(MediaPackage sourceMediaPackage, AccessControlList acl, Date now) throws SolrServerException,
          UnauthorizedException {
    try {
      SolrInputDocument episodeDocument = createEpisodeInputDocument(sourceMediaPackage, acl);
      Schema.setOcModified(episodeDocument, now);

      SolrInputDocument seriesDocument = createSeriesInputDocument(sourceMediaPackage.getSeries(), acl);
      if (seriesDocument != null)
        Schema.enrich(episodeDocument, seriesDocument);

      // If neither an episode nor a series was contained, there is no point in trying to update
      if (episodeDocument == null && seriesDocument == null) {
        logger.warn("Neither episode nor series metadata found");
        return false;
      }

      // Post everything to the search index
      if (episodeDocument != null)
        solrServer.add(episodeDocument);
      if (seriesDocument != null)
        solrServer.add(seriesDocument);
      solrServer.commit();
      return true;
    } catch (Exception e) {
      logger.error("Unable to add mediapackage {} to index", sourceMediaPackage.getIdentifier());
      throw new SolrServerException(e);
    }
  }


	public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText)
		throws JOSEException {

		JWEAlgorithm alg = header.getAlgorithm();

		if (! alg.equals(JWEAlgorithm.DIR)) {
			throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS));
		}

		// Check key length matches encryption method
		EncryptionMethod enc = header.getEncryptionMethod();

		if (enc.cekBitLength() != ByteUtils.bitLength(getKey().getEncoded())) {
			throw new KeyLengthException(enc.cekBitLength(), enc);
		}

		final Base64URL encryptedKey = null; // The second JWE part

		return ContentCryptoProvider.encrypt(header, clearText, getKey(), encryptedKey, getJCAContext());
	}


    public void emptyHeaderNameNotAllowed() {
        newEmptyHeaders().add("", "foo");
    }


    public List<Entry> search(final String filter, final Object[] filterArgs, final int maxResultCount) {
        return search(filter, filterArgs, resultWrapper -> (Entry) resultWrapper.getResult(), maxResultCount);
    }


    public void existingDocumentFromUITemplateProviderSpecifiedTerminal() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
        String templateProviderFullName = "XWiki.MyTemplateProvider";
        String spaceReferenceString = "X";
        when(mockRequest.getParameter("spaceReference")).thenReturn(spaceReferenceString);
        when(mockRequest.getParameter("name")).thenReturn("Y");
        when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);

        // Mock 1 existing template provider that creates terminal documents.
        mockExistingTemplateProviders(templateProviderFullName,
            new DocumentReference("xwiki", Arrays.asList("XWiki"), "MyTemplateProvider"), Collections.EMPTY_LIST, true);

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating the document X.Y as terminal and using a template, as specified in the template
        // provider.
        verify(mockURLFactory).createURL("X", "Y", "edit", "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y",
            null, "xwiki", context);
    }


    public void newDocumentFromURLWhenNoType() throws Exception
    {
        // No type has been set by the user
        when(mockRequest.get("type")).thenReturn(null);

        // new document = xwiki:X.Y
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("X"), "Y");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(true);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertEquals("create", result);
    }


    public void translate(ServerUpdateViewDistancePacket packet, GeyserSession session) {
        session.setRenderDistance(packet.getViewDistance());
    }


    public void testEqualsInsertionOrderDifferentHeaderNames() {
        final HttpHeadersBase h1 = newEmptyHeaders();
        h1.add("a", "b");
        h1.add("c", "d");
        final HttpHeadersBase h2 = newEmptyHeaders();
        h2.add("c", "d");
        h2.add("a", "b");
        assertThat(h1).isEqualTo(h2);
    }


    public void headersWithSameNamesButDifferentValuesShouldNotBeEquivalent() {
        final HttpHeadersBase headers1 = newEmptyHeaders();
        headers1.add("name1", "value1");
        final HttpHeadersBase headers2 = newEmptyHeaders();
        headers1.add("name1", "value2");
        assertThat(headers1).isNotEqualTo(headers2);
    }


    public boolean isValid(String value, ConstraintValidatorContext context) {
      if (StringUtils.isEmpty(value)) {
        return true;
      }

      try {
        Pattern.compile(value);
        return true;
      } catch (Exception ex) {
        String errorMessage = String.format("URL parameter '%s' is not a valid regexp", value);
        LOG.warn(errorMessage);

        context.buildConstraintViolationWithTemplate(errorMessage).addConstraintViolation();
      }
      return false;
    }


    public void testToString() {
        HttpHeadersBase headers = newEmptyHeaders();
        headers.add("name1", "value1");
        headers.add("name1", "value2");
        headers.add("name2", "value3");
        assertThat(headers.toString()).isEqualTo("[name1=value1, name1=value2, name2=value3]");

        headers = newEmptyHeaders();
        headers.add("name1", "value1");
        headers.add("name2", "value2");
        headers.add("name3", "value3");
        assertThat(headers.toString()).isEqualTo("[name1=value1, name2=value2, name3=value3]");

        headers = newEmptyHeaders();
        headers.add("name1", "value1");
        assertThat(headers.toString()).isEqualTo("[name1=value1]");

        headers = newEmptyHeaders();
        headers.endOfStream(true);
        headers.add("name1", "value1");
        assertThat(headers.toString()).isEqualTo("[EOS, name1=value1]");

        headers = newEmptyHeaders();
        assertThat(headers.toString()).isEqualTo("[]");

        headers = newEmptyHeaders();
        headers.endOfStream(true);
        assertThat(headers.toString()).isEqualTo("[EOS]");
    }


    private SearchResult lookupUser(String accountName) throws NamingException {
        InitialDirContext context = initContext();

        String searchString = searchFilter.replace(":login", accountName);

        SearchControls searchControls = new SearchControls();
        String[] attributeFilter = {idAttribute, nameAttribute, mailAttribute};
        searchControls.setReturningAttributes(attributeFilter);
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        NamingEnumeration<SearchResult> results = context.search(searchBase, searchString, searchControls);

        SearchResult searchResult = null;
        if (results.hasMoreElements()) {
            searchResult = results.nextElement();
            if (results.hasMoreElements()) {
                LOGGER.warn("Matched multiple users for the accountName: " + accountName);
                return null;
            }
        }

        return searchResult;
    }


    public void addViolation(String propertyName, String key, String message) {
        violationOccurred = true;
        String messageTemplate = escapeEl(message);
        context.buildConstraintViolationWithTemplate(messageTemplate)
                .addPropertyNode(propertyName)
                .addBeanNode().inIterable().atKey(key)
                .addConstraintViolation();
    }


    public void whenNameContainsMultipleValuesGetShouldReturnTheFirst() {
        final HttpHeadersBase headers = newEmptyHeaders();
        headers.add("name1", "value1", "value2");
        assertThat(headers.get("name1")).isEqualTo("value1");
    }


    static DataSource lookupDataSource(Hints hints) throws FactoryException {
        Object hint = hints.get(Hints.EPSG_DATA_SOURCE);
        if (hint instanceof DataSource) {
            return (DataSource) hint;
        } else if (hint instanceof String) {
            String name = (String) hint;
            InitialContext context;
            try {
                context = GeoTools.getInitialContext();
                // name = GeoTools.fixName( context, name );
                return (DataSource) context.lookup(name);
            } catch (Exception e) {
                throw new FactoryException("EPSG_DATA_SOURCE '" + name + "' not found:" + e, e);
            }
        }
        throw new FactoryException("EPSG_DATA_SOURCE must be provided");
    }


    public void testEqualsInsertionOrderSameHeaderName() {
        final HttpHeadersBase h1 = newEmptyHeaders();
        h1.add("a", "b");
        h1.add("a", "c");
        final HttpHeadersBase h2 = newEmptyHeaders();
        h2.add("a", "c");
        h2.add("a", "b");
        assertThat(h1).isNotEqualTo(h2);
    }


    /*package*/ static SecretKey getLegacyKey() throws GeneralSecurityException {
        String secret = SECRET;
        if(secret==null)    return Jenkins.getInstance().getSecretKeyAsAES128();
        return Util.toAes128Key(secret);
    }


    default boolean isKeepAlive() {
        return getFirst(CONNECTION, String.class).map(val -> val.equalsIgnoreCase("keep-alive")).orElse(false);
    }


    public void existingDocumentFromUITemplateProviderSpecifiedTerminalOverridenFromUIToNonTerminal() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
        String templateProviderFullName = "XWiki.MyTemplateProvider";
        String spaceReferenceString = "X";
        when(mockRequest.getParameter("spaceReference")).thenReturn(spaceReferenceString);
        when(mockRequest.getParameter("name")).thenReturn("Y");
        when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);
        when(mockRequest.getParameter("tocreate")).thenReturn("nonterminal");

        // Mock 1 existing template provider that creates terminal documents.
        mockExistingTemplateProviders(templateProviderFullName,
            new DocumentReference("xwiki", Arrays.asList("XWiki"), "MyTemplateProvider"), Collections.EMPTY_LIST, true);

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating the document X.Y.WebHome as non-terminal, even if the template provider says otherwise.
        // Also using a template, as specified in the template provider.
        verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y", null, "xwiki", context);
    }


    private ServiceStat getRateLimitOpCountStat() throws Throwable {
        URI managementServiceUri = this.host.getManagementServiceUri();
        return this.host.getServiceStats(managementServiceUri)
                .get(ServiceHostManagementService.STAT_NAME_RATE_LIMITED_OP_COUNT);
    }


    private List<GHPoint> getPointsFromRequest(HttpServletRequest httpServletRequest, String profile) {

        String url = httpServletRequest.getRequestURI();
        url = url.replaceFirst("/navigate/directions/v5/gh/" + profile + "/", "");
        url = url.replaceAll("\\?[*]", "");

        String[] pointStrings = url.split(";");

        List<GHPoint> points = new ArrayList<>(pointStrings.length);
        for (int i = 0; i < pointStrings.length; i++) {
            points.add(GHPoint.fromStringLonLat(pointStrings[i]));
        }

        return points;
    }


    public void translate(ServerEntityPositionPacket packet, GeyserSession session) {
        Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
        if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
            entity = session.getPlayerEntity();
        }
        if (entity == null) return;

        entity.moveRelative(session, packet.getMoveX(), packet.getMoveY(), packet.getMoveZ(), entity.getRotation(), packet.isOnGround());
    }


    public void afterClearHeadersShouldBeEmpty() {
        final HttpHeadersBase headers = newEmptyHeaders();
        headers.add("name1", "value1");
        headers.add("name2", "value2");
        assertThat(headers.size()).isEqualTo(2);
        headers.clear();
        assertThat(headers.size()).isEqualTo(0);
        assertThat(headers.isEmpty()).isTrue();
        assertThat(headers.contains("name1")).isFalse();
        assertThat(headers.contains("name2")).isFalse();
    }


    public void existingDocumentNonTerminalFromUIDeprecated() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI space=X&tocreate=space
        when(mockRequest.getParameter("space")).thenReturn("X");
        when(mockRequest.getParameter("tocreate")).thenReturn("space");

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating X.WebHome because the tocreate parameter says "space".
        verify(mockURLFactory).createURL("X", "WebHome", "edit", "template=&parent=Main.WebHome&title=X", null, "xwiki",
            context);
    }


  public void testAddAndGetUser() throws Exception {
    JpaUser user = createUserWithRoles(org1, "user1", "ROLE_ASTRO_101_SPRING_2011_STUDENT");
    provider.addUser(user);

    User loadUser = provider.loadUser("user1");
    assertNotNull(loadUser);

    assertEquals(user.getUsername(), loadUser.getUsername());
    assertEquals(PasswordEncoder.encode(user.getPassword(), user.getUsername()), loadUser.getPassword());
    assertEquals(user.getOrganization(), loadUser.getOrganization());
    assertEquals(user.getRoles(), loadUser.getRoles());

    assertNull("Loading 'does not exist' should return null", provider.loadUser("does not exist"));
    assertNull("Loading 'does not exist' should return null", provider.loadUser("user1", org2.getId()));

    loadUser = provider.loadUser("user1", org1.getId());
    assertNotNull(loadUser);

    assertEquals(user.getUsername(), loadUser.getUsername());
    assertEquals(PasswordEncoder.encode(user.getPassword(), user.getUsername()), loadUser.getPassword());
    assertEquals(user.getOrganization(), loadUser.getOrganization());
    assertEquals(user.getRoles(), loadUser.getRoles());
  }


    private void verifyRewrite(File dir) throws Exception {
        File xml = new File(dir, "foo.xml");
        assertEquals("<foo>" + encryptNew(TEST_KEY) + "</foo>".trim(),
                FileUtils.readFileToString(xml).trim());
    }


   protected OHttpSessionManager() {
     expirationTime = OGlobalConfiguration.NETWORK_HTTP_SESSION_EXPIRE_TIMEOUT.getValueAsInteger() * 1000;

     Orient.instance().scheduleTask(new TimerTask() {
       @Override
       public void run() {
         final int expired = checkSessionsValidity();
         if (expired > 0)
           OLogManager.instance().debug(this, "Removed %d session because expired", expired);
       }
     }, expirationTime, expirationTime);
   }


    public boolean verify(String token) {
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET))
                .withIssuer(ISSUER)
                .build();
        try {
            verifier.verify(token);
            return true;
        } catch (JWTVerificationException e) {
            log.warn("verify jwt token failed " + e.getMessage());
            return false;
        }
    }


    private String templatePath() {
        if (templatePath == null) {
            String file = HgCommand.class.getResource("/hg.template").getFile();
            try {
                templatePath = URLDecoder.decode(new File(file).getAbsolutePath(), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                templatePath = URLDecoder.decode(new File(file).getAbsolutePath());
            }
        }
        return templatePath;
    }


    public void translate(SetPlayerGameTypePacket packet, GeyserSession session) {
        // no
        SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
        playerGameTypePacket.setGamemode(session.getGameMode().ordinal());
        session.sendUpstreamPacket(playerGameTypePacket);
    }


    default Argument<?> getErrorType(MediaType mediaType) {
        if (mediaType.equals(MediaType.APPLICATION_JSON_TYPE)) {
            return Argument.of(JsonError.class);
        } else if (mediaType.equals(MediaType.APPLICATION_VND_ERROR_TYPE)) {
            return Argument.of(VndError.class);
        } else {
            return Argument.of(String.class);
        }
    }


    public HgVersion version() {
        CommandLine hg = createCommandLine("hg").withArgs("version").withEncoding("utf-8");
        String hgOut = execute(hg, new NamedProcessTag("hg version check")).outputAsString();
        return HgVersion.parse(hgOut);
    }


    public void translate(ServerPluginMessagePacket packet, GeyserSession session) {
        // The only plugin messages it has to listen for are Floodgate plugin messages
        if (session.getRemoteAuthType() != AuthType.FLOODGATE) {
            return;
        }

        String channel = packet.getChannel();

        if (channel.equals("floodgate:form")) {
            byte[] data = packet.getData();

            // receive: first byte is form type, second and third are the id, remaining is the form data
            // respond: first and second byte id, remaining is form response data

            FormType type = FormType.getByOrdinal(data[0]);
            if (type == null) {
                throw new NullPointerException(
                        "Got type " + data[0] + " which isn't a valid form type!");
            }

            String dataString = new String(data, 3, data.length - 3, Charsets.UTF_8);

            Form form = Forms.fromJson(dataString, type);
            form.setResponseHandler(response -> {
                byte[] raw = response.getBytes(StandardCharsets.UTF_8);
                byte[] finalData = new byte[raw.length + 2];

                finalData[0] = data[1];
                finalData[1] = data[2];
                System.arraycopy(raw, 0, finalData, 2, raw.length);

                session.sendDownstreamPacket(new ClientPluginMessagePacket(channel, finalData));
            });
            session.sendForm(form);
        }
    }


    private boolean isAdmin(String accountName) {
        if (this.adminFilter != null) {
            try {
                InitialDirContext context = initContext();
                String searchString = adminFilter.replace(":login", accountName);
                SearchControls searchControls = new SearchControls();
                searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
                NamingEnumeration<SearchResult> results = context.search(searchBase, searchString, searchControls);
                if (results.hasMoreElements()) {
                    results.nextElement();
                    if (results.hasMoreElements()) {
                        LOGGER.warn("Matched multiple users for the accountName: " + accountName);
                        return false;
                    }
                    return true;
                }
            } catch (NamingException e) {
                return false;
            }
        }
        return false;
    }


    public void translate(ServerPlayerAbilitiesPacket packet, GeyserSession session) {
        session.setCanFly(packet.isCanFly());
        session.setFlying(packet.isFlying());
        session.sendAdventureSettings();
    }


    public void existingDocumentFromUITemplateProviderSpecifiedButOldSpaceType() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
        String templateProviderFullName = "XWiki.MyTemplateProvider";
        when(mockRequest.getParameter("spaceReference")).thenReturn("X");
        when(mockRequest.getParameter("name")).thenReturn("Y");
        when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);

        // Mock 1 existing template provider
        mockExistingTemplateProviders(templateProviderFullName,
            new DocumentReference("xwiki", Arrays.asList("XWiki"), "MyTemplateProvider"), Collections.EMPTY_LIST, null,
            "space");

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating X.Y.WebHome as non-terminal, since the template provider does not specify a "terminal"
        // property and we fallback on the "type" property's value. Also using the template extracted from the template
        // provider.
        verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y", null, "xwiki", context);
    }


    public UploadFileResponse getCloudUrl(String contextPath, String uri, String finalFilePath, HttpServletRequest request) {
        UploadFileResponse uploadFileResponse = new UploadFileResponse();
        // try push to cloud
        Map<String, String[]> map = new HashMap<>();
        map.put("fileInfo", new String[]{finalFilePath + "," + uri});
        map.put("name", new String[]{"uploadService"});
        String url;
        try {
            List<Map> urls = HttpUtil.getInstance().sendGetRequest(Constants.pluginServer + "/service", map
                    , new HttpJsonArrayHandle<Map>(), PluginHelper.genHeaderMapByRequest(request)).getT();
            if (urls != null && !urls.isEmpty()) {
                url = (String) urls.get(0).get("url");
                if (!url.startsWith("https://") && !url.startsWith("http://")) {
                    String tUrl = url;
                    if (!url.startsWith("/")) {
                        tUrl = "/" + url;
                    }
                    url = contextPath + tUrl;
                }
            } else {
                url = contextPath + uri;
            }
        } catch (Exception e) {
            url = contextPath + uri;
            LOGGER.error(e);
        }
        uploadFileResponse.setUrl(url);
        return uploadFileResponse;
    }


    public void translate(ServerWindowItemsPacket packet, GeyserSession session) {
        Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
        if (inventory == null)
            return;

        inventory.setStateId(packet.getStateId());

        for (int i = 0; i < packet.getItems().length; i++) {
            GeyserItemStack newItem = GeyserItemStack.from(packet.getItems()[i]);
            inventory.setItem(i, newItem, session);
        }

        InventoryTranslator translator = session.getInventoryTranslator();
        if (translator != null) {
            translator.updateInventory(session, inventory);
        }

        session.getPlayerInventory().setCursor(GeyserItemStack.from(packet.getCarriedItem()), session);
        InventoryUtils.updateCursor(session);
    }


    public void setUp(@TempDir Path tempDir) throws IOException {
        serverRepo = TempDirUtils.createTempDirectoryIn(tempDir, "testHgServerRepo").toFile();
        clientRepo =  TempDirUtils.createTempDirectoryIn(tempDir, "testHgClientRepo").toFile();
        secondBranchWorkingCopy =  TempDirUtils.createTempDirectoryIn(tempDir, "second").toFile();

        setUpServerRepoFromHgBundle(serverRepo, new File("../common/src/test/resources/data/hgrepo.hgbundle"));
        workingDirectory = new File(clientRepo.getPath());
        hgCommand = new HgCommand(null, workingDirectory, "default", serverRepo.getAbsolutePath(), null);
        hgCommand.clone(outputStreamConsumer, new UrlArgument(serverRepo.getAbsolutePath()));
    }


    public void testDirectContextUsage() throws Exception {
        assertThat(ConstraintViolations.format(validator.validate(new DirectContextExample())))
            .containsExactlyInAnyOrder(FAILED_RESULT);
        assertThat(TestLoggerFactory.getAllLoggingEvents())
            .isEmpty();
    }


    public void testPseudoHeadersWithRemovePreservesPseudoIterationOrder() {
        final HttpHeadersBase headers = newHttp2Headers();
        final HttpHeadersBase nonPseudoHeaders = newEmptyHeaders();
        for (Map.Entry<AsciiString, String> entry : headers) {
            if (entry.getKey().isEmpty() ||
                entry.getKey().charAt(0) != ':' && !nonPseudoHeaders.contains(entry.getKey())) {
                nonPseudoHeaders.add(entry.getKey(), entry.getValue());
            }
        }

        assertThat(nonPseudoHeaders.isEmpty()).isFalse();

        // Remove all the non-pseudo headers and verify
        for (Map.Entry<AsciiString, String> nonPseudoHeaderEntry : nonPseudoHeaders) {
            assertThat(headers.remove(nonPseudoHeaderEntry.getKey())).isTrue();
            verifyPseudoHeadersFirst(headers);
            verifyAllPseudoHeadersPresent(headers);
        }

        // Add back all non-pseudo headers
        for (Map.Entry<AsciiString, String> nonPseudoHeaderEntry : nonPseudoHeaders) {
            headers.add(nonPseudoHeaderEntry.getKey(), "goo");
            verifyPseudoHeadersFirst(headers);
            verifyAllPseudoHeadersPresent(headers);
        }
    }


    public static final String getRevision() {
        return "a";
    }


    public void testOfCharSequence() {
        // Should produce a lower-cased AsciiString.
        assertThat((Object) HttpHeaderNames.of("Foo")).isEqualTo(AsciiString.of("foo"));

        // Should reuse known header name instances.
        assertThat((Object) HttpHeaderNames.of("date")).isSameAs(HttpHeaderNames.DATE);
    }


	protected int getExpectedErrors() {
		return hasJavaNetURLPermission ? 2 : 1; // Security error must be reported as an error. Java 8 reports two errors.
	}


    public void translate(RiderJumpPacket packet, GeyserSession session) {
        Entity vehicle = session.getRidingVehicleEntity();
        if (vehicle instanceof AbstractHorseEntity) {
            ClientPlayerStatePacket playerStatePacket = new ClientPlayerStatePacket((int) vehicle.getEntityId(),  PlayerState.START_HORSE_JUMP, packet.getJumpStrength());
            session.sendDownstreamPacket(playerStatePacket);
        }
    }


    default Optional<String> findFirst(CharSequence name) {
        return getFirst(name, String.class);
    }


    public void existingDocumentFromUITemplateProviderSpecifiedButOldSpaceTypeButOverridenFromUIToTerminal()
        throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
        String templateProviderFullName = "XWiki.MyTemplateProvider";
        when(mockRequest.getParameter("spaceReference")).thenReturn("X");
        when(mockRequest.getParameter("name")).thenReturn("Y");
        when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);
        when(mockRequest.getParameter("tocreate")).thenReturn("terminal");

        // Mock 1 existing template provider
        mockExistingTemplateProviders(templateProviderFullName,
            new DocumentReference("xwiki", Arrays.asList("XWiki"), "MyTemplateProvider"), Collections.EMPTY_LIST, null,
            "space");

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating X.Y as terminal, since it is overriden from the UI, regardless of any backwards
        // compatibility resolutions. Also using the template extracted from the template provider.
        verify(mockURLFactory).createURL("X", "Y", "edit", "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y",
            null, "xwiki", context);
    }


    public void translate(SetLocalPlayerAsInitializedPacket packet, GeyserSession session) {
        if (session.getPlayerEntity().getGeyserId() == packet.getRuntimeEntityId()) {
            if (!session.getUpstream().isInitialized()) {
                session.getUpstream().setInitialized(true);
                session.login();

                for (PlayerEntity entity : session.getEntityCache().getEntitiesByType(PlayerEntity.class)) {
                    if (!entity.isValid()) {
                        SkinManager.requestAndHandleSkinAndCape(entity, session, null);
                        entity.sendPlayer(session);
                    }
                }

                // Send Skulls
                for (PlayerEntity entity : session.getSkullCache().values()) {
                    entity.spawnEntity(session);

                    SkullSkinManager.requestAndHandleSkin(entity, session, (skin) ->  {
                        entity.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false);
                        entity.updateBedrockMetadata(session);
                    });
                }
            }
        }
    }


    public void translate(LoginPluginRequestPacket packet, GeyserSession session) {
        // A vanilla client doesn't know any PluginMessage in the Login state, so we don't know any either.
        // Note: Fabric Networking API v1 will not let the client log in without sending this
        session.sendDownstreamPacket(
                new LoginPluginResponsePacket(packet.getMessageId(), null)
        );
    }


	public static String getPropertyDef(InputSpec inputSpec, Map<String, Integer> indexes,
			String pattern, DefaultValueProvider defaultValueProvider) {
		int index = indexes.get(inputSpec.getName());
		StringBuffer buffer = new StringBuffer();
		inputSpec.appendField(buffer, index, "String");
		inputSpec.appendCommonAnnotations(buffer, index);
		if (!inputSpec.isAllowEmpty())
			buffer.append("    @NotEmpty\n");
		if (pattern != null)
			buffer.append("    @Pattern(regexp=\"" + pattern + "\", message=\"Should match regular expression: " + pattern + "\")\n");
		inputSpec.appendMethods(buffer, index, "String", null, defaultValueProvider);

		return buffer.toString();
	}


    public void multipleValuesPerNameIteratorEmpty() {
        final HttpHeadersBase headers = newEmptyHeaders();
        assertThat(headers.valueIterator("name")).isExhausted();
        assertThatThrownBy(() -> headers.valueIterator("name").next())
                .isInstanceOf(NoSuchElementException.class);
    }


    private boolean pull(ConsoleOutputStreamConsumer outputStreamConsumer) {
        CommandLine hg = hg("pull", "-b", branch, "--config", String.format("paths.default=%s", url));
        return execute(hg, outputStreamConsumer) == 0;
    }


   protected void traceLdapEnv(Properties env)
   {
      if (trace)
      {
         Properties tmp = new Properties();
         tmp.putAll(env);
         String credentials = tmp.getProperty(Context.SECURITY_CREDENTIALS);
         String bindCredential = tmp.getProperty(BIND_CREDENTIAL);
         if (credentials != null && credentials.length() > 0)
            tmp.setProperty(Context.SECURITY_CREDENTIALS, "***");

         if (bindCredential != null && bindCredential.length() > 0)
             tmp.setProperty(BIND_CREDENTIAL, "***");

         log.trace("Logging into LDAP server, env=" + tmp.toString());
      }
   }


    public void newDocumentWebHomeFromURL() throws Exception
    {
        // new document = xwiki:X.Y.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("X", "Y"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(true);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note1: The bebavior is the same for both a top level space and a child space WebHome.
        // Note2: The title is not "WebHome", but "Y" (the space's name) to avoid exposing "WebHome" in the UI.
        verify(mockURLFactory).createURL("X.Y", "WebHome", "edit", "template=&parent=Main.WebHome&title=Y", null,
            "xwiki", context);
    }


	protected String escape(String string) {
		String escaped = JavaEscape.escapeJava(string);
		// escape $ character since it has special meaning in groovy string
		escaped = escaped.replace("$", "\\$");

		return escaped;
	}


    public void translate(ServerEntityAttachPacket packet, GeyserSession session) {

        Entity holderId;
        if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
            holderId = session.getPlayerEntity();
        } else {
            holderId = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
            if (holderId == null) {
                return;
            }
        }

        Entity attachedToId;
        if (packet.getAttachedToId() == session.getPlayerEntity().getEntityId()) {
            attachedToId = session.getPlayerEntity();
        } else {
            attachedToId = session.getEntityCache().getEntityByJavaId(packet.getAttachedToId());
            if ((attachedToId == null || packet.getAttachedToId() == 0)) {
                // Is not being leashed
                holderId.getMetadata().getFlags().setFlag(EntityFlag.LEASHED, false);
                holderId.getMetadata().put(EntityData.LEASH_HOLDER_EID, -1L);
                holderId.updateBedrockMetadata(session);
                EntityEventPacket eventPacket = new EntityEventPacket();
                eventPacket.setRuntimeEntityId(holderId.getGeyserId());
                eventPacket.setType(EntityEventType.REMOVE_LEASH);
                eventPacket.setData(0);
                session.sendUpstreamPacket(eventPacket);
                return;
            }
        }

        holderId.getMetadata().getFlags().setFlag(EntityFlag.LEASHED, true);
        holderId.getMetadata().put(EntityData.LEASH_HOLDER_EID, attachedToId.getGeyserId());
        holderId.updateBedrockMetadata(session);
    }


    public void translate(ItemFrameDropItemPacket packet, GeyserSession session) {
        Entity entity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
        if (entity != null) {
            ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
                    InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking());
            session.sendDownstreamPacket(interactPacket);
        }
    }


    public static AsciiString of(CharSequence name) {
        if (name instanceof AsciiString) {
            return of((AsciiString) name);
        }

        final String lowerCased = Ascii.toLowerCase(requireNonNull(name, "name"));
        final AsciiString cached = map.get(lowerCased);
        return cached != null ? cached : AsciiString.cached(lowerCased);
    }


    List<Modification> findRecentModifications(int count) {
        // Currently impossible to check modifications on a remote repository.
        InMemoryStreamConsumer consumer = inMemoryConsumer();
        bombUnless(pull(consumer), "Failed to run hg pull command: " + consumer.getAllOutput());
        CommandLine hg = hg("log", "--limit", String.valueOf(count), "-b", branch, "--style", templatePath());
        return new HgModificationSplitter(execute(hg)).modifications();
    }


    public void translate(ServerPlayerHealthPacket packet, GeyserSession session) {
        SessionPlayerEntity entity = session.getPlayerEntity();

        int health = (int) Math.ceil(packet.getHealth());
        SetHealthPacket setHealthPacket = new SetHealthPacket();
        setHealthPacket.setHealth(health);
        session.sendUpstreamPacket(setHealthPacket);

        entity.setHealth(packet.getHealth());

        UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
        List<AttributeData> attributes = attributesPacket.getAttributes();

        AttributeData healthAttribute = entity.createHealthAttribute();
        entity.getAttributes().put(GeyserAttributeType.HEALTH, healthAttribute);
        attributes.add(healthAttribute);

        AttributeData hungerAttribute = GeyserAttributeType.HUNGER.getAttribute(packet.getFood());
        entity.getAttributes().put(GeyserAttributeType.HUNGER, hungerAttribute);
        attributes.add(hungerAttribute);

        AttributeData saturationAttribute = GeyserAttributeType.SATURATION.getAttribute(packet.getSaturation());
        entity.getAttributes().put(GeyserAttributeType.SATURATION, saturationAttribute);
        attributes.add(saturationAttribute);

        attributesPacket.setRuntimeEntityId(entity.getGeyserId());
        session.sendUpstreamPacket(attributesPacket);
    }


    public void existingDocumentTerminalFromUI() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI spaceReference=X&name=Y&tocreate=terminal
        when(mockRequest.getParameter("spaceReference")).thenReturn("X");
        when(mockRequest.getParameter("name")).thenReturn("Y");
        when(mockRequest.getParameter("tocreate")).thenReturn("terminal");

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating X.Y instead of X.Y.WebHome because the tocreate parameter says "terminal".
        verify(mockURLFactory).createURL("X", "Y", "edit", "template=&parent=Main.WebHome&title=Y", null, "xwiki",
            context);
    }


    public void translate(ServerEntityVelocityPacket packet, GeyserSession session) {
        Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
        if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
            entity = session.getPlayerEntity();
        }
        if (entity == null) return;

        entity.setMotion(Vector3f.from(packet.getMotionX(), packet.getMotionY(), packet.getMotionZ()));

        if (entity == session.getRidingVehicleEntity() && entity instanceof AbstractHorseEntity) {
            // Horses for some reason teleport back when a SetEntityMotionPacket is sent while
            // a player is riding on them. Java clients seem to ignore it anyways.
            return;
        }

        if (entity instanceof ItemEntity) {
            // Don't bother sending entity motion packets for items
            // since the client doesn't seem to care
            return;
        }

        SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
        entityMotionPacket.setRuntimeEntityId(entity.getGeyserId());
        entityMotionPacket.setMotion(entity.getMotion());

        session.sendUpstreamPacket(entityMotionPacket);
    }


    public void authorizeRequest(Operation op) {
        op.complete();
    }


  private static void verifyPeerName(PGStream stream, Properties info, SSLSocket newConnection)
      throws PSQLException {
    HostnameVerifier hvn;
    String sslhostnameverifier = PGProperty.SSL_HOSTNAME_VERIFIER.get(info);
    if (sslhostnameverifier == null) {
      hvn = PGjdbcHostnameVerifier.INSTANCE;
      sslhostnameverifier = "PgjdbcHostnameVerifier";
    } else {
      try {
        hvn = (HostnameVerifier) instantiate(sslhostnameverifier, info, false, null);
      } catch (Exception e) {
        throw new PSQLException(
            GT.tr("The HostnameVerifier class provided {0} could not be instantiated.",
                sslhostnameverifier),
            PSQLState.CONNECTION_FAILURE, e);
      }
    }

    if (hvn.verify(stream.getHostSpec().getHost(), newConnection.getSession())) {
      return;
    }

    throw new PSQLException(
        GT.tr("The hostname {0} could not be verified by hostnameverifier {1}.",
            stream.getHostSpec().getHost(), sslhostnameverifier),
        PSQLState.CONNECTION_FAILURE);
  }


    public void existingDocumentFromUIDeprecatedCheckEscaping() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI space=X.Y&page=Z
        when(mockRequest.getParameter("space")).thenReturn("X.Y");
        when(mockRequest.getParameter("page")).thenReturn("Z");

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note1: The space parameter was previously considered as space name, not space reference, so it is escaped.
        // Note2: We are creating X\.Y.Z since the deprecated parameters were creating terminal documents by default.
        verify(mockURLFactory).createURL("X\\.Y", "Z", "edit", "template=&parent=Main.WebHome&title=Z", null, "xwiki",
            context);
    }


	public void switchToConversation(Conversation conversation, String text) {
		switchToConversation(conversation, text, false, null, false);
	}


    protected final String constructValidationUrl(final String ticket, final String serviceUrl) {
        final Map<String, String> urlParameters = new HashMap<String, String>();

        logger.debug("Placing URL parameters in map.");
        urlParameters.put("ticket", ticket);
        urlParameters.put("service", encodeUrl(serviceUrl));

        if (this.renew) {
            urlParameters.put("renew", "true");
        }

        logger.debug("Calling template URL attribute map.");
        populateUrlAttributeMap(urlParameters);

        logger.debug("Loading custom parameters from configuration.");
        if (this.customParameters != null) {
            urlParameters.putAll(this.customParameters);
        }

        final String suffix = getUrlSuffix();
        final StringBuilder buffer = new StringBuilder(urlParameters.size() * 10 + this.casServerUrlPrefix.length()
                + suffix.length() + 1);

        int i = 0;

        buffer.append(this.casServerUrlPrefix);
        if (!this.casServerUrlPrefix.endsWith("/")) {
            buffer.append("/");
        }
        buffer.append(suffix);

        for (Map.Entry<String, String> entry : urlParameters.entrySet()) {
            final String key = entry.getKey();
            final String value = entry.getValue();

            if (value != null) {
                buffer.append(i++ == 0 ? "?" : "&");
                buffer.append(key);
                buffer.append("=");
                buffer.append(value);
            }
        }

        return buffer.toString();

    }


    public void newDocumentWebHomeFromURLTemplateProviderSpecifiedButNotAllowed() throws Exception
    {
        // new document = xwiki:X.Y.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("X", "Y"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(true);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Specifying a template provider in the URL: templateprovider=XWiki.MyTemplateProvider
        String templateProviderFullName = "XWiki.MyTemplateProvider";
        when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);

        // Mock 1 existing template provider
        mockExistingTemplateProviders(templateProviderFullName,
            new DocumentReference("xwiki", Arrays.asList("XWiki"), "MyTemplateProvider"),
            Arrays.asList("AnythingButX"));

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify that the create template is rendered, so the UI is displayed for the user to see the error.
        assertEquals("create", result);

        // Check that the exception is properly set in the context for the UI to display.
        XWikiException exception = (XWikiException) this.oldcore.getScriptContext().getAttribute("createException");
        assertNotNull(exception);
        assertEquals(XWikiException.ERROR_XWIKI_APP_TEMPLATE_NOT_AVAILABLE, exception.getCode());

        // We should not get this far so no redirect should be done, just the template will be rendered.
        verify(mockURLFactory, never()).createURL(any(), any(), any(), any(), any(),
            any(), any(XWikiContext.class));
    }


    public static Map<String, String> genHeaderMapByRequest(HttpServletRequest request) {
        Map<String, String> map = new HashMap<>();
        AdminTokenVO adminTokenVO = AdminTokenThreadLocal.getUser();
        if (adminTokenVO != null) {
            User user = User.dao.findById(adminTokenVO.getUserId());
            map.put("LoginUserName", user.get("userName").toString());
            map.put("LoginUserId", adminTokenVO.getUserId() + "");
        }
        map.put("IsLogin", (adminTokenVO != null) + "");
        map.put("Current-Locale", I18nUtil.getCurrentLocale());
        map.put("Blog-Version", ((Map) JFinal.me().getServletContext().getAttribute("zrlog")).get("version").toString());
        if (request != null) {
            String fullUrl = ZrLogUtil.getFullUrl(request);
            if (request.getQueryString() != null) {
                fullUrl = fullUrl + "?" + request.getQueryString();
            }
            if (adminTokenVO != null) {
                fullUrl = adminTokenVO.getProtocol() + ":" + fullUrl;
            }
            map.put("Cookie", request.getHeader("Cookie"));
            map.put("AccessUrl", "http://127.0.0.1:" + request.getServerPort() + request.getContextPath());
            if (request.getHeader("Content-Type") != null) {
                map.put("Content-Type", request.getHeader("Content-Type"));
            }
            map.put("Full-Url", fullUrl);
        }
        return map;
    }


    public void save(String comment, boolean minorEdit) throws XWikiException
    {
        if (hasAccessLevel("edit")) {
            // If the current author does not have PR don't let it set current user as author of the saved document
            // since it can lead to right escalation
            if (hasProgrammingRights()) {
                saveDocument(comment, minorEdit);
            } else {
                saveAsAuthor(comment, minorEdit);
            }
        } else {
            java.lang.Object[] args = {getDefaultEntityReferenceSerializer().serialize(getDocumentReference())};
            throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
                "Access denied in edit mode on document {0}", null, args);
        }
    }


    protected void afterFeaturesReceived() throws SecurityRequiredException, NotConnectedException, InterruptedException {
        StartTls startTlsFeature = getFeature(StartTls.ELEMENT, StartTls.NAMESPACE);
        if (startTlsFeature != null) {
            if (startTlsFeature.required() && config.getSecurityMode() == SecurityMode.disabled) {
                notifyConnectionError(new SecurityRequiredByServerException());
                return;
            }

            if (config.getSecurityMode() != ConnectionConfiguration.SecurityMode.disabled) {
                sendNonza(new StartTls());
            }
        }
        // If TLS is required but the server doesn't offer it, disconnect
        // from the server and throw an error. First check if we've already negotiated TLS
        // and are secure, however (features get parsed a second time after TLS is established).
        if (!isSecureConnection() && startTlsFeature == null
                        && getConfiguration().getSecurityMode() == SecurityMode.required) {
            throw new SecurityRequiredByClientException();
        }

        if (getSASLAuthentication().authenticationSuccessful()) {
            // If we have received features after the SASL has been successfully completed, then we
            // have also *maybe* received, as it is an optional feature, the compression feature
            // from the server.
            maybeCompressFeaturesReceived.reportSuccess();
        }
    }


    public void giveWarningIfNoValidationMethods() throws Exception {
        assertThat(ConstraintViolations.format(validator.validate(new NoValidations())))
            .isEmpty();
        assertThat(TestLoggerFactory.getAllLoggingEvents())
        .containsExactlyInAnyOrder(
                new LoggingEvent(
                        Level.WARN,
                        "The class {} is annotated with @SelfValidating but contains no valid methods that are annotated with @SelfValidation",
                        NoValidations.class
                )

        );
    }


    public void init(KeyGenerationParameters param)
    {
        this.param = (RSAKeyGenerationParameters)param;
        this.iterations = getNumberOfIterations(this.param.getStrength(), this.param.getCertainty());
    }


    public Optional<CorsOriginConfiguration> convert(Object object, Class<CorsOriginConfiguration> targetType, ConversionContext context) {
        CorsOriginConfiguration configuration = new CorsOriginConfiguration();
        if (object instanceof Map) {
            Map mapConfig = (Map) object;
            ConvertibleValues<Object> convertibleValues = new ConvertibleValuesMap<>(mapConfig);

            convertibleValues
                .get(ALLOWED_ORIGINS, Argument.listOf(String.class))
                .ifPresent(configuration::setAllowedOrigins);

            convertibleValues
                .get(ALLOWED_METHODS, Argument.listOf(HttpMethod.class))
                .ifPresent(configuration::setAllowedMethods);

            convertibleValues
                .get(ALLOWED_HEADERS, Argument.listOf(String.class))
                .ifPresent(configuration::setAllowedHeaders);

            convertibleValues
                .get(EXPOSED_HEADERS, Argument.listOf(String.class))
                .ifPresent(configuration::setExposedHeaders);

            convertibleValues
                .get(ALLOW_CREDENTIALS, Boolean.class)
                .ifPresent(configuration::setAllowCredentials);

            convertibleValues
                .get(MAX_AGE, Long.class)
                .ifPresent(configuration::setMaxAge);
        }
        return Optional.of(configuration);
    }


    public void translate(ServerSpawnExpOrbPacket packet, GeyserSession session) {
        Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());

        Entity entity = new ExpOrbEntity(
                packet.getExp(), packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
                EntityType.EXPERIENCE_ORB, position, Vector3f.ZERO, Vector3f.ZERO
        );

        session.getEntityCache().spawnEntity(entity);
    }


    public void multipleValuesPerNameIterator() {
        final HttpHeadersBase headers = newEmptyHeaders();
        headers.add("name1", "value1");
        headers.add("name1", "value2");
        assertThat(headers.size()).isEqualTo(2);

        final List<String> values = ImmutableList.copyOf(headers.valueIterator("name1"));
        assertThat(values).containsExactly("value1", "value2");
    }


    void validSaveNewTranslation() throws Exception
    {
        when(mockForm.getLanguage()).thenReturn("fr");
        when(mockClonedDocument.getTranslatedDocument("fr", this.context)).thenReturn(mockClonedDocument);
        when(mockClonedDocument.getDocumentReference()).thenReturn(new DocumentReference("xwiki", "My", "Page"));
        when(mockClonedDocument.getStore()).thenReturn(this.oldcore.getMockStore());
        when(xWiki.getStore()).thenReturn(this.oldcore.getMockStore());
        context.put("ajax", true);
        when(xWiki.isMultiLingual(this.context)).thenReturn(true);
        when(mockRequest.getParameter("previousVersion")).thenReturn("1.1");
        when(mockRequest.getParameter("isNew")).thenReturn("true");
        assertFalse(saveAction.save(this.context));
        assertEquals(new Version("1.1"), this.context.get("SaveAction.savedObjectVersion"));
        verify(this.xWiki).checkSavingDocument(eq(USER_REFERENCE), any(XWikiDocument.class), eq(""),
            eq(false), eq(this.context));
        verify(this.xWiki).saveDocument(any(XWikiDocument.class), eq(""), eq(false), eq(this.context));
    }


    public void translate(ServerUnloadChunkPacket packet, GeyserSession session) {
        session.getChunkCache().removeChunk(packet.getX(), packet.getZ());

        //Checks if a skull is in an unloaded chunk then removes it
        Iterator<Vector3i> iterator = session.getSkullCache().keySet().iterator();
        while (iterator.hasNext()) {
            Vector3i position = iterator.next();
            if ((position.getX() >> 4) == packet.getX() && (position.getZ() >> 4) == packet.getZ()) {
                session.getSkullCache().get(position).despawnEntity(session);
                iterator.remove();
            }
        }

        // Do the same thing with lecterns
        iterator = session.getLecternCache().iterator();
        while (iterator.hasNext()) {
            Vector3i position = iterator.next();
            if ((position.getX() >> 4) == packet.getX() && (position.getZ() >> 4) == packet.getZ()) {
                iterator.remove();
            }
        }
    }


    public void translate(ServerEntityPositionRotationPacket packet, GeyserSession session) {
        Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
        if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
            entity = session.getPlayerEntity();
        }
        if (entity == null) return;

        entity.updatePositionAndRotation(session, packet.getMoveX(), packet.getMoveY(), packet.getMoveZ(), packet.getYaw(), packet.getPitch(), packet.isOnGround());
    }


    protected void deactivateConversationContext(HttpServletRequest request) {
        ConversationContext conversationContext = httpConversationContext();
        if (conversationContext.isActive()) {
            // Only deactivate the context if one is already active, otherwise we get Exceptions
            if (conversationContext instanceof LazyHttpConversationContextImpl) {
                LazyHttpConversationContextImpl lazyConversationContext = (LazyHttpConversationContextImpl) conversationContext;
                if (!lazyConversationContext.isInitialized()) {
                    // if this lazy conversation has not been touched yet, just deactivate it
                    lazyConversationContext.deactivate();
                    return;
                }
            }
            boolean isTransient = conversationContext.getCurrentConversation().isTransient();
            if (ConversationLogger.LOG.isTraceEnabled()) {
                if (isTransient) {
                    ConversationLogger.LOG.cleaningUpTransientConversation();
                } else {
                    ConversationLogger.LOG.cleaningUpConversation(conversationContext.getCurrentConversation().getId());
                }
            }
            conversationContext.invalidate();
            conversationContext.deactivate();
            if (isTransient) {
                conversationDestroyedEvent.fire(request);
            }
        }
    }


    public Group createDefaultReadGroup(Context context, Collection collection, String typeOfGroupString,
                                        int defaultRead)
        throws SQLException, AuthorizeException {
        Group role = groupService.create(context);
        groupService.setName(role, "COLLECTION_" + collection.getID().toString() + "_" + typeOfGroupString +
            "_DEFAULT_READ");

        // Remove existing privileges from the anonymous group.
        authorizeService.removePoliciesActionFilter(context, collection, defaultRead);

        // Grant our new role the default privileges.
        authorizeService.addPolicy(context, collection, defaultRead, role);
        groupService.update(context, role);
        return role;
    }


    public boolean isValid(Integer value, ConstraintValidatorContext context) {
      if (proxyManager.get(value) != null) {
        return true;
      }

      String errorMessage = String.format("No proxy server found for specified port %d", value);
      LOG.warn(errorMessage);

      context.buildConstraintViolationWithTemplate(errorMessage).addPropertyNode(PARAM_NAME).addConstraintViolation();
      return false;
    }


    public void headersWithSameNamesAndValuesShouldBeEquivalent() {
        final HttpHeadersBase headers1 = newEmptyHeaders();
        headers1.add("name1", "value1");
        headers1.add("name2", "value2");
        headers1.add("name2", "value3");

        final HttpHeadersBase headers2 = newEmptyHeaders();
        headers2.add("name1", "value1");
        headers2.add("name2", "value2");
        headers2.add("name2", "value3");

        assertThat(headers2).isEqualTo(headers1);
        assertThat(headers1).isEqualTo(headers2);
        assertThat(headers1).isEqualTo(headers1);
        assertThat(headers2).isEqualTo(headers2);
        assertThat(headers2.hashCode()).isEqualTo(headers1.hashCode());
        assertThat(headers1.hashCode()).isEqualTo(headers1.hashCode());
        assertThat(headers2.hashCode()).isEqualTo(headers2.hashCode());
    }


    public AbstractEpsgFactory(final Hints userHints) throws FactoryException {
        super(MAXIMUM_PRIORITY - 20);
        // The following hints have no effect on this class behaviour,
        // but tell to the user what this factory do about axis order.
        hints.put(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.FALSE);
        hints.put(Hints.FORCE_STANDARD_AXIS_DIRECTIONS, Boolean.FALSE);
        hints.put(Hints.FORCE_STANDARD_AXIS_UNITS, Boolean.FALSE);

        //
        // We need to obtain our DataSource
        if (userHints != null) {
            Object hint = userHints.get(Hints.EPSG_DATA_SOURCE);
            if (hint instanceof String) {
                String name = (String) hint;
                try {
                    dataSource = (DataSource) GeoTools.getInitialContext().lookup(name);
                } catch (NamingException e) {
                    throw new FactoryException("A EPSG_DATA_SOURCE hint is required:" + e);
                }
                hints.put(Hints.EPSG_DATA_SOURCE, dataSource);
            } else if (hint instanceof DataSource) {
                dataSource = (DataSource) hint;
                hints.put(Hints.EPSG_DATA_SOURCE, dataSource);
            } else {
                throw new FactoryException("A EPSG_DATA_SOURCE hint is required.");
            }
        } else {
            throw new FactoryException("A EPSG_DATA_SOURCE hint is required.");
        }
    }


    public boolean isValid(Object value, ConstraintValidatorContext context) {
      if (value != null && StringUtils.isNotEmpty(String.valueOf(value))) {
        return true;
      }
      String errorMessage = String.format("Expected not empty value, got '%s'", value);
      LOG.warn(errorMessage);

      context.buildConstraintViolationWithTemplate(errorMessage).addConstraintViolation();
      return false;
    }


	protected void runTeardown() {
		Assert.assertTrue("Error during initialization", messagedInitialization);
		Assert.assertTrue("HTTP connection is not allowed", messagedAccessDenied);
	}


    protected void initOther() throws ServletException {
        PropertyUtils.addBeanIntrospector(
                SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS);
        PropertyUtils.clearDescriptors();

        String value = null;
        value = getServletConfig().getInitParameter("config");
        if (value != null) {
            config = value;
        }

        // Backwards compatibility for form beans of Java wrapper classes
        // Set to true for strict Struts 1.0 compatibility
        value = getServletConfig().getInitParameter("convertNull");
        if ("true".equalsIgnoreCase(value)
            || "yes".equalsIgnoreCase(value)
            || "on".equalsIgnoreCase(value)
            || "y".equalsIgnoreCase(value)
            || "1".equalsIgnoreCase(value)) {

            convertNull = true;
        }

        if (convertNull) {
            ConvertUtils.deregister();
            ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
            ConvertUtils.register(new BigIntegerConverter(null), BigInteger.class);
            ConvertUtils.register(new BooleanConverter(null), Boolean.class);
            ConvertUtils.register(new ByteConverter(null), Byte.class);
            ConvertUtils.register(new CharacterConverter(null), Character.class);
            ConvertUtils.register(new DoubleConverter(null), Double.class);
            ConvertUtils.register(new FloatConverter(null), Float.class);
            ConvertUtils.register(new IntegerConverter(null), Integer.class);
            ConvertUtils.register(new LongConverter(null), Long.class);
            ConvertUtils.register(new ShortConverter(null), Short.class);
        }

    }


    public void shouldThrowExceptionIfUpdateFails() throws Exception {
        InMemoryStreamConsumer output =
                ProcessOutputStreamConsumer.inMemoryConsumer();

        // delete repository in order to fail the hg pull command
        assertThat(FileUtils.deleteQuietly(serverRepo), is(true));

        // now hg pull will fail and throw an exception
        assertThatThrownBy(() -> hgCommand.updateTo(new StringRevision("tip"), output))
                .isExactlyInstanceOf(RuntimeException.class);
    }


    public void translate(ServerDifficultyPacket packet, GeyserSession session) {
        SetDifficultyPacket setDifficultyPacket = new SetDifficultyPacket();
        setDifficultyPacket.setDifficulty(packet.getDifficulty().ordinal());
        session.sendUpstreamPacket(setDifficultyPacket);

        session.getWorldCache().setDifficulty(packet.getDifficulty());
    }


    public void translate(PacketViolationWarningPacket packet, GeyserSession session) {
        // Not translated since this is something that the developers need to know
        session.getConnector().getLogger().error("Packet violation warning sent from client! " + packet.toString());
    }


    protected void parseModuleConfigFile(Digester digester, String path)
        throws UnavailableException {

        InputStream input = null;
        try {
            URL url = getServletContext().getResource(path);

            // If the config isn't in the servlet context, try the class loader
            // which allows the config files to be stored in a jar
            if (url == null) {
                url = getClass().getResource(path);
            }

            if (url == null) {
                String msg = internal.getMessage("configMissing", path);
                log.error(msg);
                throw new UnavailableException(msg);
            }

            InputSource is = new InputSource(url.toExternalForm());
            input = url.openStream();
            is.setByteStream(input);
            digester.parse(is);

        } catch (MalformedURLException e) {
            handleConfigException(path, e);
        } catch (IOException e) {
            handleConfigException(path, e);
        } catch (SAXException e) {
            handleConfigException(path, e);
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                    throw new UnavailableException(e.getMessage());
                }
            }
        }
    }


    private File doDownload(String driverFileUrl, String filePath) {
        Path path = Path.of(filePath);

        // create parent directory
        if (Files.notExists(path)) {
            path.getParent().toFile().mkdirs();
            try {
                Files.createFile(path);
            } catch (IOException e) {
                log.error("create file error " + filePath, e);
                throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e.getMessage());
            }
        }

        // download
        try {
            return restTemplate.execute(driverFileUrl, HttpMethod.GET, null, response -> {
                if (response.getStatusCode().is2xxSuccessful()) {
                    File file = path.toFile();
                    FileOutputStream out = new FileOutputStream(file);
                    StreamUtils.copy(response.getBody(), out);
                    IOUtils.closeQuietly(out, ex -> log.error("close file error", ex));
                    log.info("{} download success ", filePath);
                    return file;
                } else {
                    log.error("{} download error from {}: {} ", filePath, driverFileUrl, response);
                    throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception("驱动下载失败："
                            + response.getStatusCode()
                            + ", "
                            + response.getStatusText());
                }
            });
        } catch (IllegalArgumentException e) {
            log.error(filePath + " download driver error", e);
            throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e.getMessage());
        }
    }


    private void populateAuthCacheInAllPeers(AuthorizationContext authContext) throws Throwable {

        // send a GET request to the ExampleService factory to populate auth cache on each peer.
        // since factory is not OWNER_SELECTION service, request goes to the specified node.
        for (VerificationHost peer : this.host.getInProcessHostMap().values()) {
            peer.setAuthorizationContext(authContext);

            // based on the role created in test, all users have access to ExampleService
            this.host.sendAndWaitExpectSuccess(
                    Operation.createGet(UriUtils.buildStatsUri(peer, ExampleService.FACTORY_LINK)));
        }

        this.host.waitFor("Timeout waiting for correct auth cache state",
                () -> checkCacheInAllPeers(authContext.getToken(), true));
    }


    public Argument<CompletableFuture> argumentType() {
        return Argument.of(CompletableFuture.class);
    }


    private Secret(String value) {
        this.value = value;
    }


	public int size() {

		return ByteUtils.bitLength(n.decode());
	}


    void validSave() throws Exception
    {
        when(mockClonedDocument.getRCSVersion()).thenReturn(new Version("1.2"));
        when(mockClonedDocument.getComment()).thenReturn("My Changes");
        when(mockClonedDocument.getLock(this.context)).thenReturn(mock(XWikiLock.class));
        when(mockForm.getTemplate()).thenReturn("");
        assertFalse(saveAction.save(this.context));
        assertEquals(new Version("1.2"), this.context.get("SaveAction.savedObjectVersion"));

        verify(mockClonedDocument).readFromTemplate("", this.context);
        verify(mockClonedDocument).setAuthor("XWiki.FooBar");
        verify(mockClonedDocument).setMetaDataDirty(true);
        verify(this.xWiki).checkSavingDocument(USER_REFERENCE, mockClonedDocument, "My Changes", false, this.context);
        verify(this.xWiki).saveDocument(mockClonedDocument, "My Changes", false, this.context);
        verify(mockClonedDocument).removeLock(this.context);
    }


  public static Object instantiate(String classname, Properties info, boolean tryString,
      @Nullable String stringarg)
      throws ClassNotFoundException, SecurityException, NoSuchMethodException,
          IllegalArgumentException, InstantiationException, IllegalAccessException,
          InvocationTargetException {
    @Nullable Object[] args = {info};
    Constructor<?> ctor = null;
    Class<?> cls = Class.forName(classname);
    try {
      ctor = cls.getConstructor(Properties.class);
    } catch (NoSuchMethodException ignored) {
    }
    if (tryString && ctor == null) {
      try {
        ctor = cls.getConstructor(String.class);
        args = new String[]{stringarg};
      } catch (NoSuchMethodException ignored) {
      }
    }
    if (ctor == null) {
      ctor = cls.getConstructor();
      args = new Object[0];
    }
    return ctor.newInstance(args);
  }


    public void translate(CommandRequestPacket packet, GeyserSession session) {
        String command = packet.getCommand().replace("/", "");
        CommandManager commandManager = GeyserConnector.getInstance().getCommandManager();
        if (session.getConnector().getPlatformType() == PlatformType.STANDALONE && command.trim().startsWith("geyser ") && commandManager.getCommands().containsKey(command.split(" ")[1])) {
            commandManager.runCommand(session, command);
        } else {
            String message = packet.getCommand().trim();

            if (MessageTranslator.isTooLong(message, session)) {
                return;
            }

            ClientChatPacket chatPacket = new ClientChatPacket(message);
            session.sendDownstreamPacket(chatPacket);
        }
    }


    public void newDocumentWebHomeFromURLTemplateProviderSpecifiedButOldPageType() throws Exception
    {
        // new document = xwiki:X.Y.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("X", "Y"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(true);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Specifying a template provider in the URL: templateprovider=XWiki.MyTemplateProvider
        String templateProviderFullName = "XWiki.MyTemplateProvider";
        when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);

        // Mock 1 existing template provider
        mockExistingTemplateProviders(templateProviderFullName,
            new DocumentReference("xwiki", Arrays.asList("XWiki"), "MyTemplateProvider"), Collections.EMPTY_LIST, null,
            "page");

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating the document X.Y as terminal, since the template provider did not specify a "terminal"
        // property and it used the old "page" type instead. Also using a template, as specified in the template
        // provider.
        verify(mockURLFactory).createURL("X", "Y", "edit", "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y",
            null, "xwiki", context);
    }


    public void complexExample() throws Exception {
        assertThat(ConstraintViolations.format(validator.validate(new ComplexExample())))
            .containsExactlyInAnyOrder(
                FAILED_RESULT + "1",
                FAILED_RESULT + "2",
                FAILED_RESULT + "3"
            );
        assertThat(TestLoggerFactory.getAllLoggingEvents())
            .isEmpty();
    }


    public void translate(ServerUnlockRecipesPacket packet, GeyserSession session) {
        if (packet.getAction() == UnlockRecipesAction.REMOVE) {
            session.getUnlockedRecipes().removeAll(Arrays.asList(packet.getRecipes()));
        } else {
            session.getUnlockedRecipes().addAll(Arrays.asList(packet.getRecipes()));
        }
    }


   public static OHttpSessionManager getInstance() {
     return instance;
   }


    public void existingDocumentTerminalFromUICheckEscaping() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI spaceReference=X.Y&name=Z&tocreate=termina
        when(mockRequest.getParameter("spaceReference")).thenReturn("X.Y");
        when(mockRequest.getParameter("name")).thenReturn("Z");
        when(mockRequest.getParameter("tocreate")).thenReturn("terminal");

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating X.Y.Z instead of X.Y.Z.WebHome because the tocreate parameter says "terminal".
        verify(mockURLFactory).createURL("X.Y", "Z", "edit", "template=&parent=Main.WebHome&title=Z", null, "xwiki",
            context);
    }


    protected boolean isProbablePrime(BigInteger x, int iterations)
    {
        /*
         * Primes class for FIPS 186-4 C.3 primality checking
         */
        return !Primes.hasAnySmallFactors(x) && Primes.isMRProbablePrime(x, param.getRandom(), iterations);
    }


    public void existingDocumentFromUITemplateProviderSpecifiedNonTerminal() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
        String templateProviderFullName = "XWiki.MyTemplateProvider";
        String spaceReferenceString = "X";
        when(mockRequest.getParameter("spaceReference")).thenReturn(spaceReferenceString);
        when(mockRequest.getParameter("name")).thenReturn("Y");
        when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);

        // Mock 1 existing template provider that creates terminal documents.
        mockExistingTemplateProviders(templateProviderFullName,
            new DocumentReference("xwiki", Arrays.asList("XWiki"), "MyTemplateProvider"), Collections.EMPTY_LIST,
            false);

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating the document X.Y.WebHome as non-terminal and using a template, as specified in the
        // template provider.
        verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y", null, "xwiki", context);
    }


    public void translate(ServerSpawnPlayerPacket packet, GeyserSession session) {
        Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
        Vector3f rotation = Vector3f.from(packet.getYaw(), packet.getPitch(), packet.getYaw());

        PlayerEntity entity;
        if (packet.getUuid().equals(session.getPlayerEntity().getUuid())) {
            // Server is sending a fake version of the current player
            entity = new PlayerEntity(session.getPlayerEntity().getProfile(), packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), position, Vector3f.ZERO, rotation);
        } else {
            entity = session.getEntityCache().getPlayerEntity(packet.getUuid());
            if (entity == null) {
                GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.entity.player.failed_list", packet.getUuid()));
                return;
            }

            entity.setEntityId(packet.getEntityId());
            entity.setPosition(position);
            entity.setRotation(rotation);
        }
        session.getEntityCache().cacheEntity(entity);

        entity.sendPlayer(session);
        SkinManager.requestAndHandleSkinAndCape(entity, session, null);
    }


  public AuthorizationCodeRequestUrl newAuthorizationUrl() {
    return new AuthorizationCodeRequestUrl(authorizationServerEncodedUrl, clientId).setScopes(
        scopes);
  }


    public void translate(NetworkStackLatencyPacket packet, GeyserSession session) {
        long pingId;
        // so apparently, as of 1.16.200
        // PS4 divides the network stack latency timestamp FOR US!!!
        // WTF
        if (session.getClientData().getDeviceOs().equals(DeviceOs.PS4)) {
            pingId = packet.getTimestamp();
        } else {
            pingId = packet.getTimestamp() / 1000;
        }

        // negative timestamps are used as hack to fix the url image loading bug
        if (packet.getTimestamp() > 0) {
            if (session.getConnector().getConfig().isForwardPlayerPing()) {
                ClientKeepAlivePacket keepAlivePacket = new ClientKeepAlivePacket(pingId);
                session.sendDownstreamPacket(keepAlivePacket);
            }
            return;
        }

        // Hack to fix the url image loading bug
        UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
        attributesPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());

        AttributeData attribute = session.getPlayerEntity().getAttributes().get(GeyserAttributeType.EXPERIENCE_LEVEL);
        if (attribute != null) {
            attributesPacket.setAttributes(Collections.singletonList(attribute));
        } else {
            attributesPacket.setAttributes(Collections.singletonList(GeyserAttributeType.EXPERIENCE_LEVEL.getAttribute(0)));
        }

        session.getConnector().getGeneralThreadPool().schedule(
                () -> session.sendUpstreamPacket(attributesPacket),
                500, TimeUnit.MILLISECONDS);
    }


    public void invalidExample() throws Exception {
        assertThat(ConstraintViolations.format(validator.validate(new InvalidExample())))
            .isEmpty();
        assertThat(TestLoggerFactory.getAllLoggingEvents())
            .containsExactlyInAnyOrder(
                    new LoggingEvent(
                            Level.ERROR,
                            "The method {} is annotated with @SelfValidation but does not have a single parameter of type {}",
                            InvalidExample.class.getMethod("validateFailAdditionalParameters", ViolationCollector.class, int.class),
                            ViolationCollector.class
                    ),
                    new LoggingEvent(
                            Level.ERROR,
                            "The method {} is annotated with @SelfValidation but does not return void. It is ignored",
                            InvalidExample.class.getMethod("validateFailReturn", ViolationCollector.class)
                    ),
                    new LoggingEvent(
                            Level.ERROR,
                            "The method {} is annotated with @SelfValidation but is not public",
                            InvalidExample.class.getDeclaredMethod("validateFailPrivate", ViolationCollector.class)
                    )
            );
    }


    private String tryRewrite(String s) throws IOException, InvalidKeyException {
        if (s.length()<24)
            return s;   // Encrypting "" in Secret produces 24-letter characters, so this must be the minimum length
        if (!isBase64(s))
            return s;   // decode throws IOException if the input is not base64, and this is also a very quick way to filter

        byte[] in;
        try {
            in = Base64.decode(s.toCharArray());
        } catch (IOException e) {
            return s;   // not a valid base64
        }
        cipher.init(Cipher.DECRYPT_MODE, key);
        Secret sec = Secret.tryDecrypt(cipher, in);
        if(sec!=null) // matched
            return sec.getEncryptedValue(); // replace by the new encrypted value
        else // not encrypted with the legacy key. leave it unmodified
            return s;
    }


    public void nullHeaderNameNotAllowed() {
        newEmptyHeaders().add(null, "foo");
    }


    public void newDocumentWebHomeFromURLTemplateProviderSpecifiedTerminal() throws Exception
    {
        // new document = xwiki:X.Y.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("X", "Y"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(true);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Specifying a template provider in the URL: templateprovider=XWiki.MyTemplateProvider
        String templateProviderFullName = "XWiki.MyTemplateProvider";
        when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);

        // Mock 1 existing template provider
        mockExistingTemplateProviders(templateProviderFullName,
            new DocumentReference("xwiki", Arrays.asList("XWiki"), "MyTemplateProvider"), Collections.EMPTY_LIST, true);

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating the document X.Y as terminal and using a template, as specified in the template
        // provider.
        verify(mockURLFactory).createURL("X", "Y", "edit", "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y",
            null, "xwiki", context);
    }


    public void existingDocumentFromUICheckEscaping() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI spaceReference=X.Y&name=Z
        when(mockRequest.getParameter("spaceReference")).thenReturn("X.Y");
        when(mockRequest.getParameter("name")).thenReturn("Z");

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating X.Y.Z.WebHome since we default to non-terminal documents.
        verify(mockURLFactory).createURL("X.Y.Z", "WebHome", "edit", "template=&parent=Main.WebHome&title=Z", null,
            "xwiki", context);
    }


  public IdImpl(String id) {
    this.id = id;
  }


    final void setObject(CharSequence name, Object... values) {
        final AsciiString normalizedName = normalizeName(name);
        requireNonNull(values, "values");

        final int h = normalizedName.hashCode();
        final int i = index(h);

        remove0(h, i, normalizedName);
        for (Object v: values) {
            requireNonNullElement(values, v);
            add0(h, i, normalizedName, fromObject(v));
        }
    }


  private X509HostnameVerifier createHostNameVerifier() {
    X509HostnameVerifier verifier = new X509HostnameVerifier() {

      /**
       * {@InheritDoc}
       *
       * @see org.apache.http.conn.ssl.X509HostnameVerifier#verify(java.lang.String, javax.net.ssl.SSLSocket)
       */
      public void verify(String host, SSLSocket ssl) throws IOException {
        logger.trace("Skipping SSL host name check on {}", host);
      }

      /**
       * {@InheritDoc}
       *
       * @see org.apache.http.conn.ssl.X509HostnameVerifier#verify(java.lang.String, java.security.cert.X509Certificate)
       */
      public void verify(String host, X509Certificate xc) throws SSLException {
        logger.trace("Skipping X509 certificate host name check on {}", host);
      }

      /**
       * {@InheritDoc}
       *
       * @see org.apache.http.conn.ssl.X509HostnameVerifier#verify(java.lang.String, java.lang.String[],
       *      java.lang.String[])
       */
      public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
        logger.trace("Skipping DNS host name check on {}", host);
      }

      /**
       * {@InheritDoc}
       *
       * @see javax.net.ssl.HostnameVerifier#verify(java.lang.String, javax.net.ssl.SSLSession)
       */
      public boolean verify(String host, SSLSession ssl) {
        logger.trace("Skipping SSL session host name check on {}", host);
        return true;
      }
    };

    return verifier;
  }


    public void existingDocumentFromUITemplateProviderSpecifiedRestrictionExistsOnParentSpace() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI spaceReference=X.Y.Z&name=W&templateProvider=XWiki.MyTemplateProvider
        String templateProviderFullName = "XWiki.MyTemplateProvider";
        String spaceReferenceString = "X.Y.Z";
        when(mockRequest.getParameter("spaceReference")).thenReturn(spaceReferenceString);
        when(mockRequest.getParameter("name")).thenReturn("W");
        when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);

        // Mock 1 existing template provider that allows usage in one of the target space's parents (top level in this
        // case).
        mockExistingTemplateProviders(templateProviderFullName,
            new DocumentReference("xwiki", Arrays.asList("XWiki"), "MyTemplateProvider"), Arrays.asList("X"));

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note1: We are allowed to create anything under space X or its children, be it a terminal or a non-terminal
        // document
        // Note2: We are creating X.Y.Z.W and using the template extracted from the template provider.
        verify(mockURLFactory).createURL("X.Y.Z.W", "WebHome", "edit",
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=W", null, "xwiki", context);
    }


    public void update(DatabaseTypeUpdateRequest request) {
        databaseTypeDao.selectOptionalById(request.getId()).ifPresent(data -> {
            if (DatabaseTypes.has(data.getDatabaseType())) {
                throw DomainErrors.MUST_NOT_MODIFY_SYSTEM_DEFAULT_DATABASE_TYPE.exception();
            }

            DatabaseTypePojo pojo = databaseTypePojoConverter.of(request);
            try {
                databaseTypeDao.updateById(pojo);
            } catch (DuplicateKeyException e) {
                throw DomainErrors.DATABASE_TYPE_NAME_DUPLICATE.exception();
            }

            // 名称修改，下载地址修改需要删除原有的 driver
            if (!Objects.equals(request.getDatabaseType(), data.getDatabaseType())
                    || !Objects.equals(request.getJdbcDriverFileUrl(), data.getJdbcDriverFileUrl())) {
                driverResources.delete(data.getDatabaseType());
            }
        });

    }


    public void translate(ServerSettingsRequestPacket packet, GeyserSession session) {
        CustomForm window = SettingsUtils.buildForm(session);
        int windowId = session.getFormCache().addForm(window);

        // Fixes https://bugs.mojang.com/browse/MCPE-94012 because of the delay
        session.getConnector().getGeneralThreadPool().schedule(() -> {
            ServerSettingsResponsePacket serverSettingsResponsePacket = new ServerSettingsResponsePacket();
            serverSettingsResponsePacket.setFormData(window.getJsonData());
            serverSettingsResponsePacket.setFormId(windowId);
            session.sendUpstreamPacket(serverSettingsResponsePacket);
        }, 1, TimeUnit.SECONDS);
    }


    public SecretRewriter() throws GeneralSecurityException {
        cipher = Secret.getCipher("AES");
        key = Secret.getLegacyKey();
    }


    public void shouldGetLatestModifications() throws Exception {
        List<Modification> actual = hgCommand.latestOneModificationAsModifications();
        assertThat(actual.size(), is(1));
        final Modification modification = actual.get(0);
        assertThat(modification.getComment(), is("test"));
        assertThat(modification.getUserName(), is("cruise"));
        assertThat(modification.getModifiedFiles().size(), is(1));
    }


    public void existingDocumentFromUITemplateProviderSpecified() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
        String templateProviderFullName = "XWiki.MyTemplateProvider";
        when(mockRequest.getParameter("spaceReference")).thenReturn("X");
        when(mockRequest.getParameter("name")).thenReturn("Y");
        when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);

        // Mock 1 existing template provider
        mockExistingTemplateProviders(templateProviderFullName,
            new DocumentReference("xwiki", Arrays.asList("XWiki"), "MyTemplateProvider"), Collections.EMPTY_LIST);

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating X.Y and using the template extracted from the template provider.
        verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y", null, "xwiki", context);
    }


    public boolean isAvailable() {
        try {
            GeoTools.getInitialContext();
            return true;
        } catch (NamingException e) {
            return false;
        }
    }


    public void translate(ServerSpawnParticlePacket packet, GeyserSession session) {
        Function<Vector3f, BedrockPacket> particleCreateFunction = createParticle(session, packet.getParticle());
        if (particleCreateFunction != null) {
            if (packet.getAmount() == 0) {
                // 0 means don't apply the offset
                Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
                session.sendUpstreamPacket(particleCreateFunction.apply(position));
            } else {
                Random random = ThreadLocalRandom.current();
                for (int i = 0; i < packet.getAmount(); i++) {
                    double offsetX = random.nextGaussian() * (double) packet.getOffsetX();
                    double offsetY = random.nextGaussian() * (double) packet.getOffsetY();
                    double offsetZ = random.nextGaussian() * (double) packet.getOffsetZ();
                    Vector3f position = Vector3f.from(packet.getX() + offsetX, packet.getY() + offsetY, packet.getZ() + offsetZ);

                    session.sendUpstreamPacket(particleCreateFunction.apply(position));
                }
            }
        } else {
            // Null is only returned when no particle of this type is found
            session.getConnector().getLogger().debug("Unhandled particle packet: " + packet);
        }
    }


    public void setUp() throws Exception
    {
        context = oldcore.getXWikiContext();

        Utils.setComponentManager(oldcore.getMocker());

        QueryManager mockSecureQueryManager =
            oldcore.getMocker().registerMockComponent((Type) QueryManager.class, "secure");

        mockTemplateProvidersQuery = mock(Query.class);
        when(mockSecureQueryManager.createQuery(any(), any())).thenReturn(mockTemplateProvidersQuery);
        when(mockTemplateProvidersQuery.execute()).thenReturn(Collections.emptyList());

        when(oldcore.getMockContextualAuthorizationManager().hasAccess(any(Right.class), any(EntityReference.class)))
            .thenReturn(true);

        Provider<DocumentReference> mockDocumentReferenceProvider =
            oldcore.getMocker().registerMockComponent(DocumentReference.TYPE_PROVIDER);
        when(mockDocumentReferenceProvider.get())
            .thenReturn(new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome"));

        mockURLFactory = mock(XWikiURLFactory.class);
        context.setURLFactory(mockURLFactory);

        action = new CreateAction();

        mockRequest = mock(XWikiRequest.class);
        context.setRequest(mockRequest);

        mockResponse = mock(XWikiResponse.class);
        context.setResponse(mockResponse);

        when(mockRequest.get("type")).thenReturn("plain");

        this.oldcore.getMocker().registerMockComponent(ObservationManager.class);
    }


   public OHttpSession[] getSessions() {
     acquireSharedLock();
     try {

       return (OHttpSession[]) sessions.values().toArray(new OHttpSession[sessions.size()]);

     } finally {
       releaseSharedLock();
     }
   }


   public int getExpirationTime() {
     return expirationTime;
   }


    public void translate(ItemStackRequestPacket packet, GeyserSession session) {
        Inventory inventory = session.getOpenInventory();
        if (inventory == null)
            return;

        InventoryTranslator translator = session.getInventoryTranslator();
        translator.translateRequests(session, inventory, packet.getRequests());
    }


  public static SSLSocketFactory getSslSocketFactory(Properties info) throws PSQLException {
    String classname = PGProperty.SSL_FACTORY.get(info);
    if (classname == null
        || "org.postgresql.ssl.jdbc4.LibPQFactory".equals(classname)
        || "org.postgresql.ssl.LibPQFactory".equals(classname)) {
      return new LibPQFactory(info);
    }
    try {
      return (SSLSocketFactory) ObjectFactory.instantiate(classname, info, true,
          PGProperty.SSL_FACTORY_ARG.get(info));
    } catch (Exception e) {
      throw new PSQLException(
          GT.tr("The SSLSocketFactory class provided {0} could not be instantiated.", classname),
          PSQLState.CONNECTION_FAILURE, e);
    }
  }