/* * Data Hub Service (DHuS) - For Space data distribution. * Copyright (C) 2016 GAEL Systems * * This file is part of DHuS software sources. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package fr.gael.dhus.olingo.v1.entity; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.CREATION_DATE; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.CURSOR; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.FORCE; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.ID; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.LABEL; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.MODIFICATION_DATE; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.PAGE_SIZE; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.REQUEST; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.SCHEDULE; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.SERVICE_LOGIN; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.SERVICE_PASSWORD; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.SERVICE_URL; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.STATUS; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.STATUS_DATE; import static fr.gael.dhus.olingo.v1.entityset.UserSynchronizerEntitySet.STATUS_MESSAGE; import fr.gael.dhus.database.object.SynchronizerConf; import fr.gael.dhus.olingo.v1.ExpectedException; import fr.gael.dhus.olingo.v1.ExpectedException.IncompleteDocException; import fr.gael.dhus.olingo.v1.ExpectedException.InvalidKeyException; import fr.gael.dhus.olingo.v1.ExpectedException.InvalidValueException; import fr.gael.dhus.service.ISynchronizerService; import fr.gael.dhus.service.exception.InvokeSynchronizerException; import fr.gael.dhus.spring.context.ApplicationContextProvider; import fr.gael.dhus.sync.SynchronizerStatus; import java.text.ParseException; import java.util.HashMap; import java.util.Map; import java.util.Objects; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.olingo.odata2.api.ep.entry.ODataEntry; import org.apache.olingo.odata2.api.exception.ODataException; /** * UserSynchronizer OData Entity. */ public final class UserSynchronizer extends AbstractEntity { /** Log. */ private static final Logger LOGGER = LogManager.getLogger(Synchronizer.class); /** Database Object. */ private final SynchronizerConf syncConf; /** Synchronizer Service, the underlying service. */ private static final ISynchronizerService SYNC_SERVICE = ApplicationContextProvider.getBean(ISynchronizerService.class); /** * Creates a new UserSynchronizer from its ID. * * @param sync_id synchronizer's ID. * @throws NullPointerException if no synchronizer has the given ID. * @throws ODataException if synchronizer #sync_id is not an instance of ODataUserSynchronizer. */ public UserSynchronizer(long sync_id) throws ODataException { this(SYNC_SERVICE.getSynchronizerConfById(sync_id)); } /** * Creates a new UserSynchronizer from a database object. * * @param sync_conf database object. * @throws ODataException if `sync_conf` is not an instance of ODataUserSynchronizer. */ public UserSynchronizer(SynchronizerConf sync_conf) throws ODataException { Objects.requireNonNull(sync_conf); if (!sync_conf.getType().equals("ODataUserSynchronizer")) { throw new InvalidKeyException(String.valueOf(sync_conf.getId()), this.getClass().getSimpleName()); } this.syncConf = sync_conf; } /** * Creates an new UserSynchronizer from the given ODataEntry. * * @param odata_entry created by a POST request on the OData interface. * @throws ODataException if the given entry is malformed. */ public UserSynchronizer(ODataEntry odata_entry) throws ODataException { Map<String, Object> props = odata_entry.getProperties(); String label = (String) props.get(LABEL); String schedule = (String) props.get(SCHEDULE); String request = (String) props.get(REQUEST); String service_url = (String) props.get(SERVICE_URL); if (schedule == null || schedule.isEmpty() || service_url == null || service_url.isEmpty()) { throw new IncompleteDocException(); } if (request != null && !request.equals("start") && !request.equals("stop")) { throw new InvalidValueException(REQUEST, request); } try { this.syncConf = SYNC_SERVICE.createSynchronizer(label, "ODataUserSynchronizer", schedule); updateFromEntry(odata_entry); } catch (ParseException e) { throw new ExpectedException(e.getMessage()); } } @Override public void updateFromEntry(ODataEntry entry) throws ODataException { Map<String, Object> props = entry.getProperties (); String schedule = (String) props.remove(SCHEDULE); String request = (String) props.remove(REQUEST); String service_url = (String) props.remove(SERVICE_URL); Long page_size = (Long) props.remove(PAGE_SIZE); Long skip = (Long) props.remove(CURSOR); Boolean force = (Boolean) props.remove (FORCE); // Nullable fields boolean has_label = props.containsKey(LABEL); boolean has_login = props.containsKey(SERVICE_LOGIN); boolean has_password = props.containsKey(SERVICE_PASSWORD); String label = (String) props.remove(LABEL); String service_login = (String) props.remove(SERVICE_LOGIN); String service_password = (String) props.remove(SERVICE_PASSWORD); for (String pname : props.keySet ()) { LOGGER.debug ("Unknown or ReadOnly property: " + pname); } if (schedule != null && !schedule.isEmpty()) { try { this.syncConf.setCronExpression(schedule); } catch (ParseException ex) { throw new ExpectedException(ex.getMessage()); } } if (request != null && !request.isEmpty()) { if (request.equals("start")) { this.syncConf.setActive(true); } else if (request.equals("stop")) { this.syncConf.setActive(false); } else { throw new InvalidValueException(REQUEST, request); } } if (service_url != null && !service_url.isEmpty()) { this.syncConf.setConfig("service_uri", service_url); } if (page_size != null) { this.syncConf.setConfig("page_size", page_size.toString()); } if (skip != null) { this.syncConf.setConfig("skip", skip.toString()); } if (force != null) { this.syncConf.setConfig("force", force.toString ()); } if (has_label) { this.syncConf.setLabel(label); } if (has_login) { updateNullableProperty("service_username", service_login); } if (has_password) { updateNullableProperty("service_password", service_password); } try { SYNC_SERVICE.saveSynchronizerConf(this.syncConf); } catch (InvokeSynchronizerException e) { throw new ODataException(e); } } @Override public Map<String, Object> toEntityResponse(String root_url) { SynchronizerStatus ss = SYNC_SERVICE.getStatus(this.syncConf); Map<String, Object> res = new HashMap<>(); res.put(ID, this.syncConf.getId()); res.put(LABEL, this.syncConf.getLabel()); res.put(SCHEDULE, this.syncConf.getCronExpression()); res.put(REQUEST, this.syncConf.getActive() ? "start" : "stop"); res.put(STATUS, ss.status.toString()); res.put(STATUS_DATE, ss.since); res.put(STATUS_MESSAGE, ss.message); res.put(CREATION_DATE, this.syncConf.getCreated()); res.put(MODIFICATION_DATE, this.syncConf.getModified()); res.put(SERVICE_URL, this.syncConf.getConfig("service_uri")); res.put(SERVICE_LOGIN, this.syncConf.getConfig("service_username")); res.put(SERVICE_PASSWORD, "***"); // Handling of default values is not done by Olingo! String skip = this.syncConf.getConfig("skip"); res.put(CURSOR, skip != null? Long.parseLong(skip): Long.valueOf(0)); String page_size = this.syncConf.getConfig("page_size"); res.put(PAGE_SIZE, page_size != null? Long.parseLong(page_size): Long.valueOf(500)); String force = this.syncConf.getConfig("force"); res.put(FORCE, force != null? Boolean.parseBoolean (force): false); return res; } @Override public Object getProperty(String prop_name) throws ODataException { SynchronizerStatus ss = SYNC_SERVICE.getStatus(this.syncConf); Object res; switch (prop_name) { case ID: res = this.syncConf.getId(); break; case LABEL: res = this.syncConf.getLabel(); break; case SCHEDULE: res = this.syncConf.getCronExpression(); break; case REQUEST: res = this.syncConf.getActive() ? "start" : "stop"; break; case STATUS: res = ss.status.toString(); break; case STATUS_DATE: res = ss.since; break; case STATUS_MESSAGE: res = ss.message; break; case CREATION_DATE: res = this.syncConf.getCreated(); break; case MODIFICATION_DATE: res = this.syncConf.getModified(); break; case SERVICE_URL: res = this.syncConf.getConfig("service_uri"); break; case SERVICE_LOGIN: res = this.syncConf.getConfig("service_username"); break; case SERVICE_PASSWORD: res = this.syncConf.getConfig("service_password"); break; // Handling of default values is not done by Olingo! case CURSOR: String skip = this.syncConf.getConfig("skip"); res = skip != null? Long.parseLong(skip): Long.valueOf(0); break; case PAGE_SIZE: String page_size = this.syncConf.getConfig("page_size"); res = page_size != null? Long.parseLong(page_size): Long.valueOf(500); break; case FORCE: String force = this.syncConf.getConfig ("force"); res = force != null ? Boolean.parseBoolean (force): false; break; default: throw new ODataException("Unknown property: "+prop_name); } return res; } /** * If `value` is null or empty, remove `key` from the configuration. * @param key of config entry to update. * @param value to set, if null its entry will be removed from the config. */ private void updateNullableProperty(final String key, final String value) { if (value == null || value.isEmpty()) { this.syncConf.removeConfig(key); } else { this.syncConf.setConfig(key, value); } } }