diff --git a/ccm-cms/src/main/java/org/librecms/pagemodel/AbstractContentItemComponentJsonConverter.java b/ccm-cms/src/main/java/org/librecms/pagemodel/AbstractContentItemComponentJsonConverter.java
new file mode 100644
index 000000000..efaf54cc8
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/pagemodel/AbstractContentItemComponentJsonConverter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.pagemodel;
+
+import org.libreccm.pagemodel.AbstractComponentModelJsonConverter;
+
+import java.util.Objects;
+
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public abstract class AbstractContentItemComponentJsonConverter
+ extends AbstractComponentModelJsonConverter {
+
+ private static final String MODE = "mode";
+
+ protected void convertContentItemComponentPropertiesToJson(
+ final ContentItemComponent component,
+ final JsonObjectBuilder objectBuilder) {
+
+ Objects.requireNonNull(component);
+ Objects.requireNonNull(objectBuilder);
+
+ objectBuilder.add(MODE, component.getMode());
+ }
+
+ protected void readContentItemComponentPropertiesFromJson(
+ final JsonObject jsonObject, final ContentItemComponent component) {
+
+ Objects.requireNonNull(jsonObject);
+ Objects.requireNonNull(component);
+
+ if (!jsonObject.isNull(MODE)) {
+
+ component.setMode(jsonObject.getString(MODE));
+ }
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/pagemodel/CategorizedItemComponentJsonBuilder.java b/ccm-cms/src/main/java/org/librecms/pagemodel/CategorizedItemComponentJsonBuilder.java
new file mode 100644
index 000000000..cb0eec47b
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/pagemodel/CategorizedItemComponentJsonBuilder.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.pagemodel;
+
+import org.libreccm.pagemodel.ComponentModel;
+import org.libreccm.pagemodel.ConvertsComponentModel;
+
+import java.util.Objects;
+
+import javax.enterprise.context.RequestScoped;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@ConvertsComponentModel(componentModel = CategorizedItemComponent.class)
+public class CategorizedItemComponentJsonBuilder
+ extends AbstractContentItemComponentJsonConverter {
+
+ @Override
+ public JsonObject toJson(final ComponentModel componentModel) {
+
+ Objects.requireNonNull(componentModel);
+
+ final JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+
+ if (!(componentModel instanceof CategorizedItemComponent)) {
+ throw new IllegalArgumentException(
+ "This converter only processes CategorizedItemComponents.");
+ }
+
+ final CategorizedItemComponent component
+ = (CategorizedItemComponent) componentModel;
+
+ convertBasePropertiesToJson(component, objectBuilder);
+ convertContentItemComponentPropertiesToJson(component, objectBuilder);
+
+ return objectBuilder.build();
+ }
+
+ @Override
+ public ComponentModel fromJson(final JsonObject jsonObject) {
+
+ Objects.requireNonNull(jsonObject);
+
+ final CategorizedItemComponent component = new CategorizedItemComponent();
+
+ readBasePropertiesFromJson(jsonObject, component);
+ readContentItemComponentPropertiesFromJson(jsonObject, component);
+
+ return component;
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/pagemodel/CategoryTreeComponentJsonConverter.java b/ccm-cms/src/main/java/org/librecms/pagemodel/CategoryTreeComponentJsonConverter.java
new file mode 100644
index 000000000..c90928bf4
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/pagemodel/CategoryTreeComponentJsonConverter.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.pagemodel;
+
+import org.libreccm.pagemodel.AbstractComponentModelJsonConverter;
+import org.libreccm.pagemodel.ComponentModel;
+import org.libreccm.pagemodel.ConvertsComponentModel;
+
+import java.util.Objects;
+
+import javax.enterprise.context.RequestScoped;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@ConvertsComponentModel(componentModel = CategoryTreeComponent.class)
+public class CategoryTreeComponentJsonConverter
+ extends AbstractComponentModelJsonConverter {
+
+ private static final String SHOW_FULL_TREE = "showFullTree";
+
+ @Override
+ public JsonObject toJson(final ComponentModel componentModel) {
+
+ Objects.requireNonNull(componentModel);
+
+ if (!(componentModel instanceof CategoryTreeComponent)) {
+ throw new IllegalArgumentException(
+ "This converter only processes CategoryTreeComponents.");
+ }
+
+ final CategoryTreeComponent categoryTree
+ = (CategoryTreeComponent) componentModel;
+
+ final JsonObjectBuilder builder = Json.createObjectBuilder();
+ convertBasePropertiesToJson(categoryTree, builder);
+
+ builder.add(SHOW_FULL_TREE, categoryTree.isShowFullTree());
+
+ return builder.build();
+ }
+
+
+ @Override
+ public ComponentModel fromJson(final JsonObject jsonObject) {
+
+ Objects.requireNonNull(jsonObject);
+
+ final CategoryTreeComponent categoryTree = new CategoryTreeComponent();
+
+ readBasePropertiesFromJson(jsonObject, categoryTree);
+
+ if (!jsonObject.isNull(SHOW_FULL_TREE)) {
+ categoryTree.setShowFullTree(jsonObject.getBoolean(SHOW_FULL_TREE));
+ }
+
+ return categoryTree;
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/pagemodel/FixedContentItemComponentJsonConverter.java b/ccm-cms/src/main/java/org/librecms/pagemodel/FixedContentItemComponentJsonConverter.java
new file mode 100644
index 000000000..d1796159e
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/pagemodel/FixedContentItemComponentJsonConverter.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.pagemodel;
+
+import org.libreccm.core.UnexpectedErrorException;
+import org.libreccm.pagemodel.ComponentModel;
+import org.libreccm.pagemodel.ConvertsComponentModel;
+import org.librecms.contentsection.ContentItemRepository;
+
+import java.util.Objects;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@ConvertsComponentModel(componentModel = FixedContentItemComponent.class)
+public class FixedContentItemComponentJsonConverter
+ extends AbstractContentItemComponentJsonConverter {
+
+ private static final String CONTENT_ITEM = "contentItem";
+
+ @Inject
+ private ContentItemRepository itemRepo;
+
+ @Override
+ public JsonObject toJson(final ComponentModel componentModel) {
+
+ Objects.requireNonNull(componentModel);
+
+ if (!(componentModel instanceof FixedContentItemComponent)) {
+ throw new IllegalArgumentException(
+ "This converter only processes FixedContentItemComponents.");
+ }
+
+ final FixedContentItemComponent component
+ = (FixedContentItemComponent) componentModel;
+
+ final JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+
+ convertBasePropertiesToJson(component, objectBuilder);
+ convertContentItemComponentPropertiesToJson(component, objectBuilder);
+
+ if (component.getContentItem() != null) {
+ objectBuilder.add(CONTENT_ITEM,
+ component.getContentItem().getUuid());
+ }
+
+ return objectBuilder.build();
+ }
+
+ @Override
+ public ComponentModel fromJson(final JsonObject jsonObject) {
+
+ Objects.requireNonNull(jsonObject);
+
+ final FixedContentItemComponent component
+ = new FixedContentItemComponent();
+
+ readBasePropertiesFromJson(jsonObject, component);
+ readContentItemComponentPropertiesFromJson(jsonObject, component);
+
+ if (!jsonObject.isNull(CONTENT_ITEM)) {
+
+ final String uuid = jsonObject.getString(CONTENT_ITEM);
+
+ component
+ .setContentItem(itemRepo
+ .findByUuid(uuid)
+ .orElseThrow(() -> new UnexpectedErrorException(
+ String.format("No ContentItem with UUID \"%s\" exists.",
+ uuid))));
+
+ }
+
+ return component;
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/pagemodel/GreetingItemComponentJsonConverter.java b/ccm-cms/src/main/java/org/librecms/pagemodel/GreetingItemComponentJsonConverter.java
new file mode 100644
index 000000000..520c22e47
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/pagemodel/GreetingItemComponentJsonConverter.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.pagemodel;
+
+import org.libreccm.pagemodel.ComponentModel;
+import org.libreccm.pagemodel.ConvertsComponentModel;
+
+import java.util.Objects;
+
+import javax.enterprise.context.RequestScoped;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@ConvertsComponentModel(componentModel = GreetingItemComponent.class)
+public class GreetingItemComponentJsonConverter
+ extends AbstractContentItemComponentJsonConverter {
+
+ @Override
+ public JsonObject toJson(final ComponentModel componentModel) {
+
+ Objects.requireNonNull(componentModel);
+
+ if (!(componentModel instanceof GreetingItemComponent)) {
+ throw new IllegalArgumentException(
+ "This converter only processes GreetingItemComponents.");
+ }
+
+ final GreetingItemComponent component
+ = (GreetingItemComponent) componentModel;
+
+ final JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+
+ convertBasePropertiesToJson(component, objectBuilder);
+ convertContentItemComponentPropertiesToJson(component, objectBuilder);
+
+ return objectBuilder.build();
+ }
+
+ @Override
+ public ComponentModel fromJson(final JsonObject jsonObject) {
+
+ Objects.requireNonNull(jsonObject);
+
+ final GreetingItemComponent component = new GreetingItemComponent();
+
+ readBasePropertiesFromJson(jsonObject, component);
+ readContentItemComponentPropertiesFromJson(jsonObject, component);
+
+ return component;
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/pagemodel/ItemListComponentJsonConverter.java b/ccm-cms/src/main/java/org/librecms/pagemodel/ItemListComponentJsonConverter.java
new file mode 100644
index 000000000..b96a1fece
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/pagemodel/ItemListComponentJsonConverter.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.pagemodel;
+
+import org.libreccm.pagemodel.AbstractComponentModelJsonConverter;
+import org.libreccm.pagemodel.ComponentModel;
+import org.libreccm.pagemodel.ConvertsComponentModel;
+
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import javax.enterprise.context.RequestScoped;
+import javax.json.Json;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@ConvertsComponentModel(componentModel = ItemListComponent.class)
+public class ItemListComponentJsonConverter
+ extends AbstractComponentModelJsonConverter {
+
+ private static final String LIST_ORDER = "listOrder";
+ private static final String PAGE_SIZE = "pageSize";
+ private static final String LIMIT_TO_TYPE = "limitToType";
+ private static final String DESCENDING = "descending";
+
+ @Override
+ public JsonObject toJson(final ComponentModel componentModel) {
+
+ Objects.requireNonNull(componentModel);
+
+ if (!(componentModel instanceof ItemListComponent)) {
+ throw new IllegalArgumentException(
+ "This implementation does only handle ItemListComponents.");
+ }
+
+ final ItemListComponent itemList = (ItemListComponent) componentModel;
+ final JsonObjectBuilder builder = Json.createObjectBuilder();
+
+ convertBasePropertiesToJson(itemList, builder);
+
+ builder.add(DESCENDING, itemList.isDescending());
+ builder.add(LIMIT_TO_TYPE, itemList.getLimitToType());
+ builder.add(PAGE_SIZE, itemList.getPageSize());
+
+ final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+ if (itemList.getListOrder() != null) {
+
+ itemList
+ .getListOrder()
+ .stream()
+ .forEach(value -> arrayBuilder.add(value));
+ }
+ builder.add(LIST_ORDER, arrayBuilder.build());
+
+ return builder.build();
+ }
+
+ @Override
+ public ComponentModel fromJson(final JsonObject jsonObject) {
+
+ Objects.requireNonNull(jsonObject);
+
+ final ItemListComponent itemList = new ItemListComponent();
+ readBasePropertiesFromJson(jsonObject, itemList);
+
+ if (!jsonObject.isNull(DESCENDING)) {
+ itemList.setDescending(jsonObject.getBoolean(DESCENDING));
+ }
+
+ if (!jsonObject.isNull(LIMIT_TO_TYPE)) {
+ itemList.setLimitToType(jsonObject.getString(LIMIT_TO_TYPE));
+ }
+
+ if (!jsonObject.isNull(PAGE_SIZE)) {
+ itemList.setPageSize(jsonObject.getInt(PAGE_SIZE));
+ }
+
+ if (!jsonObject.isNull(LIST_ORDER)) {
+
+ itemList.setListOrder(
+ jsonObject
+ .getJsonArray(LIST_ORDER)
+ .stream()
+ .map(value -> value.toString())
+ .collect(Collectors.toList()));
+
+ }
+
+ return itemList;
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/AbstractComponentModelJsonConverter.java b/ccm-core/src/main/java/org/libreccm/pagemodel/AbstractComponentModelJsonConverter.java
new file mode 100644
index 000000000..d9da153ac
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/pagemodel/AbstractComponentModelJsonConverter.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.pagemodel;
+
+import java.util.Objects;
+
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+
+/**
+ * Converter for the basic properties of a {
+ *
+ * @ComponentModel}. Can be used as base for implementations.
+ *
+ * @author Jens Pelzetter
+ */
+public abstract class AbstractComponentModelJsonConverter
+ implements ComponentModelJsonConverter {
+
+ /**
+ * Converts the basic properties of a {@link ComponentModel} to JSON.
+ *
+ * @param componentModel The {@link ComponentModel}.
+ * @param objectBuilder The {@link JsonObjectBuilder} to use.
+ */
+ protected void convertBasePropertiesToJson(
+ final ComponentModel componentModel,
+ final JsonObjectBuilder objectBuilder) {
+
+ Objects.requireNonNull(componentModel);
+ Objects.requireNonNull(objectBuilder);
+
+ objectBuilder
+ .add("componentModelId",
+ Long.toString(componentModel.getComponentModelId()))
+ .add("uuid", componentModel.getUuid())
+ .add("modelUuid", componentModel.getModelUuid())
+ .add("key", componentModel.getKey())
+ .add("type", componentModel.getClass().getName());
+
+ if (componentModel.getIdAttribute() != null) {
+ objectBuilder.add("idAttribute",
+ componentModel.getIdAttribute());
+ }
+
+ if (componentModel.getClassAttribute() != null) {
+ objectBuilder.add("classAttribute",
+ componentModel.getClassAttribute());
+ }
+
+ if (componentModel.getStyleAttribute() != null) {
+ objectBuilder.add("styleAttribute",
+ componentModel.getStyleAttribute());
+ }
+ }
+
+ /**
+ * Read the basic properties of a {@link ComponentModel} from a
+ * {@link JsonObject}.
+ *
+ * @param jsonObject The {@link JsonObject}.
+ * @param componentModel The {@link ComponentModel}.
+ */
+ protected void readBasePropertiesFromJson(
+ final JsonObject jsonObject, final ComponentModel componentModel) {
+
+ Objects.requireNonNull(jsonObject);
+ Objects.requireNonNull(componentModel);
+
+ componentModel.setComponentModelId(
+ jsonObject.getInt("componentModelId"));
+ componentModel.setUuid(jsonObject.getString("uuid"));
+ componentModel.setModelUuid(jsonObject.getString("modelUuid"));
+ componentModel.setKey(jsonObject.getString("key"));
+
+ if (jsonObject.getString("idAttribute") != null) {
+ componentModel.setIdAttribute(jsonObject.getString("idAttribute"));
+ }
+
+ if (jsonObject.getString("classAttribute") != null) {
+ componentModel
+ .setClassAttribute(jsonObject.getString("classAttribute"));
+ }
+
+ if (jsonObject.getString("styleAttribute") != null) {
+ componentModel
+ .setStyleAttribute(jsonObject.getString("styleAttribute"));
+ }
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModelJsonConverter.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModelJsonConverter.java
new file mode 100644
index 000000000..78ad29ba3
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModelJsonConverter.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.pagemodel;
+
+import javax.json.JsonObject;
+
+/**
+ * Interface which the JSON converters for {@link ComponentModel}s.
+ *
+ * @author Jens Pelzetter
+ */
+public interface ComponentModelJsonConverter {
+
+ /**
+ * Convert a {@link ComponentModel} to JSON.
+ *
+ * @param componentModel The {@link ComponentModel} to convert.
+ * @return The JSON representation of the provided {@link ComponentModel}.
+ */
+ JsonObject toJson(ComponentModel componentModel);
+
+ /**
+ * Read the values of a {@link ComponentModel} from a JSON object.
+ *
+ * @param jsonObject The JSON object with the values.
+ * @return The {@link ComponentModel}.
+ */
+ ComponentModel fromJson(JsonObject jsonObject);
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentRendererManager.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentRendererManager.java
index 2bb8a76b1..88607b08c 100644
--- a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentRendererManager.java
+++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentRendererManager.java
@@ -71,7 +71,7 @@ public class ComponentRendererManager {
+ "and type \"{}\"...",
componentModelClass.getName());
- final ComponentModelTypeLiteral literal = new ComponentModelTypeLiteral(
+ final RenderComponentLiteral literal = new RenderComponentLiteral(
componentModelClass);
final Instance> instance = componentRenderers
@@ -99,7 +99,7 @@ public class ComponentRendererManager {
/**
* Annotation literal for the {@link RendersComponent} annotation.
*/
- private static class ComponentModelTypeLiteral
+ private static class RenderComponentLiteral
extends AnnotationLiteral
implements RendersComponent {
@@ -107,7 +107,7 @@ public class ComponentRendererManager {
private final Class extends ComponentModel> componentModel;
- public ComponentModelTypeLiteral(
+ public RenderComponentLiteral(
final Class extends ComponentModel> componentModel) {
this.componentModel = componentModel;
}
diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ConvertsComponentModel.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ConvertsComponentModel.java
new file mode 100644
index 000000000..be9966595
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ConvertsComponentModel.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.pagemodel;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD,
+ ElementType.METHOD,
+ ElementType.PARAMETER,
+ ElementType.TYPE})
+public @interface ConvertsComponentModel {
+
+ Class extends ComponentModel> componentModel();
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/rs/Components.java b/ccm-core/src/main/java/org/libreccm/pagemodel/rs/Components.java
index 5c464c54c..38fd3fd42 100644
--- a/ccm-core/src/main/java/org/libreccm/pagemodel/rs/Components.java
+++ b/ccm-core/src/main/java/org/libreccm/pagemodel/rs/Components.java
@@ -19,10 +19,13 @@
package org.libreccm.pagemodel.rs;
import org.libreccm.core.CoreConstants;
+import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.pagemodel.ComponentModel;
+import org.libreccm.pagemodel.ComponentModelJsonConverter;
import org.libreccm.pagemodel.ComponentModelRepository;
import org.libreccm.pagemodel.ContainerModel;
import org.libreccm.pagemodel.ContainerModelManager;
+import org.libreccm.pagemodel.ConvertsComponentModel;
import org.libreccm.pagemodel.PageModel;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.RequiresPrivilege;
@@ -34,14 +37,17 @@ import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
+import javax.enterprise.inject.Any;
+import javax.enterprise.inject.Instance;
+import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;
import javax.json.Json;
import javax.json.JsonArray;
@@ -79,6 +85,10 @@ public class Components {
@Inject
private PageModelsController controller;
+ @Inject
+ @Any
+ private Instance jsonConverters;
+
/**
* Retrieve all {@link ComponentModel} of a {@link ContainerModel}.
*
@@ -285,63 +295,74 @@ public class Components {
private JsonObject mapComponentModelToJson(
final ComponentModel componentModel) {
- Objects.requireNonNull(componentModel);
+ final Class extends ComponentModel> clazz = Objects
+ .requireNonNull(componentModel.getClass());
- final JsonObjectBuilder objectBuilder = Json
- .createObjectBuilder()
- .add("componentModelId",
- Long.toString(componentModel.getComponentModelId()))
- .add("uuid", componentModel.getUuid())
- .add("modelUuid", componentModel.getModelUuid())
- .add("key", componentModel.getKey())
- .add("type", componentModel.getClass().getName());
+ final ComponentModelJsonConverter jsonConverter
+ = findJsonConverter(clazz)
+ .orElseThrow(() -> new WebApplicationException(String.format(
+ "No JSON converter available for component model \"%s\".",
+ clazz.getName())));
+
+ return jsonConverter.toJson(componentModel);
- if (componentModel.getIdAttribute() != null) {
- objectBuilder.add("idAttribute", componentModel.getIdAttribute());
- }
-
- if (componentModel.getClassAttribute() != null) {
- objectBuilder.add("classAttribute",
- componentModel.getClassAttribute());
- }
-
- if (componentModel.getStyleAttribute() != null) {
- objectBuilder.add("styleAttribute",
- componentModel.getStyleAttribute());
- }
-
- final Class extends ComponentModel> clazz = componentModel.getClass();
- final BeanInfo beanInfo;
- try {
- beanInfo = Introspector.getBeanInfo(clazz);
- } catch (IntrospectionException ex) {
- throw new WebApplicationException(ex);
- }
-
- for (final PropertyDescriptor propertyDescriptor
- : beanInfo.getPropertyDescriptors()) {
-
- final Method readMethod = propertyDescriptor.getReadMethod();
- final Object value;
- try {
- value = readMethod.invoke(componentModel);
- } catch (IllegalAccessException
- | InvocationTargetException ex) {
- throw new WebApplicationException(ex);
- }
-
- final String valueStr;
- if (value == null) {
- valueStr = "";
- } else {
- valueStr = value.toString();
- }
-
- objectBuilder.add(propertyDescriptor.getName(), valueStr);
-
- }
-
- return objectBuilder.build();
+// Objects.requireNonNull(componentModel);
+//
+// final JsonObjectBuilder objectBuilder = Json
+// .createObjectBuilder()
+// .add("componentModelId",
+// Long.toString(componentModel.getComponentModelId()))
+// .add("uuid", componentModel.getUuid())
+// .add("modelUuid", componentModel.getModelUuid())
+// .add("key", componentModel.getKey())
+// .add("type", componentModel.getClass().getName());
+//
+// if (componentModel.getIdAttribute() != null) {
+// objectBuilder.add("idAttribute", componentModel.getIdAttribute());
+// }
+//
+// if (componentModel.getClassAttribute() != null) {
+// objectBuilder.add("classAttribute",
+// componentModel.getClassAttribute());
+// }
+//
+// if (componentModel.getStyleAttribute() != null) {
+// objectBuilder.add("styleAttribute",
+// componentModel.getStyleAttribute());
+// }
+//
+// final Class extends ComponentModel> clazz = componentModel.getClass();
+// final BeanInfo beanInfo;
+// try {
+// beanInfo = Introspector.getBeanInfo(clazz);
+// } catch (IntrospectionException ex) {
+// throw new WebApplicationException(ex);
+// }
+//
+// for (final PropertyDescriptor propertyDescriptor
+// : beanInfo.getPropertyDescriptors()) {
+//
+// final Method readMethod = propertyDescriptor.getReadMethod();
+// final Object value;
+// try {
+// value = readMethod.invoke(componentModel);
+// } catch (IllegalAccessException
+// | InvocationTargetException ex) {
+// throw new WebApplicationException(ex);
+// }
+//
+// final String valueStr;
+// if (value == null) {
+// valueStr = "";
+// } else {
+// valueStr = value.toString();
+// }
+//
+// objectBuilder.add(propertyDescriptor.getName(), valueStr);
+//
+// }
+//
+// return objectBuilder.build();
}
/**
@@ -373,9 +394,9 @@ public class Components {
try {
componentModel = clazz.getConstructor().newInstance();
} catch (IllegalAccessException
- | InstantiationException
- | InvocationTargetException
- | NoSuchMethodException ex) {
+ | InstantiationException
+ | InvocationTargetException
+ | NoSuchMethodException ex) {
throw new WebApplicationException(ex);
}
@@ -488,7 +509,7 @@ public class Components {
} else if (propertyType == Double.TYPE) {
writeMethod.invoke(
componentModel,
- Double.parseDouble(value.toString()));
+ Double.parseDouble(value.toString()));
} else if (propertyType == Float.TYPE) {
writeMethod.invoke(componentModel,
Float.parseFloat(value.toString()));
@@ -540,11 +561,57 @@ public class Components {
}
} catch (IllegalAccessException
- | InvocationTargetException ex) {
+ | InvocationTargetException ex) {
throw new WebApplicationException(ex);
}
}
}
}
+ private Optional
+ findJsonConverter(
+ final Class extends ComponentModel> componentModelClass) {
+
+ final ConvertsComponentModelLiteral literal
+ = new ConvertsComponentModelLiteral(
+ componentModelClass);
+ final Instance instance = jsonConverters
+ .select(literal);
+ if (instance.isUnsatisfied()) {
+ return Optional.empty();
+ } else if (instance.isAmbiguous()) {
+ throw new IllegalStateException(String.format(
+ "Multiple JSONConverter for \"%s\".",
+ componentModelClass.getName()));
+ } else {
+ final Iterator iterator = instance
+ .iterator();
+ @SuppressWarnings("unchecked")
+ final ComponentModelJsonConverter converter = iterator.next();
+
+ return Optional.of(converter);
+ }
+ }
+
+ private static class ConvertsComponentModelLiteral
+ extends AnnotationLiteral
+ implements ConvertsComponentModel {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Class extends ComponentModel> componentModel;
+
+ public ConvertsComponentModelLiteral(
+ final Class extends ComponentModel> componentModel) {
+
+ this.componentModel = componentModel;
+ }
+
+ @Override
+ public Class extends ComponentModel> componentModel() {
+ return componentModel;
+ }
+
+ }
+
}