View Javadoc
1   package ca.uhn.fhir.jpa.util;
2   
3   /*-
4    * #%L
5    * HAPI FHIR JPA Server
6    * %%
7    * Copyright (C) 2014 - 2019 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
24  import com.google.common.collect.ImmutableSet;
25  import com.google.common.reflect.ClassPath;
26  import com.google.common.reflect.ClassPath.ClassInfo;
27  import org.apache.commons.lang3.Validate;
28  import org.hl7.fhir.instance.model.api.IBaseResource;
29  import org.hl7.fhir.r4.model.InstantType;
30  import org.hl7.fhir.r4.model.Patient;
31  
32  import javax.persistence.*;
33  import java.io.IOException;
34  import java.lang.reflect.AnnotatedElement;
35  import java.lang.reflect.Field;
36  import java.util.Date;
37  import java.util.HashSet;
38  import java.util.Set;
39  
40  import static org.apache.commons.lang3.StringUtils.isBlank;
41  import static org.apache.commons.lang3.StringUtils.isNotBlank;
42  
43  public class TestUtil {
44  	private static final int MAX_LENGTH = 30;
45  	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestUtil.class);
46  
47  	/**
48  	 * non instantiable
49  	 */
50  	private TestUtil() {
51  		super();
52  	}
53  
54  	/**
55  	 * This is really only useful for unit tests, do not call otherwise
56  	 */
57  	public static void scanEntities(String packageName) throws IOException, ClassNotFoundException {
58  		ImmutableSet<ClassInfo> classes = ClassPath.from(TestUtil.class.getClassLoader()).getTopLevelClasses(packageName);
59  		Set<String> names = new HashSet<String>();
60  
61  		if (classes.size() <= 1) {
62  			throw new InternalErrorException("Found no classes");
63  		}
64  
65  		for (ClassInfo classInfo : classes) {
66  			Class<?> clazz = Class.forName(classInfo.getName());
67  			Entity entity = clazz.getAnnotation(Entity.class);
68  			if (entity == null) {
69  				continue;
70  			}
71  
72  			scanClass(names, clazz, false);
73  
74  		}
75  	}
76  
77  	private static void scanClass(Set<String> theNames, Class<?> theClazz, boolean theIsSuperClass) {
78  		ourLog.info("Scanning: {}", theClazz.getSimpleName());
79  
80  		scan(theClazz, theNames, theIsSuperClass);
81  
82  		for (Field nextField : theClazz.getDeclaredFields()) {
83  			ourLog.info(" * Scanning field: {}", nextField.getName());
84  			scan(nextField, theNames, theIsSuperClass);
85  
86  			Lob lobClass = nextField.getAnnotation(Lob.class);
87  			if (lobClass != null) {
88  				if (nextField.getType().equals(byte[].class) == false) {
89  					//Validate.isTrue(false);
90  				}
91  			}
92  
93  		}
94  
95  		if (theClazz.getSuperclass().equals(Object.class)) {
96  			return;
97  		}
98  
99  		scanClass(theNames, theClazz.getSuperclass(), true);
100 	}
101 
102 	private static void scan(AnnotatedElement theAnnotatedElement, Set<String> theNames, boolean theIsSuperClass) {
103 		Table table = theAnnotatedElement.getAnnotation(Table.class);
104 		if (table != null) {
105 			assertNotADuplicateName(table.name(), theNames);
106 			for (UniqueConstraint nextConstraint : table.uniqueConstraints()) {
107 				assertNotADuplicateName(nextConstraint.name(), theNames);
108 				Validate.isTrue(nextConstraint.name().startsWith("IDX_"), nextConstraint.name() + " must start with IDX_");
109 			}
110 			for (Index nextConstraint : table.indexes()) {
111 				assertNotADuplicateName(nextConstraint.name(), theNames);
112 				Validate.isTrue(nextConstraint.name().startsWith("IDX_"), nextConstraint.name() + " must start with IDX_");
113 			}
114 		}
115 
116 		JoinColumn joinColumn = theAnnotatedElement.getAnnotation(JoinColumn.class);
117 		if (joinColumn != null) {
118 			assertNotADuplicateName(joinColumn.name(), null);
119 			ForeignKey fk = joinColumn.foreignKey();
120 			if (theIsSuperClass) {
121 				Validate.isTrue(isBlank(fk.name()), "Foreign key on " + theAnnotatedElement.toString() + " has a name() and should not as it is a superclass");
122 			} else {
123 				Validate.notNull(fk);
124 				Validate.isTrue(isNotBlank(fk.name()), "Foreign key on " + theAnnotatedElement.toString() + " has no name()");
125 				Validate.isTrue(fk.name().startsWith("FK_"));
126 				assertNotADuplicateName(fk.name(), theNames);
127 			}
128 		}
129 
130 		Column column = theAnnotatedElement.getAnnotation(Column.class);
131 		if (column != null) {
132 			assertNotADuplicateName(column.name(), null);
133 			Validate.isTrue(column.unique() == false, "Should not use unique attribute on column (use named @UniqueConstraint instead) on " + theAnnotatedElement.toString());
134 		}
135 
136 		GeneratedValue gen = theAnnotatedElement.getAnnotation(GeneratedValue.class);
137 		SequenceGenerator sg = theAnnotatedElement.getAnnotation(SequenceGenerator.class);
138 		Validate.isTrue((gen != null) == (sg != null));
139 		if (gen != null) {
140 			assertNotADuplicateName(gen.generator(), theNames);
141 			assertNotADuplicateName(sg.name(), null);
142 			assertNotADuplicateName(sg.sequenceName(), null);
143 			assertEquals(gen.generator(), sg.name());
144 			assertEquals(gen.generator(), sg.sequenceName());
145 		}
146 
147 	}
148 
149 	private static void assertEquals(String theGenerator, String theName) {
150 		Validate.isTrue(theGenerator.equals(theName));
151 	}
152 
153 	private static void assertNotADuplicateName(String theName, Set<String> theNames) {
154 		if (isBlank(theName)) {
155 			return;
156 		}
157 		Validate.isTrue(theName.length() <= MAX_LENGTH, "Identifier \"" + theName + "\" is " + theName.length() + " chars long");
158 		if (theNames != null) {
159 			Validate.isTrue(theNames.add(theName), "Duplicate name: " + theName);
160 		}
161 	}
162 
163 	public static void sleepAtLeast(int theMillis) {
164 		long start = System.currentTimeMillis();
165 		while (System.currentTimeMillis() <= start + theMillis) {
166 			try {
167 				long timeSinceStarted = System.currentTimeMillis() - start;
168 				long timeToSleep = Math.max(0, theMillis - timeSinceStarted);
169 				ourLog.info("Sleeping for {}ms", timeToSleep);
170 				Thread.sleep(timeToSleep);
171 			} catch (InterruptedException theE) {
172 				ourLog.error("Interrupted", theE);
173 			}
174 		}
175 	}
176 
177 
178 	public static void clearAllStaticFieldsForUnitTest() {
179 		ca.uhn.fhir.util.TestUtil.clearAllStaticFieldsForUnitTest();
180 	}
181 
182 	public static InstantType getTimestamp(IBaseResource resource) {
183 		return new InstantType(new Date(resource.getMeta().getLastUpdated().getTime()));
184 	}
185 
186 	public static void sleepOneClick() {
187 		sleepAtLeast(1);
188 	}
189 }