From 4f006d639a5972157e114fda3a14047e3c3f3a00 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Thu, 15 Feb 2018 10:19:47 -0600 Subject: [PATCH 01/17] dotCMS/core#13482 --- OSGi/com.dotcms.override/build.gradle | 25 ++++++++++++++++++- .../portlets/folders/model/Folder.java | 17 ++++++------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/OSGi/com.dotcms.override/build.gradle b/OSGi/com.dotcms.override/build.gradle index 1edd8984..78b4ed65 100644 --- a/OSGi/com.dotcms.override/build.gradle +++ b/OSGi/com.dotcms.override/build.gradle @@ -17,6 +17,7 @@ dependencies { compile('com.dotcms:dotcms:4.3.0') { transitive = true } } +//Plugin jar jar { manifest { attributes ( @@ -25,12 +26,34 @@ jar { 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotmarketing.osgi.override.Activator', 'Override-Classes': 'com.dotmarketing.portlets.folders.model.Folder', - 'DynamicImport-Package': '*', 'Import-Package': '!com.dotmarketing.portlets.folders.model,*;version=0' ) } } +//Fragment jar +task fragmentJar(type: Jar) { + + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + + manifest { + attributes ( + 'Bundle-Vendor': 'dotCMS', + 'Bundle-Description': 'dotCMS - Override fragment', + 'Bundle-DocURL': 'https://dotcms.com/', + 'Fragment-Host': 'system.bundle; extension:=framework', + 'Export-Package': 'com.dotcms.repackage.org.apache.logging.log4j,' + + 'com.dotcms.repackage.org.apache.logging.log4j.core,' + + 'com.dotcms.repackage.org.apache.logging.log4j.spi,' + + 'org.apache.commons.lang.builder' + ) + } +} + +jar.finalizedBy 'fragmentJar' + task wrapper(type: Wrapper) { gradleVersion = '4.2' } \ No newline at end of file diff --git a/OSGi/com.dotcms.override/src/main/java/com/dotmarketing/portlets/folders/model/Folder.java b/OSGi/com.dotcms.override/src/main/java/com/dotmarketing/portlets/folders/model/Folder.java index 0ffc4d1c..71815d9b 100644 --- a/OSGi/com.dotcms.override/src/main/java/com/dotmarketing/portlets/folders/model/Folder.java +++ b/OSGi/com.dotcms.override/src/main/java/com/dotmarketing/portlets/folders/model/Folder.java @@ -1,14 +1,6 @@ package com.dotmarketing.portlets.folders.model; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; - import com.dotcms.api.tree.Parentable; -import com.dotcms.repackage.org.apache.commons.lang.builder.ToStringBuilder; -import com.dotcms.api.tree.TreeableAPI; import com.dotmarketing.beans.Identifier; import com.dotmarketing.beans.Inode; import com.dotmarketing.business.APILocator; @@ -27,6 +19,12 @@ import com.dotmarketing.util.Logger; import com.dotmarketing.util.UtilMethods; import com.liferay.portal.model.User; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang.builder.ToStringBuilder; /** @author Hibernate CodeGenerator */ public class Folder extends Inode implements Serializable, Permissionable, Treeable, Ruleable, Parentable { @@ -282,7 +280,6 @@ public String getPath() { Identifier id = null; try { - id = APILocator.getIdentifierAPI().find(this.getIdentifier()); } catch (DotDataException e) { Logger.error(Folder.class, e.getMessage(), e); @@ -341,4 +338,4 @@ public boolean equals(Object o){ return true; } -} +} \ No newline at end of file From f0a32e7247158782fe6c229b74a587f7f3698495 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Thu, 15 Feb 2018 11:13:48 -0600 Subject: [PATCH 02/17] dotCMS/core#13482 --- OSGi/com.dotcms.override/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OSGi/com.dotcms.override/build.gradle b/OSGi/com.dotcms.override/build.gradle index 78b4ed65..86a228ce 100644 --- a/OSGi/com.dotcms.override/build.gradle +++ b/OSGi/com.dotcms.override/build.gradle @@ -41,7 +41,11 @@ task fragmentJar(type: Jar) { manifest { attributes ( 'Bundle-Vendor': 'dotCMS', + 'Bundle-Version': "${version}", + 'Bundle-Name': 'dotCMS Override fragment', + 'Bundle-SymbolicName': "${baseName}.fragment", 'Bundle-Description': 'dotCMS - Override fragment', + 'Bundle-ManifestVersion': '2', 'Bundle-DocURL': 'https://dotcms.com/', 'Fragment-Host': 'system.bundle; extension:=framework', 'Export-Package': 'com.dotcms.repackage.org.apache.logging.log4j,' + From 1ca0cc4f2263f2f63454845c86cb867c1f496b28 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Thu, 15 Feb 2018 15:02:50 -0600 Subject: [PATCH 03/17] dotCMS/core#13482 --- OSGi/com.dotcms.override/build.gradle | 72 +++++++++++++++++++-------- 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/OSGi/com.dotcms.override/build.gradle b/OSGi/com.dotcms.override/build.gradle index 86a228ce..b1af009d 100644 --- a/OSGi/com.dotcms.override/build.gradle +++ b/OSGi/com.dotcms.override/build.gradle @@ -17,7 +17,20 @@ dependencies { compile('com.dotcms:dotcms:4.3.0') { transitive = true } } +import java.util.jar.* + +ext { + bundleSymbolicName = "" + bundleVersion = "" + importPackage = "" + bundleManifestVersion = "" + bundleDocURL = "" + bundleVendor = "" +} + +///////////////////////// //Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -30,33 +43,52 @@ jar { ) } } +jar.finalizedBy 'fragmentJar' +///////////////////////// //Fragment jar +///////////////////////// + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} task fragmentJar(type: Jar) { - //Setting the fragment jar name - baseName = project.name - archiveName = "${baseName}.fragment-${version}.jar" + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" - manifest { - attributes ( - 'Bundle-Vendor': 'dotCMS', - 'Bundle-Version': "${version}", - 'Bundle-Name': 'dotCMS Override fragment', - 'Bundle-SymbolicName': "${baseName}.fragment", - 'Bundle-Description': 'dotCMS - Override fragment', - 'Bundle-ManifestVersion': '2', - 'Bundle-DocURL': 'https://dotcms.com/', - 'Fragment-Host': 'system.bundle; extension:=framework', - 'Export-Package': 'com.dotcms.repackage.org.apache.logging.log4j,' + - 'com.dotcms.repackage.org.apache.logging.log4j.core,' + - 'com.dotcms.repackage.org.apache.logging.log4j.spi,' + - 'org.apache.commons.lang.builder' - ) + manifest { + attributes ( + 'Bundle-Name': 'dotCMS Override fragment', + 'Bundle-Description': 'dotCMS - Override fragment', + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': 'system.bundle; extension:=framework', + 'Export-Package': "${importPackage}" + ) + } } } - -jar.finalizedBy 'fragmentJar' +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' From 47e9a5f30b768d8e7ac3a48341efbd52ef9c9774 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Thu, 15 Feb 2018 15:11:47 -0600 Subject: [PATCH 04/17] dotCMS/core#13482 --- OSGi/com.dotcms.override/build.gradle | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/OSGi/com.dotcms.override/build.gradle b/OSGi/com.dotcms.override/build.gradle index b1af009d..b3f86459 100644 --- a/OSGi/com.dotcms.override/build.gradle +++ b/OSGi/com.dotcms.override/build.gradle @@ -19,15 +19,6 @@ dependencies { import java.util.jar.* -ext { - bundleSymbolicName = "" - bundleVersion = "" - importPackage = "" - bundleManifestVersion = "" - bundleDocURL = "" - bundleVendor = "" -} - ///////////////////////// //Plugin jar ///////////////////////// @@ -49,6 +40,18 @@ jar.finalizedBy 'fragmentJar' //Fragment jar ///////////////////////// +ext { + bundleName = "dotCMS Override fragment" + bundleDescription = "dotCMS - Override fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} + /** * Reads the Manifest file of the just created plugin jar in order to get the required info * to automatically create the fragment jar. @@ -75,14 +78,14 @@ task fragmentJar(type: Jar) { manifest { attributes ( - 'Bundle-Name': 'dotCMS Override fragment', - 'Bundle-Description': 'dotCMS - Override fragment', + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", 'Bundle-Vendor': "${bundleVendor}", 'Bundle-Version': "${version}", 'Bundle-SymbolicName': "${baseName}.fragment", 'Bundle-ManifestVersion': "${bundleManifestVersion}", 'Bundle-DocURL': "${bundleDocURL}", - 'Fragment-Host': 'system.bundle; extension:=framework', + 'Fragment-Host': "${fragmentHost}", 'Export-Package': "${importPackage}" ) } From f2d292b5cd580d02661002b3078c8711bfb62d65 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Wed, 21 Feb 2018 13:43:11 -0600 Subject: [PATCH 05/17] dotCMS/core#13482 --- OSGi/com.dotcms.override/README.md | 94 +++++++++++++++++++++++++ OSGi/com.dotcms.override/README.textile | 77 -------------------- 2 files changed, 94 insertions(+), 77 deletions(-) create mode 100644 OSGi/com.dotcms.override/README.md delete mode 100644 OSGi/com.dotcms.override/README.textile diff --git a/OSGi/com.dotcms.override/README.md b/OSGi/com.dotcms.override/README.md new file mode 100644 index 00000000..2d0a83cc --- /dev/null +++ b/OSGi/com.dotcms.override/README.md @@ -0,0 +1,94 @@ +# README + +This bundle plugin is an example of how to override dotCMS classes with our bundle plugin. + +## How to build this example + +To install all you need to do is build the JAR. to do this run +`./gradlew jar` + +This will build a jar in the `build/libs` directory + +#### To install this bundle: +Copy the bundle jar file inside the Felix OSGI container (*dotCMS/felix/load*). + +OR + +Upload the bundle jar file using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + +#### To uninstall this bundle: +Remove the bundle jar file from the Felix OSGI container (*dotCMS/felix/load*). + +OR + +Undeploy the bundle using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to create a bundle plugin to override dotCMS classes + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath + +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Override-Classes: Comma separated list of classes. + List of classes we are trying to override, in order to override any dotCMS class is mandatory to add it to this list because dotCMS implementation of the class will be already loaded by the dotCMS class loader so if we don't explicit specify the classes to override the class loader wont try to load them. + DynamicImport-Package: * + Dynamically add required imports the plugin may need without add them explicitly + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* Exported Packages + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* Fragment + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +--- +## Components + +### com.dotmarketing.portlets.folders.model.Folder + +Copy of the original *com.dotmarketing.portlets.folders.model.Folder* that lives inside dotCMS but with small changes (Just logging code) inside the `getPath()` method in order to demonstrate we can override a dotCMS class with our implementation. + +### Activator + +This bundle activator extends from *com.dotmarketing.osgi.GenericBundleActivator* and implements `BundleActivator.start()`. + +* PLEASE note the `initializeServices( context )` call, this call is MANDATORY (!) as it will allow us to share resources between the bundle and the host container (dotCMS) and override classes. + +--- +## Testing + +For our redefinition of the class *com.dotmarketing.portlets.folders.model.Folder* we modified the body of the `getPath()` method, we added some debugging lines in order to prove it is working. +The *Folder* class is call it by our activator (*com.dotmarketing.osgi.override.Activator*) which prove the OSGI bundle is using our redefinition of the Folder class, but in order to demonstrate the dotCMS context is using it too go to: +*dotCMS Admin -> Site Browser -> Select any folder on the tree -> Add Folder*. + +The *Add Folder* uses the *Folder* class and if you deployed your OSGI plugin the debugging code we added should be visible on the logs. + +--- + +## Limitations (!) + +There are limitations on the hot deploy functionality for the OSGI Override plugin, there are some rules for the code you can modify on the redefined classes in order to see those changes when you upload the plugin. + +In order to support the overriding of a class inside ours OSGI plugins we use the java hot swapping, it allows to redefine classes, unfortunately, this redefinition is limited only to changing method bodies: + +> The redefinition may change method bodies, the constant pool and attributes. The redefinition must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance. + +As long as you don't add, remove or change methods (ONLY the methods bodies) for your redefined classes you will have an OSGI plugin that will reflect you changes when a deploy is done. \ No newline at end of file diff --git a/OSGi/com.dotcms.override/README.textile b/OSGi/com.dotcms.override/README.textile deleted file mode 100644 index 48ca55a7..00000000 --- a/OSGi/com.dotcms.override/README.textile +++ /dev/null @@ -1,77 +0,0 @@ - -h1. README - -This bundle plugin is an example of how to override dotCMS classes with our bundle plugin. - -h2. How to build this example - -To install all you need to do is build the JAR. to do this run -*./gradlew jar* -This will build a jar in the build/libs directory - -* To install this bundle: - Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR - Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -* To uninstall this bundle: - Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR - Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - -h2. How to create a bundle plugin to override dotCMS classes - -In order to create this OSGI plugin, you must create a META-INF/MANIFEST to be inserted into OSGI jar. -This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. -The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath - -If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): - - * *Bundle-Name*: The name of your bundle - * *Bundle-SymbolicName*: A short an unique name for the bundle - * *Bundle-Activator*: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) - * *Override-Classes*: Comma separated list of classes. - List of classes we are trying to override, in order to override any dotCMS class is mandatory to add it to this list because dotCMS implementation of the class will be already loaded by the dotCMS class loader so if we don't explicit specify the classes to override the class loader wont try to load them. - * *DynamicImport-Package: ** - Dynamically add required imports the plugin may need without add them explicitly - * *Import-Package*: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. - -h2. Beware (!) - -In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional. - -The DotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. -This is possible also using the dotCMS UI (CMS Admin->Dynamic Plugins->Exported Packages). - -Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. - - -h2. Components - -h3. com.dotmarketing.portlets.folders.model.Folder - -Copy of the original *com.dotmarketing.portlets.folders.model.Folder* that lives inside dotCMS but with small changes (Just logging code) inside the *getPath()* method in order to demonstrate we can override a dotCMS class with our implementation. - -h3. Activator - -This bundle activator extends from *com.dotmarketing.osgi.GenericBundleActivator* and implements *BundleActivator.start()*. - -* PLEASE note the *publishBundleServices( context )* call, this call is MANDATORY (!) as it will allow us to share resources between the bundle and the host container (dotCMS) and override classes. - -h2. Testing - -For our redefinition of the class *com.dotmarketing.portlets.folders.model.Folder* we modified the body of the *getPath()* method, we just added some debugging lines in order to prove it is working. -The *Folder* class is call it by our activator (*com.dotmarketing.osgi.override.Activator*) which prove the OSGI bundle is using our redefinition of the Folder class, but in order to demonstrate the dotCMS context is using it too go to: -*dotCMS Admin* -> *Site Browser* -> Select any folder on the tree -> *Add Folder* -The *Add Folder* use the *Folder* class and if you deployed your OSGI plugin the debugging code we added should be visible on the logs. -________________________________________________________________________________________ - -h1. Limitations (!) - -There are limitations on the hot deploy functionality for the OSGI Override plugin, there are some rules for the code you can modify on the redefined classes in order to see those changes when you upload the plugin. - -In order to support the overriding of a class inside ours OSGI plugins we use the java hot swapping, it allows to redefine classes, unfortunately, this redefinition is limited only to changing method bodies: - -bq. The redefinition may change method bodies, the constant pool and attributes. The redefinition must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance. - -As long as you don't add, remove or change methods (ONLY the methods bodies) for your redefined classes you will have an OSGI plugin that will reflect you changes when a deploy is done. \ No newline at end of file From 92bfeedb8c088a2080afb01f14ca6a744ec6b33a Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Wed, 21 Feb 2018 13:46:26 -0600 Subject: [PATCH 06/17] dotCMS/core#13482 --- OSGi/com.dotcms.override/README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/OSGi/com.dotcms.override/README.md b/OSGi/com.dotcms.override/README.md index 2d0a83cc..de02ed12 100644 --- a/OSGi/com.dotcms.override/README.md +++ b/OSGi/com.dotcms.override/README.md @@ -9,19 +9,21 @@ To install all you need to do is build the JAR. to do this run This will build a jar in the `build/libs` directory -#### To install this bundle: -Copy the bundle jar file inside the Felix OSGI container (*dotCMS/felix/load*). +* **To install this bundle:** + + Copy the bundle jar file inside the Felix OSGI container (*dotCMS/felix/load*). -OR + OR -Upload the bundle jar file using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + Upload the bundle jar file using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). -#### To uninstall this bundle: -Remove the bundle jar file from the Felix OSGI container (*dotCMS/felix/load*). +* **To uninstall this bundle:** + + Remove the bundle jar file from the Felix OSGI container (*dotCMS/felix/load*). -OR + OR -Undeploy the bundle using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + Undeploy the bundle using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). ## How to create a bundle plugin to override dotCMS classes @@ -46,14 +48,14 @@ If you are building the MANIFEST on your own or desire more info on it below is In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: -* Exported Packages +* **Exported Packages** The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. -* Fragment +* **Fragment** A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. From 865496979d430ec1da299d1e693e2409e83fc336 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Thu, 22 Feb 2018 08:27:32 -0600 Subject: [PATCH 07/17] dotCMS/core#13482 --- OSGi/com.dotcms.override/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/OSGi/com.dotcms.override/README.md b/OSGi/com.dotcms.override/README.md index de02ed12..1519b24d 100644 --- a/OSGi/com.dotcms.override/README.md +++ b/OSGi/com.dotcms.override/README.md @@ -39,8 +39,7 @@ If you are building the MANIFEST on your own or desire more info on it below is Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) Override-Classes: Comma separated list of classes. List of classes we are trying to override, in order to override any dotCMS class is mandatory to add it to this list because dotCMS implementation of the class will be already loaded by the dotCMS class loader so if we don't explicit specify the classes to override the class loader wont try to load them. - DynamicImport-Package: * - Dynamically add required imports the plugin may need without add them explicitly + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. ``` From f11445f56b4874a5a41d0c800a87313bfc4aa9fa Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Thu, 1 Mar 2018 15:07:47 -0600 Subject: [PATCH 08/17] dotCMS/core#13482 --- OSGi/com.dotcms.override/README.md | 10 +++++----- .../java/com/dotmarketing/osgi/override/Activator.java | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/OSGi/com.dotcms.override/README.md b/OSGi/com.dotcms.override/README.md index 1519b24d..77e70976 100644 --- a/OSGi/com.dotcms.override/README.md +++ b/OSGi/com.dotcms.override/README.md @@ -7,23 +7,23 @@ This bundle plugin is an example of how to override dotCMS classes with our bund To install all you need to do is build the JAR. to do this run `./gradlew jar` -This will build a jar in the `build/libs` directory +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar * **To install this bundle:** - Copy the bundle jar file inside the Felix OSGI container (*dotCMS/felix/load*). + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). OR - Upload the bundle jar file using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). * **To uninstall this bundle:** - Remove the bundle jar file from the Felix OSGI container (*dotCMS/felix/load*). + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). OR - Undeploy the bundle using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). ## How to create a bundle plugin to override dotCMS classes diff --git a/OSGi/com.dotcms.override/src/main/java/com/dotmarketing/osgi/override/Activator.java b/OSGi/com.dotcms.override/src/main/java/com/dotmarketing/osgi/override/Activator.java index d7962fe5..c468ab51 100644 --- a/OSGi/com.dotcms.override/src/main/java/com/dotmarketing/osgi/override/Activator.java +++ b/OSGi/com.dotcms.override/src/main/java/com/dotmarketing/osgi/override/Activator.java @@ -1,10 +1,10 @@ package com.dotmarketing.osgi.override; -import com.dotcms.repackage.org.apache.logging.log4j.LogManager; -import com.dotcms.repackage.org.apache.logging.log4j.core.LoggerContext; import com.dotmarketing.loggers.Log4jUtil; import com.dotmarketing.osgi.GenericBundleActivator; import com.dotmarketing.portlets.folders.model.Folder; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; import org.osgi.framework.BundleContext; /** From 4b31b5a94672d4c0c78cfb3ec1798eead40ed0e7 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Thu, 1 Mar 2018 15:16:57 -0600 Subject: [PATCH 09/17] 3rd party Example dotCMS/core#13482 --- OSGi/com.dotcms.3rd.party/build.gradle | 64 +++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/OSGi/com.dotcms.3rd.party/build.gradle b/OSGi/com.dotcms.3rd.party/build.gradle index 2a1b2299..8d6fa486 100644 --- a/OSGi/com.dotcms.3rd.party/build.gradle +++ b/OSGi/com.dotcms.3rd.party/build.gradle @@ -18,6 +18,11 @@ dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -26,11 +31,68 @@ jar { 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotmarketing.osgi.external.Activator', 'Bundle-ClassPath': '.,libs/date4j.jar', - 'DynamicImport-Package': '*', 'Import-Package': '!hirondelle.date4j.*,*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "dotCMS OSGI Party library fragment" + bundleDescription = "dotCMS - dotCMS OSGI Party library fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' From 3ed12d9c2cfb295d27e6143080869af423cba1e8 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Tue, 17 Apr 2018 10:19:06 -0600 Subject: [PATCH 10/17] #13482 --- OSGi/com.dotcms.override/build.gradle | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/OSGi/com.dotcms.override/build.gradle b/OSGi/com.dotcms.override/build.gradle index da6acddc..76e0bc7a 100644 --- a/OSGi/com.dotcms.override/build.gradle +++ b/OSGi/com.dotcms.override/build.gradle @@ -3,7 +3,7 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } @@ -51,6 +51,14 @@ ext { bundleDocURL = "" //Auto generated based on the plugin jar bundleVendor = "" //Auto generated based on the plugin jar } +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} /** * Reads the Manifest file of the just created plugin jar in order to get the required info @@ -75,6 +83,7 @@ task fragmentJar(type: Jar) { //Setting the fragment jar name baseName = project.name archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) manifest { attributes ( From 57b270f77d68649a059a294f4924e754d92696a7 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Tue, 17 Apr 2018 13:51:19 -0600 Subject: [PATCH 11/17] #13482 --- OSGi/com.dotcms.3rd.party/README.md | 71 +++++++++++++++ OSGi/com.dotcms.3rd.party/README.txt | 85 ------------------ OSGi/com.dotcms.3rd.party/build.gradle | 15 ++-- OSGi/com.dotcms.actionlet/README.md | 71 +++++++++++++++ OSGi/com.dotcms.actionlet/README.txt | 78 ---------------- OSGi/com.dotcms.actionlet/build.gradle | 80 +++++++++++++++-- .../osgi/actionlet/Activator.java | 4 +- .../osgi/actionlet/MyActionlet.java | 1 - OSGi/com.dotcms.aop/INSTALL.md | 61 ------------- OSGi/com.dotcms.aop/README.md | 33 ++++--- OSGi/com.dotcms.aop/build.gradle | 88 +++++++++++++++++-- OSGi/com.dotcms.custom.spring/README.md | 68 ++++++++------ OSGi/com.dotcms.custom.spring/build.gradle | 87 ++++++++++++++++-- .../osgi/custom/spring/Activator.java | 4 +- .../osgi/custom/spring/CustomView.java | 3 +- OSGi/com.dotcms.override/build.gradle | 4 - 16 files changed, 448 insertions(+), 305 deletions(-) create mode 100644 OSGi/com.dotcms.3rd.party/README.md delete mode 100644 OSGi/com.dotcms.3rd.party/README.txt create mode 100644 OSGi/com.dotcms.actionlet/README.md delete mode 100644 OSGi/com.dotcms.actionlet/README.txt delete mode 100644 OSGi/com.dotcms.aop/INSTALL.md diff --git a/OSGi/com.dotcms.3rd.party/README.md b/OSGi/com.dotcms.3rd.party/README.md new file mode 100644 index 00000000..d94175a9 --- /dev/null +++ b/OSGi/com.dotcms.3rd.party/README.md @@ -0,0 +1,71 @@ +# README + +This bundle plugin is an example of how to add 3rd party jars to a bundle plugin. + +## How to build this example + +To install all you need to do is build the JAR. to do this run +`./gradlew jar` + +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to create a bundle plugin with external jars + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath + +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* **Exported Packages** + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +--- +## Components + +### com.dotmarketing.osgi.external.Examples + +Example class that will run simple examples using the 3rd party library that was added to the lib folder of the bundle (lib/date4j.jar). + +### Activator + +This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). +Calls the Examples class static methods to run simple tests that use the 3rd party library date4j.jar diff --git a/OSGi/com.dotcms.3rd.party/README.txt b/OSGi/com.dotcms.3rd.party/README.txt deleted file mode 100644 index a3f874e2..00000000 --- a/OSGi/com.dotcms.3rd.party/README.txt +++ /dev/null @@ -1,85 +0,0 @@ - -README ------- - -This bundle plugin is an example of how to add 3rd party jars to a bundle plugin. - -How to build this example -------------------------- - -To install all you need to do is build the JAR. to do this run -./gradlew jar -This will build a jar in the build/libs directory - -1. To install this bundle: - -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -2. To uninstall this bundle: - -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - -How to create a bundle plugin with external jars -------------------------------------------- - --- -In order to create this OSGI plugin, you must write the META-INF/MANIFEST -to be inserted into OSGI jar. - -This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. -The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath - -In this MANIFEST you must specify (see template plugin): - -Bundle-Name: The name of your bundle - -Bundle-SymbolicName: A short an unique name for the bundle - -Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.external.Activator) - -Bundle-ClassPath: The Bundle-ClassPath specifies where to load classes from from the bundle. - This is a comma separated list of elements to load (such as current folder,lib.jar,other.jar). - (example: .,lib/date4j.jar ) - -DynamicImport-Package: * - Dynamically add required imports the plugin may need without add them explicitly - -Import-Package: This is a comma separated list of package's name. - In this list there must be the packages that you are using inside - the bundle plugin and that are exported by the dotCMS runtime. - -Beware!!! ---------- - -In order to work inside the Apache Felix OSGI runtime, the import -and export directive must be bidirectional. - -As of dotcms 2.5.2 if you do not start with a dotCMS/WEB-INF/felix/osgi-extra.conf ALL packages will be exported for you. -So there is nothing for you to do - - -The DotCMS must declare the set of packages that will be available to -the OSGI plugins by changing the file: dotCMS/WEB-INF/felix/osgi-extra.conf. -This is possible also using the dotCMS UI (CMS Admin->Dynamic Plugins->Exported Packages). - -Only after that exported packages are defined in this list, -a plugin can Import the packages to use them inside the OSGI blundle. - --- --- --- -com.dotmarketing.osgi.external.Examples ------------------------------------------------ - -Example class that will run simple examples using the 3rd party library that was added to the lib folder of the bundle (lib/date4j.jar). - --- -Activator ---------- - -This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). -Calls the Examples class static methods to run simple tests that use the 3rd party library date4j.jar diff --git a/OSGi/com.dotcms.3rd.party/build.gradle b/OSGi/com.dotcms.3rd.party/build.gradle index 8d6fa486..c18cc7df 100644 --- a/OSGi/com.dotcms.3rd.party/build.gradle +++ b/OSGi/com.dotcms.3rd.party/build.gradle @@ -3,16 +3,12 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { compile fileTree(dir: 'src/main/resources/libs', include: '*.jar') compile('com.dotcms:dotcms:4.3.2') { transitive = true } @@ -52,6 +48,14 @@ ext { bundleDocURL = "" //Auto generated based on the plugin jar bundleVendor = "" //Auto generated based on the plugin jar } +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} /** * Reads the Manifest file of the just created plugin jar in order to get the required info @@ -76,6 +80,7 @@ task fragmentJar(type: Jar) { //Setting the fragment jar name baseName = project.name archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) manifest { attributes ( diff --git a/OSGi/com.dotcms.actionlet/README.md b/OSGi/com.dotcms.actionlet/README.md new file mode 100644 index 00000000..4cb3180f --- /dev/null +++ b/OSGi/com.dotcms.actionlet/README.md @@ -0,0 +1,71 @@ +# README + +This bundle plugin is an example of how to add dotcms WorkFlowActionlet classes with our bundle plugin. + +## How to build this example + +To install all you need to do is build the JAR. to do this run +`./gradlew jar` + +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to add a Actionlet OSGI plugin + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath + +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* **Exported Packages** + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +--- +## Components + +### com.dotmarketing.osgi.actionlet.MyActionlet + +Implementation of a WorkFlowActionlet object. + +### Activator + +This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). +This activator will allow you to register the WorkFlowActionlet object using the GenericBundleActivator.registerActionlet method diff --git a/OSGi/com.dotcms.actionlet/README.txt b/OSGi/com.dotcms.actionlet/README.txt deleted file mode 100644 index efbf3859..00000000 --- a/OSGi/com.dotcms.actionlet/README.txt +++ /dev/null @@ -1,78 +0,0 @@ - -README ------- - -This bundle plugin is an example of how to add dotcms WorkFlowActionlet classes with our bundle plugin. - -How to build this example -------------------------- - -To install all you need to do is build the JAR. to do this run -./gradlew jar -This will build a jar in the build/libs directory - -1. To install this bundle: - -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -2. To uninstall this bundle: - -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - -How to add a Actionlet OSGI plugin ---------------------------------- - --- -In order to create this OSGI plugin, you must write the META-INF/MANIFEST -to be inserted into OSGI jar. - -This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. -The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath - -If you are building the MANIFEST on your own or desire more info on it below is a descrition of what is required -In this MANIFEST you must specify (see template plugin): - -Bundle-Name: The name of your bundle - -Bundle-SymbolicName: A short an unique name for the bundle - -Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.actionlet.Activator) - -DynamicImport-Package: * - Dynamically add required imports the plugin may need without add them explicitly - -Import-Package: This is a comma separated list of package's name. - In this list there must be the packages that you are using inside - the bundle plugin and that are exported by the dotCMS runtime. - -Beware!!! ---------- - -In order to work inside the Apache Felix OSGI runtime, the import -and export directive must be bidirectional. - -The DotCMS must declare the set of packages that will be available to -the OSGI plugins by changing the file: dotCMS/WEB-INF/felix/osgi-extra.conf. -This is possible also using the dotCMS UI (CMS Admin->Dynamic Plugins->Exported Packages). - -Only after that exported packages are defined in this list, -a plugin can Import the packages to use them inside the OSGI blundle. - --- --- --- -com.dotmarketing.osgi.actionlet.MyActionlet ------------------------------------------------ - -Implementation of a WorkFlowActionlet object. - --- -Activator ---------- - -This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). -This activator will allow you to register the WorkFlowActionlet object using the GenericBundleActivator.registerActionlet method diff --git a/OSGi/com.dotcms.actionlet/build.gradle b/OSGi/com.dotcms.actionlet/build.gradle index b3809866..14ed7cef 100644 --- a/OSGi/com.dotcms.actionlet/build.gradle +++ b/OSGi/com.dotcms.actionlet/build.gradle @@ -3,20 +3,21 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -24,12 +25,79 @@ jar { 'Bundle-Description': 'dotCMS - OSGI Actionlet example', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotmarketing.osgi.actionlet.Activator', - 'DynamicImport-Package': '*', 'Import-Package': '*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "dotCMS Actionlet fragment" + bundleDescription = "dotCMS - OSGI Actionlet example fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' + task wrapper(type: Wrapper) { gradleVersion = '4.2' } \ No newline at end of file diff --git a/OSGi/com.dotcms.actionlet/src/main/java/com/dotmarketing/osgi/actionlet/Activator.java b/OSGi/com.dotcms.actionlet/src/main/java/com/dotmarketing/osgi/actionlet/Activator.java index 5d71aea2..55ef4969 100644 --- a/OSGi/com.dotcms.actionlet/src/main/java/com/dotmarketing/osgi/actionlet/Activator.java +++ b/OSGi/com.dotcms.actionlet/src/main/java/com/dotmarketing/osgi/actionlet/Activator.java @@ -1,9 +1,9 @@ package com.dotmarketing.osgi.actionlet; -import com.dotcms.repackage.org.apache.logging.log4j.LogManager; -import com.dotcms.repackage.org.apache.logging.log4j.core.LoggerContext; import com.dotmarketing.loggers.Log4jUtil; import com.dotmarketing.osgi.GenericBundleActivator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; import org.osgi.framework.BundleContext; public class Activator extends GenericBundleActivator { diff --git a/OSGi/com.dotcms.actionlet/src/main/java/com/dotmarketing/osgi/actionlet/MyActionlet.java b/OSGi/com.dotcms.actionlet/src/main/java/com/dotmarketing/osgi/actionlet/MyActionlet.java index 03ff91f9..df141ebf 100644 --- a/OSGi/com.dotcms.actionlet/src/main/java/com/dotmarketing/osgi/actionlet/MyActionlet.java +++ b/OSGi/com.dotcms.actionlet/src/main/java/com/dotmarketing/osgi/actionlet/MyActionlet.java @@ -5,7 +5,6 @@ import com.dotmarketing.portlets.workflows.model.WorkflowActionFailureException; import com.dotmarketing.portlets.workflows.model.WorkflowActionletParameter; import com.dotmarketing.portlets.workflows.model.WorkflowProcessor; - import java.util.List; import java.util.Map; diff --git a/OSGi/com.dotcms.aop/INSTALL.md b/OSGi/com.dotcms.aop/INSTALL.md deleted file mode 100644 index ac7e1399..00000000 --- a/OSGi/com.dotcms.aop/INSTALL.md +++ /dev/null @@ -1,61 +0,0 @@ - -# README ----- -This is an example of how to create and load Jersey Based REST resources in dotCMS via OSGi and Annotation Framework based on AOP - - -## How to build this example ----- - -To install all you need to do is build the JAR. To do this run from this directory: - -`./gradlew jar` - -or for windows - -`.\gradlew.bat jar` - -This will build a jar in the build/libs directory - -### To install this bundle - -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -### To uninstall this bundle: - -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - - - -## How to test ----- - -Once installed, you can access by get this resource by (this assumes you are on localhost) - -`http://localhost:8080/api/v1/custom/content/inode/006de26b-376c-495a-9f7d-913a578b033d` - -or this by delete, which requires an dotcms user to access(See authentication below) -Note: keep in this resources will delete last month content. - -`http://localhost:8080/api/v1/custom/content/lastMonth` - - - - - - - -## Authentication ----- -This API supports the same REST auth infrastructure as other -rest apis in dotcms. There are 4 ways to authenticate. - -* user/xxx/password/yyy in the URI -* basic http/https authentication (base64 encoded) -* DOTAUTH header similar to basic auth and base64 encoded, e.g. setHeader("DOTAUTH", base64.encode("admin@dotcms.com:admin")) -* Session based (form based login) for frontend or backend logged in user -* JWT \ No newline at end of file diff --git a/OSGi/com.dotcms.aop/README.md b/OSGi/com.dotcms.aop/README.md index c41b01cb..2de71e3d 100644 --- a/OSGi/com.dotcms.aop/README.md +++ b/OSGi/com.dotcms.aop/README.md @@ -19,30 +19,29 @@ It will provide an appropiate connection, if needed and handle the commit, rollb ## How to build this example -To install all you need to do is build the JAR. To do this run from this directory: - +To install all you need to do is build the JAR. to do this run `./gradlew jar` -or for windows - -`.\gradlew.bat jar` +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar -This will build a jar in the build/libs directory +* **To install this bundle:** -### To install this bundle + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). -### To uninstall this bundle: - -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). + OR + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). ## How to test + Once installed, you can access by get this resource by (this assumes you are on localhost) `http://localhost:8080/api/v1/custom/content/inode/006de26b-376c-495a-9f7d-913a578b033d` @@ -53,9 +52,8 @@ or this by delete, which requires an dotcms user to access(See authentication be `http://localhost:8080/api/v1/custom/content/lastMonth` - - ## Authentication + This API supports the same REST auth infrastructure as other rest apis in dotcms. There are 4 ways to authenticate. @@ -63,5 +61,4 @@ rest apis in dotcms. There are 4 ways to authenticate. * basic http/https authentication (base64 encoded) * DOTAUTH header similar to basic auth and base64 encoded, e.g. setHeader("DOTAUTH", base64.encode("admin@dotcms.com:admin")) * Session based (form based login) for frontend or backend logged in user -* Session based (form based login) for frontend or backend logged in user * JWT \ No newline at end of file diff --git a/OSGi/com.dotcms.aop/build.gradle b/OSGi/com.dotcms.aop/build.gradle index 1d565fe9..65876c4d 100644 --- a/OSGi/com.dotcms.aop/build.gradle +++ b/OSGi/com.dotcms.aop/build.gradle @@ -1,12 +1,17 @@ +plugins { + id 'biz.aQute.bnd.builder' version '3.3.0' +} + +sourceCompatibility = '1.8' + group 'com.dotcms.aop' -version '1.0-SNAPSHOT' +version '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } - } apply plugin: 'java' @@ -74,17 +79,90 @@ dependencies { providedCompile "javax.servlet:javax.servlet-api:3.1.0" } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { name = 'Osgi AOP class' instruction 'Bundle-Vendor', 'dotcms' - instruction 'Bundle-Description', 'dotCMS Osgi AOP class example' + instruction 'Bundle-Description', 'dotCMS - Osgi AOP class example' instruction 'Bundle-DocURL', 'http://www.dotcms.com' instruction 'Bundle-Activator', 'com.dotcms.plugin.aop.Activator' - instruction 'DynamicImport-Package', '*' - instruction 'Import-Package', 'version=0,*;version=0' + instruction 'Import-Package', 'com.dotcms.plugin.aop.rest.*,' + + '*;version=0' + } +} + +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "dotCMS AOP class fragment" + bundleDescription = "dotCMS - Osgi AOP class fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } } } +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' diff --git a/OSGi/com.dotcms.custom.spring/README.md b/OSGi/com.dotcms.custom.spring/README.md index 327d8d08..26db68bc 100644 --- a/OSGi/com.dotcms.custom.spring/README.md +++ b/OSGi/com.dotcms.custom.spring/README.md @@ -2,51 +2,64 @@ This bundle plugin is an example of how to add Spring support to a bundle plugin, creates and registers a simple Spring Controller using a different Spring version that the one shipped with dotCMS in order to extend the reach of this example. -### How to build this example +## How to build this example -To install all you need to do is build the JAR. to do this run -``` -./gradlew jar -``` -This will build a jar in the build/libs directory +To install all you need to do is build the JAR. to do this run +`./gradlew jar` + +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** -### To install this bundle: + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). -Upload the bundle jar file using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). - -### To uninstall this bundle: +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). -Undeploy the bundle using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + OR + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). -### How to create a bundle plugin with Spring support +## How to create a bundle plugin with Spring support -In order to create an OSGI plugin, you must create a *META-INF/MANIFEST* to be included in the OSGI jar. +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath -In this *MANIFEST* you must specify (see the included plugin as an example): - -* *Bundle-Name*: The name of your bundle -* *Bundle-SymbolicName*: A short an unique name for the bundle -* *Bundle-Activator*: Package and name of your Activator class (example: *com.dotmarketing.osgi.custom.spring.Activator*) -* *Bundle-ClassPath*: The Bundle-ClassPath specifies where to load classes and jars from from the bundle. -This is a comma separated list of elements to load (such as current folder,lib.jar,other.jar). (Example: ., lib/com.springsource.org.aopalliance-1.0.0.jar) -* *Import-Package*: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.custom.spring.Activator) + Bundle-ClassPath: The Bundle-ClassPath specifies where to load classes and jars from from the bundle. + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` -### Beware (!) +## Beware (!) -In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional. +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: -As of dotcms 2.5.2 if you do not start with a **dotCMS/WEB-INF/felix/osgi-extra.conf** ALL packages will be exported for you. So there is nothing for you to do +* **Exported Packages** -The DotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). -Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. +--- ## Components ### com.dotmarketing.osgi.custom.spring.ExampleController @@ -72,8 +85,7 @@ Will manually register making use of the class *DispatcherServlet* our spring co * PLEASE note the `publishBundleServices( context )` call, this call is MANDATORY (!) as it will allow us to share resources between the bundle, the host container (dotCMS) and the Spring context. -________________________________________________________________________________________ - +--- ## Testing The Spring controller is registered under the url pattern **"/spring"** can be test it running and assuming your dotCMS url is *localhost:8080*: diff --git a/OSGi/com.dotcms.custom.spring/build.gradle b/OSGi/com.dotcms.custom.spring/build.gradle index 8be09b9d..20c04b10 100644 --- a/OSGi/com.dotcms.custom.spring/build.gradle +++ b/OSGi/com.dotcms.custom.spring/build.gradle @@ -3,7 +3,7 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } @@ -11,7 +11,6 @@ repositories { configurations { pluginLibs - osgiLibs } dependencies { @@ -32,11 +31,16 @@ dependencies { pluginLibs group: 'xerces', name: 'xercesImpl', version: '2.11.0' } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( 'Bundle-Vendor': 'dotCMS', - 'Bundle-Description': 'Spring OSGi Example providing its own Spring for dotcms', + 'Bundle-Description': 'dotCMS - Spring OSGi Example providing its own Spring for dotcms', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotmarketing.osgi.custom.spring.Activator', 'Bundle-ClassPath' :''' @@ -61,16 +65,17 @@ jar { ''', 'Import-Package': ''' bsh;resolution:=optional, + javax.servlet;version=0, javax.servlet.jsp.*;resolution:=optional, + javax.servlet.annotation;version=0, + javax.servlet.http;version=0, org.apache.velocity.*, com.dotmarketing.osgi.*, com.dotmarketing.util.*; com.dotmarketing.filters.*, com.dotmarketing.loggers.*, - com.dotmarketing.exception.*, - com.dotcms.repackage.org.apache.logging.log4j.core;version=0, - com.dotcms.repackage.org.apache.logging.log4j;version=0, - javax.servlet.*, + org.apache.logging.log4j.core;version=0, + org.apache.logging.log4j;version=0, javax.management.*, org.apache.felix.http.api.*, org.osgi.framework.*, @@ -84,8 +89,74 @@ task copyToLib(type: Copy) { from configurations.pluginLibs } -//jar.dependsOn fragment jar.dependsOn copyToLib +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "dotCMS Custom Spring OSGi fragment" + bundleDescription = "dotCMS - Spring OSGi Example providing its own Spring for dotcms fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' tasks.copyToLib.execute() task wrapper(type: Wrapper) { diff --git a/OSGi/com.dotcms.custom.spring/src/main/java/com/dotmarketing/osgi/custom/spring/Activator.java b/OSGi/com.dotcms.custom.spring/src/main/java/com/dotmarketing/osgi/custom/spring/Activator.java index 3ec9ed21..e6fe814e 100644 --- a/OSGi/com.dotcms.custom.spring/src/main/java/com/dotmarketing/osgi/custom/spring/Activator.java +++ b/OSGi/com.dotcms.custom.spring/src/main/java/com/dotmarketing/osgi/custom/spring/Activator.java @@ -1,11 +1,11 @@ package com.dotmarketing.osgi.custom.spring; -import com.dotcms.repackage.org.apache.logging.log4j.LogManager; -import com.dotcms.repackage.org.apache.logging.log4j.core.LoggerContext; import com.dotmarketing.filters.CMSFilter; import com.dotmarketing.loggers.Log4jUtil; import com.dotmarketing.osgi.GenericBundleActivator; import org.apache.felix.http.api.ExtHttpService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.springframework.web.servlet.DispatcherServlet; diff --git a/OSGi/com.dotcms.custom.spring/src/main/java/com/dotmarketing/osgi/custom/spring/CustomView.java b/OSGi/com.dotcms.custom.spring/src/main/java/com/dotmarketing/osgi/custom/spring/CustomView.java index aa28dece..2fb591f4 100644 --- a/OSGi/com.dotcms.custom.spring/src/main/java/com/dotmarketing/osgi/custom/spring/CustomView.java +++ b/OSGi/com.dotcms.custom.spring/src/main/java/com/dotmarketing/osgi/custom/spring/CustomView.java @@ -2,7 +2,6 @@ import com.dotmarketing.filters.Constants; import com.dotmarketing.util.VelocityUtil; -import com.dotmarketing.velocity.VelocityServlet; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -43,7 +42,7 @@ public void render ( Map map, HttpServletRequest request, HttpServlet } // add the context to the request.attr where it will be picked up and used by the VelocityServlet - request.setAttribute( VelocityServlet.VELOCITY_CONTEXT, ctx ); + request.setAttribute( com.dotcms.rendering.velocity.Constants.VELOCITY_CONTEXT, ctx ); // override the page path request.setAttribute( Constants.CMS_FILTER_URI_OVERRIDE, pagePath ); diff --git a/OSGi/com.dotcms.override/build.gradle b/OSGi/com.dotcms.override/build.gradle index 76e0bc7a..438284bf 100644 --- a/OSGi/com.dotcms.override/build.gradle +++ b/OSGi/com.dotcms.override/build.gradle @@ -9,10 +9,6 @@ repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } From 766cc5769a364fc412ba2ddc158a5098467839b2 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Wed, 18 Apr 2018 16:12:50 -0600 Subject: [PATCH 12/17] #13482 --- OSGi/com.dotcms.3rd.party/build.gradle | 6 +- OSGi/com.dotcms.dynamic.skeleton/README.md | 64 ++++++++------ OSGi/com.dotcms.dynamic.skeleton/build.gradle | 80 +++++++++++++++++- OSGi/com.dotcms.fixasset/README.md | 78 +++++++++++++++++ OSGi/com.dotcms.fixasset/README.txt | 83 ------------------- OSGi/com.dotcms.fixasset/build.gradle | 78 +++++++++++++++-- 6 files changed, 269 insertions(+), 120 deletions(-) create mode 100644 OSGi/com.dotcms.fixasset/README.md delete mode 100644 OSGi/com.dotcms.fixasset/README.txt diff --git a/OSGi/com.dotcms.3rd.party/build.gradle b/OSGi/com.dotcms.3rd.party/build.gradle index c18cc7df..db826eda 100644 --- a/OSGi/com.dotcms.3rd.party/build.gradle +++ b/OSGi/com.dotcms.3rd.party/build.gradle @@ -23,7 +23,7 @@ jar { manifest { attributes ( 'Bundle-Vendor': 'dotCMS', - 'Bundle-Description': 'dotCMS - dotCMS OSGI Party library example', + 'Bundle-Description': 'dotCMS - OSGI 3rd Party library example', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotmarketing.osgi.external.Activator', 'Bundle-ClassPath': '.,libs/date4j.jar', @@ -38,8 +38,8 @@ jar.finalizedBy 'fragmentJar' ///////////////////////// ext { - bundleName = "dotCMS OSGI Party library fragment" - bundleDescription = "dotCMS - dotCMS OSGI Party library fragment" + bundleName = "OSGI 3rd Party library fragment" + bundleDescription = "dotCMS - OSGI 3rd Party library fragment" fragmentHost = "system.bundle; extension:=framework" bundleSymbolicName = "" //Auto generated based on the plugin jar bundleVersion = "" //Auto generated based on the plugin jar diff --git a/OSGi/com.dotcms.dynamic.skeleton/README.md b/OSGi/com.dotcms.dynamic.skeleton/README.md index 3fe96839..1095183c 100644 --- a/OSGi/com.dotcms.dynamic.skeleton/README.md +++ b/OSGi/com.dotcms.dynamic.skeleton/README.md @@ -1,45 +1,57 @@ - # README --------- ## How to build an OSGi project -------------------------------- To install all you need to do is build the JAR. to do this run `./gradlew jar` -This will build a jar in the build/libs directory, refer to the build.gradle for more -information on how this is done. -1. To install this bundle: +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). -Copy the bundle jar file inside the Felix OSGI container (`WEB-INF/felix/load`). -_OR_ -Upload the bundle jar file using the dotCMS UI (`CMS Admin->Dynamic Plugins->Upload Plugin`). +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). -2. To uninstall this bundle: + OR -Remove the bundle jar file from the Felix OSGI container (`WEB-INF/felix/load`). -_OR_ -Undeploy the bundle using the dotCMS UI (`CMS Admin->Dynamic Plugins->Undeploy`). + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). -## Notes --------- -In order to create this OSGI plugin, you must create a META-INF/MANIFEST to be inserted into OSGI jar. +## How to create a bundle plugin + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath -If you are building the MANIFEST on your own or desire more info on it below is a description of what is required -in this MANIFEST you must specify (see template plugin): +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) -Bundle-Name: The name of your bundle +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: -Bundle-SymbolicName: A short an unique name for the bundle +* **Exported Packages** -Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.viewtools.Activator) + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). -DynamicImport-Package: * - Dynamically add required imports the plugin may need without add them explicitly + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** -Import-Package: This is a comma separated list of package's name. - In this list there must be the packages that you are using inside - the bundle plugin and that are exported by the dotCMS runtime. + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. diff --git a/OSGi/com.dotcms.dynamic.skeleton/build.gradle b/OSGi/com.dotcms.dynamic.skeleton/build.gradle index 2bc13f96..27ef61ac 100644 --- a/OSGi/com.dotcms.dynamic.skeleton/build.gradle +++ b/OSGi/com.dotcms.dynamic.skeleton/build.gradle @@ -6,7 +6,7 @@ plugins { sourceCompatibility = '1.8' // Plugin version -version = '0.1' +version = '0.2' // Repositories // reference to "artifactory" our oficial repository @@ -26,7 +26,13 @@ dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } -// gradle task creates the manifest for the OSGi bundle +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// + +// gradle task creates the jar for the OSGi bundle plugin // replace and jar { manifest { @@ -35,11 +41,79 @@ jar { 'Bundle-Description': '', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': '.Activator', - 'DynamicImport-Package': '*', 'Import-Package': '*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +// gradle task creates the jar for the OSGi bundle fragment plugin +// replace and +ext { + bundleName = "" + bundleDescription = "" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' diff --git a/OSGi/com.dotcms.fixasset/README.md b/OSGi/com.dotcms.fixasset/README.md new file mode 100644 index 00000000..fafab34f --- /dev/null +++ b/OSGi/com.dotcms.fixasset/README.md @@ -0,0 +1,78 @@ +# README + +This bundle plugin is an example of how to add a dotcms fix tasks OSGi. FixTasks are run from the CMS Maintenance scree and can be used to fix corrupt data or other issues where the dotCMS store gets wonky. + +Example use cases: + +* To prep legacy data in anticipation of an upgrade +* To remove orphaned files on the file system +* to clean up permission references +* to add missing version_info records + +## How to build this example + +To install all you need to do is build the JAR. to do this run +`./gradlew jar` + +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to create a bundle plugin for fix tasks + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath + +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* **Exported Packages** + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +--- +## Components + +### com.dotmarketing.osgi.hooks.SamplePostContentHook AND com.dotmarketing.osgi.hooks.SamplePreContentHook + +Hooks classes that will override the contentletCount method to see how they work. + +### Activator + +This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). +Calls ContentletAPI.contentletCount() who will fire ours hooks methods. diff --git a/OSGi/com.dotcms.fixasset/README.txt b/OSGi/com.dotcms.fixasset/README.txt deleted file mode 100644 index c5dfd5b4..00000000 --- a/OSGi/com.dotcms.fixasset/README.txt +++ /dev/null @@ -1,83 +0,0 @@ - -README ------- - -This bundle plugin is an example of how to add a dotcms fix tasks OSGi. FixTasks are run from the CMS Maintenance scree and can be used to fix corrupt data or other issues where the dotCMS store gets wonky. - -Example use cases: - -* To prep legacy data in anticipation of an upgrade -* To remove orphaned files on the file system -* to clean up permission references -* to add missing version_info records - -How to build this example -------------------------- - -To install all you need to do is build the JAR. to do this run -./gradlew jar -This will build a jar in the build/libs directory - -1. To install this bundle: - -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -2. To uninstall this bundle: - -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - -How to create a bundle plugin for dotcms hook classes -------------------------------------------- - --- -In order to create this OSGI plugin, you must create a META-INF/MANIFEST to be inserted into OSGI jar. -This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. -The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath - -If you are building the MANIFEST on your own or desire more info on it below is a description of what is required -in this MANIFEST you must specify (see template plugin): - -Bundle-Name: The name of your bundle - -Bundle-SymbolicName: A short an unique name for the bundle - -Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.hooks.Activator) - -DynamicImport-Package: * - Dynamically add required imports the plugin may need without add them explicitly - -Import-Package: This is a comma separated list of package's name. - In this list there must be the packages that you are using inside - the bundle plugin and that are exported by the dotCMS runtime. - -Beware!!! ---------- - -In order to work inside the Apache Felix OSGI runtime, the import -and export directive must be bidirectional. - -The DotCMS must declare the set of packages that will be available to -the OSGI plugins by changing the file: dotCMS/WEB-INF/felix/osgi-extra.conf. -This is possible also using the dotCMS UI (CMS Admin->Dynamic Plugins->Exported Packages). - -Only after that exported packages are defined in this list, -a plugin can Import the packages to use them inside the OSGI blundle. - --- --- --- -com.dotmarketing.osgi.hooks.SamplePostContentHook AND com.dotmarketing.osgi.hooks.SamplePreContentHook ------------------------------------------------ - -Hooks classes that will override the contentletCount method to see how they work. - --- -Activator ---------- - -This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). -Calls ContentletAPI.contentletCount() who will fire ours hooks methods. diff --git a/OSGi/com.dotcms.fixasset/build.gradle b/OSGi/com.dotcms.fixasset/build.gradle index cd46fabd..3aa0b3a1 100644 --- a/OSGi/com.dotcms.fixasset/build.gradle +++ b/OSGi/com.dotcms.fixasset/build.gradle @@ -3,20 +3,21 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -29,6 +30,73 @@ jar { ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "dotCMS OSGI FixAsset fragment" + bundleDescription = "dotCMS - OSGI FixAsset fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' From dd2fde524fa7ef8351b6249fca7a0729333c3e28 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Wed, 18 Apr 2018 17:35:37 -0600 Subject: [PATCH 13/17] #13482 --- OSGi/com.dotcms.hooks/README.md | 80 ++++++++++++++++ OSGi/com.dotcms.hooks/README.txt | 86 ----------------- OSGi/com.dotcms.hooks/build.gradle | 79 ++++++++++++++-- .../dotmarketing/osgi/hooks/Activator.java | 6 +- OSGi/com.dotcms.job/README.md | 93 +++++++++++++++++++ OSGi/com.dotcms.job/README.textile | 80 ---------------- OSGi/com.dotcms.job/build.gradle | 79 ++++++++++++++-- .../com/dotmarketing/osgi/job/Activator.java | 4 +- 8 files changed, 324 insertions(+), 183 deletions(-) create mode 100644 OSGi/com.dotcms.hooks/README.md delete mode 100644 OSGi/com.dotcms.hooks/README.txt create mode 100644 OSGi/com.dotcms.job/README.md delete mode 100644 OSGi/com.dotcms.job/README.textile diff --git a/OSGi/com.dotcms.hooks/README.md b/OSGi/com.dotcms.hooks/README.md new file mode 100644 index 00000000..1805b9a1 --- /dev/null +++ b/OSGi/com.dotcms.hooks/README.md @@ -0,0 +1,80 @@ +# README + +This bundle plugin is an example of how to add dotcms content hooks via OSGi. Content hooks work like interceptors and can be called before content has been modified (pre-hooks) or after the content has been checked in (post-hooks). + +Example use cases: + +* To apply extra or richer validation on a content object before checkin. +* To modify or auto-assign values to specific fields on a content object on checkin +* To perform an action once a content object has been checked in, i.e. synchronize content that has been checked in with a 3rd party system + + +To see all the methods that can be overridden, see the interfaces: + +## How to build this example + +To install all you need to do is build the JAR. to do this run +`./gradlew jar` + +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to create a bundle plugin for dotCMS hook classes + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath + +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* **Exported Packages** + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +--- +## Components + +### com.dotmarketing.osgi.hooks.SamplePostContentHook AND com.dotmarketing.osgi.hooks.SamplePreContentHook + +Hooks classes that will override the contentletCount method to see how they work. + +### Activator + +This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). +Calls ContentletAPI.contentletCount() who will fire ours hooks methods. diff --git a/OSGi/com.dotcms.hooks/README.txt b/OSGi/com.dotcms.hooks/README.txt deleted file mode 100644 index 2d4bfe0e..00000000 --- a/OSGi/com.dotcms.hooks/README.txt +++ /dev/null @@ -1,86 +0,0 @@ - -README ------- - -This bundle plugin is an example of how to add dotcms content hooks via OSGi. Content hooks work like interceptors and can be called before content has been modified (pre-hooks) or after the content has been checked in (post-hooks). - -Example use cases: - -* To apply extra or richer validation on a content object before checkin. -* To modify or auto-assign values to specific fields on a content object on checkin -* To perform an action once a content object has been checked in, i.e. synchronize content that has been checked in with a 3rd party system - - -To see all the methods that can be overridden, see the interfaces: - - -How to build this example -------------------------- - -To install all you need to do is build the JAR. to do this run -./gradlew jar -This will build a jar in the build/libs directory - -1. To install this bundle: - -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -2. To uninstall this bundle: - -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - -How to create a bundle plugin for dotcms hook classes -------------------------------------------- - --- -In order to create this OSGI plugin, you must create a META-INF/MANIFEST to be inserted into OSGI jar. -This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. -The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath - -If you are building the MANIFEST on your own or desire more info on it below is a description of what is required -in this MANIFEST you must specify (see template plugin): - -Bundle-Name: The name of your bundle - -Bundle-SymbolicName: A short an unique name for the bundle - -Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.hooks.Activator) - -DynamicImport-Package: * - Dynamically add required imports the plugin may need without add them explicitly - -Import-Package: This is a comma separated list of package's name. - In this list there must be the packages that you are using inside - the bundle plugin and that are exported by the dotCMS runtime. - -Beware!!! ---------- - -In order to work inside the Apache Felix OSGI runtime, the import -and export directive must be bidirectional. - -The DotCMS must declare the set of packages that will be available to -the OSGI plugins by changing the file: dotCMS/WEB-INF/felix/osgi-extra.conf. -This is possible also using the dotCMS UI (CMS Admin->Dynamic Plugins->Exported Packages). - -Only after that exported packages are defined in this list, -a plugin can Import the packages to use them inside the OSGI blundle. - --- --- --- -com.dotmarketing.osgi.hooks.SamplePostContentHook AND com.dotmarketing.osgi.hooks.SamplePreContentHook ------------------------------------------------ - -Hooks classes that will override the contentletCount method to see how they work. - --- -Activator ---------- - -This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). -Calls ContentletAPI.contentletCount() who will fire ours hooks methods. diff --git a/OSGi/com.dotcms.hooks/build.gradle b/OSGi/com.dotcms.hooks/build.gradle index bd77ffa9..888b3055 100644 --- a/OSGi/com.dotcms.hooks/build.gradle +++ b/OSGi/com.dotcms.hooks/build.gradle @@ -3,20 +3,21 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -24,11 +25,77 @@ jar { 'Bundle-Description': 'dotCMS - OSGI Hooks example', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotmarketing.osgi.hooks.Activator', - 'DynamicImport-Package': '*', 'Import-Package': '*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "OSGI Hooks example fragment" + bundleDescription = "dotCMS - OSGI Hooks example fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = { importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' diff --git a/OSGi/com.dotcms.hooks/src/main/java/com/dotmarketing/osgi/hooks/Activator.java b/OSGi/com.dotcms.hooks/src/main/java/com/dotmarketing/osgi/hooks/Activator.java index a5b38dfe..72fcf246 100644 --- a/OSGi/com.dotcms.hooks/src/main/java/com/dotmarketing/osgi/hooks/Activator.java +++ b/OSGi/com.dotcms.hooks/src/main/java/com/dotmarketing/osgi/hooks/Activator.java @@ -1,13 +1,13 @@ package com.dotmarketing.osgi.hooks; -import com.dotcms.repackage.org.apache.logging.log4j.LogManager; -import com.dotcms.repackage.org.apache.logging.log4j.core.LoggerContext; -import org.osgi.framework.BundleContext; import com.dotmarketing.business.APILocator; import com.dotmarketing.loggers.Log4jUtil; import com.dotmarketing.osgi.GenericBundleActivator; import com.dotmarketing.portlets.contentlet.business.ContentletAPI; import com.dotmarketing.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; +import org.osgi.framework.BundleContext; /** * Created by Jonathan Gamba diff --git a/OSGi/com.dotcms.job/README.md b/OSGi/com.dotcms.job/README.md new file mode 100644 index 00000000..8079aa79 --- /dev/null +++ b/OSGi/com.dotcms.job/README.md @@ -0,0 +1,93 @@ +# README + +This bundle plugin is an example of how to Schedule Quartz Jobs using an OSGI bundle plugin. + +## How to build this example + +To install all you need to do is build the JAR. to do this run +`./gradlew jar` + +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to create a bundle plugin for Schedule Quartz Jobs + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath + +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* **Exported Packages** + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +--- +## Components + +### com.dotmarketing.osgi.job.CustomJob + +Simple Job class that implements the regular Quartz Job interface + +### Activator + +This bundle activator extends from *com.dotmarketing.osgi.GenericBundleActivator* and implements *BundleActivator.start()*. +Will manually register a *CronScheduledTask* making use of the method *scheduleQuartzJob* + +* PLEASE note the *unregisterServices()* call on the *stop* method, this call is MANDATORY (!) as it will allow us to stop and +remove the register Quartz Job when the plugin is undeploy. + +--- + +# Limitations (!) + +There are limitations on the hot deploy functionality for the OSGI Quartz Job plugin, once you upload this plugin you are limited +on what code you can modify for the Quartz Job classes in order to see those changes the next time you upload the plugin. + +This will apply only for the OSGI Quartz plugins, exactly to the Quartz Job class you implement and the classes use it by it, because +in order to integrate this plugin with the dotCMS/Quartz code we are using our plugin code outside the OSGI and the plugin context, +trying to let know to dotCMS/Quartz that there is a Job outside its classpath trying to be use it and instantiate by them. + +In order to support the use of the Quartz Jobs inside ours OSGI plugins we use the java hot swapping, it allows to redefine classes, +unfortunately, this redefinition is limited only to changing method bodies: + +> The redefinition may change method bodies, the constant pool and attributes. The redefinition must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance. + +As long as you don't add, remove or change methods (ONLY the methods bodies) for your Job code you will have an OSGI plugin that +will reflect you changes when a redeploy is done. If you need to change the signature of the Job classes a restart of the dotCMS app will be require. \ No newline at end of file diff --git a/OSGi/com.dotcms.job/README.textile b/OSGi/com.dotcms.job/README.textile deleted file mode 100644 index 7839fd43..00000000 --- a/OSGi/com.dotcms.job/README.textile +++ /dev/null @@ -1,80 +0,0 @@ - -h1. README - -This bundle plugin is an example of how to Schedule Quartz Jobs using an OSGI bundle plugin. - -h2. How to build this example - -To install all you need to do is build the JAR. to do this run -*./gradlew jar* -This will build a jar in the build/libs directory - -* To install this bundle: - Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR - Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -* To uninstall this bundle: - Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR - Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - -h2. How to create a bundle plugin for Schedule Quartz Jobs - -In order to create this OSGI plugin, you must create a META-INF/MANIFEST to be inserted into OSGI jar. -This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. -The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath - -If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): - - * *Bundle-Name*: The name of your bundle - * *Bundle-SymbolicName*: A short an unique name for the bundle - * *Bundle-Activator*: Package and name of your Activator class (example: com.dotmarketing.osgi.job.Activator) - * *DynamicImport-Package: ** - Dynamically add required imports the plugin may need without add them explicitly - * *Import-Package*: This is a comma separated list of package's name. In this list there must be the packages that you are - using inside the bundle plugin and that are exported by the dotCMS runtime. - - -h2. Beware (!) - -In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional. - -The DotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. -This is possible also using the dotCMS UI (CMS Admin->Dynamic Plugins->Exported Packages). - -Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. - - -h2. Components - -h3. com.dotmarketing.osgi.job.CustomJob - -Simple Job class that implements the regular Quartz Job interface - -h3. Activator - -This bundle activator extends from *com.dotmarketing.osgi.GenericBundleActivator* and implements *BundleActivator.start()*. -Will manually register a *CronScheduledTask* making use of the method *scheduleQuartzJob* - -* PLEASE note the *unregisterServices()* call on the *stop* method, this call is MANDATORY (!) as it will allow us to stop and -remove the register Quartz Job when the plugin is undeploy. - -________________________________________________________________________________________ - -h1. Limitations (!) - -There are limitations on the hot deploy functionality for the OSGI Quartz Job plugin, once you upload this plugin you are limited -on what code you can modify for the Quartz Job classes in order to see those changes the next time you upload the plugin. - -This will apply only for the OSGI Quartz plugins, exactly to the Quartz Job class you implement and the classes use it by it, because -in order to integrate this plugin with the dotCMS/Quartz code we are using our plugin code outside the OSGI and the plugin context, -trying to let know to dotCMS/Quartz that there is a Job outside its classpath trying to be use it and instantiate by them. - -In order to support the use of the Quartz Jobs inside ours OSGI plugins we use the java hot swapping, it allows to redefine classes, -unfortunately, this redefinition is limited only to changing method bodies: - -bq. The redefinition may change method bodies, the constant pool and attributes. The redefinition must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance. - -As long as you don't add, remove or change methods (ONLY the methods bodies) for your Job code you will have an OSGI plugin that -will reflect you changes when a redeploy is done. If you need to change the signature of the Job classes a restart of the dotCMS app will be require. \ No newline at end of file diff --git a/OSGi/com.dotcms.job/build.gradle b/OSGi/com.dotcms.job/build.gradle index 4907254e..75cca743 100644 --- a/OSGi/com.dotcms.job/build.gradle +++ b/OSGi/com.dotcms.job/build.gradle @@ -3,20 +3,21 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -25,11 +26,77 @@ jar { 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotmarketing.osgi.job.Activator', 'Override-Classes': 'com.dotmarketing.osgi.job.TestClass', - 'DynamicImport-Package': '*', 'Import-Package': '*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "Dinamyc Quartz Jobs example fragment" + bundleDescription = "dotCMS - Dinamyc Quartz Jobs example fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = { importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' diff --git a/OSGi/com.dotcms.job/src/main/java/com/dotmarketing/osgi/job/Activator.java b/OSGi/com.dotcms.job/src/main/java/com/dotmarketing/osgi/job/Activator.java index b4886ad6..d23a87da 100644 --- a/OSGi/com.dotcms.job/src/main/java/com/dotmarketing/osgi/job/Activator.java +++ b/OSGi/com.dotcms.job/src/main/java/com/dotmarketing/osgi/job/Activator.java @@ -1,13 +1,13 @@ package com.dotmarketing.osgi.job; -import com.dotcms.repackage.org.apache.logging.log4j.LogManager; -import com.dotcms.repackage.org.apache.logging.log4j.core.LoggerContext; import com.dotmarketing.loggers.Log4jUtil; import com.dotmarketing.osgi.GenericBundleActivator; import com.dotmarketing.quartz.CronScheduledTask; import java.util.Date; import java.util.HashMap; import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; import org.osgi.framework.BundleContext; import org.quartz.CronTrigger; From b39bfae54f52b2b372e94b5f5cb75c7b987793bf Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Thu, 19 Apr 2018 08:18:09 -0600 Subject: [PATCH 14/17] #13482 --- OSGi/com.dotcms.portlet/README.md | 103 ++++++++++++++++++ OSGi/com.dotcms.portlet/README.textile | 88 --------------- OSGi/com.dotcms.portlet/build.gradle | 79 +++++++++++++- .../dotmarketing/osgi/portlet/Activator.java | 6 +- .../osgi/portlet/HelloWorldAction.java | 6 +- .../com.dotcms.pushpublish.listener/README.md | 66 ++++++----- .../build.gradle | 78 ++++++++++++- 7 files changed, 293 insertions(+), 133 deletions(-) create mode 100644 OSGi/com.dotcms.portlet/README.md delete mode 100644 OSGi/com.dotcms.portlet/README.textile diff --git a/OSGi/com.dotcms.portlet/README.md b/OSGi/com.dotcms.portlet/README.md new file mode 100644 index 00000000..9c512540 --- /dev/null +++ b/OSGi/com.dotcms.portlet/README.md @@ -0,0 +1,103 @@ +# README + +This bundle plugin is an example of how to create and add Portlets for use in the dotCMS' administrative console using an OSGI bundle plugin. +This example includes the code to create three different types of Portlets, all of which are supported by dotCMS: +* **Velocity Portlet**: a Portlet implementation that will display portlet content using a velocity file +* **Jsp Portlet**: a Portlet implementation that will display portlet content using a jsp file +* **Struts Portlet**: a Portlet implementation that will call an Struts Action class that will use a jsp file in order to display portlet content. + +## How to build this example + +To install all you need to do is build the JAR. to do this run +`./gradlew jar` + +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to create a bundle plugin for Portlets + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath + +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* **Exported Packages** + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +--- +## Components + +### Resources: + +* **conf/** Folder (Folder that contains the configuration files for the Portlets definitions) - Both files in this folder are MANDATORY (!) + * **conf/portlet.xml** The standard JSR-286 portlet configuration file. + * **conf/liferay-portlet.xml** This file describes some optional Liferay-specific enhancements for JSR-286 portlets that are installed on a Liferay Portal server. + +* **ext/** Folder (Folder that contains the files used by the defined Portles) + * **ext/view.vtl** Velocity file used by the Velocity Portlet + * **ext/hello.jsp** Jsp file use it by the Jsp Portlet + * **ext/strutshelloworld/view.jsp** Used by the Struts Portlet and invoked inside the *HelloWorldAction* + * **ext/strutshelloworld/view_hello.jsp** Used by the Struts Portlet and invoked inside the *HelloWorldAction* + +### com.dotmarketing.osgi.portlet.HelloWorldAction + +Simple Action class that extends *com.dotmarketing.portal.struts.DotPortletAction* +The *conf/portlet.xml* file has the definition for an *StrutsPortlet* and that definition has a reference to the mapping for the *HelloWorldAction*. + +### Activator + +This bundle activator extends *com.dotmarketing.osgi.GenericBundleActivator* and implements *BundleActivator.start()*. +This activator have 2 main important fragments of code: +* It will manually register an *ActionMapping* for our Struts action class *HelloWorldAction* that will be used by the StrutsPortlet we defined in the configuration files (*conf/*). +* It will manually register the Portlets making use of the method *registerPortlets*. + +**PLEASE note** the *unregisterServices()* call on the *stop* method, this call is MANDATORY (!) as it will allow us to clean and remove the registered Portlets and related code (like the ActionMappings registered in Struts ). + +### Multi language support + +The creation of the Portlets will generate the following language keys and use them as the title for the Portlets: +* javax.portlet.title.EXT_HELLO_WORLD +* javax.portlet.title.EXT_JSP_HELLO_WORLD +* javax.portlet.title.EXT_STRUTS_HELLO_WORLD + +In order to add multilanguage values for those keys: +*CMS Admin* -> *Language Variables* -> *Edit Default Language Variables* -> Add the values for the above keys + diff --git a/OSGi/com.dotcms.portlet/README.textile b/OSGi/com.dotcms.portlet/README.textile deleted file mode 100644 index 7fdb5095..00000000 --- a/OSGi/com.dotcms.portlet/README.textile +++ /dev/null @@ -1,88 +0,0 @@ -h1. README - -This bundle plugin is an example of how to create and add Portlets for use in the dotCMS' administrative console using an OSGI bundle plugin. -This example includes the code to create three different types of Portlets, all of which are supported by dotCMS: -* *Velocity Portlet*: a Portlet implementation that will display portlet content using a velocity file -* *Jsp Portlet*: a Portlet implementation that will display portlet content using a jsp file -* *Struts Portlet*: a Portlet implementation that will call an Struts Action class that will use a jsp file in order to display portlet content. - -h2. How to build this example - -To install all you need to do is build the JAR. to do this run -*./gradlew jar* -This will build a jar in the build/libs directory - -* To install this bundle: - Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR - Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -* To uninstall this bundle: - Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR - Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - -h2. How to create an OSGI bundle for Portlets - -In order to create this OSGI plugin, you must create a META-INF/MANIFEST to be inserted into OSGI jar. -This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. -The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath - -If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): - -* *Bundle-Name*: The name of your bundle -* *Bundle-SymbolicName*: A short and unique name for the bundle -* *Bundle-Activator*: Package and name of your Activator class (example: com.dotmarketing.osgi.portlet.Activator) -* *DynamicImport-Package: ** -Dynamically add required imports the plugin without having to add them explicitly -* *Import-Package*: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. - - -h2. Beware (!) - -In order to work inside Apache Felix OSGI runtime, the import and export directive must be bidirectional. - -The DotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. -This is possible also using the dotCMS UI (CMS Admin->Dynamic Plugins->Exported Packages). - -Only after the exported packages are defined in this property can an OSGI plugin import the packages and use them inside the OSGI blundle. - - -h2. Components - -h3. Resources: - -* *conf/* Folder (Folder that contains the configuration files for the Portlets definitions) - Both files in this folder are MANDATORY (!) -** *conf/portlet.xml* The standard JSR-286 portlet configuration file. -** *conf/liferay-portlet.xml* This file describes some optional Liferay-specific enhancements for JSR-286 portlets that are installed on a Liferay Portal server. - -* *ext/* Folder (Folder that contains the files used by the defined Portles) -** *ext/view.vtl* Velocity file used by the Velocity Portlet -** *ext/hello.jsp* Jsp file use it by the Jsp Portlet -** *ext/strutshelloworld/view.jsp* Used by the Struts Portlet and invoked inside the *HelloWorldAction* -** *ext/strutshelloworld/view_hello.jsp* Used by the Struts Portlet and invoked inside the *HelloWorldAction* - -h3. com.dotmarketing.osgi.portlet.HelloWorldAction - -Simple Action class that extends *com.dotmarketing.portal.struts.DotPortletAction* -The *conf/portlet.xml* file has the definition for an *StrutsPortlet* and that definition has a reference to the mapping for the *HelloWorldAction*. - -h3. Activator - -This bundle activator extends *com.dotmarketing.osgi.GenericBundleActivator* and implements *BundleActivator.start()*. -This activator have 2 main important fragments of code: -# It will manually register an *ActionMapping* for our Struts action class *HelloWorldAction* that will be used by the StrutsPortlet we defined in the configuration files (*conf/*). -# It will manually register the Portlets making use of the method *registerPortlets*. - -* PLEASE note the *unregisterServices()* call on the *stop* method, this call is MANDATORY (!) as it will allow us to clean and remove the registered Portlets and related code (like the ActionMappings registered in Struts ). - -h3. Multi language support - -The creation of the Portlets will generate the following language keys and use them as the title for the Portlets: -* javax.portlet.title.EXT_HELLO_WORLD -* javax.portlet.title.EXT_JSP_HELLO_WORLD -* javax.portlet.title.EXT_STRUTS_HELLO_WORLD - -In order to add multilanguage values for those keys: -*CMS Admin* -> *Language Variables* -> *Edit Default Language Variables* -> Add the values for the above keys - diff --git a/OSGi/com.dotcms.portlet/build.gradle b/OSGi/com.dotcms.portlet/build.gradle index 04fae0ab..a418804b 100644 --- a/OSGi/com.dotcms.portlet/build.gradle +++ b/OSGi/com.dotcms.portlet/build.gradle @@ -3,20 +3,21 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -24,11 +25,77 @@ jar { 'Bundle-Description': 'dotCMS - Osgi Portlets example', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotmarketing.osgi.portlet.Activator', - 'DynamicImport-Package': '*', 'Import-Package': '*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "Osgi Portlets fragment" + bundleDescription = "dotCMS - Osgi Portlets fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' diff --git a/OSGi/com.dotcms.portlet/src/main/java/com/dotmarketing/osgi/portlet/Activator.java b/OSGi/com.dotcms.portlet/src/main/java/com/dotmarketing/osgi/portlet/Activator.java index 38bd4b0b..39d1f181 100644 --- a/OSGi/com.dotcms.portlet/src/main/java/com/dotmarketing/osgi/portlet/Activator.java +++ b/OSGi/com.dotcms.portlet/src/main/java/com/dotmarketing/osgi/portlet/Activator.java @@ -1,10 +1,10 @@ package com.dotmarketing.osgi.portlet; -import com.dotcms.repackage.org.apache.logging.log4j.LogManager; -import com.dotcms.repackage.org.apache.logging.log4j.core.LoggerContext; -import com.dotcms.repackage.org.apache.struts.action.ActionMapping; import com.dotmarketing.loggers.Log4jUtil; import com.dotmarketing.osgi.GenericBundleActivator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.struts.action.ActionMapping; import org.osgi.framework.BundleContext; public class Activator extends GenericBundleActivator { diff --git a/OSGi/com.dotcms.portlet/src/main/java/com/dotmarketing/osgi/portlet/HelloWorldAction.java b/OSGi/com.dotcms.portlet/src/main/java/com/dotmarketing/osgi/portlet/HelloWorldAction.java index 226662fb..d3ccd3b6 100644 --- a/OSGi/com.dotcms.portlet/src/main/java/com/dotmarketing/osgi/portlet/HelloWorldAction.java +++ b/OSGi/com.dotcms.portlet/src/main/java/com/dotmarketing/osgi/portlet/HelloWorldAction.java @@ -4,10 +4,10 @@ import com.dotcms.repackage.javax.portlet.RenderRequest; import com.dotcms.repackage.javax.portlet.RenderResponse; import com.dotcms.repackage.javax.portlet.WindowState; -import com.dotcms.repackage.org.apache.struts.action.ActionForm; -import com.dotcms.repackage.org.apache.struts.action.ActionForward; -import com.dotcms.repackage.org.apache.struts.action.ActionMapping; import com.dotmarketing.portal.struts.DotPortletAction; +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionForward; +import org.apache.struts.action.ActionMapping; public class HelloWorldAction extends DotPortletAction { diff --git a/OSGi/com.dotcms.pushpublish.listener/README.md b/OSGi/com.dotcms.pushpublish.listener/README.md index 3fe96839..94ba6cd9 100644 --- a/OSGi/com.dotcms.pushpublish.listener/README.md +++ b/OSGi/com.dotcms.pushpublish.listener/README.md @@ -1,45 +1,57 @@ - # README --------- -## How to build an OSGi project -------------------------------- +## How to build this example To install all you need to do is build the JAR. to do this run `./gradlew jar` -This will build a jar in the build/libs directory, refer to the build.gradle for more -information on how this is done. -1. To install this bundle: +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). -Copy the bundle jar file inside the Felix OSGI container (`WEB-INF/felix/load`). -_OR_ -Upload the bundle jar file using the dotCMS UI (`CMS Admin->Dynamic Plugins->Upload Plugin`). +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). -2. To uninstall this bundle: + OR -Remove the bundle jar file from the Felix OSGI container (`WEB-INF/felix/load`). -_OR_ -Undeploy the bundle using the dotCMS UI (`CMS Admin->Dynamic Plugins->Undeploy`). + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). -## Notes --------- -In order to create this OSGI plugin, you must create a META-INF/MANIFEST to be inserted into OSGI jar. +## How to create a bundle plugin + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath -If you are building the MANIFEST on your own or desire more info on it below is a description of what is required -in this MANIFEST you must specify (see template plugin): +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) -Bundle-Name: The name of your bundle +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: -Bundle-SymbolicName: A short an unique name for the bundle +* **Exported Packages** -Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.viewtools.Activator) + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). -DynamicImport-Package: * - Dynamically add required imports the plugin may need without add them explicitly + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** -Import-Package: This is a comma separated list of package's name. - In this list there must be the packages that you are using inside - the bundle plugin and that are exported by the dotCMS runtime. + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. diff --git a/OSGi/com.dotcms.pushpublish.listener/build.gradle b/OSGi/com.dotcms.pushpublish.listener/build.gradle index 3d55ca54..8ca2a145 100644 --- a/OSGi/com.dotcms.pushpublish.listener/build.gradle +++ b/OSGi/com.dotcms.pushpublish.listener/build.gradle @@ -7,17 +7,17 @@ version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } - maven { url "http://repo.dotcms.com/artifactory/libs-snapshot" } -} - -configurations { - osgiLibs } dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -25,11 +25,77 @@ jar { 'Bundle-Description': 'dotCMS - OSGI PP Event Listener example', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotcms.pushpublish.Activator', - 'DynamicImport-Package': '*', 'Import-Package': '*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "OSGI PP Event Listener fragment" + bundleDescription = "dotCMS - OSGI PP Event Listener fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' From 54360c74a0b3d7b52e14b803637d31e4d03ce3e4 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Thu, 19 Apr 2018 16:44:50 -0600 Subject: [PATCH 15/17] #13482 --- OSGi/com.dotcms.rest/INSTALL.md | 63 --------- OSGi/com.dotcms.rest/README.md | 69 ++++++---- OSGi/com.dotcms.rest/build.gradle | 79 ++++++++++- .../README.md | 60 +++++++-- .../build.gradle | 126 +++++++++++++----- .../gradle/wrapper/gradle-wrapper.jar | Bin 53319 -> 54783 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 +- .../gradlew | 74 +++++----- .../gradlew.bat | 14 +- .../actionlet/VelocityScriptingActionlet.java | 11 +- .../README.md | 52 ++++++-- .../build.gradle | 126 +++++++++++++----- .../gradle/wrapper/gradle-wrapper.jar | Bin 53319 -> 54783 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 +- .../gradlew | 74 +++++----- .../gradlew.bat | 14 +- 16 files changed, 493 insertions(+), 277 deletions(-) delete mode 100644 OSGi/com.dotcms.rest/INSTALL.md diff --git a/OSGi/com.dotcms.rest/INSTALL.md b/OSGi/com.dotcms.rest/INSTALL.md deleted file mode 100644 index 52f95c59..00000000 --- a/OSGi/com.dotcms.rest/INSTALL.md +++ /dev/null @@ -1,63 +0,0 @@ - -# README ----- -This is an example of how to create and load Jersey Based REST resources in dotCMS via OSGi - - -## How to build this example ----- - -To install all you need to do is build the JAR. To do this run from this directory: - -`./gradlew jar` - -or for windows - -`.\gradlew.bat jar` - -This will build a jar in the build/libs directory - -### To install this bundle - -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -### To uninstall this bundle: - -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - - - -## How to test ----- - -Once installed, you can access this resource by (this assumes you are on localhost) - -`http://localhost:8080/api/example` - -or this, which requires an dotcms user to access(See authentication below) - -`http://localhost:8080/api/example/auth` - - -You can try the put and post resources by - -`curl -XPUT http://localhost:8080/api/example` - -`curl -XPOST http://localhost:8080/api/example` - - - - -## Authentication ----- -This API supports the same REST auth infrastructure as other -rest apis in dotcms. There are 4 ways to authenticate. - -* user/xxx/password/yyy in the URI -* basic http/https authentication (base64 encoded) -* DOTAUTH header similar to basic auth and base64 encoded, e.g. setHeader("DOTAUTH", base64.encode("admin@dotcms.com:admin")) -* Session based (form based login) for frontend or backend logged in user \ No newline at end of file diff --git a/OSGi/com.dotcms.rest/README.md b/OSGi/com.dotcms.rest/README.md index 8d66e411..826053d4 100644 --- a/OSGi/com.dotcms.rest/README.md +++ b/OSGi/com.dotcms.rest/README.md @@ -1,38 +1,65 @@ - # README ----- -This is an example of how to create and load Jersey Based REST resources in dotCMS via OSGi +This is an example of how to create and load Jersey Based REST resources in dotCMS via OSGi ## How to build this example ----- - -To install all you need to do is build the JAR. To do this run from this directory: +To install all you need to do is build the JAR. to do this run `./gradlew jar` -or for windows +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + + OR -`.\gradlew.bat jar` + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). -This will build a jar in the build/libs directory +## How to create a bundle plugin for a rest resource -### To install this bundle +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): -### To uninstall this bundle: +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). +## Beware (!) +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: +* **Exported Packages** + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +--- ## How to test ----- Once installed, you can access this resource by (this assumes you are on localhost) @@ -42,18 +69,14 @@ or this, which requires an dotcms user to access(See authentication below) `http://localhost:8080/api/example/auth` - You can try the put and post resources by `curl -XPUT http://localhost:8080/api/example` `curl -XPOST http://localhost:8080/api/example` - - - ## Authentication ----- + This API supports the same REST auth infrastructure as other rest apis in dotcms. There are 4 ways to authenticate. diff --git a/OSGi/com.dotcms.rest/build.gradle b/OSGi/com.dotcms.rest/build.gradle index ae6f469a..29b5ee68 100644 --- a/OSGi/com.dotcms.rest/build.gradle +++ b/OSGi/com.dotcms.rest/build.gradle @@ -3,20 +3,21 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -24,11 +25,77 @@ jar { 'Bundle-Description': 'dotCMS - Rest Endpoint example', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotcms.plugin.rest.Activator', - 'DynamicImport-Package': '*', 'Import-Package': '*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "Rest Endpoint fragment" + bundleDescription = "dotCMS - Rest Endpoint fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' diff --git a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/README.md b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/README.md index 191e72f6..2d6ba544 100644 --- a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/README.md +++ b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/README.md @@ -1,31 +1,65 @@ - # README - This plugin allows to add an Rules Engine Actionlet (VelocityScriptingActionlet) that allow users to execute arbitrary velocity code in a rule. Using this, an admin can leverage the power of Velocity and work directly with the request, response and session objects. ## How to build this example To install all you need to do is build the JAR. to do this run -```./gradlew jar``` -This will build a jar in the build/libs directory +`./gradlew jar` -1. To install this bundle: +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). +* **To install this bundle:** -2. To uninstall this bundle: + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + OR -## How to Use + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to create a bundle plugin + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath + +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* **Exported Packages** + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +## How to Use Once the plugin is installed, then : diff --git a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/build.gradle b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/build.gradle index 50ba8689..cf813f7c 100644 --- a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/build.gradle +++ b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/build.gradle @@ -1,35 +1,21 @@ -apply plugin: 'osgi' -apply plugin: 'eclipse' -apply plugin: 'java' - -task wrapper(type: Wrapper) { - gradleVersion = '2.9' +plugins { + id 'biz.aQute.bnd.builder' version '3.3.0' } -version = '1.0' - -/* This plugin uses Java 1.8 */ sourceCompatibility = '1.8' +version = '0.2' +repositories { + maven { url "http://repo.dotcms.com/artifactory/libs-release" } +} configurations { provided provided.extendsFrom(compile) } -repositories { - maven { - url "http://repo.dotcms.com/artifactory/libs-release" - } - - mavenCentral() -} - dependencies { - compile (group: 'com.dotcms', name: 'dotcms', version: '4.3.2'){ - transitive = true - } - compile group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0' + compile('com.dotcms:dotcms:4.3.2') { transitive = true } testCompile 'org.mockito:mockito-core:2.0.31-beta' testCompile 'org.hamcrest:hamcrest-all:1.3' @@ -37,14 +23,13 @@ dependencies { exclude(group: 'org.hamcrest') } - } /* To compile we must add the additional configuration we created to our classpaths. */ sourceSets { - main.compileClasspath += configurations.provided; - test.compileClasspath += configurations.provided; - test.runtimeClasspath += configurations.provided; + main.compileClasspath += configurations.provided + test.compileClasspath += configurations.provided + test.runtimeClasspath += configurations.provided } test { @@ -54,16 +39,91 @@ test { testLogging.showStandardStreams = true } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { - name = 'Osgi Velocity Scripting Actionlet' - instruction 'Bundle-Vendor', 'dotcms' - instruction 'Bundle-Description', 'dotCMS OSGI Party library example - Rules Engine Velocity Scripting Actionlet' - instruction 'Bundle-DocURL', 'http://www.dotcms.com' - instruction 'Bundle-Activator', 'com.dotmarketing.osgi.ruleengine.actionlet.Activator' - instruction 'DynamicImport-Package', '*' - instruction 'Import-Package', '*;version=0' + attributes ( + 'Bundle-Vendor': 'dotCMS', + 'Bundle-Description': 'dotCMS - Rules Engine Velocity Scripting Actionlet', + 'Bundle-DocURL': 'https://dotcms.com/', + 'Bundle-Activator': 'com.dotmarketing.osgi.ruleengine.actionlet.Activator', + 'Import-Package': '*;version=0' + ) } } - jar.dependsOn test +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "Rules Engine Velocity Scripting Actionlet fragment" + bundleDescription = "dotCMS - Rules Engine Velocity Scripting Actionlet fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' + +task wrapper(type: Wrapper) { + gradleVersion = '4.2' +} \ No newline at end of file diff --git a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradle/wrapper/gradle-wrapper.jar b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradle/wrapper/gradle-wrapper.jar index d3b83982b9b1bccad955349d702be9b884c6e049..f92d3c54ff78190576ed94d443622645360dd440 100644 GIT binary patch delta 22441 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 H4PW{nPz;Mi delta 20979 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 diff --git a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradle/wrapper/gradle-wrapper.properties b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradle/wrapper/gradle-wrapper.properties index ec15f99e..d36c14b5 100644 --- a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradle/wrapper/gradle-wrapper.properties +++ b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Apr 28 10:27:22 CST 2017 +#Thu Apr 19 15:52:28 CST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip diff --git a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradlew b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradlew index 91a7e269..4453ccea 100755 --- a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradlew +++ b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,12 +6,30 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradlew.bat b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradlew.bat index aec99730..e95643d6 100644 --- a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradlew.bat +++ b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/src/main/java/com/dotmarketing/osgi/ruleengine/actionlet/VelocityScriptingActionlet.java b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/src/main/java/com/dotmarketing/osgi/ruleengine/actionlet/VelocityScriptingActionlet.java index 0b3148eb..27def309 100644 --- a/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/src/main/java/com/dotmarketing/osgi/ruleengine/actionlet/VelocityScriptingActionlet.java +++ b/OSGi/com.dotcms.ruleengine.velocityscriptingactionlet/src/main/java/com/dotmarketing/osgi/ruleengine/actionlet/VelocityScriptingActionlet.java @@ -1,7 +1,8 @@ package com.dotmarketing.osgi.ruleengine.actionlet; +import static com.dotcms.repackage.com.google.common.base.Preconditions.checkState; + import com.dotcms.repackage.com.google.common.base.Preconditions; -import com.dotcms.repackage.org.apache.commons.lang.StringUtils; import com.dotmarketing.portlets.rules.RuleComponentInstance; import com.dotmarketing.portlets.rules.actionlet.RuleActionlet; import com.dotmarketing.portlets.rules.model.ParameterModel; @@ -9,17 +10,13 @@ import com.dotmarketing.portlets.rules.parameter.display.TextInput; import com.dotmarketing.portlets.rules.parameter.type.TextType; import com.dotmarketing.util.Logger; - +import com.dotmarketing.util.VelocityUtil; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - +import org.apache.commons.lang.StringUtils; import org.apache.velocity.context.Context; -import com.dotmarketing.util.VelocityUtil; - -import static com.dotcms.repackage.com.google.common.base.Preconditions.checkState; - /** * This actionlet allow to execute some velocity code and return the result as a request attribute. * diff --git a/OSGi/com.dotcms.ruleengine.visitoripconditionlet/README.md b/OSGi/com.dotcms.ruleengine.visitoripconditionlet/README.md index f8d476f0..045591b3 100644 --- a/OSGi/com.dotcms.ruleengine.visitoripconditionlet/README.md +++ b/OSGi/com.dotcms.ruleengine.visitoripconditionlet/README.md @@ -8,23 +8,55 @@ If you are using tomcat to retrieve the IP's with IPv4 format. `JAVA_OPTS="$JAVA ## How to build this example To install all you need to do is build the JAR. to do this run -```./gradlew jar``` -This will build a jar in the build/libs directory +`./gradlew jar` -1. To install this bundle: +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). +* **To install this bundle:** -2. To uninstall this bundle: + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + OR + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). +## How to create a bundle plugin +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* **Exported Packages** + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. diff --git a/OSGi/com.dotcms.ruleengine.visitoripconditionlet/build.gradle b/OSGi/com.dotcms.ruleengine.visitoripconditionlet/build.gradle index 6243fa74..31870ff0 100644 --- a/OSGi/com.dotcms.ruleengine.visitoripconditionlet/build.gradle +++ b/OSGi/com.dotcms.ruleengine.visitoripconditionlet/build.gradle @@ -1,34 +1,21 @@ -apply plugin: 'osgi' -apply plugin: 'eclipse' -apply plugin: 'java' - -task wrapper(type: Wrapper) { - gradleVersion = '2.9' +plugins { + id 'biz.aQute.bnd.builder' version '3.3.0' } -version = '1.0' - -/* This plugin uses Java 1.8 */ sourceCompatibility = '1.8' +version = '0.2' + +repositories { + maven { url "http://repo.dotcms.com/artifactory/libs-release" } +} configurations { provided provided.extendsFrom(compile) } -repositories { - maven { - url "http://repo.dotcms.com/artifactory/libs-release" - } - mavenCentral() -} - dependencies { - compile (group: 'com.dotcms', name: 'dotcms', version: '4.3.2'){ - transitive = true - } - /* Specifying an alternate configuration (see above 'configurations') keeps servlet-api.jar from being added to our own output Jar. */ - compile group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0' + compile('com.dotcms:dotcms:4.3.2') { transitive = true } testCompile 'org.mockito:mockito-core:2.0.31-beta' testCompile 'org.hamcrest:hamcrest-all:1.3' @@ -36,14 +23,13 @@ dependencies { exclude(group: 'org.hamcrest') } - } /* To compile we must add the additional configuration we created to our classpaths. */ sourceSets { - main.compileClasspath += configurations.provided; - test.compileClasspath += configurations.provided; - test.runtimeClasspath += configurations.provided; + main.compileClasspath += configurations.provided + test.compileClasspath += configurations.provided + test.runtimeClasspath += configurations.provided } test { @@ -53,15 +39,91 @@ test { testLogging.showStandardStreams = true } +import java.util.jar.* +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { - name = 'Osgi Visitor IP Conditionlet' - instruction 'Bundle-Vendor', 'dotcms' - instruction 'Bundle-Description', 'dotCMS OSGI Visitor IP Conditionlet' - instruction 'Bundle-DocURL', 'http://www.dotcms.com' - instruction 'Bundle-Activator', 'com.dotmarketing.osgi.ruleengine.conditionlet.Activator' - instruction 'DynamicImport-Package', '*' - instruction 'Import-Package', '*;version=0' + attributes ( + 'Bundle-Vendor': 'dotCMS', + 'Bundle-Description': 'dotCMS - OSGI Visitor IP Conditionlet', + 'Bundle-DocURL': 'https://dotcms.com/', + 'Bundle-Activator': 'com.dotmarketing.osgi.ruleengine.conditionlet.Activator', + 'Import-Package': '*;version=0' + ) } } +jar.dependsOn test +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "OSGI Visitor IP Conditionlet fragment" + bundleDescription = "dotCMS - OSGI Visitor IP Conditionlet fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' + +task wrapper(type: Wrapper) { + gradleVersion = '4.2' +} \ No newline at end of file diff --git a/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradle/wrapper/gradle-wrapper.jar b/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradle/wrapper/gradle-wrapper.jar index d3b83982b9b1bccad955349d702be9b884c6e049..a86c55b7b76b02f85ad345374fc77adf2801a3f7 100644 GIT binary patch delta 22441 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 H4PW{nhmVVd delta 20979 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 diff --git a/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradle/wrapper/gradle-wrapper.properties b/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradle/wrapper/gradle-wrapper.properties index ec15f99e..1b66643f 100644 --- a/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradle/wrapper/gradle-wrapper.properties +++ b/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Apr 28 10:27:22 CST 2017 +#Thu Apr 19 16:37:17 CST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip diff --git a/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradlew b/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradlew index 91a7e269..4453ccea 100755 --- a/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradlew +++ b/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,12 +6,30 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradlew.bat b/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradlew.bat index aec99730..e95643d6 100644 --- a/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradlew.bat +++ b/OSGi/com.dotcms.ruleengine.visitoripconditionlet/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line From 13a03c8c35d93dfa899744bb692fe9c56cf407ca Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Mon, 23 Apr 2018 13:39:04 -0600 Subject: [PATCH 16/17] #13482 --- OSGi/com.dotcms.servlet/README.md | 86 +++++++++++++++++ OSGi/com.dotcms.servlet/README.txt | 106 --------------------- OSGi/com.dotcms.servlet/build.gradle | 93 ++++++++++++++---- OSGi/com.dotcms.simpleService/README.md | 72 ++++++++++++++ OSGi/com.dotcms.simpleService/README.txt | 78 --------------- OSGi/com.dotcms.simpleService/build.gradle | 79 +++++++++++++-- 6 files changed, 306 insertions(+), 208 deletions(-) create mode 100644 OSGi/com.dotcms.servlet/README.md delete mode 100644 OSGi/com.dotcms.servlet/README.txt create mode 100644 OSGi/com.dotcms.simpleService/README.md delete mode 100644 OSGi/com.dotcms.simpleService/README.txt diff --git a/OSGi/com.dotcms.servlet/README.md b/OSGi/com.dotcms.servlet/README.md new file mode 100644 index 00000000..c7cc058a --- /dev/null +++ b/OSGi/com.dotcms.servlet/README.md @@ -0,0 +1,86 @@ +# README + +This bundle plugin is an example of how to use services provide by other bundles and how to register servlets and filters. + +## How to build this example + +To install all you need to do is build the JAR. to do this run +`./gradlew jar` + +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to create a bundle plugin using services and registering servlets and filters + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath + +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* **Exported Packages** + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +--- +## Components + +### com.dotmarketing.osgi.servlet.HelloWorldServlet + +Simple and standard implementation of a HttpServlet that will use +the HelloWorld service provide by the com.dotcms.service bundle plugin (Please refer to INSTALL.txt (!)). + +### com.dotmarketing.osgi.servlet.TestFilter + +Simple and standard implementation of a Filter + +### Activator + +This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). +Gets a reference for the HelloWorldService via HelloWorld interface (com.dotcms.service bundle plugin - Please refer to INSTALL.txt (!)) and register +our HelloWorldServlet servlet and the TestFilter filter. + +## Testing + +The HelloWorldServlet is registered under the url pattern "/helloworld" can be test it running and assuming your dotcms url is localhost:8080: + http://localhost:8080/app/helloworld + +The TestFilter filter is registered for the url pattern "/helloworld/.*" can be test it running and assuming your dotcms url is localhost:8080: + http://localhost:8080/app/helloworld/ + http://localhost:8080/app/helloworld/testing.dot diff --git a/OSGi/com.dotcms.servlet/README.txt b/OSGi/com.dotcms.servlet/README.txt deleted file mode 100644 index 33c5df1f..00000000 --- a/OSGi/com.dotcms.servlet/README.txt +++ /dev/null @@ -1,106 +0,0 @@ - -README ------- - -This bundle plugin is an example of how to use services provide by other bundles and - how to register servlets and filters. - -How to build this example -------------------------- - -Note: As you can see, this plugin depends on com.dotcms.service example. -It will use com.dotmarketing.osgi.service.HelloWorld. - -In order to compile this plugin without error you need to run first under com.dotcms.service example: -./gradlew jar - -As you can see the build.gradle will use the jar compiled under that example. - -Then to compile this plugin all you need to do is to do: -./gradlew jar -This will build a jar in the build/libs directory - -1. To install this bundle: - -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -2. To uninstall this bundle: - -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - -How to create a bundle plugin using services and registering servlets and filters -------------------------------------------- - --- -In order to create this OSGI plugin, you must create a META-INF/MANIFEST to be inserted into OSGI jar. -This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. -The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath - -If you are building the MANIFEST on your own or desire more info on it below is a description of what is required -in this MANIFEST you must specify (see template plugin): - -Bundle-Name: The name of your bundle - -Bundle-SymbolicName: A short an unique name for the bundle - -Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.servlet.Activator) - -DynamicImport-Package: * - Dynamically add required imports the plugin may need without add them explicitly - -Import-Package: This is a comma separated list of package's name. - In this list there must be the packages that you are using inside - the bundle plugin and that are exported by the dotCMS runtime. - -Beware!!! ---------- - -In order to work inside the Apache Felix OSGI runtime, the import -and export directive must be bidirectional. - -The DotCMS must declare the set of packages that will be available to -the OSGI plugins by changing the file: dotCMS/WEB-INF/felix/osgi-extra.conf. -This is possible also using the dotCMS UI (CMS Admin->Dynamic Plugins->Exported Packages). - -Only after that exported packages are defined in this list, -a plugin can Import the packages to use them inside the OSGI blundle. - --- --- --- -com.dotmarketing.osgi.servlet.HelloWorldServlet ------------------------------------------------ - -Simple and standard implementation of a HttpServlet that will use -the HelloWorld service provide by the com.dotcms.service bundle plugin (Please refer to INSTALL.txt (!)). - --- -com.dotmarketing.osgi.servlet.TestFilter ----------------------------------------- - -Simple and standard implementation of a Filter - --- -Activator ---------- - -This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). -Gets a reference for the HelloWorldService via HelloWorld interface (com.dotcms.service bundle plugin - Please refer to INSTALL.txt (!)) and register -our HelloWorldServlet servlet and the TestFilter filter. - --- --- --- -Testing -------- - -The HelloWorldServlet is registered under the url pattern "/helloworld" can be test it running and assuming your dotcms url is localhost:8080: - http://localhost:8080/app/helloworld - -The TestFilter filter is registered for the url pattern "/helloworld/.*" can be test it running and assuming your dotcms url is localhost:8080: - http://localhost:8080/app/helloworld/ - http://localhost:8080/app/helloworld/testing.dot diff --git a/OSGi/com.dotcms.servlet/build.gradle b/OSGi/com.dotcms.servlet/build.gradle index bb1eb852..aef4777f 100644 --- a/OSGi/com.dotcms.servlet/build.gradle +++ b/OSGi/com.dotcms.servlet/build.gradle @@ -3,21 +3,22 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { - compile fileTree(dir: '../com.dotcms.service/build/libs/', include: '*.jar')//As this example depends on the com.dotcms.service example + compile fileTree(dir: '../com.dotcms.simpleService/build/libs/', include: '*.jar')//As this example depends on the com.dotcms.simpleService example compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -25,21 +26,77 @@ jar { 'Bundle-Description': 'dotCMS - Osgi Servlet example', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotmarketing.osgi.servlet.Activator', - 'DynamicImport-Package': '*', - 'Import-Package': ''' - javax.servlet.http;version=0, - javax.servlet;version=0, - com.dotmarketing.filters;version=0, - com.dotmarketing.osgi;version=0, - com.dotmarketing.util;version=0, - com.dotmarketing.exception;version=0, - org.apache.felix.http.api;version=0, - org.osgi.framework;version=0, - org.osgi.service.http;version=0 - ''' + 'Import-Package': '*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "Osgi Servlet example fragment" + bundleDescription = "dotCMS - Osgi Servlet example fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' diff --git a/OSGi/com.dotcms.simpleService/README.md b/OSGi/com.dotcms.simpleService/README.md new file mode 100644 index 00000000..57e89149 --- /dev/null +++ b/OSGi/com.dotcms.simpleService/README.md @@ -0,0 +1,72 @@ +# README + +This bundle plugin is an example of how to add a simple service class that can be use it by others bundles inside the Felix OSGI container (dotCMS/felix/load) + +## How to build this example + +To install all you need to do is build the JAR. to do this run +`./gradlew jar` + +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to create a bundle plugin with services + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath + +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* **Exported Packages** + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +--- +## Components + +### Exposed service + +The exposed service for this example will be a simple Interface class with it implementation (com.dotmarketing.osgi.service.HelloWorld AND com.dotmarketing.osgi.service.HelloWorldService). + +### Activator + +This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). +Registers an instance of a our test service using the bundle context; and attaches properties to the service that can be queried +when performing a service look-up. diff --git a/OSGi/com.dotcms.simpleService/README.txt b/OSGi/com.dotcms.simpleService/README.txt deleted file mode 100644 index 31579dd2..00000000 --- a/OSGi/com.dotcms.simpleService/README.txt +++ /dev/null @@ -1,78 +0,0 @@ - -README ------- - -This bundle plugin is an example of how to add a simple service class that can be use it by others bundles inside the Felix OSGI container (dotCMS/felix/load) - -How to build this example -------------------------- - -To install all you need to do is build the JAR. to do this run -./gradlew jar -This will build a jar in the build/libs directory - -1. To install this bundle: - -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -2. To uninstall this bundle: - -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - -How to create a bundle plugin with services -------------------------------------------- - --- -In order to create this OSGI plugin, you must create a META-INF/MANIFEST to be inserted into OSGI jar. -This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. -The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath - -If you are building the MANIFEST on your own or desire more info on it below is a description of what is required -in this MANIFEST you must specify (see template plugin): - -Bundle-Name: The name of your bundle - -Bundle-SymbolicName: A short an unique name for the bundle - -Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.service.Activator) - -Export-Package: This is a comma separated list of package's name. - In this list there must be the packages that you want to make - available for other bundles. - -Import-Package: This is a comma separated list of package's name. - In this list there must be the packages that you are using inside - the bundle plugin and that are exported by the dotCMS runtime. - -Beware!!! ---------- - -In order to work inside the Apache Felix OSGI runtime, the import -and export directive must be bidirectional. - -The DotCMS must declare the set of packages that will be available to -the OSGI plugins by changing the file: dotCMS/WEB-INF/felix/osgi-extra.conf. -This is possible also using the dotCMS UI (CMS Admin->Dynamic Plugins->Exported Packages). - -Only after that exported packages are defined in this list, -a plugin can Import the packages to use them inside the OSGI blundle. - --- --- --- -Exposed service ---------- - -The exposed service for this example will be a simple Interface class with it implementation (com.dotmarketing.osgi.service.HelloWorld AND com.dotmarketing.osgi.service.HelloWorldService). - --- -Activator ---------- - -This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). -Registers an instance of a our test service using the bundle context; and attaches properties to the service that can be queried -when performing a service look-up. diff --git a/OSGi/com.dotcms.simpleService/build.gradle b/OSGi/com.dotcms.simpleService/build.gradle index 877b0182..1b45edd0 100644 --- a/OSGi/com.dotcms.simpleService/build.gradle +++ b/OSGi/com.dotcms.simpleService/build.gradle @@ -3,20 +3,21 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -25,11 +26,77 @@ jar { 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotmarketing.osgi.service.Activator', 'Export-Package': 'com.dotmarketing.osgi.service', - 'DynamicImport-Package': '*', 'Import-Package': '*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "Service fragment" + bundleDescription = "dotCMS - A bundle that creates a custom service fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' From cdd7009f7fab6a70d19f97e937ee84c8722b2c72 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Mon, 23 Apr 2018 14:32:39 -0600 Subject: [PATCH 17/17] #13482 --- OSGi/com.dotcms.spring/README.md | 85 ++++++++++++++++ OSGi/com.dotcms.spring/README.txt | 97 ------------------- OSGi/com.dotcms.spring/build.gradle | 79 +++++++++++++-- .../dotmarketing/osgi/spring/Activator.java | 4 +- .../README.md | 82 ++++++++++------ .../build.gradle | 82 ++++++++++++++-- .../com/dotcms/staticpublish/Activator.java | 5 +- OSGi/com.dotcms.tuckey/.gitignore | 1 - .../org.eclipse.buildship.core.prefs | 11 --- .../org.eclipse.wst.common.component | 7 -- ....eclipse.wst.common.project.facet.core.xml | 7 -- OSGi/com.dotcms.tuckey/README.md | 67 +++++++++---- OSGi/com.dotcms.tuckey/build.gradle | 79 +++++++++++++-- .../java/com/dotcms/tuckey/Activator.java | 10 +- OSGi/com.dotcms.viewtool/README.md | 73 ++++++++++++++ OSGi/com.dotcms.viewtool/README.txt | 72 -------------- OSGi/com.dotcms.viewtool/build.gradle | 79 +++++++++++++-- 17 files changed, 558 insertions(+), 282 deletions(-) create mode 100644 OSGi/com.dotcms.spring/README.md delete mode 100644 OSGi/com.dotcms.spring/README.txt delete mode 100644 OSGi/com.dotcms.tuckey/.gitignore delete mode 100644 OSGi/com.dotcms.tuckey/.settings/org.eclipse.buildship.core.prefs delete mode 100644 OSGi/com.dotcms.tuckey/.settings/org.eclipse.wst.common.component delete mode 100644 OSGi/com.dotcms.tuckey/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 OSGi/com.dotcms.viewtool/README.md delete mode 100644 OSGi/com.dotcms.viewtool/README.txt diff --git a/OSGi/com.dotcms.spring/README.md b/OSGi/com.dotcms.spring/README.md new file mode 100644 index 00000000..ee4d67fc --- /dev/null +++ b/OSGi/com.dotcms.spring/README.md @@ -0,0 +1,85 @@ +# README + +This bundle plugin is an example of how to add Spring support to a bundle plugin, creates and registers a simple Spring Controller. + +## How to build this example + +To install all you need to do is build the JAR. to do this run +`./gradlew jar` + +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to create a bundle plugin with Spring support + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath + +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* **Exported Packages** + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + + +--- +## Components + +### com.dotmarketing.osgi.spring.ExampleController + +Simple annotated Spring Controller. + +### example-servlet.xml + +Standard Spring configuration file where basically we enabled the support for anntotation-driven controllers. + +### Activator + +This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). +Will manually register making use of the class DispatcherServlet our spring configuration file (example-servlet.xml). + +* PLEASE note the "publishBundleServices( context )" call, this call is MANDATORY (!) as it will allow us to share resources + between the bundle and the host container (dotcms) required to a fully Spring integration with dotcms. + +## Testing + +The Spring controller is registered under the url pattern "/spring" can be test it running and assuming your dotcms url is localhost:8080: + http://localhost:8080/app/spring/examplecontroller/ + http://localhost:8080/app/spring/examplecontroller/Testing diff --git a/OSGi/com.dotcms.spring/README.txt b/OSGi/com.dotcms.spring/README.txt deleted file mode 100644 index 7a9cd149..00000000 --- a/OSGi/com.dotcms.spring/README.txt +++ /dev/null @@ -1,97 +0,0 @@ - -README ------- - -This bundle plugin is an example of how to add Spring support to a bundle plugin, creates -and registers a simple Spring Controller. - -How to build this example -------------------------- - -To install all you need to do is build the JAR. to do this run -./gradlew jar -This will build a jar in the build/libs directory - -1. To install this bundle: - -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -2. To uninstall this bundle: - -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - -How to create a bundle plugin with Spring support -------------------------------------------- - --- -In order to create this OSGI plugin, you must create a META-INF/MANIFEST to be inserted into OSGI jar. -This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. -The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath - -If you are building the MANIFEST on your own or desire more info on it below is a description of what is required -in this MANIFEST you must specify (see template plugin): - -Bundle-Name: The name of your bundle - -Bundle-SymbolicName: A short an unique name for the bundle - -Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.spring.Activator) - -DynamicImport-Package: * - Dynamically add required imports the plugin may need without add them explicitly - -Import-Package: This is a comma separated list of package's name. - In this list there must be the packages that you are using inside - the bundle plugin and that are exported by the dotCMS runtime. - (Note Spring package) - -Beware!!! ---------- - -In order to work inside the Apache Felix OSGI runtime, the import -and export directive must be bidirectional. - -The DotCMS must declare the set of packages that will be available to -the OSGI plugins by changing the file: dotCMS/WEB-INF/felix/osgi-extra.conf. -This is possible also using the dotCMS UI (CMS Admin->Dynamic Plugins->Exported Packages). - -Only after that exported packages are defined in this list, -a plugin can Import the packages to use them inside the OSGI blundle. - --- --- --- -com.dotmarketing.osgi.spring.ExampleController ------------------------------------------------ - -Simple annotated Spring Controller. - --- -example-servlet.xml ----------------------------------------- - -Standard Spring configuration file where basically we enabled the support for anntotation-driven controllers. - --- -Activator ---------- - -This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). -Will manually register making use of the class DispatcherServlet our spring configuration file (example-servlet.xml). - -* PLEASE note the "publishBundleServices( context )" call, this call is MANDATORY (!) as it will allow us to share resources - between the bundle and the host container (dotcms) required to a fully Spring integration with dotcms. - --- --- --- -Testing -------- - -The Spring controller is registered under the url pattern "/spring" can be test it running and assuming your dotcms url is localhost:8080: - http://localhost:8080/app/spring/examplecontroller/ - http://localhost:8080/app/spring/examplecontroller/Testing diff --git a/OSGi/com.dotcms.spring/build.gradle b/OSGi/com.dotcms.spring/build.gradle index c0206e4b..ce608a28 100644 --- a/OSGi/com.dotcms.spring/build.gradle +++ b/OSGi/com.dotcms.spring/build.gradle @@ -3,20 +3,21 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -24,11 +25,77 @@ jar { 'Bundle-Description': 'dotCMS - Spring Controller example', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotmarketing.osgi.spring.Activator', - 'DynamicImport-Package': '*', 'Import-Package': '*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "Spring Controller fragment" + bundleDescription = "dotCMS - Spring Controller fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' diff --git a/OSGi/com.dotcms.spring/src/main/java/com/dotmarketing/osgi/spring/Activator.java b/OSGi/com.dotcms.spring/src/main/java/com/dotmarketing/osgi/spring/Activator.java index d600905a..bb86d91b 100644 --- a/OSGi/com.dotcms.spring/src/main/java/com/dotmarketing/osgi/spring/Activator.java +++ b/OSGi/com.dotcms.spring/src/main/java/com/dotmarketing/osgi/spring/Activator.java @@ -1,11 +1,11 @@ package com.dotmarketing.osgi.spring; -import com.dotcms.repackage.org.apache.logging.log4j.LogManager; -import com.dotcms.repackage.org.apache.logging.log4j.core.LoggerContext; import com.dotmarketing.filters.CMSFilter; import com.dotmarketing.loggers.Log4jUtil; import com.dotmarketing.osgi.GenericBundleActivator; import org.apache.felix.http.api.ExtHttpService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.springframework.web.servlet.DispatcherServlet; diff --git a/OSGi/com.dotcms.staticpublish.listener/README.md b/OSGi/com.dotcms.staticpublish.listener/README.md index ad03a60c..1a868e46 100644 --- a/OSGi/com.dotcms.staticpublish.listener/README.md +++ b/OSGi/com.dotcms.staticpublish.listener/README.md @@ -1,6 +1,4 @@ - -# Static Publish Plugin - +# README This plugin enables the push publishing of static objects from a dotCMS sender to a remote server using SSH File Transfer Protocol (SFTP). It is done through a push publishing listener that is subscribed to an event `SingleStaticPublishEndpointSuccessEvent`, once this plugin is deployed on dotCMS as a dynamic plugin. @@ -9,55 +7,77 @@ For further information about dotCMS Dynamic Plugins and Push Publishing Listene * https://dotcms.com/docs/latest/osgi-plugins * https://dotcms.com/docs/latest/push-publish-listeners-configuration +## How to build this example -## Important Notes: - -1. Be aware that you need to subscribe and stop the listener under Activator's start and stop method respectively. -2. dotCMS needs to be installed on the sender under a Platform License in order to use the Static Publish feature. However, the receiver does not need to be a dotCMS instance. -3. We use sshj project in this example to establish the connection between sender and receiver. This library relies on a Bouncy Castle dependency in order to secure all requests. Bouncy Castle needs to be verified against Java Security Framework, in order to do that Bouncy Castle jars can't be part of this osgi plugin (see link). So we will be using dotCMS own Bouncy Castle library for this purpose. [http://side-effects-bang.blogspot.com/2015/02/deploying-uberjars-that-use-bouncy.html](http://side-effects-bang.blogspot.com/2015/02/deploying-uberjars-that-use-bouncy.html) - -## How to Configure +To install all you need to do is build the JAR. to do this run +`./gradlew jar` -All information required by the sender to establish a secure connection with a remote server needs to be included in the file `src/main/resources/plugin.properties`. +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar -The following key properties are required: +* **To install this bundle:** -* **key.file.path**: absolute path (in the sender) where the .pem certificate to be used for the connection is located -* **ssh.user**: ssh user to be used to connect with the receiver -* **hosts**: receiver's ip address -* **remote.path**: absolute path where static content will be stored in the receiver + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). -**Note:** The plugin needs to be built and redeployed as a dynamic plugin in dotCMS each time the `plugin.properties` file is modified +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + OR + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). -## How to build an OSGi project +## How to create a bundle plugin +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath -To install, all you need to do is build the JAR. To do this, run `./gradlew jar`. This will build a jar in the build/libs directory. Refer to the build.gradle for more information. +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` +## Beware (!) -1. To install this bundle: +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: -Copy the bundle jar file inside the Felix OSGI container (`WEB-INF/felix/load`) OR Upload the bundle jar file using the dotCMS UI (`CMS Admin->Dynamic Plugins->Upload Plugin`). +* **Exported Packages** -2. To uninstall this bundle: + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). -Remove the bundle jar file from the Felix OSGI container (`WEB-INF/felix/load`) OR Undeploy the bundle using the dotCMS UI (`CMS Admin->Dynamic Plugins->Undeploy`). + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** -## Additional Notes + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. -In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. This file is being created for you by Gradle. If you need to, you can alter our config for this, but our out of the box config should work. The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath +## Important Notes: -If you are building the MANIFEST on your own, or desire more info, below is a description of what is required in the MANIFEST that you must specify (see template plugin): +1. Be aware that you need to subscribe and stop the listener under Activator's start and stop method respectively. +2. dotCMS needs to be installed on the sender under a Platform License in order to use the Static Publish feature. However, the receiver does not need to be a dotCMS instance. +3. We use sshj project in this example to establish the connection between sender and receiver. This library relies on a Bouncy Castle dependency in order to secure all requests. Bouncy Castle needs to be verified against Java Security Framework, in order to do that Bouncy Castle jars can't be part of this osgi plugin (see link). So we will be using dotCMS own Bouncy Castle library for this purpose. [http://side-effects-bang.blogspot.com/2015/02/deploying-uberjars-that-use-bouncy.html](http://side-effects-bang.blogspot.com/2015/02/deploying-uberjars-that-use-bouncy.html) -**Bundle-Name**: The name of your bundle +## How to Configure -**Bundle-SymbolicName**: A short and unique name for the bundle +All information required by the sender to establish a secure connection with a remote server needs to be included in the file `src/main/resources/plugin.properties`. -**Bundle-Activator**: Package and name of your Activator class (example: com.dotmarketing.osgi.viewtools.Activator) +The following key properties are required: -**DynamicImport-Package**: Dynamically add required imports the plugin may need without add them explicitly +* **key.file.path**: absolute path (in the sender) where the .pem certificate to be used for the connection is located +* **ssh.user**: ssh user to be used to connect with the receiver +* **hosts**: receiver's ip address +* **remote.path**: absolute path where static content will be stored in the receiver -**Import-Package**: This is a comma separated list of package names. In this list there must be the packages that you are using inside the plugin bundle and that are exported during dotCMS runtime. +**Note:** The plugin needs to be built and redeployed as a dynamic plugin in dotCMS each time the `plugin.properties` file is modified diff --git a/OSGi/com.dotcms.staticpublish.listener/build.gradle b/OSGi/com.dotcms.staticpublish.listener/build.gradle index bb7b76d9..de274300 100644 --- a/OSGi/com.dotcms.staticpublish.listener/build.gradle +++ b/OSGi/com.dotcms.staticpublish.listener/build.gradle @@ -3,25 +3,30 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } - maven { url "http://repo.dotcms.com/artifactory/libs-snapshot" } } configurations { - osgiLibs + pluginLibs } dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } + compileOnly('com.hierynomus:sshj:0.23.0'){ exclude group: 'org.bouncycastle' //Why we exlude? See README's important notes. } compileOnly('org.slf4j:slf4j-simple:1.7.7') } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -29,7 +34,7 @@ jar { 'Bundle-Description': 'dotCMS - OSGI Static Event Listener example', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotcms.staticpublish.Activator', - 'Bundle-ClassPath' :''' + 'Bundle-ClassPath': ''' ., libs/eddsa-0.2.0.jar, libs/jzlib-1.1.3.jar, @@ -37,7 +42,6 @@ jar { libs/slf4j-simple-1.7.7.jar, libs/sshj-0.23.0.jar, ''', - 'DynamicImport-Package': '*', 'Import-Package': '*;version=0' ) } @@ -48,10 +52,74 @@ task copyToLib(type: Copy) { from configurations.compileOnly } -//jar.dependsOn fragment jar.dependsOn copyToLib +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "OSGI Static Event Listener fragment" + bundleDescription = "dotCMS - OSGI Static Event Listener fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} -tasks.copyToLib.execute() +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' diff --git a/OSGi/com.dotcms.staticpublish.listener/src/main/java/com/dotcms/staticpublish/Activator.java b/OSGi/com.dotcms.staticpublish.listener/src/main/java/com/dotcms/staticpublish/Activator.java index 3277a487..ec291e1d 100644 --- a/OSGi/com.dotcms.staticpublish.listener/src/main/java/com/dotcms/staticpublish/Activator.java +++ b/OSGi/com.dotcms.staticpublish.listener/src/main/java/com/dotcms/staticpublish/Activator.java @@ -1,14 +1,13 @@ package com.dotcms.staticpublish; -import com.dotcms.repackage.org.apache.logging.log4j.LogManager; -import com.dotcms.repackage.org.apache.logging.log4j.core.LoggerContext; import com.dotcms.staticpublish.listener.SuccessEndpointsSubscriber; import com.dotcms.system.event.local.business.LocalSystemEventsAPI; import com.dotcms.system.event.local.type.staticpublish.SingleStaticPublishEndpointSuccessEvent; import com.dotmarketing.business.APILocator; import com.dotmarketing.loggers.Log4jUtil; import com.dotmarketing.osgi.GenericBundleActivator; - +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; import org.osgi.framework.BundleContext; diff --git a/OSGi/com.dotcms.tuckey/.gitignore b/OSGi/com.dotcms.tuckey/.gitignore deleted file mode 100644 index ae3c1726..00000000 --- a/OSGi/com.dotcms.tuckey/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/OSGi/com.dotcms.tuckey/.settings/org.eclipse.buildship.core.prefs b/OSGi/com.dotcms.tuckey/.settings/org.eclipse.buildship.core.prefs deleted file mode 100644 index dbe91a2e..00000000 --- a/OSGi/com.dotcms.tuckey/.settings/org.eclipse.buildship.core.prefs +++ /dev/null @@ -1,11 +0,0 @@ -build.commands=org.eclipse.jdt.core.javabuilder,org.eclipse.wst.common.project.facet.core.builder,org.eclipse.wst.validation.validationbuilder -connection.arguments= -connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) -connection.java.home=null -connection.jvm.arguments= -connection.project.dir= -containers=org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/,org.eclipse.jst.j2ee.internal.web.container -derived.resources=.gradle,build -eclipse.preferences.version=1 -natures=org.eclipse.jdt.core.javanature,org.eclipse.wst.common.project.facet.core.nature,org.eclipse.wst.common.modulecore.ModuleCoreNature,org.eclipse.jem.workbench.JavaEMFNature -project.path=\: diff --git a/OSGi/com.dotcms.tuckey/.settings/org.eclipse.wst.common.component b/OSGi/com.dotcms.tuckey/.settings/org.eclipse.wst.common.component deleted file mode 100644 index 925e9399..00000000 --- a/OSGi/com.dotcms.tuckey/.settings/org.eclipse.wst.common.component +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/OSGi/com.dotcms.tuckey/.settings/org.eclipse.wst.common.project.facet.core.xml b/OSGi/com.dotcms.tuckey/.settings/org.eclipse.wst.common.project.facet.core.xml deleted file mode 100644 index cea524ad..00000000 --- a/OSGi/com.dotcms.tuckey/.settings/org.eclipse.wst.common.project.facet.core.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/OSGi/com.dotcms.tuckey/README.md b/OSGi/com.dotcms.tuckey/README.md index 29a78d3c..95117999 100644 --- a/OSGi/com.dotcms.tuckey/README.md +++ b/OSGi/com.dotcms.tuckey/README.md @@ -7,43 +7,68 @@ This bundle plugin is an example of how add Tuckey rewrite Rules using an OSGI b To install all you need to do is build the JAR. to do this run `./gradlew jar` -This will build a jar in the build/libs directory -* To install this bundle: - Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR - Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar -* To uninstall this bundle: - Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR - Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). +* **To install this bundle:** -## How to create a bundle plugin for Tuckeys rewrite Rules + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). -In order to create this OSGI plugin, you must create a META-INF/MANIFEST to be inserted into OSGI jar. +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to create a bundle plugin + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): -* *Bundle-Name*: The name of your bundle -* *Bundle-SymbolicName*: A short an unique name for the bundle -* *Bundle-Activator*: Package and name of your Activator class (example: com.dotmarketing.osgi.tuckey.Activator) -* *DynamicImport-Package: ** -Dynamically add required imports the plugin may need without add them explicitly -* *Import-Package*: This is a comma separated list of package's name. In this list there must be the packages that you are -using inside the bundle plugin and that are exported by the dotCMS runtime. +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: -## Activator +* **Exported Packages** + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +--- +## Components + +### Activator This bundle activator extends from *com.dotmarketing.osgi.GenericBundleActivator* and implements *BundleActivator.start()*. Will manually register Tuckey Rewrite Rules making use of the method *addRewriteRule* * PLEASE note the *unregisterServices()* call on the *stop* method, this call is MANDATORY (!) as it will allow us to stop and remove the registered Tuckey Rewrite Rules when the plugin is undeploy. -________________________________________________________________________________________ - ## Testing * *Testing the forward rule (non dotCMS resource)*: http://localhost:8080/example/forwardTuckey/ diff --git a/OSGi/com.dotcms.tuckey/build.gradle b/OSGi/com.dotcms.tuckey/build.gradle index 927a574d..bb5e8974 100644 --- a/OSGi/com.dotcms.tuckey/build.gradle +++ b/OSGi/com.dotcms.tuckey/build.gradle @@ -3,20 +3,21 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -24,11 +25,77 @@ jar { 'Bundle-Description': 'dotCMS - Tuckey Rewrite Rules example', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotcms.tuckey.Activator', - 'DynamicImport-Package': '*', 'Import-Package': '*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "Tuckey Rewrite Rules fragment" + bundleDescription = "dotCMS - Tuckey Rewrite Rules fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2' diff --git a/OSGi/com.dotcms.tuckey/src/main/java/com/dotcms/tuckey/Activator.java b/OSGi/com.dotcms.tuckey/src/main/java/com/dotcms/tuckey/Activator.java index 16af5422..239d2b19 100644 --- a/OSGi/com.dotcms.tuckey/src/main/java/com/dotcms/tuckey/Activator.java +++ b/OSGi/com.dotcms.tuckey/src/main/java/com/dotcms/tuckey/Activator.java @@ -1,14 +1,14 @@ package com.dotcms.tuckey; -import com.dotcms.repackage.org.apache.logging.log4j.LogManager; -import com.dotcms.repackage.org.apache.logging.log4j.core.LoggerContext; -import com.dotcms.repackage.org.tuckey.web.filters.urlrewrite.Condition; -import com.dotcms.repackage.org.tuckey.web.filters.urlrewrite.NormalRule; -import com.dotcms.repackage.org.tuckey.web.filters.urlrewrite.SetAttribute; import com.dotmarketing.filters.Constants; import com.dotmarketing.loggers.Log4jUtil; import com.dotmarketing.osgi.GenericBundleActivator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; import org.osgi.framework.BundleContext; +import org.tuckey.web.filters.urlrewrite.Condition; +import org.tuckey.web.filters.urlrewrite.NormalRule; +import org.tuckey.web.filters.urlrewrite.SetAttribute; public class Activator extends GenericBundleActivator { diff --git a/OSGi/com.dotcms.viewtool/README.md b/OSGi/com.dotcms.viewtool/README.md new file mode 100644 index 00000000..f67337b6 --- /dev/null +++ b/OSGi/com.dotcms.viewtool/README.md @@ -0,0 +1,73 @@ +# README + +## How to build this example + +To install all you need to do is build the JAR. to do this run +`./gradlew jar` + +This will build two jars in the `build/libs` directory: a bundle fragment (in order to expose needed 3rd party libraries from dotCMS) and the plugin jar + +* **To install this bundle:** + + Copy the bundle jar files inside the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Upload the bundle jars files using the dotCMS UI (*CMS Admin->Dynamic Plugins->Upload Plugin*). + +* **To uninstall this bundle:** + + Remove the bundle jars files from the Felix OSGI container (*dotCMS/felix/load*). + + OR + + Undeploy the bundle jars using the dotCMS UI (*CMS Admin->Dynamic Plugins->Undeploy*). + +## How to add a ViewTool OSGI plugin + +In order to create this OSGI plugin, you must create a `META-INF/MANIFEST` to be inserted into OSGI jar. +This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. +The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath + +If you are building the MANIFEST on your own or desire more info on it below is a description of what is required in this MANIFEST you must specify (see template plugin): + +``` + Bundle-Name: The name of your bundle + Bundle-SymbolicName: A short an unique name for the bundle + Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.override.Activator) + Export-Package: Declares the packages that are visible outside the plugin. Any package not declared here has visibility only within the bundle. + Import-Package: This is a comma separated list of the names of packages to import. In this list there must be the packages that you are using inside your osgi bundle plugin and are exported and exposed by the dotCMS runtime. +``` + +## Beware (!) + +In order to work inside the Apache Felix OSGI runtime, the import and export directive must be bidirectional, there are two ways to accomplish this: + +* **Exported Packages** + + The dotCMS must declare the set of packages that will be available to the OSGI plugins by changing the file: *dotCMS/WEB-INF/felix/osgi-extra.conf*. +This is possible also using the dotCMS UI (*CMS Admin->Dynamic Plugins->Exported Packages*). + + Only after that exported packages are defined in this list, a plugin can Import the packages to use them inside the OSGI blundle. + +* **Fragment** + + A Bundle fragment, is a bundle whose contents are made available to another bundles exporting 3rd party libraries from dotCMS. +One notable difference is that fragments do not participate in the lifecycle of the bundle, and therefore cannot have an Bundle-Activator. +As it not contain a Bundle-Activator a fragment cannot be started so after deploy it will have its state as Resolved and NOT as Active as a normal bundle plugin. + +--- +## Components + +### com.dotmarketing.osgi.viewtools.MyToolInfo + +For registering and initialization of our ViewTool implementation + +### com.dotmarketing.osgi.viewtools.MyViewTool + +ViewTool implementation + +### Activator + +This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). +This activator will allow you to register the MyToolInfo object using the GenericBundleActivator.registerViewToolService method diff --git a/OSGi/com.dotcms.viewtool/README.txt b/OSGi/com.dotcms.viewtool/README.txt deleted file mode 100644 index bae58e01..00000000 --- a/OSGi/com.dotcms.viewtool/README.txt +++ /dev/null @@ -1,72 +0,0 @@ - -README ------- - -How to build this example -------------------------- - -To install all you need to do is build the JAR. to do this run -./gradlew jar -This will build a jar in the build/libs directory - -1. To install this bundle: - -Copy the bundle jar file inside the Felix OSGI container (dotCMS/felix/load). - OR -Upload the bundle jar file using the dotCMS UI (CMS Admin->Dynamic Plugins->Upload Plugin). - -2. To uninstall this bundle: - -Remove the bundle jar file from the Felix OSGI container (dotCMS/felix/load). - OR -Undeploy the bundle using the dotCMS UI (CMS Admin->Dynamic Plugins->Undeploy). - -How to add a ViewTool OSGI plugin ---------------------------------- - --- -In order to create this OSGI plugin, you must create a META-INF/MANIFEST to be inserted into OSGI jar. -This file is being created for you by Gradle. If you need you can alter our config for this but in general our out of the box config should work. -The Gradle plugin uses BND to generate the Manifest. The main reason you need to alter the config is when you need to exclude a package you are including on your Bundle-ClassPath - -If you are building the MANIFEST on your own or desire more info on it below is a description of what is required -in this MANIFEST you must specify (see template plugin): - -Bundle-Name: The name of your bundle - -Bundle-SymbolicName: A short an unique name for the bundle - -Bundle-Activator: Package and name of your Activator class (example: com.dotmarketing.osgi.viewtools.Activator) - -DynamicImport-Package: * - Dynamically add required imports the plugin may need without add them explicitly - -Import-Package: This is a comma separated list of package's name. - In this list there must be the packages that you are using inside - the bundle plugin and that are exported by the dotCMS runtime. - -Beware!!! ---------- - -In order to work inside the Apache Felix OSGI runtime, the import -and export directive must be bidirectional. - -The DotCMS must declare the set of packages that will be available to -the OSGI plugins by changing the file: dotCMS/WEB-INF/felix/osgi-extra.conf. -This is possible also using the dotCMS UI (CMS Admin->Dynamic Plugins->Exported Packages). - -Only after that exported packages are defined in this list, -a plugin can Import the packages to use them inside the OSGI blundle. - --- --- --- -com.dotmarketing.osgi.viewtools.MyToolInfo -> For registering and initialization of our ViewTool implementation -com.dotmarketing.osgi.viewtools.MyViewTool -> ViewTool implementation - --- -Activator ---------- - -This bundle activator extends from com.dotmarketing.osgi.GenericBundleActivator and implements BundleActivator.start(). -This activator will allow you to register the MyToolInfo object using the GenericBundleActivator.registerViewToolService method diff --git a/OSGi/com.dotcms.viewtool/build.gradle b/OSGi/com.dotcms.viewtool/build.gradle index 04ab8f37..a1da8ef1 100644 --- a/OSGi/com.dotcms.viewtool/build.gradle +++ b/OSGi/com.dotcms.viewtool/build.gradle @@ -3,20 +3,21 @@ plugins { } sourceCompatibility = '1.8' -version = '0.1' +version = '0.2' repositories { maven { url "http://repo.dotcms.com/artifactory/libs-release" } } -configurations { - osgiLibs -} - dependencies { compile('com.dotcms:dotcms:4.3.2') { transitive = true } } +import java.util.jar.* + +///////////////////////// +//Plugin jar +///////////////////////// jar { manifest { attributes ( @@ -24,11 +25,77 @@ jar { 'Bundle-Description': 'dotCMS - Osgi View Tool example', 'Bundle-DocURL': 'https://dotcms.com/', 'Bundle-Activator': 'com.dotmarketing.osgi.viewtools.Activator', - 'DynamicImport-Package': '*', 'Import-Package': '*;version=0' ) } } +jar.finalizedBy 'fragmentJar' + +///////////////////////// +//Fragment jar +///////////////////////// + +ext { + bundleName = "Osgi View Tool fragment" + bundleDescription = "dotCMS - Osgi View Tool fragment" + fragmentHost = "system.bundle; extension:=framework" + bundleSymbolicName = "" //Auto generated based on the plugin jar + bundleVersion = "" //Auto generated based on the plugin jar + importPackage = "" //Auto generated based on the plugin jar + bundleManifestVersion = "" //Auto generated based on the plugin jar + bundleDocURL = "" //Auto generated based on the plugin jar + bundleVendor = "" //Auto generated based on the plugin jar +} +/** + * The import generates versions like this: version="[1.8,2)" + * That format does not work for the export, so we need to replace it + * to: version=0 + */ +ext.fixVersionNumber = {importValue -> + return importValue.replaceAll("\"\\[[0-9.,]+\\)\"", "0") +} + +/** + * Reads the Manifest file of the just created plugin jar in order to get the required info + * to automatically create the fragment jar. + */ +task readManifesttAttributes { + doFirst { + File file = configurations.baseline.singleFile + JarFile jar = new JarFile(file) + Attributes manifest = jar.getManifest().getMainAttributes() + bundleSymbolicName = "${manifest.getValue('Bundle-SymbolicName')}" + bundleVersion = "${manifest.getValue('Bundle-Version')}" + importPackage = "${manifest.getValue('Import-Package')}" + bundleManifestVersion = "${manifest.getValue('Bundle-ManifestVersion')}" + bundleDocURL = "${manifest.getValue('Bundle-DocURL')}" + bundleVendor = "${manifest.getValue('Bundle-Vendor')}" + } +} +task fragmentJar(type: Jar) { + + doFirst { + //Setting the fragment jar name + baseName = project.name + archiveName = "${baseName}.fragment-${version}.jar" + importPackage = fixVersionNumber(importPackage) + + manifest { + attributes ( + 'Bundle-Name': "${bundleName}", + 'Bundle-Description': "${bundleDescription}", + 'Bundle-Vendor': "${bundleVendor}", + 'Bundle-Version': "${version}", + 'Bundle-SymbolicName': "${baseName}.fragment", + 'Bundle-ManifestVersion': "${bundleManifestVersion}", + 'Bundle-DocURL': "${bundleDocURL}", + 'Fragment-Host': "${fragmentHost}", + 'Export-Package': "${importPackage}" + ) + } + } +} +fragmentJar.dependsOn 'readManifesttAttributes' task wrapper(type: Wrapper) { gradleVersion = '4.2'