This is an automated email from the ASF dual-hosted git repository.
rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push:
new 5408d21ada James 1409 - Change JPARecipientRewriteTable to store
separate record per target address (#2444)
5408d21ada is described below
commit 5408d21adabe37555f1d1a5c0f8e87ebc90ea00d
Author: amichair <[email protected]>
AuthorDate: Mon Oct 14 07:45:16 2024 +0300
James 1409 - Change JPARecipientRewriteTable to store separate record per
target address (#2444)
---
.../james/modules/server/DataRoutesModules.java | 4 +-
.../rrt/file/XMLRecipientRewriteTableTest.java | 6 +
.../james/rrt/jpa/JPARecipientRewriteTable.java | 172 +++++++--------------
.../james/rrt/jpa/model/JPARecipientRewrite.java | 21 ++-
.../rrt/lib/RecipientRewriteTableContract.java | 13 ++
.../apache/james/webadmin/utils/JsonExtractor.java | 27 +++-
.../james/webadmin/dto/MappingSourceModule.java | 51 ------
.../apache/james/webadmin/dto/MappingValueDTO.java | 46 ------
.../apache/james/webadmin/dto/MappingsModule.java | 132 ++++++++++++++++
.../james/webadmin/routes/MappingRoutes.java | 63 +++++---
.../james/webadmin/routes/AliasRoutesTest.java | 5 +-
.../james/webadmin/routes/ForwardRoutesTest.java | 5 +-
.../james/webadmin/routes/GroupsRoutesTest.java | 5 +-
.../james/webadmin/routes/MappingRoutesTest.java | 51 +++++-
upgrade-instructions.md | 21 +++
15 files changed, 363 insertions(+), 259 deletions(-)
diff --git
a/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
index 48fcf5efe2..d161e326b7 100644
---
a/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
+++
b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
@@ -28,7 +28,7 @@ import org.apache.james.task.TaskExecutionDetails;
import org.apache.james.user.api.UsersRepository;
import org.apache.james.webadmin.Routes;
import org.apache.james.webadmin.dto.DTOModuleInjections;
-import org.apache.james.webadmin.dto.MappingSourceModule;
+import org.apache.james.webadmin.dto.MappingsModule;
import org.apache.james.webadmin.mdc.RequestLogger;
import org.apache.james.webadmin.routes.AddressMappingRoutes;
import org.apache.james.webadmin.routes.AliasRoutes;
@@ -75,7 +75,7 @@ public class DataRoutesModules extends AbstractModule {
routesMultibinder.addBinding().to(DeleteUserDataRoutes.class);
Multibinder<JsonTransformerModule> jsonTransformerModuleMultibinder =
Multibinder.newSetBinder(binder(), JsonTransformerModule.class);
-
jsonTransformerModuleMultibinder.addBinding().to(MappingSourceModule.class);
+ jsonTransformerModuleMultibinder.addBinding().to(MappingsModule.class);
Multibinder.newSetBinder(binder(),
RequestLogger.class).addBinding().to(UserCreationRequestLogger.class);
}
diff --git
a/server/data/data-file/src/test/java/org/apache/james/rrt/file/XMLRecipientRewriteTableTest.java
b/server/data/data-file/src/test/java/org/apache/james/rrt/file/XMLRecipientRewriteTableTest.java
index 4a206f9440..01c6aee368 100644
---
a/server/data/data-file/src/test/java/org/apache/james/rrt/file/XMLRecipientRewriteTableTest.java
+++
b/server/data/data-file/src/test/java/org/apache/james/rrt/file/XMLRecipientRewriteTableTest.java
@@ -162,6 +162,12 @@ class XMLRecipientRewriteTableTest implements
RecipientRewriteTableContract {
}
+ @Test
+ @Disabled("XMLRecipientRewriteTable is read only")
+ public void listSourcesShouldReturnWhenSourceHasMultipleMappings() {
+
+ }
+
@Test
@Disabled("XMLRecipientRewriteTable is read only")
public void listSourcesShouldReturnWhenHasForwardMapping() {
diff --git
a/server/data/data-jpa/src/main/java/org/apache/james/rrt/jpa/JPARecipientRewriteTable.java
b/server/data/data-jpa/src/main/java/org/apache/james/rrt/jpa/JPARecipientRewriteTable.java
index 0a6990cde6..9ba6b3500d 100644
---
a/server/data/data-jpa/src/main/java/org/apache/james/rrt/jpa/JPARecipientRewriteTable.java
+++
b/server/data/data-jpa/src/main/java/org/apache/james/rrt/jpa/JPARecipientRewriteTable.java
@@ -22,11 +22,10 @@ import static
org.apache.james.rrt.jpa.model.JPARecipientRewrite.DELETE_MAPPING_
import static
org.apache.james.rrt.jpa.model.JPARecipientRewrite.SELECT_ALL_MAPPINGS_QUERY;
import static
org.apache.james.rrt.jpa.model.JPARecipientRewrite.SELECT_SOURCES_BY_MAPPING_QUERY;
import static
org.apache.james.rrt.jpa.model.JPARecipientRewrite.SELECT_USER_DOMAIN_MAPPING_QUERY;
-import static
org.apache.james.rrt.jpa.model.JPARecipientRewrite.UPDATE_MAPPING_QUERY;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.inject.Inject;
@@ -73,12 +72,44 @@ public class JPARecipientRewriteTable extends
AbstractRecipientRewriteTable {
@Override
public void addMapping(MappingSource source, Mapping mapping) throws
RecipientRewriteTableException {
- Mappings map = getStoredMappings(source);
- if (!map.isEmpty()) {
- Mappings updatedMappings =
MappingsImpl.from(map).add(mapping).build();
- doUpdateMapping(source, updatedMappings.serialize());
- } else {
- doAddMapping(source, mapping.asString());
+ EntityManager entityManager =
entityManagerFactory.createEntityManager();
+ final EntityTransaction transaction = entityManager.getTransaction();
+ try {
+ JPARecipientRewrite jpaRecipientRewrite = new
JPARecipientRewrite(source.getFixedUser(), Domain.of(source.getFixedDomain()),
mapping.asString());
+ transaction.begin();
+ entityManager.merge(jpaRecipientRewrite);
+ transaction.commit();
+ } catch (PersistenceException e) {
+ LOGGER.debug("Failed to save virtual user", e);
+ if (transaction.isActive()) {
+ transaction.rollback();
+ }
+ throw new RecipientRewriteTableException("Unable to add mapping",
e);
+ } finally {
+ EntityManagerUtils.safelyClose(entityManager);
+ }
+ }
+
+ @Override
+ public void removeMapping(MappingSource source, Mapping mapping) throws
RecipientRewriteTableException {
+ EntityManager entityManager =
entityManagerFactory.createEntityManager();
+ final EntityTransaction transaction = entityManager.getTransaction();
+ try {
+ transaction.begin();
+ entityManager.createNamedQuery(DELETE_MAPPING_QUERY)
+ .setParameter("user", source.getFixedUser())
+ .setParameter("domain", source.getFixedDomain())
+ .setParameter("targetAddress", mapping.asString())
+ .executeUpdate();
+ transaction.commit();
+ } catch (PersistenceException e) {
+ LOGGER.debug("Failed to remove mapping", e);
+ if (transaction.isActive()) {
+ transaction.rollback();
+ }
+ throw new RecipientRewriteTableException("Unable to remove
mapping", e);
+ } finally {
+ EntityManagerUtils.safelyClose(entityManager);
}
}
@@ -96,18 +127,17 @@ public class JPARecipientRewriteTable extends
AbstractRecipientRewriteTable {
}
@Override
+ @SuppressWarnings("unchecked")
public Mappings getStoredMappings(MappingSource source) throws
RecipientRewriteTableException {
EntityManager entityManager =
entityManagerFactory.createEntityManager();
try {
- @SuppressWarnings("unchecked")
- List<JPARecipientRewrite> virtualUsers =
entityManager.createNamedQuery(SELECT_USER_DOMAIN_MAPPING_QUERY)
+ List<Mapping> mappings = (List<Mapping>)
entityManager.createNamedQuery(SELECT_USER_DOMAIN_MAPPING_QUERY)
.setParameter("user", source.getFixedUser())
.setParameter("domain", source.getFixedDomain())
- .getResultList();
- if (virtualUsers.size() > 0) {
- return
MappingsImpl.fromRawString(virtualUsers.get(0).getTargetAddress());
- }
- return MappingsImpl.empty();
+ .getResultStream()
+ .map(r ->
Mapping.of((((JPARecipientRewrite)r).getTargetAddress())))
+ .collect(Collectors.toList());
+ return MappingsImpl.fromMappings(mappings.stream());
} catch (PersistenceException e) {
LOGGER.debug("Failed to get user domain mappings", e);
throw new RecipientRewriteTableException("Error while retrieve
mappings", e);
@@ -117,16 +147,16 @@ public class JPARecipientRewriteTable extends
AbstractRecipientRewriteTable {
}
@Override
+ @SuppressWarnings("unchecked")
public Map<MappingSource, Mappings> getAllMappings() throws
RecipientRewriteTableException {
EntityManager entityManager =
entityManagerFactory.createEntityManager();
- Map<MappingSource, Mappings> mapping = new HashMap<>();
try {
- @SuppressWarnings("unchecked")
- List<JPARecipientRewrite> virtualUsers =
entityManager.createNamedQuery(SELECT_ALL_MAPPINGS_QUERY).getResultList();
- for (JPARecipientRewrite virtualUser : virtualUsers) {
- mapping.put(MappingSource.fromUser(virtualUser.getUser(),
virtualUser.getDomain()),
MappingsImpl.fromRawString(virtualUser.getTargetAddress()));
- }
- return mapping;
+ return (Map<MappingSource, Mappings>)
entityManager.createNamedQuery(SELECT_ALL_MAPPINGS_QUERY)
+ .getResultStream()
+ .collect(Collectors.toMap(
+ r ->
MappingSource.fromUser(((JPARecipientRewrite)r).getUser(),
((JPARecipientRewrite)r).getDomain()),
+ r ->
MappingsImpl.fromRawString(((JPARecipientRewrite)r).getTargetAddress()),
+ (m1, m2) -> MappingsImpl.from(m1).addAll(m2).build()));
} catch (PersistenceException e) {
LOGGER.debug("Failed to get all mappings", e);
throw new RecipientRewriteTableException("Error while retrieve
mappings", e);
@@ -144,9 +174,8 @@ public class JPARecipientRewriteTable extends
AbstractRecipientRewriteTable {
try {
return
entityManager.createNamedQuery(SELECT_SOURCES_BY_MAPPING_QUERY,
JPARecipientRewrite.class)
.setParameter("targetAddress", mapping.asString())
- .getResultList()
- .stream()
- .map(user -> MappingSource.fromUser(user.getUser(),
user.getDomain()));
+ .getResultStream()
+ .map(r -> MappingSource.fromUser(r.getUser(), r.getDomain()));
} catch (PersistenceException e) {
String error = "Unable to list sources by mapping";
LOGGER.debug(error, e);
@@ -155,97 +184,4 @@ public class JPARecipientRewriteTable extends
AbstractRecipientRewriteTable {
EntityManagerUtils.safelyClose(entityManager);
}
}
-
- @Override
- public void removeMapping(MappingSource source, Mapping mapping) throws
RecipientRewriteTableException {
- Mappings map = getStoredMappings(source);
- if (map.size() > 1) {
- Mappings updatedMappings = map.remove(mapping);
- doUpdateMapping(source, updatedMappings.serialize());
- } else {
- doRemoveMapping(source, mapping.asString());
- }
- }
-
- /**
- * Update the mapping for the given user and domain
- *
- * @return true if update was successfully
- */
- private boolean doUpdateMapping(MappingSource source, String mapping)
throws RecipientRewriteTableException {
- EntityManager entityManager =
entityManagerFactory.createEntityManager();
- final EntityTransaction transaction = entityManager.getTransaction();
- try {
- transaction.begin();
- int updated = entityManager
- .createNamedQuery(UPDATE_MAPPING_QUERY)
- .setParameter("targetAddress", mapping)
- .setParameter("user", source.getFixedUser())
- .setParameter("domain", source.getFixedDomain())
- .executeUpdate();
- transaction.commit();
- if (updated > 0) {
- return true;
- }
- } catch (PersistenceException e) {
- LOGGER.debug("Failed to update mapping", e);
- if (transaction.isActive()) {
- transaction.rollback();
- }
- throw new RecipientRewriteTableException("Unable to update
mapping", e);
- } finally {
- EntityManagerUtils.safelyClose(entityManager);
- }
- return false;
- }
-
- /**
- * Remove a mapping for the given user and domain
- */
- private void doRemoveMapping(MappingSource source, String mapping) throws
RecipientRewriteTableException {
- EntityManager entityManager =
entityManagerFactory.createEntityManager();
- final EntityTransaction transaction = entityManager.getTransaction();
- try {
- transaction.begin();
- entityManager.createNamedQuery(DELETE_MAPPING_QUERY)
- .setParameter("user", source.getFixedUser())
- .setParameter("domain", source.getFixedDomain())
- .setParameter("targetAddress", mapping)
- .executeUpdate();
- transaction.commit();
-
- } catch (PersistenceException e) {
- LOGGER.debug("Failed to remove mapping", e);
- if (transaction.isActive()) {
- transaction.rollback();
- }
- throw new RecipientRewriteTableException("Unable to remove
mapping", e);
-
- } finally {
- EntityManagerUtils.safelyClose(entityManager);
- }
- }
-
- /**
- * Add mapping for given user and domain
- */
- private void doAddMapping(MappingSource source, String mapping) throws
RecipientRewriteTableException {
- EntityManager entityManager =
entityManagerFactory.createEntityManager();
- final EntityTransaction transaction = entityManager.getTransaction();
- try {
- transaction.begin();
- JPARecipientRewrite jpaRecipientRewrite = new
JPARecipientRewrite(source.getFixedUser(), Domain.of(source.getFixedDomain()),
mapping);
- entityManager.persist(jpaRecipientRewrite);
- transaction.commit();
- } catch (PersistenceException e) {
- LOGGER.debug("Failed to save virtual user", e);
- if (transaction.isActive()) {
- transaction.rollback();
- }
- throw new RecipientRewriteTableException("Unable to add mapping",
e);
- } finally {
- EntityManagerUtils.safelyClose(entityManager);
- }
- }
-
}
diff --git
a/server/data/data-jpa/src/main/java/org/apache/james/rrt/jpa/model/JPARecipientRewrite.java
b/server/data/data-jpa/src/main/java/org/apache/james/rrt/jpa/model/JPARecipientRewrite.java
index ee0f5185fa..e0ee8f63a2 100644
---
a/server/data/data-jpa/src/main/java/org/apache/james/rrt/jpa/model/JPARecipientRewrite.java
+++
b/server/data/data-jpa/src/main/java/org/apache/james/rrt/jpa/model/JPARecipientRewrite.java
@@ -22,7 +22,6 @@ import static
org.apache.james.rrt.jpa.model.JPARecipientRewrite.DELETE_MAPPING_
import static
org.apache.james.rrt.jpa.model.JPARecipientRewrite.SELECT_ALL_MAPPINGS_QUERY;
import static
org.apache.james.rrt.jpa.model.JPARecipientRewrite.SELECT_SOURCES_BY_MAPPING_QUERY;
import static
org.apache.james.rrt.jpa.model.JPARecipientRewrite.SELECT_USER_DOMAIN_MAPPING_QUERY;
-import static
org.apache.james.rrt.jpa.model.JPARecipientRewrite.UPDATE_MAPPING_QUERY;
import java.io.Serializable;
@@ -30,6 +29,7 @@ import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
+import jakarta.persistence.Index;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.Table;
@@ -42,36 +42,40 @@ import com.google.common.base.Objects;
* persistence.
*/
@Entity(name = "JamesRecipientRewrite")
-@Table(name = JPARecipientRewrite.JAMES_RECIPIENT_REWRITE)
+@Table(name = JPARecipientRewrite.JAMES_RECIPIENT_REWRITE, indexes = {
+ // note: the generated primary key index also includes these 3 fields, but
in unuseful alphabetic order
+ @Index(name = "USER_NAME_DOMAIN_NAME_TARGET_ADDRESS_INDEX", columnList =
"USER_NAME,DOMAIN_NAME,TARGET_ADDRESS"),
+ @Index(name = "TARGET_ADDRESS_INDEX", columnList = "TARGET_ADDRESS")
+})
@NamedQuery(name = SELECT_USER_DOMAIN_MAPPING_QUERY, query = "SELECT rrt FROM
JamesRecipientRewrite rrt WHERE rrt.user=:user AND rrt.domain=:domain")
@NamedQuery(name = SELECT_ALL_MAPPINGS_QUERY, query = "SELECT rrt FROM
JamesRecipientRewrite rrt")
@NamedQuery(name = DELETE_MAPPING_QUERY, query = "DELETE FROM
JamesRecipientRewrite rrt WHERE rrt.user=:user AND rrt.domain=:domain AND
rrt.targetAddress=:targetAddress")
-@NamedQuery(name = UPDATE_MAPPING_QUERY, query = "UPDATE JamesRecipientRewrite
rrt SET rrt.targetAddress=:targetAddress WHERE rrt.user=:user AND
rrt.domain=:domain")
@NamedQuery(name = SELECT_SOURCES_BY_MAPPING_QUERY, query = "SELECT rrt FROM
JamesRecipientRewrite rrt WHERE rrt.targetAddress=:targetAddress")
@IdClass(JPARecipientRewrite.RecipientRewriteTableId.class)
public class JPARecipientRewrite {
public static final String SELECT_USER_DOMAIN_MAPPING_QUERY =
"selectUserDomainMapping";
public static final String SELECT_ALL_MAPPINGS_QUERY = "selectAllMappings";
public static final String DELETE_MAPPING_QUERY = "deleteMapping";
- public static final String UPDATE_MAPPING_QUERY = "updateMapping";
public static final String SELECT_SOURCES_BY_MAPPING_QUERY =
"selectSourcesByMapping";
public static final String JAMES_RECIPIENT_REWRITE =
"JAMES_RECIPIENT_REWRITE";
public static class RecipientRewriteTableId implements Serializable {
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 2L;
private String user;
private String domain;
+ private String targetAddress;
+
public RecipientRewriteTableId() {
}
@Override
public int hashCode() {
- return Objects.hashCode(user, domain);
+ return Objects.hashCode(user, domain, targetAddress);
}
@Override
@@ -83,7 +87,9 @@ public class JPARecipientRewrite {
return false;
}
final RecipientRewriteTableId other = (RecipientRewriteTableId)
obj;
- return Objects.equal(this.user, other.user) &&
Objects.equal(this.domain, other.domain);
+ return Objects.equal(this.user, other.user)
+ && Objects.equal(this.domain, other.domain)
+ && Objects.equal(this.targetAddress, other.targetAddress);
}
}
@@ -106,6 +112,7 @@ public class JPARecipientRewrite {
* The target address. column name is chosen to be compatible with the
* JDBCRecipientRewriteTableList.
*/
+ @Id
@Column(name = "TARGET_ADDRESS", nullable = false, length = 100)
private String targetAddress = "";
diff --git
a/server/data/data-library/src/test/java/org/apache/james/rrt/lib/RecipientRewriteTableContract.java
b/server/data/data-library/src/test/java/org/apache/james/rrt/lib/RecipientRewriteTableContract.java
index a2767895bf..1acc5cab38 100644
---
a/server/data/data-library/src/test/java/org/apache/james/rrt/lib/RecipientRewriteTableContract.java
+++
b/server/data/data-library/src/test/java/org/apache/james/rrt/lib/RecipientRewriteTableContract.java
@@ -411,6 +411,19 @@ public interface RecipientRewriteTableContract {
assertThat(virtualUserTable().listSources(mapping)).contains(source,
source2);
}
+ @Test
+ default void listSourcesShouldReturnWhenSourceHasMultipleMappings() throws
Exception {
+ MappingSource source = MappingSource.fromUser(USER,
Domain.of("james"));
+ Mapping mapping = Mapping.group(ADDRESS);
+ Mapping mapping2 = Mapping.group(ADDRESS_2);
+
+ virtualUserTable().addMapping(source, mapping);
+ virtualUserTable().addMapping(source, mapping2);
+
+ assertThat(virtualUserTable().listSources(mapping)).contains(source);
+ assertThat(virtualUserTable().listSources(mapping2)).contains(source);
+ }
+
@Test
default void listSourcesShouldReturnWhenHasForwardMapping() throws
Exception {
Mapping mapping = Mapping.forward("forward");
diff --git
a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
index edb71adb68..e81e0f2f95 100644
---
a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
+++
b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
@@ -22,6 +22,7 @@ package org.apache.james.webadmin.utils;
import java.io.IOException;
import java.util.List;
+import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.guava.GuavaModule;
@@ -33,23 +34,37 @@ public class JsonExtractor<RequestT> {
private final ObjectMapper objectMapper;
private final Class<RequestT> type;
+ private final TypeReference<RequestT> typeReference;
- public JsonExtractor(Class<RequestT> type, Module... modules) {
- this(type, ImmutableList.copyOf(modules));
- }
-
- public JsonExtractor(Class<RequestT> type, List<Module> modules) {
+ private JsonExtractor(Class<RequestT> type, TypeReference<RequestT>
typeReference, List<Module> modules) {
this.objectMapper = new ObjectMapper()
.registerModule(new Jdk8Module())
.registerModule(new GuavaModule())
.registerModules(modules);
this.type = type;
+ this.typeReference = typeReference;
+ }
+
+ public JsonExtractor(Class<RequestT> type, Module... modules) {
+ this(type, ImmutableList.copyOf(modules));
+ }
+
+ public JsonExtractor(Class<RequestT> type, List<Module> modules) {
+ this(type, null, modules);
+ }
+
+ public JsonExtractor(TypeReference<RequestT> typeReference, Module...
modules) {
+ this(typeReference, ImmutableList.copyOf(modules));
+ }
+
+ public JsonExtractor(TypeReference<RequestT> typeReference, List<Module>
modules) {
+ this(null, typeReference, modules);
}
public RequestT parse(String text) throws JsonExtractException {
Preconditions.checkNotNull(text);
try {
- return objectMapper.readValue(text, type);
+ return type != null ? objectMapper.readValue(text, type) :
objectMapper.readValue(text, typeReference);
} catch (IOException | IllegalArgumentException e) {
throw new JsonExtractException(e);
}
diff --git
a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/MappingSourceModule.java
b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/MappingSourceModule.java
deleted file mode 100644
index a8b41814de..0000000000
---
a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/MappingSourceModule.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one *
- * or more contributor license agreements. See the NOTICE file *
- * distributed with this work for additional information *
- * regarding copyright ownership. The ASF licenses this file *
- * to you under the Apache License, Version 2.0 (the *
- * "License"); you may not use this file except in compliance *
- * with the License. You may obtain a copy of the License at *
- * *
- * http://www.apache.org/licenses/LICENSE-2.0 *
- * *
- * Unless required by applicable law or agreed to in writing, *
- * software distributed under the License is distributed on an *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
- * KIND, either express or implied. See the License for the *
- * specific language governing permissions and limitations *
- * under the License. *
- ****************************************************************/
-
-package org.apache.james.webadmin.dto;
-
-import java.io.IOException;
-
-import org.apache.james.rrt.lib.MappingSource;
-import org.apache.james.webadmin.utils.JsonTransformerModule;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.Module;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-
-public class MappingSourceModule implements JsonTransformerModule {
-
- private final SimpleModule simpleModule;
-
- public MappingSourceModule() {
- simpleModule = new SimpleModule()
- .addSerializer(MappingSource.class, new
JsonSerializer<MappingSource>() {
- @Override
- public void serialize(MappingSource mappingSource,
JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws
IOException {
-
jsonGenerator.writeString(mappingSource.asMailAddressString());
- }
- });
- }
-
- @Override
- public Module asJacksonModule() {
- return simpleModule;
- }
-}
\ No newline at end of file
diff --git
a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/MappingValueDTO.java
b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/MappingValueDTO.java
deleted file mode 100644
index ea75c26103..0000000000
---
a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/MappingValueDTO.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one *
- * or more contributor license agreements. See the NOTICE file *
- * distributed with this work for additional information *
- * regarding copyright ownership. The ASF licenses this file *
- * to you under the Apache License, Version 2.0 (the *
- * "License"); you may not use this file except in compliance *
- * with the License. You may obtain a copy of the License at *
- * *
- * http://www.apache.org/licenses/LICENSE-2.0 *
- * *
- * Unless required by applicable law or agreed to in writing, *
- * software distributed under the License is distributed on an *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
- * KIND, either express or implied. See the License for the *
- * specific language governing permissions and limitations *
- * under the License. *
- ****************************************************************/
-
-package org.apache.james.webadmin.dto;
-
-import org.apache.james.rrt.lib.Mapping;
-
-public class MappingValueDTO {
-
- public static MappingValueDTO fromMapping(Mapping mapping) {
- return new MappingValueDTO(mapping.getType().toString(),
mapping.getMappingValue());
- }
-
- private final String type;
- private final String mapping;
-
- private MappingValueDTO(String type, String mapping) {
- this.type = type;
- this.mapping = mapping;
- }
-
- public String getType() {
- return type;
- }
-
- public String getMapping() {
- return mapping;
- }
-
-}
\ No newline at end of file
diff --git
a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/MappingsModule.java
b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/MappingsModule.java
new file mode 100644
index 0000000000..1f268543ca
--- /dev/null
+++
b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/MappingsModule.java
@@ -0,0 +1,132 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.webadmin.dto;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.james.rrt.lib.Mapping;
+import org.apache.james.rrt.lib.MappingSource;
+import org.apache.james.rrt.lib.Mappings;
+import org.apache.james.rrt.lib.MappingsImpl;
+import org.apache.james.webadmin.utils.JsonTransformerModule;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.KeyDeserializer;
+import com.fasterxml.jackson.databind.Module;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+public class MappingsModule implements JsonTransformerModule {
+
+ private static class MappingSourceSerializer extends
JsonSerializer<MappingSource> {
+ @Override
+ public void serialize(MappingSource mappingSource, JsonGenerator
jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+ jsonGenerator.writeString(mappingSource.asString());
+ }
+ }
+
+ private static class MappingSourceDeserializer extends
JsonDeserializer<MappingSource> {
+ @Override
+ public MappingSource deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
+ return MappingSource.parse(jsonParser.getText());
+ }
+ }
+
+ private static class MappingSourceKeySerializer extends
JsonSerializer<MappingSource> {
+ @Override
+ public void serialize(MappingSource mappingSource, JsonGenerator
jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+ jsonGenerator.writeFieldName(mappingSource.asString());
+ }
+ }
+
+ private static class MappingSourceKeyDeserializer extends KeyDeserializer {
+ @Override
+ public Object deserializeKey(String key, DeserializationContext
deserializationContext) throws IOException {
+ return MappingSource.parse(key);
+ }
+ }
+
+ private static class MappingSerializer extends JsonSerializer<Mapping> {
+ @Override
+ public void serialize(Mapping mapping, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
+ jsonGenerator.writeStartObject();
+ jsonGenerator.writeStringField("type",
mapping.getType().toString());
+ jsonGenerator.writeStringField("mapping",
mapping.getMappingValue());
+ jsonGenerator.writeEndObject();
+ }
+ }
+
+ private static class MappingDeserializer extends JsonDeserializer<Mapping>
{
+ @Override
+ public Mapping deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
+ JsonNode node = jsonParser.getCodec().readTree(jsonParser);
+ String type = node.get("type").asText();
+ String mapping = node.get("mapping").asText();
+ return Mapping.of(Mapping.Type.valueOf(type), mapping);
+ }
+ }
+
+ private static class MappingsSerializer extends JsonSerializer<Mappings> {
+ @Override
+ public void serialize(Mappings mappings, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
+ jsonGenerator.writeStartArray();
+ for (Mapping mapping : mappings) {
+ jsonGenerator.writeObject(mapping);
+ }
+ jsonGenerator.writeEndArray();
+ }
+ }
+
+ private static class MappingsDeserializer extends
JsonDeserializer<Mappings> {
+ @Override
+ public Mappings deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
+ ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();
+ List<Mapping> items = mapper.readValue(jsonParser, new
TypeReference<>() {});
+ return MappingsImpl.fromMappings(items.stream());
+ }
+ }
+
+ private final SimpleModule simpleModule;
+
+ public MappingsModule() {
+ simpleModule = new SimpleModule()
+ .addSerializer(MappingSource.class, new MappingSourceSerializer())
+ .addDeserializer(MappingSource.class, new
MappingSourceDeserializer())
+ .addKeySerializer(MappingSource.class, new
MappingSourceKeySerializer())
+ .addKeyDeserializer(MappingSource.class, new
MappingSourceKeyDeserializer())
+ .addSerializer(Mapping.class, new MappingSerializer())
+ .addDeserializer(Mapping.class, new MappingDeserializer())
+ .addSerializer(Mappings.class, new MappingsSerializer())
+ .addDeserializer(Mappings.class, new MappingsDeserializer());
+ }
+
+ @Override
+ public Module asJacksonModule() {
+ return simpleModule;
+ }
+}
\ No newline at end of file
diff --git
a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/MappingRoutes.java
b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/MappingRoutes.java
index d6f85b4ac3..8e33fe9018 100644
---
a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/MappingRoutes.java
+++
b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/MappingRoutes.java
@@ -19,23 +19,26 @@
package org.apache.james.webadmin.routes;
-import java.util.List;
+import java.util.Map;
import jakarta.inject.Inject;
-import org.apache.commons.lang3.tuple.Pair;
import org.apache.james.core.Username;
import org.apache.james.rrt.api.RecipientRewriteTable;
import org.apache.james.rrt.api.RecipientRewriteTableException;
+import org.apache.james.rrt.lib.Mapping;
import org.apache.james.rrt.lib.MappingSource;
+import org.apache.james.rrt.lib.Mappings;
import org.apache.james.webadmin.Routes;
-import org.apache.james.webadmin.dto.MappingValueDTO;
+import org.apache.james.webadmin.dto.MappingsModule;
import org.apache.james.webadmin.utils.ErrorResponder;
+import org.apache.james.webadmin.utils.JsonExtractException;
+import org.apache.james.webadmin.utils.JsonExtractor;
import org.apache.james.webadmin.utils.JsonTransformer;
+import org.apache.james.webadmin.utils.Responses;
import org.eclipse.jetty.http.HttpStatus;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
+import com.fasterxml.jackson.core.type.TypeReference;
import spark.Request;
import spark.Response;
@@ -48,11 +51,14 @@ public class MappingRoutes implements Routes {
static final String USER = "user";
private final JsonTransformer jsonTransformer;
+ private final JsonExtractor<Map<MappingSource, Mappings>> jsonExtractor;
private final RecipientRewriteTable recipientRewriteTable;
@Inject
MappingRoutes(JsonTransformer jsonTransformer, RecipientRewriteTable
recipientRewriteTable) {
this.jsonTransformer = jsonTransformer;
+ TypeReference<Map<MappingSource, Mappings>> typeRef = new
TypeReference<>() {};
+ this.jsonExtractor = new JsonExtractor<>(typeRef, new
MappingsModule().asJacksonModule());
this.recipientRewriteTable = recipientRewriteTable;
}
@@ -64,19 +70,13 @@ public class MappingRoutes implements Routes {
@Override
public void define(Service service) {
service.get(BASE_PATH, this::getMappings, jsonTransformer);
+ service.put(BASE_PATH, this::addMappings);
service.get(USER_MAPPING_PATH + ":" + USER, this::getUserMappings,
jsonTransformer);
}
- private ImmutableListMultimap<String, MappingValueDTO> getMappings(Request
request, Response response) {
+ private Map<MappingSource, Mappings> getMappings(Request request, Response
response) {
try {
- return recipientRewriteTable.getAllMappings()
- .entrySet()
- .stream()
- .flatMap(entry -> entry.getValue().asStream()
- .map(mapping -> Pair.of(
- entry.getKey().asString(),
- MappingValueDTO.fromMapping(mapping))))
-
.collect(ImmutableListMultimap.toImmutableListMultimap(Pair::getLeft,
Pair::getRight));
+ return recipientRewriteTable.getAllMappings();
} catch (RecipientRewriteTableException e) {
throw ErrorResponder.builder()
.statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500)
@@ -86,13 +86,34 @@ public class MappingRoutes implements Routes {
}
}
- private List<MappingValueDTO> getUserMappings(Request request, Response
response) throws RecipientRewriteTableException {
- Username username = Username.of(request.params(USER).toLowerCase());
+ public String addMappings(Request request, Response response) {
+ try {
+ Map<MappingSource, Mappings> mappings =
jsonExtractor.parse(request.body());
+ for (Map.Entry<MappingSource, Mappings> entry :
mappings.entrySet()) {
+ for (Mapping mapping : entry.getValue()) {
+ recipientRewriteTable.addMapping(entry.getKey(), mapping);
+ }
+ }
+ return Responses.returnNoContent(response);
+ } catch (JsonExtractException e) {
+ throw ErrorResponder.builder()
+ .statusCode(HttpStatus.BAD_REQUEST_400)
+ .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+ .message("error parsing mappings")
+ .cause(e)
+ .haltError();
+ } catch (RecipientRewriteTableException e) {
+ throw ErrorResponder.builder()
+ .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500)
+ .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+ .message("error adding mappings")
+ .cause(e)
+ .haltError();
+ }
+ }
- return
recipientRewriteTable.getStoredMappings(MappingSource.fromUser(username))
- .asStream()
- .map(MappingValueDTO::fromMapping)
- .collect(ImmutableList.toImmutableList());
+ private Mappings getUserMappings(Request request, Response response)
throws RecipientRewriteTableException {
+ Username username = Username.of(request.params(USER).toLowerCase());
+ return
recipientRewriteTable.getStoredMappings(MappingSource.fromUser(username));
}
}
-
diff --git
a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/AliasRoutesTest.java
b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/AliasRoutesTest.java
index 9d0d9e32ae..d1d7506c51 100644
---
a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/AliasRoutesTest.java
+++
b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/AliasRoutesTest.java
@@ -52,8 +52,9 @@ import org.apache.james.user.api.UsersRepository;
import org.apache.james.user.memory.MemoryUsersRepository;
import org.apache.james.webadmin.WebAdminServer;
import org.apache.james.webadmin.WebAdminUtils;
-import org.apache.james.webadmin.dto.MappingSourceModule;
+import org.apache.james.webadmin.dto.MappingsModule;
import org.apache.james.webadmin.utils.JsonTransformer;
+import org.apache.james.webadmin.utils.JsonTransformerModule;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -115,7 +116,7 @@ class AliasRoutesTest {
domainList.addDomain(DOMAIN);
domainList.addDomain(ALIAS_DOMAIN);
domainList.addDomain(DOMAIN_MAPPING);
- MappingSourceModule module = new MappingSourceModule();
+ JsonTransformerModule module = new MappingsModule();
memoryRecipientRewriteTable.setDomainList(domainList);
memoryRecipientRewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED);
diff --git
a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/ForwardRoutesTest.java
b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/ForwardRoutesTest.java
index 317997dfb0..be2a489de0 100644
---
a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/ForwardRoutesTest.java
+++
b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/ForwardRoutesTest.java
@@ -53,8 +53,9 @@ import org.apache.james.user.api.UsersRepository;
import org.apache.james.user.memory.MemoryUsersRepository;
import org.apache.james.webadmin.WebAdminServer;
import org.apache.james.webadmin.WebAdminUtils;
-import org.apache.james.webadmin.dto.MappingSourceModule;
+import org.apache.james.webadmin.dto.MappingsModule;
import org.apache.james.webadmin.utils.JsonTransformer;
+import org.apache.james.webadmin.utils.JsonTransformerModule;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -115,7 +116,7 @@ class ForwardRoutesTest {
domainList.addDomain(DOMAIN_MAPPING);
memoryRecipientRewriteTable.setDomainList(domainList);
memoryRecipientRewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED);
- MappingSourceModule mappingSourceModule = new
MappingSourceModule();
+ JsonTransformerModule mappingSourceModule = new MappingsModule();
usersRepository =
MemoryUsersRepository.withVirtualHosting(domainList);
diff --git
a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/GroupsRoutesTest.java
b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/GroupsRoutesTest.java
index 982c00c15d..b6f78e4bed 100644
---
a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/GroupsRoutesTest.java
+++
b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/GroupsRoutesTest.java
@@ -54,8 +54,9 @@ import org.apache.james.user.api.UsersRepositoryException;
import org.apache.james.user.memory.MemoryUsersRepository;
import org.apache.james.webadmin.WebAdminServer;
import org.apache.james.webadmin.WebAdminUtils;
-import org.apache.james.webadmin.dto.MappingSourceModule;
+import org.apache.james.webadmin.dto.MappingsModule;
import org.apache.james.webadmin.utils.JsonTransformer;
+import org.apache.james.webadmin.utils.JsonTransformerModule;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -116,7 +117,7 @@ class GroupsRoutesTest {
memoryRecipientRewriteTable.setDomainList(domainList);
memoryRecipientRewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED);
usersRepository =
MemoryUsersRepository.withVirtualHosting(domainList);
- MappingSourceModule mappingSourceModule = new
MappingSourceModule();
+ JsonTransformerModule mappingSourceModule = new MappingsModule();
UserEntityValidator validator = UserEntityValidator.aggregate(
new DefaultUserEntityValidator(usersRepository),
new
RecipientRewriteTableUserEntityValidator(memoryRecipientRewriteTable));
diff --git
a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/MappingRoutesTest.java
b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/MappingRoutesTest.java
index 7cfee64db3..046deb63d5 100644
---
a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/MappingRoutesTest.java
+++
b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/MappingRoutesTest.java
@@ -19,6 +19,7 @@
package org.apache.james.webadmin.routes;
+import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.when;
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
import static org.hamcrest.CoreMatchers.is;
@@ -38,6 +39,7 @@ import
org.apache.james.rrt.memory.MemoryRecipientRewriteTable;
import org.apache.james.user.memory.MemoryUsersRepository;
import org.apache.james.webadmin.WebAdminServer;
import org.apache.james.webadmin.WebAdminUtils;
+import org.apache.james.webadmin.dto.MappingsModule;
import org.apache.james.webadmin.utils.JsonTransformer;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.jupiter.api.AfterEach;
@@ -64,7 +66,7 @@ class MappingRoutesTest {
@BeforeEach
void setUp() throws Exception {
- JsonTransformer jsonTransformer = new JsonTransformer();
+ JsonTransformer jsonTransformer = new JsonTransformer(new
MappingsModule());
recipientRewriteTable = new MemoryRecipientRewriteTable();
DNSService dnsService = mock(DNSService.class);
MemoryDomainList domainList = new MemoryDomainList(dnsService);
@@ -478,7 +480,52 @@ class MappingRoutesTest {
);
}
- @Test
+ @Test
+ void addMappingsShouldAddMappings() throws Exception {
+ String importedJsonBody = "{" +
+ " \"[email protected]\": [" +
+ " {" +
+ " \"type\": \"Alias\"," +
+ " \"mapping\": \"[email protected]\"" +
+ " }" +
+ " ]," +
+ " \"domain.mapping.tld\": [" +
+ " {" +
+ " \"type\": \"Domain\"," +
+ " \"mapping\": \"realdomain.tld\"" +
+ " }" +
+ " ]," +
+ " \"[email protected]\": [" +
+ " {" +
+ " \"type\": \"Address\"," +
+ " \"mapping\": \"[email protected]\"" +
+ " }" +
+ " ]" +
+ "}";
+
+ given()
+ .contentType(ContentType.JSON)
+ .body(importedJsonBody)
+ .when()
+ .put()
+ .then()
+ .statusCode(HttpStatus.NO_CONTENT_204);
+
+ String jsonBody = when()
+ .get()
+ .then()
+ .contentType(ContentType.JSON)
+ .statusCode(HttpStatus.OK_200)
+ .extract()
+ .body()
+ .asString();
+
+ assertThatJson(jsonBody)
+ .isEqualTo(importedJsonBody);
+ }
+
+
+ @Test
void getUserMappingsShouldReturnNotFoundByDefault() {
when()
.get("/user/")
diff --git a/upgrade-instructions.md b/upgrade-instructions.md
index 0c163ec6df..b471144aab 100644
--- a/upgrade-instructions.md
+++ b/upgrade-instructions.md
@@ -34,6 +34,27 @@ Change list:
- [Migrate RabbitMQ classic queues to version
2](#migrate-rabbitmq-classic-queues-to-version-2)
- [JAMES-3946 White list removals](#james-3946-white-list-removals)
- [JAMES-4052 Details in quota index](#james-4052-details-in-quota-index)
+ - [JAMES-1409 Change JPARecipientRewriteTable to store separate record per
target
address](#james-1409-change-jparecipientrewritetable-to-store-separate-record-per-target-address)
+
+### JAMES-1409 Change JPARecipientRewriteTable to store separate record per
target address
+
+Date: 09/10/2024
+
+JIRA: https://issues.apache.org/jira/browse/JAMES-1409
+
+The JPARecipientRewriteTable was modified to store multiple mappings of a
single source as separate database rows,
+each with a single target address, instead of a single row with a long
semicolon-delimited multi-value target address.
+This solves both the limitation on the number of mappings (imposed by the
column maximum length), and the broken query
+by target address.
+
+For JPA users, the database schema update and data migration can be performed
as follows:
+
+- On the old James server (before the version upgrade), export all mappings
using the webadmin
+ interface (GET /mappings) and save the JSON result.
+- Shut down the server.
+- Drop the old table using any database administration tool (DROP TABLE
JAMES_RECIPIENT_REWRITE).
+- Upgrade and start the new James server. A new table will be created
automatically with the new schema.
+- Import the saved JSON mappings via the webadmin interface (PUT /mappings).
### JAMES-4052 Details in quota index and mailbox user
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]