001/* 002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nicolas Chapurlat <[email protected]> 018 */ 019 020package org.nuxeo.ecm.directory.io; 021 022import static java.util.Locale.ENGLISH; 023import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; 024import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.MAX_DEPTH_PARAM; 025import static org.nuxeo.ecm.core.io.registry.reflect.Instantiations.SINGLETON; 026import static org.nuxeo.ecm.core.io.registry.reflect.Priorities.REFERENCE; 027 028import java.io.Closeable; 029import java.io.IOException; 030import java.util.Locale; 031import java.util.MissingResourceException; 032import java.util.Set; 033 034import javax.inject.Inject; 035 036import org.apache.commons.lang3.StringUtils; 037import org.apache.commons.logging.Log; 038import org.apache.commons.logging.LogFactory; 039import org.nuxeo.common.utils.i18n.I18NUtils; 040import org.nuxeo.ecm.core.api.DocumentModel; 041import org.nuxeo.ecm.core.api.model.Property; 042import org.nuxeo.ecm.core.io.marshallers.json.ExtensibleEntityJsonWriter; 043import org.nuxeo.ecm.core.io.marshallers.json.OutputStreamWithJsonWriter; 044import org.nuxeo.ecm.core.io.marshallers.json.document.DocumentPropertiesJsonReader; 045import org.nuxeo.ecm.core.io.marshallers.json.enrichers.AbstractJsonEnricher; 046import org.nuxeo.ecm.core.io.registry.Writer; 047import org.nuxeo.ecm.core.io.registry.reflect.Setup; 048import org.nuxeo.ecm.core.schema.SchemaManager; 049import org.nuxeo.ecm.core.schema.types.Field; 050import org.nuxeo.ecm.core.schema.types.QName; 051import org.nuxeo.ecm.core.schema.types.Schema; 052import org.nuxeo.ecm.directory.Session; 053import org.nuxeo.ecm.directory.api.DirectoryEntry; 054import org.nuxeo.ecm.directory.api.DirectoryService; 055 056import com.fasterxml.jackson.core.JsonGenerator; 057import com.thoughtworks.xstream.io.json.JsonWriter; 058 059/** 060 * Convert {@link DirectoryEntry} to Json. 061 * <p> 062 * This marshaller is enrichable: register class implementing {@link AbstractJsonEnricher} and managing 063 * {@link DirectoryEntry}. 064 * </p> 065 * <p> 066 * This marshaller is also extensible: extend it and simply override 067 * {@link ExtensibleEntityJsonWriter#extend(DirectoryEntry, JsonWriter)}. 068 * </p> 069 * <p> 070 * Format is: 071 * 072 * <pre> 073 * { 074 * "entity-type": "directoryEntry", 075 * "directoryName": "DIRECTORY_NAME", <- use it to update an existing document 076 * "properties": { 077 * <- entry properties depending on the directory schema (password fields are hidden) 078 * <- format is managed by {@link DocumentPropertiesJsonReader} 079 * } 080 * <-- contextParameters if there are enrichers activated 081 * <-- additional property provided by extend() method 082 * } 083 * </pre> 084 * </p> 085 * 086 * @since 7.2 087 */ 088@Setup(mode = SINGLETON, priority = REFERENCE) 089public class DirectoryEntryJsonWriter extends ExtensibleEntityJsonWriter<DirectoryEntry> { 090 091 public static final String ENTITY_TYPE = "directoryEntry"; 092 093 private static final String MESSAGES_BUNDLE = "messages"; 094 095 private static final Log log = LogFactory.getLog(DirectoryEntryJsonWriter.class); 096 097 @Inject 098 private SchemaManager schemaManager; 099 100 @Inject 101 private DirectoryService directoryService; 102 103 public DirectoryEntryJsonWriter() { 104 super(ENTITY_TYPE, DirectoryEntry.class); 105 } 106 107 @Override 108 protected void writeEntityBody(DirectoryEntry entry, JsonGenerator jg) throws IOException { 109 String directoryName = entry.getDirectoryName(); 110 DocumentModel document = entry.getDocumentModel(); 111 String schemaName = directoryService.getDirectorySchema(directoryName); 112 String passwordField = directoryService.getDirectoryPasswordField(directoryName); 113 jg.writeStringField("directoryName", directoryName); 114 jg.writeStringField("id", document.getId()); 115 Schema schema = schemaManager.getSchema(schemaName); 116 Writer<Property> propertyWriter = registry.getWriter(ctx, Property.class, APPLICATION_JSON_TYPE); 117 // for each properties, fetch it 118 jg.writeObjectFieldStart("properties"); 119 Set<String> translated = ctx.getTranslated(ENTITY_TYPE); 120 Set<String> fetched = ctx.getFetched(ENTITY_TYPE); 121 for (Field field : schema.getFields()) { 122 QName fieldName = field.getName(); 123 String key = fieldName.getLocalName(); 124 jg.writeFieldName(key); 125 if (key.equals(passwordField)) { 126 jg.writeString(""); 127 } else { 128 Property property = document.getProperty(fieldName.getPrefixedName()); 129 boolean managed = false; 130 Object value = property.getValue(); 131 if (value instanceof String && StringUtils.isNotEmpty((String) value)) { 132 String valueString = (String) value; 133 if (fetched.contains(fieldName.getLocalName())) { 134 // try to fetch a referenced entry (parent for example) 135 try (Closeable resource = ctx.wrap().with(MAX_DEPTH_PARAM, "max").open()) { 136 managed = writeFetchedValue(jg, directoryName, fieldName.getLocalName(), valueString); 137 } 138 } else if (translated.contains(fieldName.getLocalName())) { 139 // try to fetch a translated property 140 managed = writeTranslatedValue(jg, fieldName.getLocalName(), valueString); 141 } 142 } 143 if (!managed) { 144 propertyWriter.write(property, Property.class, Property.class, APPLICATION_JSON_TYPE, 145 new OutputStreamWithJsonWriter(jg)); 146 } 147 } 148 } 149 jg.writeEndObject(); 150 } 151 152 protected boolean writeFetchedValue(JsonGenerator jg, String directoryName, String fieldName, String value) 153 throws IOException { 154 try (Session session = directoryService.open(directoryName)) { 155 DocumentModel entryModel = session.getEntry(value); 156 if (entryModel != null) { 157 DirectoryEntry entry = new DirectoryEntry(directoryName, entryModel); 158 writeEntity(entry, jg); 159 return true; 160 } 161 } 162 return false; 163 } 164 165 protected boolean writeTranslatedValue(JsonGenerator jg, String fieldName, String value) throws IOException { 166 Locale locale = ctx.getLocale(); 167 String msg = getMessageString(value, new Object[0], locale); 168 if (msg == null && locale != ENGLISH) { 169 msg = getMessageString(value, new Object[0], ENGLISH); 170 } 171 if (msg != null && !msg.equals(value)) { 172 jg.writeString(msg); 173 return true; 174 } 175 return false; 176 } 177 178 public static String getMessageString(String key, Object[] params, Locale locale) { 179 try { 180 return I18NUtils.getMessageString(MESSAGES_BUNDLE, key, params, locale); 181 } catch (MissingResourceException e) { 182 log.trace("No bundle found", e); 183 return null; 184 } 185 } 186 187}