Skip to content

Commit 6554163

Browse files
committed
EA-219 Adding back MetadataUtil
1 parent f9c292f commit 6554163

File tree

1 file changed

+341
-0
lines changed

1 file changed

+341
-0
lines changed
Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
/**
2+
* The contents of this file are subject to the OpenMRS Public License
3+
* Version 1.0 (the "License"); you may not use this file except in
4+
* compliance with the License. You may obtain a copy of the License at
5+
* http://license.openmrs.org
6+
*
7+
* Software distributed under the License is distributed on an "AS IS"
8+
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
9+
* License for the specific language governing rights and limitations
10+
* under the License.
11+
*
12+
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
13+
*/
14+
package org.openmrs.module.emrapi.utils;
15+
16+
import org.apache.commons.beanutils.PropertyUtils;
17+
import org.apache.commons.io.IOUtils;
18+
import org.apache.commons.logging.Log;
19+
import org.apache.commons.logging.LogFactory;
20+
import org.openmrs.Auditable;
21+
import org.openmrs.Concept;
22+
import org.openmrs.OpenmrsMetadata;
23+
import org.openmrs.api.context.Context;
24+
import org.openmrs.module.emrapi.metadata.MetadataPackageConfig;
25+
import org.openmrs.module.emrapi.metadata.MetadataPackagesConfig;
26+
import org.openmrs.module.metadatasharing.ImportConfig;
27+
import org.openmrs.module.metadatasharing.ImportedItem;
28+
import org.openmrs.module.metadatasharing.ImportedPackage;
29+
import org.openmrs.module.metadatasharing.MetadataSharing;
30+
import org.openmrs.module.metadatasharing.api.MetadataSharingService;
31+
import org.openmrs.module.metadatasharing.wrapper.PackageImporter;
32+
33+
import java.io.IOException;
34+
import java.io.InputStream;
35+
import java.util.ArrayList;
36+
import java.util.Arrays;
37+
import java.util.Collection;
38+
import java.util.Collections;
39+
import java.util.Comparator;
40+
import java.util.Date;
41+
import java.util.HashMap;
42+
import java.util.Iterator;
43+
import java.util.List;
44+
import java.util.Map;
45+
import java.util.Set;
46+
import java.util.TreeMap;
47+
import java.util.TreeSet;
48+
import java.util.regex.Matcher;
49+
import java.util.regex.Pattern;
50+
51+
/*@deprecated As of 1.31.0.This class has been moved to the openmrs Metadata Sharing Module
52+
* Ensure you have at least version 1.6.0 of Metadata Sharing Module
53+
* Change this to call class org.openmrs.module.metadatasharing.packages.MetadataUtil.java
54+
* Change your metadata files to use aliased name <MetadataPackagesConfig>
55+
* rather than the full name <org.openmrs.module.emrapi.metadata.MetadataPackagesConfig>
56+
* Change your metadata files to use aliased name <MetadataPackageConfig>
57+
* rather than the full name <org.openmrs.module.emrapi.metadata.MetadataPackageConfig>
58+
* See more info at {@link https://issues.openmrs.org/browse/EA-91}
59+
* */
60+
@Deprecated
61+
public class MetadataUtil {
62+
63+
protected static final Log log = LogFactory.getLog(MetadataUtil.class);
64+
65+
public static final String PACKAGES_FILENAME = "packages.xml";
66+
67+
/**
68+
* Setup the standard metadata packages
69+
*
70+
* @return
71+
*/
72+
public static boolean setupStandardMetadata(ClassLoader loader) throws Exception {
73+
return setupStandardMetadata(loader, PACKAGES_FILENAME);
74+
}
75+
76+
public static boolean setupStandardMetadata(ClassLoader loader, String packagesFilePath) throws Exception {
77+
MetadataPackagesConfig config = getMetadataPackagesForModule(loader, packagesFilePath);
78+
return loadPackages(config, loader);
79+
}
80+
81+
/**
82+
* Useful for testing, e.g. if you need to load up a specific MDS package
83+
* @param loader
84+
* @param namesToLoad something like Reference_Application_Visit_and_Encounter_Types
85+
* @return
86+
* @throws Exception
87+
*/
88+
public static boolean setupSpecificMetadata(ClassLoader loader, String... namesToLoad) throws Exception {
89+
List<String> namesToKeep = Arrays.asList(namesToLoad);
90+
MetadataPackagesConfig config = getMetadataPackagesForModule(loader);
91+
for (Iterator<MetadataPackageConfig> i = config.getPackages().iterator(); i.hasNext(); ) {
92+
MetadataPackageConfig packageConfig = i.next();
93+
if (!namesToKeep.contains(packageConfig.getFilenameBase())) {
94+
i.remove();
95+
}
96+
}
97+
return loadPackages(config, loader);
98+
}
99+
100+
private synchronized static boolean loadPackages(MetadataPackagesConfig config, ClassLoader loader) throws IOException {
101+
102+
List<PackageImporter> packageImporters = new ArrayList<PackageImporter>();
103+
104+
// first, instantiate and load a package importer for each package
105+
for (MetadataPackageConfig pkg : config.getPackages()) {
106+
PackageImporter packageImporter = loadPackageImporterIfNecessary(pkg, loader);
107+
if (packageImporter != null) {
108+
packageImporters.add(packageImporter);
109+
}
110+
}
111+
112+
// do the actual imports
113+
if (packageImporters.size() > 0) {
114+
115+
// sort in order of date created, with most recently created coming last: this is so in the case of inconsistent metadata between packages, the most recent one wins
116+
Collections.sort(packageImporters, new Comparator<PackageImporter>() {
117+
@Override
118+
public int compare(PackageImporter i1, PackageImporter i2) {
119+
return i1.getImportedPackage().getDateCreated().compareTo(i2.getImportedPackage().getDateCreated());
120+
}
121+
});
122+
123+
for (PackageImporter packageImporter : packageImporters) {
124+
long timer = System.currentTimeMillis();
125+
log.info("Importing package: " + packageImporter.getImportedPackage().getName());
126+
packageImporter .importPackage();
127+
log.info("Imported " + packageImporter.getImportedPackage().getName() + " in " + (System.currentTimeMillis() - timer) + "ms");
128+
}
129+
return true;
130+
}
131+
132+
return false;
133+
}
134+
135+
public static MetadataPackagesConfig getMetadataPackagesForModule(ClassLoader loader) {
136+
return getMetadataPackagesForModule(loader, PACKAGES_FILENAME);
137+
}
138+
139+
public static MetadataPackagesConfig getMetadataPackagesForModule(ClassLoader loader, String packageFilePath) {
140+
try {
141+
InputStream stream = loader.getResourceAsStream(packageFilePath);
142+
String xml = IOUtils.toString(stream);
143+
MetadataPackagesConfig config = Context.getSerializationService().getDefaultSerializer()
144+
.deserialize(xml, MetadataPackagesConfig.class);
145+
return config;
146+
}
147+
catch (Exception ex) {
148+
throw new RuntimeException("Cannot find " + packageFilePath + ", or error deserializing it", ex);
149+
}
150+
}
151+
152+
/**
153+
* Checks whether the given version of the MDS package has been installed yet, and if not,
154+
* install it
155+
*
156+
* @param config the metadata package configuration object
157+
* @param loader the class loader to use for loading the packages
158+
* @return whether any changes were made to the db
159+
* @throws IOException
160+
*/
161+
private static PackageImporter loadPackageImporterIfNecessary(MetadataPackageConfig config, ClassLoader loader)
162+
throws IOException {
163+
String filename = config.getFilenameBase() + "-" + config.getVersion().toString() + ".zip";
164+
try {
165+
166+
Matcher matcher = Pattern.compile("(?:.+/)?\\w+-(\\d+).zip").matcher(filename);
167+
if (!matcher.matches())
168+
throw new RuntimeException("Filename must match PackageNameWithNoSpaces-X.zip");
169+
Integer version = Integer.valueOf(matcher.group(1));
170+
171+
ImportedPackage installed = Context.getService(MetadataSharingService.class).getImportedPackageByGroup(
172+
config.getGroupUuid());
173+
if (installed != null && installed.getVersion() >= version) {
174+
log.info("Metadata package " + config.getFilenameBase() + " is already installed with version "
175+
+ installed.getVersion());
176+
return null;
177+
}
178+
179+
if (loader.getResource(filename) == null) {
180+
throw new RuntimeException("Cannot find " + filename + " for group " + config.getGroupUuid());
181+
}
182+
183+
PackageImporter metadataImporter = MetadataSharing.getInstance().newPackageImporter();
184+
metadataImporter.setImportConfig(ImportConfig.valueOf(config.getImportMode()));
185+
log.info("...loading package: " + filename);
186+
metadataImporter.loadSerializedPackageStream(loader.getResourceAsStream(filename));
187+
return metadataImporter;
188+
}
189+
catch (Exception ex) {
190+
log.error("Failed to install metadata package " + filename, ex);
191+
return null;
192+
}
193+
}
194+
195+
/**
196+
* If multiple MDS packages contain different versions of the same item, then loading them is order-dependent, which
197+
* is bad.
198+
* Any OpenMRS distribution that uses the #installMetadataPackageIfNecessary functionality in this class should
199+
* have a one-line unit test that calls this method, basically like:
200+
* <pre>
201+
* public class InconsistantMetadataTest extends BaseModuleContextSensitiveTest {
202+
* @Test
203+
* public void testThatThereAreNoMdsPackagesWithInconsistentVersionsOfTheSameItem() throws Exception {
204+
* MetadataUtil.verifyNoMdsPackagesWithInconsistentVersionsOfTheSameItem(getClass().getClassLoader());
205+
* }
206+
* }
207+
* </pre>
208+
*
209+
* @param classLoader
210+
* @throws Exception
211+
* @throws IllegalStateException if different versions of the same metadata item are contained in two packages
212+
*/
213+
public static void verifyNoMdsPackagesWithInconsistentVersionsOfTheSameItem(ClassLoader classLoader) throws Exception, IllegalStateException {
214+
ItemToDateMap itemToDateMap = new ItemToDateMap();
215+
boolean metadataConsistent = true;
216+
217+
MetadataPackagesConfig allConfigs = MetadataUtil.getMetadataPackagesForModule(classLoader);
218+
for (MetadataPackageConfig config : allConfigs.getPackages()) {
219+
String filenameBase = config.getFilenameBase();
220+
String filename = filenameBase + "-" + config.getVersion().toString() + ".zip";
221+
log.debug("Inspecting " + filename);
222+
System.out.println("Inspecting " + filename);
223+
224+
PackageImporter metadataImporter = MetadataSharing.getInstance().newPackageImporter();
225+
metadataImporter.setImportConfig(ImportConfig.valueOf(config.getImportMode()));
226+
metadataImporter.loadSerializedPackageStream(classLoader.getResourceAsStream(filename));
227+
for (int i = 0; i < metadataImporter.getPartsCount(); ++i) {
228+
Collection<ImportedItem> items = metadataImporter.getImportedItems(i);
229+
for (ImportedItem item : items) {
230+
metadataConsistent = itemToDateMap.addItem(item, filenameBase) && metadataConsistent;
231+
for (ImportedItem related : item.getRelatedItems()) {
232+
metadataConsistent = itemToDateMap.addItem(related, filenameBase) && metadataConsistent;
233+
}
234+
}
235+
}
236+
237+
log.debug("Finished. Running total number of distinct items: " + itemToDateMap.size());
238+
System.out.println("Finished. Running total number of distinct items: " + itemToDateMap.size());
239+
240+
Context.flushSession();
241+
Context.clearSession();
242+
}
243+
244+
if (log.isInfoEnabled()) {
245+
Map<String, Set<String>> repeated = itemToDateMap.repeatedItems();
246+
if (log.isDebugEnabled()) {
247+
log.debug("Items that occur in multiple MDS packages:");
248+
for (Map.Entry<String, Set<String>> e : repeated.entrySet()) {
249+
log.debug(e.getKey() + " -> " + e.getValue());
250+
}
251+
}
252+
log.info("Number of distinct items in multiple packages: " + repeated.size());
253+
log.info("Total number of distinct items: " + itemToDateMap.size());
254+
}
255+
256+
if (!metadataConsistent) {
257+
throw new IllegalStateException("Found inconsistent metadata");
258+
}
259+
260+
}
261+
262+
static class ItemToDateMap {
263+
264+
// classname + uuid to last date modified
265+
Map<String, Date> lastModifiedMap = new HashMap<String, Date>();
266+
Map<String, Set<String>> itemToPackages = new HashMap<String, Set<String>>();
267+
268+
public boolean addItem(ImportedItem item, String filename) {
269+
270+
String key = getKey(item);
271+
Date lastModified = getLastModified(item.getIncoming());
272+
273+
Set<String> belongsToPackages = itemToPackages.get(key);
274+
if (belongsToPackages == null) {
275+
belongsToPackages = new TreeSet<String>();
276+
itemToPackages.put(key, belongsToPackages);
277+
}
278+
279+
Date existing = lastModifiedMap.get(key);
280+
if (existing == null) {
281+
lastModifiedMap.put(key, lastModified);
282+
}
283+
else {
284+
if (!existing.equals(lastModified)) {
285+
String name;
286+
if (item.getIncoming() instanceof Concept) {
287+
name = key + " " + ((Concept) item.getIncoming()).getName();
288+
}
289+
else if (item.getIncoming() instanceof OpenmrsMetadata) {
290+
name = key + " " + ((OpenmrsMetadata) item.getIncoming()).getName();
291+
}
292+
else {
293+
name = key;
294+
}
295+
log.error(("Found inconsistent versions of " + name + " in " + filename + " (" + lastModified + ") vs " + belongsToPackages + " (" + existing + ")"));
296+
return false;
297+
}
298+
}
299+
belongsToPackages.add(filename);
300+
return true;
301+
}
302+
303+
private Date getLastModified(Object object) {
304+
if (object instanceof Auditable) {
305+
Date dateChanged = ((Auditable) object).getDateChanged();
306+
if (dateChanged != null) {
307+
return dateChanged;
308+
} else {
309+
return ((Auditable) object).getDateCreated();
310+
}
311+
}
312+
else {
313+
throw new IllegalArgumentException("object must be Auditable");
314+
}
315+
}
316+
317+
private String getKey(ImportedItem item) {
318+
try {
319+
return item.getIncomingClassSimpleName() + ":" + PropertyUtils.getProperty(item.getIncoming(), "uuid");
320+
} catch (Exception e) {
321+
throw new RuntimeException("Couldn't get UUID from " + item.getIncoming());
322+
}
323+
}
324+
325+
public int size() {
326+
return lastModifiedMap.size();
327+
}
328+
329+
public Map<String, Set<String>> repeatedItems() {
330+
Map<String, Set<String>> repeatedItems = new TreeMap<String, Set<String>>();
331+
for (Map.Entry<String, Set<String>> candidate : itemToPackages.entrySet()) {
332+
if (candidate.getValue().size() > 1) {
333+
repeatedItems.put(candidate.getKey(), candidate.getValue());
334+
}
335+
}
336+
return repeatedItems;
337+
}
338+
339+
}
340+
341+
}

0 commit comments

Comments
 (0)