Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: openmrs/openmrs-core
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1e84d0b96c37
Choose a base ref
...
head repository: openmrs/openmrs-core
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 64b539db0ea6
Choose a head ref
  • 2 commits
  • 18 files changed
  • 2 contributors

Commits on Jan 20, 2014

  1. Copy the full SHA
    a47d42e View commit details

Commits on Jan 22, 2014

  1. Merge pull request #499 from rkorytkowski/TRUNK-3644

    TRUNK-3644 Define which resources to load for module based on OpenMRS version
    rkorytkowski committed Jan 22, 2014
    Copy the full SHA
    64b539d View commit details
17 changes: 17 additions & 0 deletions api/pom.xml
Original file line number Diff line number Diff line change
@@ -196,6 +196,23 @@
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>openmrs.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>openmrs.properties</exclude>
</excludes>
</resource>
</resources>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
37 changes: 37 additions & 0 deletions api/src/main/java/org/openmrs/annotation/OpenmrsProfile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* The contents of this file are subject to the OpenMRS Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://license.openmrs.org
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
*/
package org.openmrs.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Placed on beans which should be loaded conditionally based on OpenMRS version or started modules.
*
* @since 1.10, 1.9.8, 1.8.5, 1.7.5
*/
@Target( { ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface OpenmrsProfile {

public String openmrsVersion() default "";

public String[] modules() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* The contents of this file are subject to the OpenMRS Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://license.openmrs.org
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
*/
package org.openmrs.annotation;

import org.apache.commons.lang.StringUtils;
import org.openmrs.module.Module;
import org.openmrs.module.ModuleFactory;
import org.openmrs.module.ModuleUtil;
import org.openmrs.util.OpenmrsConstants;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;
import java.util.Map;

/**
* Creates a bean only if profile is matched. It returns true if a bean should be excluded.
*/
public class OpenmrsProfileExclusionFilter implements TypeFilter {

/**
* @param metadataReader
* @param metadataReaderFactory
* @return
* @throws IOException
*
* @should not include bean for openmrs from 1_6 to 1_8
* @should include bean for openmrs 1_10 and later
* @should not include bean for openmrs 1_10 and later if module missing
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
Map<String, Object> openmrsProfile = metadataReader.getAnnotationMetadata().getAnnotationAttributes(
"org.openmrs.annotation.OpenmrsProfile");
if (openmrsProfile != null) {
Object openmrsVersion = openmrsProfile.get("openmrsVersion");
if (StringUtils.isNotBlank((String) openmrsVersion)) {
if (!ModuleUtil.matchRequiredVersions(OpenmrsConstants.OPENMRS_VERSION_SHORT, (String) openmrsVersion)) {
return true; //exclude
}
}

String[] modules = (String[]) openmrsProfile.get("modules");

for (String moduleAndVersion : modules) {
boolean exclude = true;

String[] splitModuleAndVersion = moduleAndVersion.split(":");
String moduleId = splitModuleAndVersion[0];
String moduleVersion = splitModuleAndVersion[1];

for (Module module : ModuleFactory.getStartedModules()) {
if (module.getModuleId().equals(moduleId)) {
if (ModuleUtil.matchRequiredVersions(module.getVersion(), moduleVersion)) {
exclude = false;
break;
}
}
}

if (exclude) {
return exclude;
}
}
}

return false; //include
}
}
14 changes: 14 additions & 0 deletions api/src/main/java/org/openmrs/module/Module.java
Original file line number Diff line number Diff line change
@@ -95,6 +95,8 @@ public final class Module {

private boolean mandatory = Boolean.FALSE;

private List<ModuleConditionalResource> conditionalResources = new ArrayList<ModuleConditionalResource>();

// keep a reference to the file that we got this module from so we can delete
// it if necessary
private File file = null;
@@ -361,6 +363,10 @@ public List<String> getAwareOfModules() {
return awareOfModulesMap == null ? null : new ArrayList<String>(awareOfModulesMap.keySet());
}

public String getAwareOfModuleVersion(String awareOfModule) {
return awareOfModulesMap == null ? null : awareOfModulesMap.get(awareOfModule);
}

/**
* @return the requireOpenmrsVersion
*/
@@ -751,4 +757,12 @@ public void disposeAdvicePointsClassInstance() {
advicePoint.disposeClassInstance();
}
}

public List<ModuleConditionalResource> getConditionalResources() {
return conditionalResources;
}

public void setConditionalResources(List<ModuleConditionalResource> conditionalResources) {
this.conditionalResources = conditionalResources;
}
}
117 changes: 97 additions & 20 deletions api/src/main/java/org/openmrs/module/ModuleClassLoader.java
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
@@ -40,10 +41,12 @@
import java.util.WeakHashMap;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.api.APIException;
import org.openmrs.util.OpenmrsClassLoader;
import org.openmrs.util.OpenmrsConstants;
import org.openmrs.util.OpenmrsUtil;

/**
@@ -83,8 +86,8 @@ protected ModuleClassLoader(final Module module, final List<URL> urls, final Cla
log.debug("URLs length: " + urls.size());

this.module = module;
collectRequiredModuleImports();
collectAwareOfModuleImports();
requiredModules = collectRequiredModuleImports(module);
awareOfModules = collectAwareOfModuleImports(module);
collectFilters();
libraryCache = new WeakHashMap<URL, File>();
}
@@ -212,12 +215,32 @@ private static List<URL> getUrls(final Module module) {
File libdir = new File(tmpModuleDir, "lib");

if (libdir != null && libdir.exists()) {
Map<String, String> startedRelatedModules = new HashMap<String, String>();
for (Module requiredModule : collectRequiredModuleImports(module)) {
startedRelatedModules.put(requiredModule.getModuleId(), requiredModule.getVersion());
}
for (Module awareOfModule : collectAwareOfModuleImports(module)) {
startedRelatedModules.put(awareOfModule.getModuleId(), awareOfModule.getVersion());
}

// recursively get files
Collection<File> files = (Collection<File>) FileUtils.listFiles(libdir, new String[] { "jar" }, true);
for (File file : files) {
if (log.isDebugEnabled())
log.debug("Adding file to results: " + file.getAbsolutePath());
result.add(ModuleUtil.file2url(file));
URL fileUrl = ModuleUtil.file2url(file);

boolean include = shouldResourceBeIncluded(module, fileUrl, OpenmrsConstants.OPENMRS_VERSION_SHORT,
startedRelatedModules);

if (include) {
if (log.isDebugEnabled()) {
log.debug("Including file in classpath: " + fileUrl);
}
result.add(fileUrl);
} else {
if (log.isDebugEnabled()) {
log.debug("Excluding file from classpath: " + fileUrl);
}
}
}
}
}
@@ -233,6 +256,63 @@ private static List<URL> getUrls(final Module module) {
return result;
}

/**
* Determines whether or not the given resource should be available on the classpath based on
* OpenMRS version and/or modules' version. It uses the conditionalResources section specified in config.xml.
*
* Resources that are not mentioned as conditional resources are included by default.
*
* All conditions for a conditional resource to be included must match.
*
* @param module
* @param fileUrl
* @return true if it should be included
* @should return true if file matches and openmrs version matches
* @should return false if file matches but openmrs version does not
* @should return true if file does not match and openmrs version does not match
* @should return true if file matches and module version matches
* @should return false if file matches and module version does not match
* @should return false if file matches and openmrs version matches but module version does not match
* @should return false if file matches and module not found
* @should return true if file does not match and module version does not match
*/
static boolean shouldResourceBeIncluded(Module module, URL fileUrl, String openmrsVersion,
Map<String, String> startedRelatedModules) {
boolean include = true; //all resources are included by default

for (ModuleConditionalResource conditionalResource : module.getConditionalResources()) {
if (fileUrl.getPath().matches(".*" + conditionalResource.getPath() + "$")) {
include = false; //if a resource matches a path of contidionalResource then it must meet all conditions

if (StringUtils.isNotBlank(conditionalResource.getOpenmrsVersion())) { //openmrsVersion is optional
include = ModuleUtil.matchRequiredVersions(openmrsVersion, conditionalResource.getOpenmrsVersion());

if (!include) {
return false;
}
}

if (conditionalResource.getModules() != null) { //modules are optional
for (ModuleConditionalResource.ModuleAndVersion conditionalModuleResource : conditionalResource
.getModules()) {
String moduleVersion = startedRelatedModules.get(conditionalModuleResource.getModuleId());
if (moduleVersion != null) {
include = ModuleUtil
.matchRequiredVersions(moduleVersion, conditionalModuleResource.getVersion());

if (!include) {
return false;
}
}
}

}
}
}

return include;
}

/**
* Get the library cache folder for the given module. Each module has a different cache folder
* to ease cleanup when unloading a module while openmrs is running
@@ -275,51 +355,50 @@ private static List<URL> getUrls(final Module module, final URL[] existingUrls)
* Get and cache the imports for this module. The imports should just be the modules that set as
* "required" by this module
*/
protected void collectRequiredModuleImports() {
protected static Module[] collectRequiredModuleImports(Module module) {
// collect imported modules (exclude duplicates)
Map<String, Module> publicImportsMap = new WeakHashMap<String, Module>(); //<module ID, Module>

for (String moduleId : ModuleConstants.CORE_MODULES.keySet()) {
Module module = ModuleFactory.getModuleById(moduleId);
Module coreModule = ModuleFactory.getModuleById(moduleId);

if (module == null && !ModuleUtil.ignoreCoreModules()) {
if (coreModule == null && !ModuleUtil.ignoreCoreModules()) {
log.error("Unable to find an openmrs core loaded module with id: " + moduleId);
throw new APIException(
"Should not be here. All 'core' required modules by the api should be started and their classloaders should be available");
}

// if this is already the classloader for one of the core modules, don't put it on the import list
if (module != null && !moduleId.equals(this.getModule().getModuleId())) {
publicImportsMap.put(moduleId, module);
if (coreModule != null && !moduleId.equals(module.getModuleId())) {
publicImportsMap.put(moduleId, coreModule);
}
}

for (String requiredPackage : getModule().getRequiredModules()) {
for (String requiredPackage : module.getRequiredModules()) {
Module requiredModule = ModuleFactory.getModuleByPackage(requiredPackage);
if (ModuleFactory.isModuleStarted(requiredModule)) {
publicImportsMap.put(requiredModule.getModuleId(), requiredModule);
}
}
requiredModules = publicImportsMap.values().toArray(new Module[publicImportsMap.size()]);
return publicImportsMap.values().toArray(new Module[publicImportsMap.size()]);

}

/**
* Get and cache the imports for this module. The imports should just be the modules that set as
* "aware of" by this module
*/
protected void collectAwareOfModuleImports() {
protected static Module[] collectAwareOfModuleImports(Module module) {
// collect imported modules (exclude duplicates)
Map<String, Module> publicImportsMap = new WeakHashMap<String, Module>(); //<module ID, Module>

for (String awareOfPackage : getModule().getAwareOfModules()) {
for (String awareOfPackage : module.getAwareOfModules()) {
Module awareOfModule = ModuleFactory.getModuleByPackage(awareOfPackage);
if (ModuleFactory.isModuleStarted(awareOfModule)) {
publicImportsMap.put(awareOfModule.getModuleId(), awareOfModule);
}
}
awareOfModules = publicImportsMap.values().toArray(new Module[publicImportsMap.size()]);

return publicImportsMap.values().toArray(new Module[publicImportsMap.size()]);
}

/**
@@ -359,8 +438,8 @@ protected void modulesSetChanged() {
}
log.debug(buf.toString());
}
collectRequiredModuleImports();
collectAwareOfModuleImports();
requiredModules = collectRequiredModuleImports(getModule());
awareOfModules = collectAwareOfModuleImports(getModule());
// repopulate resource URLs
//resourceLoader = ModuleResourceLoader.get(getModule());
collectFilters();
@@ -1010,7 +1089,6 @@ public void setAdditionalPackages(Set<String> additionalPackages) {
* module
*
* @param additionalPackage string package name
* @see #setProvidedPackages(Set)
*/
public void addAdditionalPackage(String additionalPackage) {
if (this.additionalPackages == null)
@@ -1027,7 +1105,6 @@ public void addAdditionalPackage(String additionalPackage) {
* module
*
* @param providedPackages list/set of strings that are package names
* @see #setProvidedPackages(Set)
*/
public void addAllAdditionalPackages(Collection<String> providedPackages) {
if (this.additionalPackages == null)
Loading