View Javadoc
1   package ca.uhn.fhir.jpa.util;
2   
3   /*-
4    * #%L
5    * HAPI FHIR JPA Server
6    * %%
7    * Copyright (C) 2014 - 2018 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  
29  import javax.persistence.*;
30  import java.io.IOException;
31  import java.lang.reflect.AnnotatedElement;
32  import java.lang.reflect.Field;
33  import java.util.HashSet;
34  import java.util.Set;
35  
36  import static org.apache.commons.lang3.StringUtils.isBlank;
37  import static org.apache.commons.lang3.StringUtils.isNotBlank;
38  
39  public class TestUtil {
40  	private static final int MAX_LENGTH = 30;
41  	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestUtil.class);
42  
43  	/**
44  	 * non instantiable
45  	 */
46  	private TestUtil() {
47  		super();
48  	}
49  
50  	/**
51  	 * This is really only useful for unit tests, do not call otherwise
52  	 */
53  	public static void scanEntities(String packageName) throws IOException, ClassNotFoundException {
54  		ImmutableSet<ClassInfo> classes = ClassPath.from(TestUtil.class.getClassLoader()).getTopLevelClasses(packageName);
55  		Set<String> names = new HashSet<String>();
56  
57  		if (classes.size() <= 1) {
58  			throw new InternalErrorException("Found no classes");
59  		}
60  
61  		for (ClassInfo classInfo : classes) {
62  			Class<?> clazz = Class.forName(classInfo.getName());
63  			Entity entity = clazz.getAnnotation(Entity.class);
64  			if (entity == null) {
65  				continue;
66  			}
67  
68  			scanClass(names, clazz, false);
69  
70  		}
71  	}
72  
73  	private static void scanClass(Set<String> theNames, Class<?> theClazz, boolean theIsSuperClass) {
74  		ourLog.info("Scanning: {}", theClazz.getSimpleName());
75  
76  		scan(theClazz, theNames, theIsSuperClass);
77  
78  		for (Field nextField : theClazz.getDeclaredFields()) {
79  			ourLog.info(" * Scanning field: {}", nextField.getName());
80  			scan(nextField, theNames, theIsSuperClass);
81  		}
82  
83  		if (theClazz.getSuperclass().equals(Object.class)) {
84  			return;
85  		}
86  
87  		scanClass(theNames, theClazz.getSuperclass(), true);
88  	}
89  
90  	private static void scan(AnnotatedElement ae, Set<String> theNames, boolean theIsSuperClass) {
91  		Table table = ae.getAnnotation(Table.class);
92  		if (table != null) {
93  			assertNotADuplicateName(table.name(), theNames);
94  			for (UniqueConstraint nextConstraint : table.uniqueConstraints()) {
95  				assertNotADuplicateName(nextConstraint.name(), theNames);
96  				Validate.isTrue(nextConstraint.name().startsWith("IDX_"), nextConstraint.name() + " must start with IDX_");
97  			}
98  			for (Index nextConstraint : table.indexes()) {
99  				assertNotADuplicateName(nextConstraint.name(), theNames);
100 				Validate.isTrue(nextConstraint.name().startsWith("IDX_"), nextConstraint.name() + " must start with IDX_");
101 			}
102 		}
103 
104 		JoinColumn joinColumn = ae.getAnnotation(JoinColumn.class);
105 		if (joinColumn != null) {
106 			assertNotADuplicateName(joinColumn.name(), null);
107 			ForeignKey fk = joinColumn.foreignKey();
108 			if (theIsSuperClass) {
109 				Validate.isTrue(isBlank(fk.name()), "Foreign key on " + ae.toString() + " has a name() and should not as it is a superclass");
110 			} else {
111 				Validate.notNull(fk);
112 				Validate.isTrue(isNotBlank(fk.name()), "Foreign key on " + ae.toString() + " has no name()");
113 				Validate.isTrue(fk.name().startsWith("FK_"));
114 				assertNotADuplicateName(fk.name(), theNames);
115 			}
116 		}
117 
118 		Column column = ae.getAnnotation(Column.class);
119 		if (column != null) {
120 			assertNotADuplicateName(column.name(), null);
121 			Validate.isTrue(column.unique() == false, "Should not use unique attribute on column (use named @UniqueConstraint instead) on " + ae.toString());
122 		}
123 
124 		GeneratedValue gen = ae.getAnnotation(GeneratedValue.class);
125 		SequenceGenerator sg = ae.getAnnotation(SequenceGenerator.class);
126 		Validate.isTrue((gen != null) == (sg != null));
127 		if (gen != null) {
128 			assertNotADuplicateName(gen.generator(), theNames);
129 			assertNotADuplicateName(sg.name(), null);
130 			assertNotADuplicateName(sg.sequenceName(), null);
131 			assertEquals(gen.generator(), sg.name());
132 			assertEquals(gen.generator(), sg.sequenceName());
133 		}
134 
135 	}
136 
137 	private static void assertEquals(String theGenerator, String theName) {
138 		Validate.isTrue(theGenerator.equals(theName));
139 	}
140 
141 	private static void assertNotADuplicateName(String theName, Set<String> theNames) {
142 		if (isBlank(theName)) {
143 			return;
144 		}
145 		Validate.isTrue(theName.length() <= MAX_LENGTH, "Identifier \"" + theName + "\" is " + theName.length() + " chars long");
146 		if (theNames != null) {
147 			Validate.isTrue(theNames.add(theName), "Duplicate name: " + theName);
148 		}
149 	}
150 
151 	public static void sleepAtLeast(int theMillis) {
152 		long start = System.currentTimeMillis();
153 		while (System.currentTimeMillis() <= start + theMillis) {
154 			try {
155 				long timeSinceStarted = System.currentTimeMillis() - start;
156 				long timeToSleep = Math.max(0, theMillis - timeSinceStarted);
157 				ourLog.info("Sleeping for {}ms", timeToSleep);
158 				Thread.sleep(timeToSleep);
159 			} catch (InterruptedException theE) {
160 				theE.printStackTrace();
161 			}
162 		}
163 	}
164 
165 
166 }