package art.servers; import art.library.interop.serialization.Serialization; import art.library.model.devices.Device; import art.library.model.devices.DeviceAction; import art.library.model.devices.DeviceInformation; import art.library.model.devices.DevicePersistenceAction; import art.library.model.devices.DevicePersistenceHistorical; import art.library.model.devices.DevicePersistenceTimeless; import art.library.model.devices.DeviceRealtime; import art.library.model.devices.DeviceSymbol; import art.library.model.devices.application.Application; import art.library.model.transactions.traces.Trace; import art.library.model.transactions.traces.TracePersistance; import art.library.model.transactions.traces.TraceResult; import art.library.persistence.Persistence; import art.library.utils.resources.Resources; import art.library.utils.synchro.Mutex; import art.servers.configuration.Configuration; import java.io.File; import java.io.FileOutputStream; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.nio.file.Files; import java.rmi.ServerException; import java.sql.ResultSet; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; public abstract class Model { public String filename = null; public String deviceClassName = null; public String deviceInformationClassName = null; protected List ldevice = new ArrayList(); protected List lsymbols = new ArrayList(); protected HashMap mdevice = new HashMap(); protected long configurationSymbolsTimestamp = 0; protected Mutex mutexListDevices = new Mutex(); protected Mutex mutexListSymbols = new Mutex(); protected HashMap mmutex = new HashMap(); public Model(Configuration configuration) throws Exception { } public Model(Configuration configuration, String filename, String deviceClassName, String deviceInformationClassName) throws Exception { this.filename = filename; this.deviceClassName = deviceClassName; this.deviceInformationClassName = deviceInformationClassName; readSymbols(); configurationSymbolsTimestamp = System.currentTimeMillis(); readDevices(); } // public void clearDevices() { mutexListDevices.lockWrite(); { ldevice.clear(); mdevice.clear(); } mutexListDevices.releaseWrite(); } public void readDevices() { if (filename == null) return; mutexListDevices.lockWrite(); try { ldevice.clear(); mdevice.clear(); // Read data from database file (--restore) if (Shared.restoreDevices == true) { Class clazzDevice = Class.forName(deviceClassName); Class clazzArray = (Class)Array.newInstance(clazzDevice, 0).getClass(); List ldevice = (List)Serialization.deserialize(clazzArray, new File(art.servers.Shared.getApplicationName() + "." + filename + ".database.json")); for (Device device : ldevice) { if ((device instanceof Device) && (device.information.serverPort == Shared.configuration.listener.port) && (device.information.serverAddress.equalsIgnoreCase(Shared.configuration.listener.address))) { device.information.serverAddress = Shared.configuration.listener.address; device.information.serverPort = Shared.configuration.listener.port; device.information.serverAddressExternal = Shared.configuration.listener.addressExternal; device.information.serverPortExternal = Shared.configuration.listener.portExternal; this.ldevice.add(device); this.mdevice.put(device.getIdentifier(), device); } } } // Read data from database else if ((Shared.controllerDatabase != null) && (Shared.reloadDevices == false)) { Class clazzDevice = Class.forName(deviceClassName); List ldevice = (List)DevicePersistenceTimeless.getDevices((List)(List)Shared.controllerDatabase.timeless_getObject(DevicePersistenceTimeless.class.getName(), "type = '" + clazzDevice.getName() + "'")); for (Device device : ldevice) { try { if ((device instanceof Device) && (device.information.serverPort == Shared.configuration.listener.port) && (device.information.serverAddress.equalsIgnoreCase(Shared.configuration.listener.address))) { device.information.serverAddress = Shared.configuration.listener.address; device.information.serverPort = Shared.configuration.listener.port; device.information.serverAddressExternal = Shared.configuration.listener.addressExternal; device.information.serverPortExternal = Shared.configuration.listener.portExternal; this.ldevice.add(device); this.mdevice.put(device.getIdentifier(), device); } } catch (Exception exception) { Shared.println(device.getIdentifier(), exception.getMessage()); } } } } catch (Exception exception) { Shared.println(Shared.getMessage("Model"), exception); } try { Class clazzDevice = Class.forName(deviceClassName); Class clazzInformation = Class.forName(deviceInformationClassName); DeviceInformation[] ldeviceInformation = null; if (new File(Shared.getApplicationName() + "." + filename + ".json").exists()) { Class clazzArray = (Class)Array.newInstance(clazzInformation, 0).getClass(); ldeviceInformation = Serialization.deserialize(clazzArray, new File(Shared.getApplicationName() + "." + filename + ".json")); } for (DeviceInformation deviceInformation : ldeviceInformation) { if (mdevice.containsKey(deviceInformation.getIdentifier()) == false) { addDevice(clazzDevice, deviceInformation); } } } catch (Exception exception) { exception.printStackTrace(); Shared.println(Shared.getMessage("Model"), exception); } for (Device device : ldevice) { device.information.serverServiceName = art.servers.Shared.controllerStatus.getApplication().getDeviceInformation().serverServiceName; } // Sort Collections.sort(ldevice, new Comparator() { public int compare(Device device1, Device device2) { return Integer.valueOf(device1.information.number).compareTo(device2.information.number); } }); // Database file if (Shared.restoreDevices == false) { try { FileOutputStream fos = new FileOutputStream(new File(art.servers.Shared.getApplicationName() + "." + filename + ".database.json")); fos.write("[\n".getBytes()); for (Device device : ldevice) { fos.write(Serialization.toPrettyString(device).getBytes()); if (device != ldevice.get(ldevice.size() - 1)) fos.write(",".getBytes()); } fos.write("]\n".getBytes()); fos.close(); } catch (Exception e) { } } mutexListDevices.releaseWrite(); } protected void addDevice(Class clazzDevice, DeviceInformation information) throws Exception { Device device = mdevice.get(information.getIdentifier()); long timestamp = System.currentTimeMillis(); if (device == null) { Constructor constructor = clazzDevice.getConstructor(String.class); Object object = constructor.newInstance(new Object[]{information.getIdentifier()}); device = (Device)object; device.information = information; device.information.setCreationTimestamp(timestamp); device.information.serverAddress = Shared.configuration.listener.address; device.information.serverPort = Shared.configuration.listener.port; device.information.serverAddressExternal = Shared.configuration.listener.addressExternal; device.information.serverPortExternal = Shared.configuration.listener.portExternal; device.setLastTimestampInformationUpdate(timestamp); if (Shared.controllerDatabase != null) { Shared.controllerDatabase.timeless_updateOrAddObject(new DevicePersistenceTimeless(device)); DevicePersistenceHistorical deviceHistorical = new DevicePersistenceHistorical(device); try { Shared.controllerDatabase.historical_addOrUpdateObject(deviceHistorical); } catch (org.postgresql.util.PSQLException exception) { if ((exception.getMessage().toUpperCase().indexOf("NO PARTITION OF RELATION") > -1) || (exception.getMessage().toUpperCase().indexOf("NO SE ENCONTRÓ UNA PARTICIÓN") > -1)) { Calendar calendar1 = Calendar.getInstance(); calendar1.setTimeInMillis(device.getLastTimestampUpdate()); calendar1.set(Calendar.DATE, 1); calendar1.set(Calendar.HOUR_OF_DAY, 0); calendar1.set(Calendar.MINUTE, 0); calendar1.set(Calendar.SECOND, 0); calendar1.set(Calendar.MILLISECOND, 0); SimpleDateFormat formato1 = new SimpleDateFormat("yyyy_MM"); String tablename = "devices_" + formato1.format(calendar1.getTimeInMillis()); long startdate = calendar1.getTimeInMillis(); calendar1.add(Calendar.MONTH, 1); long enddate = calendar1.getTimeInMillis(); String query1 = "CREATE TABLE IF NOT EXISTS " + tablename + " PARTITION OF devices FOR VALUES FROM ('" + startdate + "') TO ('" + enddate + "')"; try{Shared.controllerDatabase.historical_update(query1);} catch (Exception e){}; executeAlterTableDevicesVacuum(true, tablename); // Do again the insert try { Shared.controllerDatabase.historical_addOrUpdateObject(deviceHistorical); } catch (Exception e) { Shared.println(Shared.getMessage("Model"), e); } } } } mdevice.put(device.getIdentifier(), device); ldevice.add(device); } else { if (Serialization.equals(device.information, information) == false) { device.information = information; device.setLastTimestampInformationUpdate(timestamp); Shared.controllerDatabase.timeless_updateOrAddObject(new DevicePersistenceTimeless(device)); try { Shared.controllerDatabase.historical_addOrUpdateObject(new DevicePersistenceHistorical(device)); } catch (org.postgresql.util.PSQLException exception) { if ((exception.getMessage().toUpperCase().indexOf("NO PARTITION OF RELATION") > -1) || (exception.getMessage().toUpperCase().indexOf("NO SE ENCONTRÓ UNA PARTICIÓN") > -1)) { Calendar calendar1 = Calendar.getInstance(); calendar1.setTimeInMillis(device.getLastTimestampUpdate()); calendar1.set(Calendar.DATE, 1); calendar1.set(Calendar.HOUR_OF_DAY, 0); calendar1.set(Calendar.MINUTE, 0); calendar1.set(Calendar.SECOND, 0); calendar1.set(Calendar.MILLISECOND, 0); SimpleDateFormat formato1 = new SimpleDateFormat("yyyy_MM"); String tablename = "devices_" + formato1.format(calendar1.getTimeInMillis()); long startdate = calendar1.getTimeInMillis(); calendar1.add(Calendar.MONTH, 1); long enddate = calendar1.getTimeInMillis(); String query1 = "CREATE TABLE IF NOT EXISTS " + tablename + " PARTITION OF devices FOR VALUES FROM ('" + startdate + "') TO ('" + enddate + "')"; try{Shared.controllerDatabase.historical_update(query1);} catch (Exception e){}; executeAlterTableDevicesVacuum(true, tablename); // Do again the insert try { Shared.controllerDatabase.historical_addOrUpdateObject(new DevicePersistenceHistorical(device)); } catch (Exception e) { Shared.println(Shared.getMessage("Model"), e); } } } } } } public void addDevices(DeviceInformation[] linformation) throws Exception { // Es ineficiente, pero hay que tener en cuenta que es una operación residual mutexListDevices.lockWrite(); try { for (DeviceInformation information : linformation) { addDevice(Class.forName(this.deviceClassName), information); } Collections.sort(ldevice, new Comparator() { public int compare(Device device1, Device device2) { return Integer.valueOf(device1.information.number).compareTo(device2.information.number); } }); } finally { mutexListDevices.releaseWrite(); } } public boolean existsDevice(String identifier) { mutexListDevices.lockRead(); try { return (mdevice.containsKey(identifier)); } finally { mutexListDevices.releaseRead(); } } public Device getDevice(String classname, int number) { for (Device device : ldevice) { if ((device.getClassName().equalsIgnoreCase(classname)) && (device.getDeviceInformation().number == number)) { return device; } } return null; } public Device getDevice(String identifier) throws ServerException { mutexListDevices.lockRead(); try { Device device = mdevice.get(identifier); if (device == null) { throw new ServerException("Device does not exists"); } return device; } finally { mutexListDevices.releaseRead(); } } public Device[] getDevices() { mutexListDevices.lockRead(); try { return ldevice.toArray(new Device[ldevice.size()]); } finally { mutexListDevices.releaseRead(); } } public List getDevices(String group) { mutexListDevices.lockRead(); try { List result = new ArrayList(); for (Device device : ldevice) { try { if (device.getDeviceInformation().group.equalsIgnoreCase(group)) { result.add(device); } } catch (Exception e) { } } return result; } finally { mutexListDevices.releaseRead(); } } public Device[] getDevices(String[] lgroup) { if (lgroup == null) { return new Device[0]; } if (lgroup.length == 0) { return new Device[0]; } mutexListDevices.lockRead(); try { List result = new ArrayList(); for (String group : lgroup) { result.addAll(getDevices(group)); } return result.toArray(new Device[result.size()]); } finally { mutexListDevices.releaseRead(); } } public List getDevicesFromIdentifier(String[] lidentifier) { mutexListDevices.lockRead(); try { List result = new ArrayList(); for (String identifier : lidentifier) { try { Device device = getDevice(identifier); if (device != null) { result.add(device); } } catch (Exception exception) { } } return result; } finally { mutexListDevices.releaseRead(); } } public Device[] getDevices(long timestamp) { mutexListDevices.lockRead(); try { List result = new ArrayList(); for (Device device : ldevice) { if (device.getLastTimestampUpdate() > timestamp) { result.add(device); } } return result.toArray(new Device[result.size()]); } finally { mutexListDevices.releaseRead(); } } public Device[] getDevices(String identifier, long timestamp) { if (identifier == null) return getDevices(timestamp); mutexListDevices.lockRead(); try { List result = new ArrayList(); for (Device device : ldevice) { if (device.getIdentifier().equals(identifier)) { if (device.getLastTimestampUpdate() > timestamp) { result.add(device); } } } return result.toArray(new Device[result.size()]); } finally { mutexListDevices.releaseRead(); } } public Device[] getDevices(String identifier, long timestamp1, long timestamp2) throws Exception { // Without mutex read, we are consulting historical database and creating new objects long timestamp = getLastTimestamp(identifier, timestamp1); String where = "type = '" + deviceClassName + "' AND timestamp >= " + timestamp + " AND timestamp < " + timestamp2; if (identifier != null) { if (identifier.length() > 0) { where = where + " AND identifier = '" + identifier + "'"; } } List result = (List)DevicePersistenceHistorical.getDevices((List)(List)Shared.controllerDatabase.historical_getObject(DevicePersistenceHistorical.class.getName(), where)); Collections.sort(result, new Comparator() { public int compare(Device device1, Device device2) { return Integer.valueOf(device1.information.number).compareTo(device2.information.number); } }); return result.toArray(new Device[result.size()]); } public Device[] getDevicesWhen(String identifier, long timestamp) throws Exception { if (identifier == null) return getDevicesWhen(timestamp); // Without mutex read, we are consulting historical database and creating new objects String where = "type = '" + deviceClassName + "' AND timestamp <= " + timestamp + " AND identifier = '" + identifier + "' ORDER BY timestamp DESC limit 1"; List result = (List)DevicePersistenceHistorical.getDevices((List)(List)Shared.controllerDatabase.historical_getObject(DevicePersistenceHistorical.class.getName(), where)); Collections.sort(result, new Comparator() { public int compare(Device device1, Device device2) { return Integer.valueOf(device1.information.number).compareTo(device2.information.number); } }); return result.toArray(new Device[result.size()]); } public Device[] getDevicesNext(String identifier, long timestamp) throws Exception { if (identifier == null) return getDevicesWhen(timestamp); // Without mutex read, we are consulting historical database and creating new objects String where = "type = '" + deviceClassName + "' AND timestamp > " + timestamp + " AND identifier = '" + identifier + "' ORDER BY timestamp ASC limit 1"; List result = (List)DevicePersistenceHistorical.getDevices((List)(List)Shared.controllerDatabase.historical_getObject(DevicePersistenceHistorical.class.getName(), where)); Collections.sort(result, new Comparator() { public int compare(Device device1, Device device2) { return Integer.valueOf(device1.information.number).compareTo(device2.information.number); } }); return result.toArray(new Device[result.size()]); } public Device[] getDevicesWhen(long timestamp) throws Exception { List result = new ArrayList(); for (Device device : getDevices()) result.addAll(Arrays.asList(getDevicesWhen(device.getIdentifier(), timestamp))); Collections.sort(result, new Comparator() { public int compare(Device device1, Device device2) { return Integer.valueOf(device1.information.number).compareTo(device2.information.number); } }); return result.toArray(new Device[result.size()]); } /** * Get device from external accesses, not from this server memory devices allocation. */ public Device getDeviceExternal(String identifier) throws Exception { mutexListDevices.lockRead(); // TODO: Timelessaccess for the moment. In the future we must include direct access to servers using Services pollsters try { String where = "identifier = '" + identifier + "'"; List result = (List)DevicePersistenceTimeless.getDevices((List)(List)Shared.controllerDatabase.timeless_getObject(DevicePersistenceTimeless.class.getName(), where)); return result.get(0); } finally { mutexListDevices.releaseRead(); } } /** * Get devices from external accesses, not from this server memory devices allocation. */ public Device[] getDevicesExternal(String type) throws Exception { mutexListDevices.lockRead(); try { String where = "type = '" + type + "'"; List result = (List)DevicePersistenceTimeless.getDevices((List)(List)Shared.controllerDatabase.timeless_getObject(DevicePersistenceTimeless.class.getName(), where)); Collections.sort(result, new Comparator() { public int compare(Device device1, Device device2) { return Integer.valueOf(device1.information.number).compareTo(device2.information.number); } }); return result.toArray(new Device[result.size()]); } finally { mutexListDevices.releaseRead(); } } public boolean updateDevice(Device currentdevice, Device newdevice) { getMutex(currentdevice).lockWrite(); boolean different = false; try { newdevice.getDeviceInformation().serverAddress = Shared.configuration.listener.address; newdevice.getDeviceInformation().serverPort = Shared.configuration.listener.port; newdevice.getDeviceInformation().serverAddressExternal = Shared.configuration.listener.addressExternal; newdevice.getDeviceInformation().serverPortExternal = Shared.configuration.listener.portExternal; newdevice.getDeviceInformation().serverServiceName = Shared.controllerStatus.getApplication().getDeviceInformation().serverServiceName; different = currentdevice.updateDevice(newdevice); if ((different == true) && (Shared.configuration.database != null)) { Shared.controllerDatabase.timeless_updateOrAddObject(new DevicePersistenceTimeless(currentdevice)); } } catch (Exception e) { } finally { getMutex(currentdevice).releaseWrite(); } // newdevice sabemos que no cambiara porque nadie mas tiene la referencia try { if ((different == true) && (Shared.configuration.database != null)) { Shared.controllerDatabase.historical_addObject(new DevicePersistenceHistorical(newdevice)); } } catch (org.postgresql.util.PSQLException exception) { if ((exception.getMessage().toUpperCase().indexOf("NO PARTITION OF RELATION") > -1) || (exception.getMessage().toUpperCase().indexOf("NO SE ENCONTRÓ UNA PARTICIÓN") > -1)) { Calendar calendar1 = Calendar.getInstance(); calendar1.setTimeInMillis(newdevice.getLastTimestampUpdate()); calendar1.set(Calendar.DATE, 1); calendar1.set(Calendar.HOUR_OF_DAY, 0); calendar1.set(Calendar.MINUTE, 0); calendar1.set(Calendar.SECOND, 0); calendar1.set(Calendar.MILLISECOND, 0); SimpleDateFormat formato1 = new SimpleDateFormat("yyyy_MM"); String tablename = "devices_" + formato1.format(calendar1.getTimeInMillis()); long startdate = calendar1.getTimeInMillis(); calendar1.add(Calendar.MONTH, 1); long enddate = calendar1.getTimeInMillis(); String query1 = "CREATE TABLE IF NOT EXISTS " + tablename + " PARTITION OF devices FOR VALUES FROM ('" + startdate + "') TO ('" + enddate + "')"; try{Shared.controllerDatabase.historical_update(query1);} catch (Exception e){}; executeAlterTableDevicesVacuum(true, tablename); // Do again the insert try { Shared.controllerDatabase.historical_addObject(new DevicePersistenceHistorical(newdevice)); } catch (Exception e) { Shared.println(Shared.getMessage("Model"), e); } } } catch (Exception e) { } return different; } public void updateDevice(Device device) { updateDevice(device, System.currentTimeMillis()); } public void updateDevice(Device device, long timestamp) { getMutex(device).lockWrite(); try { Device currentDevice = (Device)mdevice.get(device.getIdentifier()); if (currentDevice == null) { device.getDeviceInformation().setCreationTimestamp(timestamp); device.setLastTimestampInformationUpdate(timestamp); if (device.getDeviceConfiguration() != null) { device.setLastTimestampConfigurationUpdate(timestamp); } device.information.serverAddress = Shared.configuration.listener.address; device.information.serverPort = Shared.configuration.listener.port; if (Shared.configuration.database != null) { Shared.controllerDatabase.timeless_updateOrAddObject(new DevicePersistenceTimeless(device)); try { Shared.controllerDatabase.historical_updateOrAddObject(new DevicePersistenceHistorical(device)); } catch (org.postgresql.util.PSQLException exception) { if ((exception.getMessage().toUpperCase().indexOf("NO PARTITION OF RELATION") > -1) || (exception.getMessage().toUpperCase().indexOf("NO SE ENCONTRÓ UNA PARTICIÓN") > -1)) { Calendar calendar1 = Calendar.getInstance(); calendar1.setTimeInMillis(device.getLastTimestampUpdate()); calendar1.set(Calendar.DATE, 1); calendar1.set(Calendar.HOUR_OF_DAY, 0); calendar1.set(Calendar.MINUTE, 0); calendar1.set(Calendar.SECOND, 0); calendar1.set(Calendar.MILLISECOND, 0); SimpleDateFormat formato1 = new SimpleDateFormat("yyyy_MM"); String tablename = "devices_" + formato1.format(calendar1.getTimeInMillis()); long startdate = calendar1.getTimeInMillis(); calendar1.add(Calendar.MONTH, 1); long enddate = calendar1.getTimeInMillis(); String query1 = "CREATE TABLE IF NOT EXISTS " + tablename + " PARTITION OF devices FOR VALUES FROM ('" + startdate + "') TO ('" + enddate + "')"; try{Shared.controllerDatabase.historical_update(query1);} catch (Exception e){}; executeAlterTableDevicesVacuum(true, tablename); // Do again the insert try { Shared.controllerDatabase.historical_updateOrAddObject(new DevicePersistenceHistorical(device)); } catch (Exception e) { Shared.println(Shared.getMessage("Model"), e); } } } } mdevice.put(device.getIdentifier(), device); ldevice.add(device); } else { if (currentDevice.updateDevice(device) == true) { if (Shared.configuration.database != null) { Shared.controllerDatabase.timeless_updateOrAddObject(new DevicePersistenceTimeless(device)); try { Shared.controllerDatabase.historical_updateOrAddObject(new DevicePersistenceHistorical(device)); } catch (org.postgresql.util.PSQLException exception) { if ((exception.getMessage().toUpperCase().indexOf("NO PARTITION OF RELATION") > -1) || (exception.getMessage().toUpperCase().indexOf("NO SE ENCONTRÓ UNA PARTICIÓN") > -1)) { Calendar calendar1 = Calendar.getInstance(); calendar1.setTimeInMillis(device.getLastTimestampUpdate()); calendar1.set(Calendar.DATE, 1); calendar1.set(Calendar.HOUR_OF_DAY, 0); calendar1.set(Calendar.MINUTE, 0); calendar1.set(Calendar.SECOND, 0); calendar1.set(Calendar.MILLISECOND, 0); SimpleDateFormat formato1 = new SimpleDateFormat("yyyy_MM"); String tablename = "devices_" + formato1.format(calendar1.getTimeInMillis()); long startdate = calendar1.getTimeInMillis(); calendar1.add(Calendar.MONTH, 1); long enddate = calendar1.getTimeInMillis(); String query1 = "CREATE TABLE IF NOT EXISTS " + tablename + " PARTITION OF devices FOR VALUES FROM ('" + startdate + "') TO ('" + enddate + "')"; try{Shared.controllerDatabase.historical_update(query1);} catch (Exception e){}; executeAlterTableDevicesVacuum(true, tablename); // Do again the insert try { Shared.controllerDatabase.historical_updateOrAddObject(new DevicePersistenceHistorical(device)); } catch (Exception e) { Shared.println(Shared.getMessage("Model"), e); } } } } } } } catch (Exception e) { } finally { getMutex(device).releaseWrite(); } } public void updateDeviceStatus(Device device, long timestamp) { try { if (device.getLastTimestampStatusUpdate() >= timestamp) { device.setLastTimestampStatusUpdate(device.getLastTimestampStatusUpdate() + 1); } else { device.setLastTimestampStatusUpdate(timestamp); } if (Shared.configuration.database != null) { try { Shared.controllerDatabase.historical_updateOrAddObject(new DevicePersistenceHistorical(device)); } catch (org.postgresql.util.PSQLException exception) { if ((exception.getMessage().toUpperCase().indexOf("NO PARTITION OF RELATION") > -1) || (exception.getMessage().toUpperCase().indexOf("NO SE ENCONTRÓ UNA PARTICIÓN") > -1)) { Calendar calendar1 = Calendar.getInstance(); calendar1.setTimeInMillis(device.getLastTimestampUpdate()); calendar1.set(Calendar.DATE, 1); calendar1.set(Calendar.HOUR_OF_DAY, 0); calendar1.set(Calendar.MINUTE, 0); calendar1.set(Calendar.SECOND, 0); calendar1.set(Calendar.MILLISECOND, 0); SimpleDateFormat formato1 = new SimpleDateFormat("yyyy_MM"); String tablename = "devices_" + formato1.format(calendar1.getTimeInMillis()); long startdate = calendar1.getTimeInMillis(); calendar1.add(Calendar.MONTH, 1); long enddate = calendar1.getTimeInMillis(); String query1 = "CREATE TABLE IF NOT EXISTS " + tablename + " PARTITION OF devices FOR VALUES FROM ('" + startdate + "') TO ('" + enddate + "')"; try{Shared.controllerDatabase.historical_update(query1);} catch (Exception e){}; executeAlterTableDevicesVacuum(true, tablename); // Do again the insert try { Shared.controllerDatabase.historical_updateOrAddObject(new DevicePersistenceHistorical(device)); } catch (Exception e) { } } } } } catch (Exception e) { } } public void updateDevice(Device[] ldevice) { mutexListDevices.lockWrite(); try { for (Device device : ldevice) { updateDevice(device); } } catch (Exception e) { } mutexListDevices.releaseWrite(); } public void deleteDevices(String[] lidentifier) throws Exception { int errors = 0; for (String identifier : lidentifier) { getMutex(identifier).lockWrite(); try { String where = "identifier = '" + identifier + "' AND type = '" + deviceClassName + "'"; Shared.controllerDatabase.timeless_deleteObject(DevicePersistenceTimeless.class.getName(), where); mutexListDevices.lockWrite(); { ldevice.remove(mdevice.remove(identifier)); } mutexListDevices.releaseWrite(); } catch (Exception e) { errors = errors + 1; } finally { getMutex(identifier).releaseWrite(); } } if (errors > 0) { throw new ServerException(Shared.getMessage("Error deleting devices")); } } public Device cloneDevice(String identifier) { // No hace falta mutex porque los devices se cambia la referencia cada vez. No hay conflicto posible Device clon = null; try { clon = (Device)Serialization.clone(getDevice(identifier)); } catch (Exception e) { } try { clon.realtime = (DeviceRealtime)Serialization.clone(getDevice(identifier).getDeviceRealtime()); } catch (Exception e) { } return clon; } public Device cloneDevice(Device device) { try { return cloneDevice(device.getIdentifier()); } catch (Exception e) { } return null; } public List getGroups() { mutexListDevices.lockRead(); try { Set groups = new HashSet<>(); for (Device device : ldevice) { try { if (device.getDeviceInformation().group != null) { groups.add(device.getDeviceInformation().group); } } catch (Exception e) { } } List result = new ArrayList(groups); result.sort(String::compareToIgnoreCase); return result; } finally { mutexListDevices.releaseRead(); } } // // public List getTraceValues(String field, String application) throws Exception { ResultSet resultset = null; Persistence persistence = Shared.controllerDatabase.getHistoricalPersistance().get(0); List result = new ArrayList(); try { { String command = "SELECT DISTINCT(" + field + ") FROM traces"; if (application != null) command = "SELECT DISTINCT(" + field + ") FROM traces WHERE application = '" + application + "'"; resultset = persistence.executeQuery(command); while (resultset.next()) { result.add(resultset.getString(1)); } resultset.getStatement().close(); resultset.close(); } } catch (Exception exception) { throw exception; } finally { try{resultset.getStatement().close();} catch (Exception e){}; try{resultset.close();} catch (Exception e){}; } return result; } public List getTraces(String language, long timestampfrom, long timestampto, String application, int limit) throws Exception { ResultSet resultset = null; Persistence persistence = Shared.controllerDatabase.getHistoricalPersistance().get(0); List traces = new ArrayList(); try { String command = "SELECT value FROM traces WHERE timestamp >= " + timestampfrom + " AND timestamp < " + timestampto; if (application != null) command = command + " AND application = '" + application + "'"; command = command + " ORDER BY timestamp ASC"; if (limit > 0) command = command + " LIMIT " + limit; resultset = persistence.executeQuery(command); while (resultset.next()) { traces.add(((TracePersistance)Serialization.deserialize(TracePersistance.class, resultset.getString(1))).trace); } return traces; } finally { try{resultset.getStatement().close();} catch (Exception e){}; try{resultset.close();} catch (Exception e){}; } } public TraceResult getTraces(TraceResult traceresult) throws Exception { ResultSet resultset = null; Persistence persistence = Shared.controllerDatabase.getHistoricalPersistance().get(0); try { { String command = "SELECT COUNT(*) FROM traces WHERE timestamp >= " + traceresult.timestampfrom + " AND timestamp <= " + traceresult.timestampto; if (traceresult.type != Trace.TRACE_NONE) command = command + " AND type = '" + traceresult.type + "'"; if (traceresult.username != null) command = command + " AND username = '" + traceresult.username + "'"; if (traceresult.application != null) command = command + " AND application = '" + traceresult.application + "'"; if (traceresult.service != null) command = command + " AND service = '" + traceresult.service + "'"; resultset = persistence.executeQuery(command); resultset.next(); traceresult.total = resultset.getLong(1); resultset.getStatement().close(); resultset.close(); } { String command = "SELECT value FROM traces WHERE timestamp >= " + traceresult.timestampfrom + " AND timestamp <= " + traceresult.timestampto; if (traceresult.type != Trace.TRACE_NONE) command = command + " AND type = '" + traceresult.type + "'"; if (traceresult.username != null) command = command + " AND username = '" + traceresult.username + "'"; if (traceresult.application != null) command = command + " AND application = '" + traceresult.application + "'"; if (traceresult.service != null) command = command + " AND service = '" + traceresult.service + "'"; command = command + " ORDER BY timestamp ASC OFFSET " + traceresult.offset + " LIMIT " + traceresult.limit; resultset = persistence.executeQuery(command); while (resultset.next()) { traceresult.traces.add(((TracePersistance)Serialization.deserialize(TracePersistance.class, resultset.getString(1))).trace); } resultset.getStatement().close(); resultset.close(); } } finally { try{resultset.getStatement().close();} catch (Exception e){}; try{resultset.close();} catch (Exception e){}; } return traceresult; } public List getLastTraces(String language, String application, int limit) throws Exception { ResultSet resultset = null; Persistence persistence = Shared.controllerDatabase.getHistoricalPersistance().get(0); List traces = new ArrayList(); try { String command = "SELECT value FROM traces "; if (application != null) command = command + " WHERE application = '" + application + "'"; command = command + " ORDER BY timestamp DESC"; if (limit > 0) command = command + " LIMIT " + limit; resultset = persistence.executeQuery(command); while (resultset.next()) { traces.add(((TracePersistance)Serialization.deserialize(TracePersistance.class, resultset.getString(1))).trace); } return traces; } finally { try{resultset.getStatement().close();} catch (Exception e){}; try{resultset.close();} catch (Exception e){}; } } // // public void readSymbols() throws Exception { mutexListSymbols.lockWrite(); try { for (int i=0; i<32; i++) { for (String symbolName : Shared.configuration.symbols) { DeviceSymbol symbol = new DeviceSymbol(); symbol.type = deviceClassName; symbol.name = symbolName; symbol.zoom = i; String resourceName = "data/" + Shared.getApplicationName() + "/symbols/" + i + "/" + symbolName + ".svg"; File file = new File(resourceName); if (file.exists() == true) { symbol.dataSVG = Files.readAllBytes(file.toPath()); } else { symbol.dataSVG = Resources.getResourceBytes(resourceName); } if (symbol.dataSVG.length > 0) { lsymbols.add(symbol); } } } } catch (Exception exception) { Shared.println(Shared.getMessage("Model"), exception); } finally { mutexListSymbols.releaseWrite(); } } public DeviceSymbol[] getSymbols() { mutexListSymbols.lockRead(); try { return lsymbols.toArray(new DeviceSymbol[lsymbols.size()]); } finally { mutexListSymbols.releaseRead(); } } public DeviceSymbol[] getSymbols(long timestamp) { if (configurationSymbolsTimestamp > timestamp) { return getSymbols(); } else { return new DeviceSymbol[0]; } } public List getSymbolsNames(String type) { List result = new ArrayList (); mutexListSymbols.lockRead(); try { result.add(""); for (DeviceSymbol symbol : lsymbols) { if (symbol.type.equalsIgnoreCase(type)) { result.add(symbol.name); } } } finally { mutexListSymbols.releaseRead(); } return result; } // // public Application getApplicationService(String serviceName) { try { Device[] devices = getDevicesExternal(Application.class.getName()); for (Device device : devices) { if (device instanceof Application) { Application application = (Application)device; if (application.getDeviceInformation().serverServiceName.equalsIgnoreCase(serviceName) == true) { return(application); } } } } catch (Exception exception) { } return(null); } public Application[] getApplication(long timestamp) throws Exception { Application application = Shared.controllerStatus.getApplication(); if (application.getLastTimestampUpdate() > timestamp) { return new Application[]{application}; } return new Application[0]; } public Application[] getApplication(long timestamp1, long timestamp2) throws Exception { // Without mutex read, we are consulting historical database String identifier = Shared.controllerStatus.getApplication().getIdentifier(); String where = "type = '" + deviceClassName + "' AND timestamp > " + timestamp1 + " AND timestamp < " + timestamp2; if (identifier != null) { if (identifier.length() > 0) { where = where + " AND identifier = '" + identifier + "'"; } } List result = (List)DevicePersistenceHistorical.getDevices((List)(List)Shared.controllerDatabase.historical_getObject(DevicePersistenceHistorical.class.getName(), where)); return result.toArray(new Application[result.size()]); } // A esta función sólo llama el servidor public void updateApplication(Application application) { // No metemos en exclusion, no podemos tener varias llamadas a la vez // Se controla en el ControllerStatus if (Shared.configuration.database != null) { try { Shared.controllerDatabase.timeless_updateOrAddObject(new DevicePersistenceTimeless(application)); } catch (Exception e) { } try { Shared.controllerDatabase.historical_updateOrAddObject(new DevicePersistenceHistorical(application)); } catch (org.postgresql.util.PSQLException exception) { if ((exception.getMessage().toUpperCase().indexOf("NO PARTITION OF RELATION") > -1) || (exception.getMessage().toUpperCase().indexOf("NO SE ENCONTRÓ UNA PARTICIÓN") > -1)) { Calendar calendar1 = Calendar.getInstance(); calendar1.setTimeInMillis(application.getLastTimestampUpdate()); calendar1.set(Calendar.DATE, 1); calendar1.set(Calendar.HOUR_OF_DAY, 0); calendar1.set(Calendar.MINUTE, 0); calendar1.set(Calendar.SECOND, 0); calendar1.set(Calendar.MILLISECOND, 0); SimpleDateFormat formato1 = new SimpleDateFormat("yyyy_MM"); String tablename = "devices_" + formato1.format(calendar1.getTimeInMillis()); long startdate = calendar1.getTimeInMillis(); calendar1.add(Calendar.MONTH, 1); long enddate = calendar1.getTimeInMillis(); String query1 = "CREATE TABLE IF NOT EXISTS " + tablename + " PARTITION OF devices FOR VALUES FROM ('" + startdate + "') TO ('" + enddate + "')"; try{Shared.controllerDatabase.historical_update(query1);} catch (Exception e){}; executeAlterTableDevicesVacuum(true, tablename); // Do again the insert try { Shared.controllerDatabase.historical_updateOrAddObject(new DevicePersistenceHistorical(application)); } catch (Exception e) { Shared.println(Shared.getMessage("Model"), e); } } } catch (Exception e) { } } } // // public void addAction(DeviceAction action) { if (Shared.configuration.database != null) { // Insert action try { DevicePersistenceAction persistenceAction = new DevicePersistenceAction(action); try { Shared.controllerDatabase.historical_addObject(persistenceAction); } catch (org.postgresql.util.PSQLException exception) { if ((exception.getMessage().toUpperCase().indexOf("NO PARTITION OF RELATION") > -1) || (exception.getMessage().toUpperCase().indexOf("NO SE ENCONTRÓ UNA PARTICIÓN") > -1)) { Calendar calendar1 = Calendar.getInstance(); calendar1.setTimeInMillis(action.requestTimestamp); calendar1.set(Calendar.DATE, 1); calendar1.set(Calendar.HOUR_OF_DAY, 0); calendar1.set(Calendar.MINUTE, 0); calendar1.set(Calendar.SECOND, 0); calendar1.set(Calendar.MILLISECOND, 0); SimpleDateFormat formato1 = new SimpleDateFormat("yyyy_MM"); String tablename = "actions_" + formato1.format(calendar1.getTimeInMillis()); long startdate = calendar1.getTimeInMillis(); calendar1.add(Calendar.MONTH, 1); long enddate = calendar1.getTimeInMillis(); String query1 = "CREATE TABLE IF NOT EXISTS " + tablename + " PARTITION OF actions FOR VALUES FROM ('" + startdate + "') TO ('" + enddate + "')"; try{Shared.controllerDatabase.historical_update(query1);} catch (Exception e){}; executeAlterTableActionsVacuum(true, tablename); // Do again the insert try { Shared.controllerDatabase.historical_addObject(persistenceAction); } catch (Exception e) { Shared.println(Shared.getMessage("Model"), e); } } } catch (Exception e) { } } catch (Exception e) { } } } public List getActions() { return getActions(null); } public List getActions(long timestamp1, long timestamp2) { String where = "timestamp > " + timestamp1 + " AND timestamp < " + timestamp2; return getActions(where); } public List getActions(long timestamp1, long timestamp2, String where) { return getActions("timestamp > " + timestamp1 + " AND timestamp < " + timestamp2 + " AND " + where); } public List getActions(String where) { List result = null; try { if (Shared.configuration.database != null) { if (where == null) { result = (List)DevicePersistenceAction.getActions((List)(List)Shared.controllerDatabase.historical_getObject(DevicePersistenceAction.class.getName())); } else { result = (List)DevicePersistenceAction.getActions((List)(List)Shared.controllerDatabase.historical_getObject(DevicePersistenceAction.class.getName(), where)); } } } catch (Exception e) { } return result; } // public void executeAlterTableDevicesVacuum(boolean vacuum, String tablename) { try { String enabled = "true"; if (vacuum == false) enabled = "false"; String autovacuum_enabled = art.servers.Shared.getPropertyValueString("devices.autovacuum_enabled"); String autovacuum_vacuum_scale_factor = art.servers.Shared.getPropertyValueString("devices.autovacuum_vacuum_scale_factor"); String autovacuum_vacuum_threshold = art.servers.Shared.getPropertyValueString("devices.autovacuum_vacuum_threshold"); String autovacuum_analyze_scale_factor = art.servers.Shared.getPropertyValueString("devices.autovacuum_analyze_scale_factor"); String autovacuum_analyze_threshold = art.servers.Shared.getPropertyValueString("devices.autovacuum_analyze_threshold"); if (autovacuum_enabled == null) autovacuum_enabled = enabled; if (autovacuum_vacuum_scale_factor == null) autovacuum_vacuum_scale_factor = "0.0"; if (autovacuum_vacuum_threshold == null) autovacuum_vacuum_threshold = "1000"; if (autovacuum_analyze_scale_factor == null) autovacuum_analyze_scale_factor = "0.0"; if (autovacuum_analyze_threshold == null) autovacuum_analyze_threshold = "1000"; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_enabled = " + enabled + ", toast.autovacuum_enabled = " + enabled + ")");} catch (Exception e){}; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_vacuum_scale_factor = 0.0)");} catch (Exception e){}; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_vacuum_threshold = 1000)");} catch (Exception e){}; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_analyze_scale_factor = 0.0)");} catch (Exception e){}; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_analyze_threshold = 1000)");} catch (Exception e){}; } catch (Exception exception) { } } public void executeAlterTableActionsVacuum(boolean vacuum, String tablename) { try { String enabled = "true"; if (vacuum == false) enabled = "false"; String autovacuum_enabled = art.servers.Shared.getPropertyValueString("actions.autovacuum_enabled"); String autovacuum_vacuum_scale_factor = art.servers.Shared.getPropertyValueString("actions.autovacuum_vacuum_scale_factor"); String autovacuum_vacuum_threshold = art.servers.Shared.getPropertyValueString("actions.autovacuum_vacuum_threshold"); String autovacuum_analyze_scale_factor = art.servers.Shared.getPropertyValueString("actions.autovacuum_analyze_scale_factor"); String autovacuum_analyze_threshold = art.servers.Shared.getPropertyValueString("actions.autovacuum_analyze_threshold"); if (autovacuum_enabled == null) autovacuum_enabled = enabled; if (autovacuum_vacuum_scale_factor == null) autovacuum_vacuum_scale_factor = "0.0"; if (autovacuum_vacuum_threshold == null) autovacuum_vacuum_threshold = "1000"; if (autovacuum_analyze_scale_factor == null) autovacuum_analyze_scale_factor = "0.0"; if (autovacuum_analyze_threshold == null) autovacuum_analyze_threshold = "1000"; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_enabled = " + enabled + ", toast.autovacuum_enabled = " + enabled + ")");} catch (Exception e){}; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_vacuum_scale_factor = 0.0)");} catch (Exception e){}; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_vacuum_threshold = 1000)");} catch (Exception e){}; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_analyze_scale_factor = 0.0)");} catch (Exception e){}; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_analyze_threshold = 1000)");} catch (Exception e){}; } catch (Exception exception) { } } public void executeAlterTableTracesVacuum(boolean vacuum, String tablename) { try { String enabled = "true"; if (vacuum == false) enabled = "false"; String autovacuum_enabled = art.servers.Shared.getPropertyValueString("traces.autovacuum_enabled"); String autovacuum_vacuum_scale_factor = art.servers.Shared.getPropertyValueString("traces.autovacuum_vacuum_scale_factor"); String autovacuum_vacuum_threshold = art.servers.Shared.getPropertyValueString("traces.autovacuum_vacuum_threshold"); String autovacuum_analyze_scale_factor = art.servers.Shared.getPropertyValueString("traces.autovacuum_analyze_scale_factor"); String autovacuum_analyze_threshold = art.servers.Shared.getPropertyValueString("traces.autovacuum_analyze_threshold"); if (autovacuum_enabled == null) autovacuum_enabled = enabled; if (autovacuum_vacuum_scale_factor == null) autovacuum_vacuum_scale_factor = "0.0"; if (autovacuum_vacuum_threshold == null) autovacuum_vacuum_threshold = "1000"; if (autovacuum_analyze_scale_factor == null) autovacuum_analyze_scale_factor = "0.0"; if (autovacuum_analyze_threshold == null) autovacuum_analyze_threshold = "1000"; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_enabled = " + enabled + ", toast.autovacuum_enabled = " + enabled + ")");} catch (Exception e){}; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_vacuum_scale_factor = 0.0)");} catch (Exception e){}; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_vacuum_threshold = 1000)");} catch (Exception e){}; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_analyze_scale_factor = 0.0)");} catch (Exception e){}; try{Shared.controllerDatabase.historical_update("ALTER TABLE " + tablename + " SET (autovacuum_analyze_threshold = 1000)");} catch (Exception e){}; } catch (Exception exception) { } } private long getLastTimestamp(String identifier, long timestamp1) throws ServerException, Exception { ResultSet result = null; try { String where = "type = '" + deviceClassName + "' AND timestamp <= " + timestamp1; if (identifier != null) { if (identifier.length() > 0) { where = where + " AND identifier = '" + identifier + "'"; } } String command = "SELECT timestamp FROM DEVICES WHERE " + where + " ORDER BY timestamp DESC limit 1"; result = Shared.controllerDatabase.getHistoricalPersistance().get(0).executeQuery(command); result.next(); long timestamp = result.getLong(1); return(timestamp); } catch (Exception e) { return(timestamp1); } finally { try { result.close(); } catch (Exception e) {}; } } protected Mutex getMutex(Device device) { return getMutex(device.getIdentifier()); } protected Mutex getMutex(String identifier) { Mutex mutex = (Mutex)mmutex.get(identifier); if (mutex == null) { mutex = new Mutex(); mmutex.put(identifier, mutex); } return mutex; } }