modules/core/domain/src/main/java/org/rhq/core/domain/criteria/MeasurementDefinitionCriteria.java
| 5
modules/core/domain/src/main/java/org/rhq/core/domain/measurement/MeasurementDefinition.java
| 7
modules/core/domain/src/main/java/org/rhq/core/domain/sync/ConsistencyValidatorFailureReport.java
| 82 ++++
modules/core/domain/src/main/java/org/rhq/core/domain/sync/ExportValidationReport.java
| 29 -
modules/core/domain/src/main/java/org/rhq/core/domain/sync/ImportReport.java
| 29 -
modules/core/domain/src/main/java/org/rhq/core/domain/sync/entity/MetricTemplate.java
| 17
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptUtil.java
| 12
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ExportReader.java
| 8
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ExportingInputStream.java
| 33 +
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ImportException.java
| 47 ++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerBean.java
| 205 ++++++++--
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerLocal.java
| 12
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerRemote.java
| 7
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ValidationException.java
| 85 ++++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/exporters/MetricTemplateExporter.java
| 5
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/Importer.java
| 10
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/MetricTemplateImporter.java
| 8
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/SystemSettingsImporter.java
| 111 +++--
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/validators/ConsistencyValidator.java
| 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/validators/DeployedAgentPluginsValidator.java
| 61 ++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
| 14
21 files changed, 613 insertions(+), 176 deletions(-)
New commits:
commit 4a9cb5d83f893b0413943c5cd946229497cd604d
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Fri Jul 29 17:44:24 2011 +0200
First impl of system settings importer. Still needs the configuration settings applied
during import.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/SystemSettingsImporter.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/SystemSettingsImporter.java
index 8c4e2e7..9ad40ff 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/SystemSettingsImporter.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/SystemSettingsImporter.java
@@ -19,15 +19,28 @@
package org.rhq.enterprise.server.sync.importers;
+import java.lang.reflect.Field;
+
+import javassist.Modifier;
+
import javax.persistence.EntityManager;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLStreamException;
+import org.rhq.core.clientapi.agent.configuration.ConfigurationUtility;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
+import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple;
+import org.rhq.core.domain.configuration.definition.PropertySimpleType;
+import org.rhq.core.domain.sync.entity.MetricTemplate;
import org.rhq.core.domain.sync.entity.SystemSettings;
import org.rhq.enterprise.server.sync.ExportReader;
import org.rhq.enterprise.server.sync.NoSingleEntity;
+import org.rhq.enterprise.server.system.SystemManagerLocal;
+import org.rhq.enterprise.server.util.LookupUtil;
/**
*
@@ -36,59 +49,93 @@ import org.rhq.enterprise.server.sync.NoSingleEntity;
*/
public class SystemSettingsImporter implements Importer<NoSingleEntity,
SystemSettings> {
- /* (non-Javadoc)
- * @see
org.rhq.enterprise.server.sync.importers.Importer#getImportConfigurationDefinition()
- */
+ private Subject subject;
+ private SystemManagerLocal systemManager = LookupUtil.getSystemManager();
+ private Configuration importConfiguration;
+ private Unmarshaller unmarshaller;
+
+ public SystemSettingsImporter() {
+ try {
+ JAXBContext context = JAXBContext.newInstance(SystemSettings.class);
+ unmarshaller = context.createUnmarshaller();
+ } catch (JAXBException e) {
+ throw new IllegalStateException("Failed to initialize JAXB marshaller
for MetricTemplate.", e);
+ }
+ }
+
@Override
public ConfigurationDefinition getImportConfigurationDefinition() {
- return new ConfigurationDefinition("SystemSettingsConfiguration",
"TBD");
+ ConfigurationDefinition def = new
ConfigurationDefinition("SystemSettingsConfiguration", null);
+
+ addSwitchToConfigDef(def, "importJAASProvider", false);
+ addSwitchToConfigDef(def, "importJDBCJAASProvider", false);
+ addSwitchToConfigDef(def, "importLDAPJAASProvider", false);
+ addSwitchToConfigDef(def, "imoprtLDAPFactory", true);
+ addSwitchToConfigDef(def, "importLDAPUrl", false);
+ addSwitchToConfigDef(def, "importLDAPProtocol", false);
+ addSwitchToConfigDef(def, "imoprtLDAPLoginProperty", false);
+ addSwitchToConfigDef(def, "importLDAPFilter", false);
+ addSwitchToConfigDef(def, "importLDAPGroupFilter", false);
+ addSwitchToConfigDef(def, "importLDAPGroupMember", false);
+ addSwitchToConfigDef(def, "importLDAPBaseDN", false);
+ addSwitchToConfigDef(def, "importLDAPBindDN", false);
+ addSwitchToConfigDef(def, "importLDAPBindPW", false);
+ addSwitchToConfigDef(def, "importBaseURL", false);
+ addSwitchToConfigDef(def, "importAgentMaxQuietTimeAllowed", true);
+ addSwitchToConfigDef(def, "importEnableAgentAutoUpdate", true);
+ addSwitchToConfigDef(def, "importEnableDebugMode", true);
+ addSwitchToConfigDef(def, "importEnableExperimentalFeatures", true);
+ addSwitchToConfigDef(def, "importDataPurge1Hour", true);
+ addSwitchToConfigDef(def, "importDataPurge6Hour", true);
+ addSwitchToConfigDef(def, "importDataPurge1Day", true);
+ addSwitchToConfigDef(def, "importDataMaintenance", true);
+ addSwitchToConfigDef(def, "importDataReindex", true);
+ addSwitchToConfigDef(def, "imoprtRtDataPurge", true);
+ addSwitchToConfigDef(def, "importAlertPurge", true);
+ addSwitchToConfigDef(def, "importEventPurge", true);
+ addSwitchToConfigDef(def, "importTraitPurge", true);
+ addSwitchToConfigDef(def, "importAvailabilityPurge", true);
+ addSwitchToConfigDef(def, "importBaselineFrequency", true);
+ addSwitchToConfigDef(def, "importBaselineDataSet", true);
+
+ ConfigurationUtility.initializeDefaultTemplate(def);
+
+ return def;
}
- /* (non-Javadoc)
- * @see
org.rhq.enterprise.server.sync.importers.Importer#init(org.rhq.core.domain.auth.Subject,
javax.persistence.EntityManager, org.rhq.core.domain.configuration.Configuration)
- */
@Override
public void init(Subject subject, EntityManager entityManager, Configuration
importConfiguration) {
- // TODO Auto-generated method stub
-
+ this.subject = subject;
+ this.importConfiguration = importConfiguration;
}
- /* (non-Javadoc)
- * @see org.rhq.enterprise.server.sync.importers.Importer#getExportedEntityMatcher()
- */
@Override
public ExportedEntityMatcher<NoSingleEntity, SystemSettings>
getExportedEntityMatcher() {
- // TODO Auto-generated method stub
- return null;
+ return new NoSingleEntityMatcher<SystemSettings>();
}
- /* (non-Javadoc)
- * @see org.rhq.enterprise.server.sync.importers.Importer#update(java.lang.Object,
java.lang.Object)
- */
@Override
- public void update(NoSingleEntity entity, SystemSettings exportedEntity) {
- // TODO Auto-generated method stub
-
+ public void update(NoSingleEntity entity, SystemSettings exportedEntity) throws
Exception {
+ //TODO implement checking for configuration before applying
+ systemManager.setSystemConfiguration(subject, exportedEntity.toProperties(),
false);
}
- /* (non-Javadoc)
- * @see
org.rhq.enterprise.server.sync.importers.Importer#unmarshallExportedEntity(org.rhq.enterprise.server.sync.ExportReader)
- */
@Override
public SystemSettings unmarshallExportedEntity(ExportReader reader) throws
XMLStreamException {
- // TODO Auto-generated method stub
- return null;
+ try {
+ return (SystemSettings) unmarshaller.unmarshal(reader);
+ } catch (JAXBException e) {
+ throw new XMLStreamException("Failed to unmarshal system
settings.", e);
+ }
}
- /* (non-Javadoc)
- * @see org.rhq.enterprise.server.sync.importers.Importer#finishImport()
- */
@Override
public void finishImport() {
- // TODO Auto-generated method stub
-
}
-
-
+ private void addSwitchToConfigDef(ConfigurationDefinition def, String name, boolean
defaultValue) {
+ PropertyDefinitionSimple prop = new PropertyDefinitionSimple(name, null, true,
PropertySimpleType.BOOLEAN);
+ prop.setDefaultValue(Boolean.toString(defaultValue));
+ def.put(prop);
+ }
}
commit 2c73bb5ad2106b7f5ccb485f605505478bbc0f63
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Fri Jul 29 17:43:40 2011 +0200
small updates here and there to make everything work. The SystemSettings import still
needs fully implemented though.
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/criteria/MeasurementDefinitionCriteria.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/criteria/MeasurementDefinitionCriteria.java
index 779e4c5..0e22859 100644
---
a/modules/core/domain/src/main/java/org/rhq/core/domain/criteria/MeasurementDefinitionCriteria.java
+++
b/modules/core/domain/src/main/java/org/rhq/core/domain/criteria/MeasurementDefinitionCriteria.java
@@ -55,6 +55,8 @@ public class MeasurementDefinitionCriteria extends Criteria {
private Boolean filterDefaultOn;
private Long filterDefaultInterval;
+ private boolean fetchResourceType;
+
private PageOrdering sortName;
private PageOrdering sortDisplayName;
private PageOrdering sortResourceTypeName; // requires overrides
@@ -180,4 +182,7 @@ public class MeasurementDefinitionCriteria extends Criteria {
this.sortDefaultInterval = sortDefaultInterval;
}
+ public void fetchResourceType(boolean value) {
+ this.fetchResourceType = value;
+ }
}
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/measurement/MeasurementDefinition.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/measurement/MeasurementDefinition.java
index 2dbc2f4..e3f5600 100644
---
a/modules/core/domain/src/main/java/org/rhq/core/domain/measurement/MeasurementDefinition.java
+++
b/modules/core/domain/src/main/java/org/rhq/core/domain/measurement/MeasurementDefinition.java
@@ -74,11 +74,12 @@ import org.rhq.core.domain.resource.ResourceType;
@NamedQuery(name = MeasurementDefinition.DISABLE_ALL, query = "" //
+ "UPDATE MeasurementDefinition md " //
+ " SET md.defaultOn = false"),
- @NamedQuery(name = MeasurementDefinition.FIND_BY_NAME_AND_RESOURCE_TYPE_NAME, query =
"" //
+ @NamedQuery(name =
MeasurementDefinition.FIND_RAW_OR_PER_MINUTE_BY_NAME_AND_RESOURCE_TYPE_NAME, query =
"" //
+ " SELECT md FROM MeasurementDefinition md"
+ " WHERE md.name = :name "
+ " AND md.resourceType.name = :resourceTypeName"
- + " AND md.resourceType.plugin = :resourceTypePlugin")
+ + " AND md.resourceType.plugin = :resourceTypePlugin"
+ + " AND ((:perMinute = 1 AND md.rawNumericType IS NOT NULL) OR (:perMinute =
0 AND md.rawNumericType IS NULL))")
})
@SequenceGenerator(name = "id", sequenceName =
"RHQ_MEASUREMENT_DEF_ID_SEQ")
@Table(name = "RHQ_MEASUREMENT_DEF")
@@ -109,7 +110,7 @@ public class MeasurementDefinition implements Serializable {
public static final String FIND_SCHEDULE_COMPOSITE_FOR_RESOURCE_TYPE =
"MeasurementDefinition.findScheduleCompositeForResourceType";
public static final String FIND_BY_IDS =
"MeasurementDefinition.findByIds";
public static final String DISABLE_ALL =
"MeasurementDefinition.disableAll";
- public static final String FIND_BY_NAME_AND_RESOURCE_TYPE_NAME =
"MeasurementDefinition.findByNameAndResourceTypeName";
+ public static final String FIND_RAW_OR_PER_MINUTE_BY_NAME_AND_RESOURCE_TYPE_NAME =
"MeasurementDefinition.findRawOrPerMinuteByNameAndResourceTypeName";
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO, generator = "id")
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ConsistencyValidatorFailureReport.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ConsistencyValidatorFailureReport.java
index 9bc3109..7e174d5 100644
---
a/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ConsistencyValidatorFailureReport.java
+++
b/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ConsistencyValidatorFailureReport.java
@@ -68,4 +68,15 @@ public class ConsistencyValidatorFailureReport implements Serializable
{
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
+
+ @Override
+ public String toString() {
+ StringBuilder bld = new StringBuilder();
+
+ bld.append("ConsistencyValidatorFailureReport[validator='")
+ .append(validatorClassName).append("', message='")
+ .append(errorMessage).append("']");
+
+ return bld.toString();
+ }
}
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/sync/entity/MetricTemplate.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/sync/entity/MetricTemplate.java
index 939a634..e510dc5 100644
---
a/modules/core/domain/src/main/java/org/rhq/core/domain/sync/entity/MetricTemplate.java
+++
b/modules/core/domain/src/main/java/org/rhq/core/domain/sync/entity/MetricTemplate.java
@@ -46,6 +46,9 @@ public class MetricTemplate extends AbstractExportedEntity {
@XmlAttribute
private String metricName;
+
+ @XmlAttribute
+ private boolean perMinute;
@XmlAttribute
private long defaultInterval;
@@ -62,6 +65,7 @@ public class MetricTemplate extends AbstractExportedEntity {
metricName = definition.getName();
defaultInterval = definition.getDefaultInterval();
enabled = definition.isDefaultOn();
+ perMinute = definition.getRawNumericType() != null;
setReferencedEntityId(definition.getId());
}
@@ -105,4 +109,17 @@ public class MetricTemplate extends AbstractExportedEntity {
this.enabled = enabled;
}
+ /**
+ * @return the perMinute
+ */
+ public boolean isPerMinute() {
+ return perMinute;
+ }
+
+ /**
+ * @param perMinute the perMinute to set
+ */
+ public void setPerMinute(boolean perMinute) {
+ this.perMinute = perMinute;
+ }
}
diff --git
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptUtil.java
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptUtil.java
index 4e81e30..b3db4c3 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptUtil.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptUtil.java
@@ -24,6 +24,7 @@ package org.rhq.bindings.util;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -75,7 +76,16 @@ public class ScriptUtil {
return resourceManager.findResourcesByCriteria(getSubjectFromEngine(),
criteria);
}
-
+ public void saveBytesToFile(byte[] bytes, String filename) throws IOException {
+ FileOutputStream fos = new FileOutputStream(filename);
+
+ try {
+ fos.write(bytes);
+ } finally {
+ fos.close();
+ }
+ }
+
public byte[] getFileBytes(String fileName) {
File file = new File(fileName);
long length = file.length();
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ExportReader.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ExportReader.java
index db87b44..d56df4b 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ExportReader.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ExportReader.java
@@ -19,6 +19,8 @@
package org.rhq.enterprise.server.sync;
+import java.util.NoSuchElementException;
+
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
@@ -74,7 +76,7 @@ public class ExportReader implements XMLStreamReader {
++depth;
break;
case XMLStreamReader.END_ELEMENT:
- if (depth > 0) {
+ if (depth >= 0) {
--depth;
} else {
return XMLStreamReader.END_DOCUMENT;
@@ -119,7 +121,7 @@ public class ExportReader implements XMLStreamReader {
if (depth > 0) {
--depth;
} else {
- throw new IllegalStateException("End of the export segment
reached.");
+ throw new NoSuchElementException("End of the export segment
reached.");
}
}
@@ -132,7 +134,7 @@ public class ExportReader implements XMLStreamReader {
* @see javax.xml.stream.XMLStreamReader#hasNext()
*/
public boolean hasNext() throws XMLStreamException {
- if (depth == 0 && reader.isEndElement()) {
+ if (depth < 0 && reader.isEndElement()) {
return false;
} else {
return reader.hasNext();
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ExportingInputStream.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ExportingInputStream.java
index e2d7d17..dff542e 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ExportingInputStream.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ExportingInputStream.java
@@ -19,6 +19,7 @@
package org.rhq.enterprise.server.sync;
+import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -173,7 +174,9 @@ public class ExportingInputStream extends InputStream {
uncaughtExporterException = e;
}
});
-
+
+
exportRunner.setContextClassLoader(Thread.currentThread().getContextClassLoader());
+
exportRunner.start();
}
@@ -183,12 +186,13 @@ public class ExportingInputStream extends InputStream {
}
private void exporterMain() {
+ XMLStreamWriter wrt = null;
+ OutputStream out = null;
try {
XMLOutputFactory ofactory = XMLOutputFactory.newInstance();
- XMLStreamWriter wrt = null;
try {
- OutputStream out = exportOutput;
+ out = exportOutput;
if (zipOutput) {
out = new GZIPOutputStream(out);
}
@@ -204,12 +208,21 @@ public class ExportingInputStream extends InputStream {
exportSingle(wrt, exp);
}
- exportEpilogue(wrt);
+ exportEpilogue(wrt);
+
+ wrt.flush();
} catch (Exception e) {
LOG.error("Error while exporting.", e);
throw new RuntimeException(e);
} finally {
- safeClose(exportOutput);
+ if (wrt != null) {
+ try {
+ wrt.close();
+ } catch (XMLStreamException e) {
+ LOG.warn("Failed to close the exporter XML stream.", e);
+ }
+ }
+ safeClose(out);
}
}
@@ -279,12 +292,10 @@ public class ExportingInputStream extends InputStream {
it.export(new ExportWriter(wrt));
wrt.writeEndElement();
- messages.getPerEntityErrorMessages().add(null);
-
String notes = it.getNotes();
- messages.getPerEntityNotes().add(it.getNotes());
if (notes != null) {
+ messages.getPerEntityNotes().add(notes);
wrt.writeStartElement(NOTES_ELEMENT);
wrt.writeCharacters(notes);
wrt.writeEndElement();
@@ -316,9 +327,11 @@ public class ExportingInputStream extends InputStream {
wrt.writeEndElement(); //entities
}
- private static void safeClose(OutputStream str) {
+ private static void safeClose(Closeable str) {
try {
- str.close();
+ if (str != null) {
+ str.close();
+ }
} catch (IOException e) {
LOG.error("Failed to close an output stream. This shouldn't
happen.", e);
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerBean.java
index 418a8b8..305dc87 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerBean.java
@@ -32,13 +32,14 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
-import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
@@ -50,14 +51,12 @@ import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.authz.Permission;
import org.rhq.core.domain.configuration.Configuration;
-import org.rhq.core.domain.sync.ExportReport;
import org.rhq.core.domain.sync.ConsistencyValidatorFailureReport;
+import org.rhq.core.domain.sync.ExportReport;
import org.rhq.core.domain.sync.ExportWrapper;
import org.rhq.core.domain.sync.ExporterMessages;
-import org.rhq.core.domain.sync.ImportReport;
import org.rhq.core.domain.sync.ImporterConfiguration;
import org.rhq.core.domain.sync.ImporterConfigurationDefinition;
-import org.rhq.core.util.exception.ThrowableUtil;
import org.rhq.enterprise.server.RHQConstants;
import org.rhq.enterprise.server.authz.RequiredPermission;
import org.rhq.enterprise.server.sync.exporters.Exporter;
@@ -138,9 +137,11 @@ public class SynchronizationManagerBean implements
SynchronizationManagerLocal,
public void importAllSubsystems(Subject subject, InputStream exportFile,
List<ImporterConfiguration> configurations) throws ValidationException,
ImportException {
try {
- processExportFile(subject, exportFile, true,
getConfigPerImporter(configurations));
+ processExportFile(subject, new GZIPInputStream(exportFile), true,
getConfigPerImporter(configurations));
} catch (XMLStreamException e) {
throw new ImportException("Failed import due to XML parsing
error.", e);
+ } catch (IOException e) {
+ throw new ImportException("The provided file is not a gzipped
XML.", e);
}
}
@@ -212,13 +213,13 @@ public class SynchronizationManagerBean implements
SynchronizationManagerLocal,
case XMLStreamReader.START_ELEMENT:
String tagName = rdr.getName().getLocalPart();
if (ExportingInputStream.VALIDATOR_ELEMENT.equals(tagName)) {
+ String validatorClass = rdr.getAttributeValue(null,
ExportingInputStream.CLASS_ATTRIBUTE);
try {
validateSingle(rdr);
} catch (Exception e) {
- String validatorClass = rdr.getAttributeValue(null,
ExportingInputStream.CLASS_ATTRIBUTE);
failures.add(new
ConsistencyValidatorFailureReport(validatorClass, printExceptionToString(e)));
}
- } else if (doImport &&
ExportingInputStream.ENTITIES_EXPORT_ELEMENT.equals(tagName)) {
+ } else if (doImport && failures.isEmpty() &&
ExportingInputStream.ENTITIES_EXPORT_ELEMENT.equals(tagName)) {
try {
importSingle(subject, importerConfigs, rdr);
} catch (Exception e) {
@@ -233,6 +234,10 @@ public class SynchronizationManagerBean implements
SynchronizationManagerLocal,
break;
}
}
+
+ if (!failures.isEmpty()) {
+ throw new ValidationException(failures);
+ }
}
private void validateSingle(XMLStreamReader rdr) throws Exception {
@@ -242,20 +247,9 @@ public class SynchronizationManagerBean implements
SynchronizationManagerLocal,
ConsistencyValidator.class,
"The validator class denoted in the export file ('%s') does not
implement the ConsistencyValidator interface. This should not happen.");
- //move into the configuration of the validator
- rdr.next();
-
//perform the validation
validator.initializeValidation(new ExportReader(rdr));
validator.validate();
-
- //now skip everything in the XML until the next element
- while (rdr.hasNext()) {
- int state = rdr.nextTag();
- if (state == XMLStreamReader.START_ELEMENT) {
- break;
- }
- }
}
private <E, X> void importSingle(Subject subject, Map<String,
Configuration> importConfigs, XMLStreamReader rdr) throws Exception {
@@ -316,8 +310,10 @@ public class SynchronizationManagerBean implements
SynchronizationManagerLocal,
private Map<String, Configuration>
getConfigPerImporter(List<ImporterConfiguration> list) {
Map<String, Configuration> ret = new HashMap<String,
Configuration>();
- for(ImporterConfiguration ic : list) {
- ret.put(ic.getImporterClassName(), ic.getConfiguration());
+ if (list != null) {
+ for(ImporterConfiguration ic : list) {
+ ret.put(ic.getImporterClassName(), ic.getConfiguration());
+ }
}
return ret;
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerLocal.java
index c4506b3..74f78b4 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerLocal.java
@@ -27,10 +27,8 @@ import javax.ejb.Local;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.authz.Permission;
-import org.rhq.core.domain.sync.ConsistencyValidatorFailureReport;
-import org.rhq.core.domain.sync.ExportWrapper;
import org.rhq.core.domain.sync.ExportReport;
-import org.rhq.core.domain.sync.ImportReport;
+import org.rhq.core.domain.sync.ExportWrapper;
import org.rhq.core.domain.sync.ImporterConfiguration;
import org.rhq.core.domain.sync.ImporterConfigurationDefinition;
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerRemote.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerRemote.java
index 9145387..0f93a53 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerRemote.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerRemote.java
@@ -20,15 +20,12 @@
package org.rhq.enterprise.server.sync;
import java.util.List;
-import java.util.Set;
import javax.ejb.Remote;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.authz.Permission;
import org.rhq.core.domain.sync.ExportReport;
-import org.rhq.core.domain.sync.ConsistencyValidatorFailureReport;
-import org.rhq.core.domain.sync.ImportReport;
import org.rhq.core.domain.sync.ImporterConfiguration;
import org.rhq.core.domain.sync.ImporterConfigurationDefinition;
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/exporters/MetricTemplateExporter.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/exporters/MetricTemplateExporter.java
index 44c3f26..b7440d1 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/exporters/MetricTemplateExporter.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/exporters/MetricTemplateExporter.java
@@ -95,10 +95,11 @@ public class MetricTemplateExporter implements
Exporter<MeasurementDefinition, M
public ExportingIterator<MetricTemplate> getExportingIterator() {
MeasurementDefinitionCriteria criteria = new MeasurementDefinitionCriteria();
criteria.setPageControl(PageControl.getUnlimitedInstance());
-
+ criteria.fetchResourceType(true);
+
List<MeasurementDefinition> defs =
measurementDefinitionManager.findMeasurementDefinitionsByCriteria(subject,
criteria);
-
+
return new MetricTemplateIterator(defs.iterator());
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/Importer.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/Importer.java
index af62706..7aefe1d 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/Importer.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/Importer.java
@@ -63,12 +63,14 @@ public interface Importer<Entity, ExportedType> {
* Updates the entity with the data from the export.
* <p>
* This method is responsible for persisting the entity in the database
- * using the provided entityManager.
+ * using the provided entityManager. Note that the actual persist can
+ * also be delayed until the {@link #finishImport()} method is called
+ * so that the importer can take advantage of batching.
*
* @param entity the entity to persist (may be null if the {@link
#getExportedEntityMatcher()} returned null of if the entity matcher didn't find a
match)
* @param exportedEntity the entity found in the export file that should be used to
update the entity in the database
*/
- void update(Entity entity, ExportedType exportedEntity);
+ void update(Entity entity, ExportedType exportedEntity) throws Exception;
/**
* Unmarshalls an entity from the provided reader.
@@ -85,5 +87,5 @@ public interface Importer<Entity, ExportedType> {
* <p>
* This is useful for importers that need to batch the updates to the database.
*/
- void finishImport();
+ void finishImport() throws Exception;
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/MetricTemplateImporter.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/MetricTemplateImporter.java
index afad9ad..400972c 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/MetricTemplateImporter.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/MetricTemplateImporter.java
@@ -145,8 +145,12 @@ public class MetricTemplateImporter implements
Importer<MeasurementDefinition, M
@Override
public MeasurementDefinition findMatch(MetricTemplate object) {
- Query q =
entityManager.createNamedQuery(MeasurementDefinition.FIND_BY_NAME_AND_RESOURCE_TYPE_NAME);
-
+ Query q =
entityManager.createNamedQuery(MeasurementDefinition.FIND_RAW_OR_PER_MINUTE_BY_NAME_AND_RESOURCE_TYPE_NAME);
+ q.setParameter("name", object.getMetricName());
+ q.setParameter("resourceTypeName",
object.getResourceTypeName());
+ q.setParameter("resourceTypePlugin",
object.getResourceTypePlugin());
+ q.setParameter("perMinute", object.isPerMinute() ? 1 : 0);
+
List<?> results = q.getResultList();
if (results.isEmpty()) {
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/validators/DeployedAgentPluginsValidator.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/validators/DeployedAgentPluginsValidator.java
index 3f099a9..20ce4e2 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/validators/DeployedAgentPluginsValidator.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/validators/DeployedAgentPluginsValidator.java
@@ -42,9 +42,45 @@ import org.rhq.enterprise.server.util.LookupUtil;
*/
public class DeployedAgentPluginsValidator implements ConsistencyValidator {
+ private static class ConsistentPlugin extends Plugin {
+
+ private static final long serialVersionUID = 1L;
+
+ public ConsistentPlugin() {
+
+ }
+
+ public ConsistentPlugin(Plugin p) {
+ setName(p.getName());
+ setVersion(p.getVersion());
+ setMd5(p.getMd5());
+ }
+
+ @Override
+ public int hashCode() {
+ return getName().hashCode() * getVersion().hashCode() * getMd5().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof ConsistentPlugin)) {
+ return false;
+ }
+
+ ConsistentPlugin p = (ConsistentPlugin) other;
+
+ return getName().equals(p.getName()) &&
getVersion().equals(p.getVersion())
+ && getMd5().equals(p.getMd5());
+ }
+ }
+
private PluginManagerLocal pluginManager;
- private Set<Plugin> pluginsToValidate;
+ private Set<ConsistentPlugin> pluginsToValidate;
public DeployedAgentPluginsValidator() {
this(LookupUtil.getPluginManager());
@@ -71,13 +107,13 @@ public class DeployedAgentPluginsValidator implements
ConsistencyValidator {
@Override
public void initializeValidation(ExportReader reader) throws XMLStreamException {
- pluginsToValidate = new HashSet<Plugin>();
+ pluginsToValidate = new HashSet<ConsistentPlugin>();
while (reader.hasNext()) {
switch (reader.next()) {
case XMLStreamReader.START_ELEMENT:
if ("plugin".equals(reader.getName().getLocalPart())) {
- Plugin p = new Plugin();
+ ConsistentPlugin p = new ConsistentPlugin();
p.setName(reader.getAttributeValue(null, "name"));
p.setMd5(reader.getAttributeValue(null, "hash"));
p.setVersion(reader.getAttributeValue(null, "version"));
@@ -94,20 +130,20 @@ public class DeployedAgentPluginsValidator implements
ConsistencyValidator {
@Override
public void validate() throws InconsistentStateException {
List<Plugin> localPlugins = pluginManager.getInstalledPlugins();
- Set<Plugin> localAgentPlugins = new HashSet<Plugin>();
+ Set<ConsistentPlugin> localAgentPlugins = new
HashSet<ConsistentPlugin>();
for(Plugin p : localPlugins) {
if (p.getDeployment() == PluginDeploymentType.AGENT) {
- localAgentPlugins.add(p);
+ localAgentPlugins.add(new ConsistentPlugin(p));
}
}
if (localAgentPlugins.size() != pluginsToValidate.size()) {
- throwIncosistentException(localPlugins, pluginsToValidate);
+ throwIncosistentException(localAgentPlugins, pluginsToValidate);
}
- for (Plugin localPlugin : localAgentPlugins) {
+ for (ConsistentPlugin localPlugin : localAgentPlugins) {
if (!pluginsToValidate.contains(localPlugin)) {
- throwIncosistentException(localPlugins, pluginsToValidate);
+ throwIncosistentException(localAgentPlugins, pluginsToValidate);
}
}
}
@@ -135,7 +171,7 @@ public class DeployedAgentPluginsValidator implements
ConsistencyValidator {
return false;
}
- private static void throwIncosistentException(Collection<Plugin> localPlugins,
Collection<Plugin> expectedPlugins)
+ private static void throwIncosistentException(Collection<ConsistentPlugin>
localPlugins, Collection<ConsistentPlugin> expectedPlugins)
throws InconsistentStateException {
StringBuilder bld = new StringBuilder(
"Installed plugins are not consistent with the plugins required by the
export.");
@@ -148,17 +184,16 @@ public class DeployedAgentPluginsValidator implements
ConsistencyValidator {
throw new InconsistentStateException(bld.toString());
}
- private static void appendPlugins(StringBuilder bld, Collection<Plugin>
plugins) {
+ private static void appendPlugins(StringBuilder bld,
Collection<ConsistentPlugin> plugins) {
bld.append("[");
int size = plugins.size();
int i = 0;
for (Plugin p : plugins) {
+ ++i;
appendPlugin(bld, p);
if (i < size) {
bld.append(", ");
}
-
- ++i;
}
bld.append("]");
}
@@ -166,6 +201,6 @@ public class DeployedAgentPluginsValidator implements
ConsistencyValidator {
private static void appendPlugin(StringBuilder bld, Plugin p) {
bld.append("Plugin[name='").append(p.getName()).append("'");
bld.append(",
version='").append(p.getVersion()).append("'");
- bld.append(",
hash='").append(p.getMd5()).append("']");
+ bld.append(",
md5='").append(p.getMd5()).append("']");
}
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
index 1725770..aa9a2fd 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
@@ -123,8 +123,6 @@ import
org.rhq.core.domain.resource.composite.ProblemResourceComposite;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.resource.group.composite.ResourceGroupComposite;
import org.rhq.core.domain.sync.ExportReport;
-import org.rhq.core.domain.sync.ConsistencyValidatorFailureReport;
-import org.rhq.core.domain.sync.ImportReport;
import org.rhq.core.domain.sync.ImporterConfiguration;
import org.rhq.core.domain.sync.ImporterConfigurationDefinition;
import org.rhq.core.domain.util.PageControl;
commit ced2ed6fe9d40a4ae670c46a9474cf518999fd75
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Jul 28 18:24:36 2011 +0200
Import workflow implemented.
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ConsistencyValidatorFailureReport.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ConsistencyValidatorFailureReport.java
new file mode 100644
index 0000000..9bc3109
--- /dev/null
+++
b/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ConsistencyValidatorFailureReport.java
@@ -0,0 +1,71 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2011 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.rhq.core.domain.sync;
+
+import java.io.Serializable;
+
+/**
+ * A simple class representing the validation failure of a single validator.
+ *
+ * @author Lukas Krejci
+ */
+public class ConsistencyValidatorFailureReport implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private String validatorClassName;
+ private String errorMessage;
+
+ public ConsistencyValidatorFailureReport() {
+ }
+
+ public ConsistencyValidatorFailureReport(String validatorClassName, String
errorMessage) {
+ this.validatorClassName = validatorClassName;
+ this.errorMessage = errorMessage;
+ }
+
+ /**
+ * @return the validatorClassName
+ */
+ public String getValidatorClassName() {
+ return validatorClassName;
+ }
+
+ /**
+ * @param validatorClassName the validatorClassName to set
+ */
+ public void setValidatorClassName(String validatorClassName) {
+ this.validatorClassName = validatorClassName;
+ }
+
+ /**
+ * @return the errorMessage
+ */
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ /**
+ * @param errorMessage the errorMessage to set
+ */
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+}
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ExportValidationReport.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ExportValidationReport.java
deleted file mode 100644
index ebf42e7..0000000
---
a/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ExportValidationReport.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2011 Red Hat, Inc.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2 of the License.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-package org.rhq.core.domain.sync;
-
-/**
- *
- *
- * @author Lukas Krejci
- */
-public class ExportValidationReport {
- //TODO implement
-}
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ImportReport.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ImportReport.java
deleted file mode 100644
index df32ae7..0000000
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/sync/ImportReport.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2011 Red Hat, Inc.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2 of the License.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-package org.rhq.core.domain.sync;
-
-/**
- *
- *
- * @author Lukas Krejci
- */
-public class ImportReport {
- //TODO implement
-}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ImportException.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ImportException.java
new file mode 100644
index 0000000..8b871ae
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ImportException.java
@@ -0,0 +1,47 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2011 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.rhq.enterprise.server.sync;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class ImportException extends SynchronizationException {
+
+ private static final long serialVersionUID = 1L;
+
+ public ImportException() {
+ }
+
+ public ImportException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ImportException(String message) {
+ super(message);
+ }
+
+ public ImportException(Throwable cause) {
+ super(cause);
+ }
+
+
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerBean.java
index 39b8c7a..418a8b8 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerBean.java
@@ -23,7 +23,10 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -33,22 +36,34 @@ import java.util.Set;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.authz.Permission;
+import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.sync.ExportReport;
-import org.rhq.core.domain.sync.ExportValidationReport;
+import org.rhq.core.domain.sync.ConsistencyValidatorFailureReport;
import org.rhq.core.domain.sync.ExportWrapper;
import org.rhq.core.domain.sync.ExporterMessages;
import org.rhq.core.domain.sync.ImportReport;
import org.rhq.core.domain.sync.ImporterConfiguration;
import org.rhq.core.domain.sync.ImporterConfigurationDefinition;
+import org.rhq.core.util.exception.ThrowableUtil;
+import org.rhq.enterprise.server.RHQConstants;
import org.rhq.enterprise.server.authz.RequiredPermission;
import org.rhq.enterprise.server.sync.exporters.Exporter;
+import org.rhq.enterprise.server.sync.importers.ExportedEntityMatcher;
import org.rhq.enterprise.server.sync.importers.Importer;
+import org.rhq.enterprise.server.sync.validators.ConsistencyValidator;
/**
*
@@ -60,22 +75,25 @@ public class SynchronizationManagerBean implements
SynchronizationManagerLocal,
private static final Log LOG = LogFactory.getLog(SynchronizationManagerBean.class);
+ @PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME)
+ private EntityManager entityManager;
+
@Override
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@RequiredPermission(Permission.MANAGE_INVENTORY)
public ExportReport exportAllSubsystems(Subject subject) {
ExportWrapper localExport = exportAllSubsystemsLocally(subject);
-
+
byte[] buffer = new byte[65536];
-
+
ByteArrayOutputStream out = new ByteArrayOutputStream(10240); //10KB is a
reasonable minimum size of an export
-
+
try {
int cnt = 0;
while ((cnt = localExport.getExportFile().read(buffer)) != -1) {
out.write(buffer, 0, cnt);
}
-
+
return new ExportReport(localExport.getMessagesPerExporter(),
out.toByteArray());
} catch (Exception e) {
return new ExportReport(e.getMessage());
@@ -86,7 +104,7 @@ public class SynchronizationManagerBean implements
SynchronizationManagerLocal,
//this doesn't happen - out is backed by just an array
LOG.error("Closing a byte array output stream failed. This should
never happen.");
}
-
+
try {
localExport.getExportFile().close();
} catch (Exception e) {
@@ -94,20 +112,20 @@ public class SynchronizationManagerBean implements
SynchronizationManagerLocal,
}
}
}
-
+
@Override
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@RequiredPermission(Permission.MANAGE_INVENTORY)
public ExportWrapper exportAllSubsystemsLocally(Subject subject) {
Set<Exporter<?, ?>> allExporters = new HashSet<Exporter<?,
?>>();
Map<String, ExporterMessages> messages = new HashMap<String,
ExporterMessages>();
-
- for(SynchronizedEntity e : SynchronizedEntity.values()) {
+
+ for (SynchronizedEntity e : SynchronizedEntity.values()) {
Exporter<?, ?> exporter = e.getExporter();
exporter.init(subject);
allExporters.add(exporter);
}
-
+
try {
return new ExportWrapper(messages, new ExportingInputStream(allExporters,
messages));
} catch (IOException e) {
@@ -116,27 +134,39 @@ public class SynchronizationManagerBean implements
SynchronizationManagerLocal,
}
@Override
- public ImportReport importAllSubsystems(Subject subject, InputStream exportFile,
List<ImporterConfiguration> configurations) {
- //TODO implement
- return null;
+ @RequiredPermission(Permission.MANAGE_INVENTORY)
+ public void importAllSubsystems(Subject subject, InputStream exportFile,
+ List<ImporterConfiguration> configurations) throws ValidationException,
ImportException {
+ try {
+ processExportFile(subject, exportFile, true,
getConfigPerImporter(configurations));
+ } catch (XMLStreamException e) {
+ throw new ImportException("Failed import due to XML parsing
error.", e);
+ }
}
@Override
- public ImportReport importAllSubsystems(Subject subject, byte[] exportFile,
List<ImporterConfiguration> configurations) {
- return importAllSubsystems(subject, new ByteArrayInputStream(exportFile),
configurations);
+ @RequiredPermission(Permission.MANAGE_INVENTORY)
+ public void importAllSubsystems(Subject subject, byte[] exportFile,
+ List<ImporterConfiguration> configurations) throws ValidationException,
ImportException {
+ importAllSubsystems(subject, new ByteArrayInputStream(exportFile),
configurations);
}
-
+
@Override
- public ExportValidationReport validate(Subject subject, InputStream exportFile) {
- // TODO implement
- return null;
+ @RequiredPermission(Permission.MANAGE_INVENTORY)
+ public void validate(Subject subject, InputStream exportFile) throws
ValidationException {
+ try {
+ processExportFile(subject, exportFile, false, Collections.<String,
Configuration>emptyMap());
+ } catch (XMLStreamException e) {
+ throw new ValidationException("Failed to parse the export file.",
e);
+ }
}
@Override
- public ExportValidationReport validate(Subject subject, byte[] exportFile) {
- return validate(subject, new ByteArrayInputStream(exportFile));
+ @RequiredPermission(Permission.MANAGE_INVENTORY)
+ public void validate(Subject subject, byte[] exportFile) throws ValidationException
{
+ validate(subject, new ByteArrayInputStream(exportFile));
}
-
+
@Override
public ImporterConfigurationDefinition getImporterConfigurationDefinition(String
importerClass) {
try {
@@ -145,9 +175,9 @@ public class SynchronizationManagerBean implements
SynchronizationManagerLocal,
LOG.debug("Supplied importer class does not implement the importer
interface: '" + importerClass + "'.");
return null;
}
-
+
Importer<?, ?> imp = (Importer<?, ?>) cls.newInstance();
-
+
return new ImporterConfigurationDefinition(importerClass,
imp.getImportConfigurationDefinition());
} catch (ClassNotFoundException e) {
LOG.debug("Supplied importer class is invalid: '" +
importerClass + "'.", e);
@@ -161,10 +191,133 @@ public class SynchronizationManagerBean implements
SynchronizationManagerLocal,
@Override
public List<ImporterConfigurationDefinition>
getConfigurationDefinitionOfAllImporters() {
List<ImporterConfigurationDefinition> ret = new
ArrayList<ImporterConfigurationDefinition>();
-
- for(SynchronizedEntity e : SynchronizedEntity.values()) {
+
+ for (SynchronizedEntity e : SynchronizedEntity.values()) {
Importer<?, ?> imp = e.getImporter();
- ret.add(new ImporterConfigurationDefinition(imp.getClass().getName(),
imp.getImportConfigurationDefinition()));
+ ret.add(new ImporterConfigurationDefinition(imp.getClass().getName(), imp
+ .getImportConfigurationDefinition()));
+ }
+
+ return ret;
+ }
+
+ private void processExportFile(Subject subject, InputStream exportFile, boolean
doImport, Map<String, Configuration> importerConfigs) throws ValidationException,
+ ImportException, XMLStreamException {
+ XMLStreamReader rdr =
XMLInputFactory.newInstance().createXMLStreamReader(exportFile);
+
+ Set<ConsistencyValidatorFailureReport> failures = new
HashSet<ConsistencyValidatorFailureReport>();
+
+ while (rdr.hasNext()) {
+ switch (rdr.next()) {
+ case XMLStreamReader.START_ELEMENT:
+ String tagName = rdr.getName().getLocalPart();
+ if (ExportingInputStream.VALIDATOR_ELEMENT.equals(tagName)) {
+ try {
+ validateSingle(rdr);
+ } catch (Exception e) {
+ String validatorClass = rdr.getAttributeValue(null,
ExportingInputStream.CLASS_ATTRIBUTE);
+ failures.add(new
ConsistencyValidatorFailureReport(validatorClass, printExceptionToString(e)));
+ }
+ } else if (doImport &&
ExportingInputStream.ENTITIES_EXPORT_ELEMENT.equals(tagName)) {
+ try {
+ importSingle(subject, importerConfigs, rdr);
+ } catch (Exception e) {
+ //fail fast on the import errors... This runs in a single
transaction
+ //so all imports done so far will get rolled-back.
+ //(Even if we change our minds later and run a transaction per
importer
+ //we should fail fast to prevent further damage due to possible
+ //constraint violations in the db, etc.)
+ throw new ImportException("Import failed.", e);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ private void validateSingle(XMLStreamReader rdr) throws Exception {
+ String validatorClassName = rdr.getAttributeValue(null,
ExportingInputStream.CLASS_ATTRIBUTE);
+ ConsistencyValidator validator = instantiate(
+ validatorClassName,
+ ConsistencyValidator.class,
+ "The validator class denoted in the export file ('%s') does not
implement the ConsistencyValidator interface. This should not happen.");
+
+ //move into the configuration of the validator
+ rdr.next();
+
+ //perform the validation
+ validator.initializeValidation(new ExportReader(rdr));
+ validator.validate();
+
+ //now skip everything in the XML until the next element
+ while (rdr.hasNext()) {
+ int state = rdr.nextTag();
+ if (state == XMLStreamReader.START_ELEMENT) {
+ break;
+ }
+ }
+ }
+
+ private <E, X> void importSingle(Subject subject, Map<String,
Configuration> importConfigs, XMLStreamReader rdr) throws Exception {
+ String importerClassName = rdr.getAttributeValue(null,
ExportingInputStream.ID_ATTRIBUTE);
+
+ @SuppressWarnings("unchecked")
+ Importer<E, X> importer = instantiate(importerClassName, Importer.class,
+ "The importer denoted in the export file ('%s') does not
implement the importer interface. This should not happen.");
+
+ importer.init(subject, entityManager, importConfigs.get(importerClassName));
+
+ ExportedEntityMatcher<E, X> matcher = importer.getExportedEntityMatcher();
+
+ while(rdr.hasNext()) {
+ boolean bailout = false;
+ switch(rdr.next()) {
+ case XMLStreamConstants.START_ELEMENT:
+ if
(ExportingInputStream.DATA_ELEMENT.equals(rdr.getName().getLocalPart())) {
+ rdr.next();
+ X exportedEntity = importer.unmarshallExportedEntity(new
ExportReader(rdr));
+ E entity = matcher == null ? null :
matcher.findMatch(exportedEntity);
+ importer.update(entity, exportedEntity);
+ }
+ break;
+ case XMLStreamConstants.END_ELEMENT:
+ if
(ExportingInputStream.ENTITIES_EXPORT_ELEMENT.equals(rdr.getName().getLocalPart())) {
+ bailout = true;
+ }
+ }
+
+ if (bailout) {
+ break;
+ }
+ }
+
+ importer.finishImport();
+ }
+
+ private static String printExceptionToString(Throwable t) {
+ StringWriter str = new StringWriter();
+ PrintWriter wrt = new PrintWriter(str);
+ t.printStackTrace(wrt);
+ return str.toString();
+ }
+
+ private <T> T instantiate(String className, Class<T> desiredClass, String
notAssignableErrorMessage)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ Class<?> cls = Class.forName(className);
+ if (!desiredClass.isAssignableFrom(cls)) {
+ throw new IllegalStateException(String.format(notAssignableErrorMessage,
className, desiredClass.getName()));
+ }
+
+ Object instance = cls.newInstance();
+
+ return desiredClass.cast(instance);
+ }
+
+ private Map<String, Configuration>
getConfigPerImporter(List<ImporterConfiguration> list) {
+ Map<String, Configuration> ret = new HashMap<String,
Configuration>();
+
+ for(ImporterConfiguration ic : list) {
+ ret.put(ic.getImporterClassName(), ic.getConfiguration());
}
return ret;
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerLocal.java
index 7c1147a..c4506b3 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerLocal.java
@@ -27,7 +27,7 @@ import javax.ejb.Local;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.authz.Permission;
-import org.rhq.core.domain.sync.ExportValidationReport;
+import org.rhq.core.domain.sync.ConsistencyValidatorFailureReport;
import org.rhq.core.domain.sync.ExportWrapper;
import org.rhq.core.domain.sync.ExportReport;
import org.rhq.core.domain.sync.ImportReport;
@@ -60,9 +60,9 @@ public interface SynchronizationManagerLocal {
*/
ExportWrapper exportAllSubsystemsLocally(Subject subject);
- ExportValidationReport validate(Subject subject, InputStream exportFile);
+ void validate(Subject subject, InputStream exportFile) throws ValidationException;
- ImportReport importAllSubsystems(Subject subject, InputStream exportFile,
List<ImporterConfiguration> importerConfigurations);
+ void importAllSubsystems(Subject subject, InputStream exportFile,
List<ImporterConfiguration> importerConfigurations) throws ValidationException,
ImportException;
//-------- THE FOLLOWING METHODS ARE SHARED WITH THE REMOTE INTERFACE ------------
@@ -80,7 +80,7 @@ public interface SynchronizationManagerLocal {
/**
* @see SynchronizationManagerRemote#validate(Subject, byte[])
*/
- ExportValidationReport validate(Subject subject, byte[] exportFile);
+ void validate(Subject subject, byte[] exportFile) throws ValidationException;
/**
* @see SynchronizationManagerRemote#getImporterConfigurationDefinition(String)
@@ -95,5 +95,5 @@ public interface SynchronizationManagerLocal {
/**
* @see SynchronizationManagerRemote#importAllSubsystems(Subject, byte[], Set)
*/
- ImportReport importAllSubsystems(Subject subject, byte[] exportFile,
List<ImporterConfiguration> importerConfigurations);
+ void importAllSubsystems(Subject subject, byte[] exportFile,
List<ImporterConfiguration> importerConfigurations) throws ValidationException,
ImportException;
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerRemote.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerRemote.java
index 937226a..9145387 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerRemote.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/SynchronizationManagerRemote.java
@@ -27,7 +27,7 @@ import javax.ejb.Remote;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.authz.Permission;
import org.rhq.core.domain.sync.ExportReport;
-import org.rhq.core.domain.sync.ExportValidationReport;
+import org.rhq.core.domain.sync.ConsistencyValidatorFailureReport;
import org.rhq.core.domain.sync.ImportReport;
import org.rhq.core.domain.sync.ImporterConfiguration;
import org.rhq.core.domain.sync.ImporterConfigurationDefinition;
@@ -58,7 +58,7 @@ public interface SynchronizationManagerRemote {
*/
ExportReport exportAllSubsystems(Subject subject);
- ExportValidationReport validate(Subject subject, byte[] exportFile);
+ void validate(Subject subject, byte[] exportFile) throws ValidationException;
/**
* Returns the configuration definition of the importer with given type.
@@ -82,5 +82,5 @@ public interface SynchronizationManagerRemote {
*
* @return the report describing the result of the export
*/
- ImportReport importAllSubsystems(Subject subject, byte[] exportFile,
List<ImporterConfiguration> importerConfigurations);
+ void importAllSubsystems(Subject subject, byte[] exportFile,
List<ImporterConfiguration> importerConfigurations) throws ValidationException,
ImportException;
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ValidationException.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ValidationException.java
new file mode 100644
index 0000000..9dee803
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/ValidationException.java
@@ -0,0 +1,85 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2011 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.rhq.enterprise.server.sync;
+
+import java.util.Set;
+
+import org.rhq.core.domain.sync.ConsistencyValidatorFailureReport;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class ValidationException extends SynchronizationException {
+
+ private static final long serialVersionUID = 1L;
+
+ private Set<ConsistencyValidatorFailureReport> validationReports;
+
+ public ValidationException(Set<ConsistencyValidatorFailureReport>
validationReports) {
+ this.validationReports = validationReports;
+ }
+
+ public ValidationException(String message,
Set<ConsistencyValidatorFailureReport> validationReports) {
+ super(message);
+ this.validationReports = validationReports;
+ }
+
+ public ValidationException(String message, Throwable cause,
Set<ConsistencyValidatorFailureReport> validationReports) {
+ super(message, cause);
+ this.validationReports = validationReports;
+ }
+
+ public ValidationException(Throwable cause,
Set<ConsistencyValidatorFailureReport> validationReports) {
+ super(cause);
+ this.validationReports = validationReports;
+ }
+
+ public ValidationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ValidationException(String message) {
+ super(message);
+ }
+
+ public ValidationException(Throwable cause) {
+ super(cause);
+ }
+
+ public Set<ConsistencyValidatorFailureReport> getValidationReports() {
+ return validationReports;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder bld = new StringBuilder(super.toString());
+
+ if (validationReports != null && !validationReports.isEmpty()) {
+ bld.append("\nReports of individual validators:\n");
+ for(ConsistencyValidatorFailureReport r : validationReports) {
+ bld.append("\n").append(r);
+ }
+ }
+
+ return bld.toString();
+ }
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/Importer.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/Importer.java
index a498303..af62706 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/Importer.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/importers/Importer.java
@@ -65,7 +65,7 @@ public interface Importer<Entity, ExportedType> {
* This method is responsible for persisting the entity in the database
* using the provided entityManager.
*
- * @param entity the entity to persist (may be null if the {@link
#getExportedEntityMatcher(EntityManager)} returned null of if the entity matcher
didn't find a match)
+ * @param entity the entity to persist (may be null if the {@link
#getExportedEntityMatcher()} returned null of if the entity matcher didn't find a
match)
* @param exportedEntity the entity found in the export file that should be used to
update the entity in the database
*/
void update(Entity entity, ExportedType exportedEntity);
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/validators/ConsistencyValidator.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/validators/ConsistencyValidator.java
index 1bf5130..b611aed 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/validators/ConsistencyValidator.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/sync/validators/ConsistencyValidator.java
@@ -88,7 +88,7 @@ public interface ConsistencyValidator {
/**
* The {@link ConsistencyValidator} interface mandates a certain behavior of
- * the {@link #equals(Object)} method and therefore the
<code>hashChode</code>
+ * the {@link #equals(Object)} method and therefore the
<code>hashCode</code>
* should be reimplemented accordingly.
*
* @return
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
index 94ff9a9..1725770 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
@@ -123,7 +123,7 @@ import
org.rhq.core.domain.resource.composite.ProblemResourceComposite;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.resource.group.composite.ResourceGroupComposite;
import org.rhq.core.domain.sync.ExportReport;
-import org.rhq.core.domain.sync.ExportValidationReport;
+import org.rhq.core.domain.sync.ConsistencyValidatorFailureReport;
import org.rhq.core.domain.sync.ImportReport;
import org.rhq.core.domain.sync.ImporterConfiguration;
import org.rhq.core.domain.sync.ImporterConfigurationDefinition;
@@ -166,7 +166,9 @@ import
org.rhq.enterprise.server.resource.group.ResourceGroupDeleteException;
import org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal;
import org.rhq.enterprise.server.resource.group.ResourceGroupNotFoundException;
import org.rhq.enterprise.server.support.SupportManagerLocal;
+import org.rhq.enterprise.server.sync.ImportException;
import org.rhq.enterprise.server.sync.SynchronizationManagerLocal;
+import org.rhq.enterprise.server.sync.ValidationException;
import org.rhq.enterprise.server.system.ServerVersion;
import org.rhq.enterprise.server.system.SystemManagerLocal;
import org.rhq.enterprise.server.util.LookupUtil;
@@ -1208,8 +1210,8 @@ public class WebservicesManagerBean implements WebservicesRemote {
return synchronizationManager.exportAllSubsystems(subject);
}
- public ExportValidationReport validate(Subject subject, byte[] exportFile) {
- return synchronizationManager.validate(subject, exportFile);
+ public void validate(Subject subject, byte[] exportFile) throws ValidationException
{
+ synchronizationManager.validate(subject, exportFile);
}
public List<ImporterConfigurationDefinition>
getConfigurationDefinitionOfAllImporters() {
@@ -1220,9 +1222,9 @@ public class WebservicesManagerBean implements WebservicesRemote {
return synchronizationManager.getImporterConfigurationDefinition(importerClass);
}
- public ImportReport importAllSubsystems(Subject subject, byte[] exportFile,
- List<ImporterConfiguration> importerConfigurations) {
- return synchronizationManager.importAllSubsystems(subject, exportFile,
importerConfigurations);
+ public void importAllSubsystems(Subject subject, byte[] exportFile,
+ List<ImporterConfiguration> importerConfigurations) throws
ValidationException, ImportException {
+ synchronizationManager.importAllSubsystems(subject, exportFile,
importerConfigurations);
}
//SYNCHRONIZATIONMANANGER: END -------------------------