07 June 2019

Now we are going to focus on converting the service layer. This can be broken down into the following steps.

My old service package path was mil.army.hrc.ikrome.feeds.service. For the new one I went with mil.army.usaac.ikrome.allfeeds. This is also the package I will use for AllFeeds-Web.

Now we need to find the current service information on our service in the database. To help build the query go to the Tomcat webapps directory for Liferay 6.2 and record the servlet context. Ours is allFeeds-portlet. We now connect to the database and run query as shown below. Record the schemaVersion and servletContextName. If no results are returned record 0.0.1 for the schemaVersion and the servlet context for the servletContextName.

Database Query for Info

select * from Release_ where servletContextName = 'allFeeds-portlet'


servletContextName | schemaVersion
-------------------|---------------
allFeeds-portlet   | 1

File -> New -> Liferay Module Project. Enter the new module name “AllFeeds-Service”. Verify the location is correct for your modules.Select build type Maven and Liferay Version 7.1. For project template we will choose service-builder. Click Next and enter your package path. Click the Finish button.

Next we edit the pom.xml for AllFeeds-Service-service. Change the buildNumberIncrement to false and add a version number. If this step is initially forgotten you will also need to update the service.properties file.

modules/AllFeeds-Service/AllFeeds-Service-service/pom.xml

...Stuff Omitted
	<build>
		<plugins>
			<plugin>
				<groupId>com.liferay</groupId>
				<artifactId>com.liferay.portal.tools.service.builder</artifactId>
				<version>1.0.248</version>
				<configuration>
					<apiDirName>../AllFeeds-Service-api/src/main/java</apiDirName>
					<autoNamespaceTables>true</autoNamespaceTables>
          <buildNumberIncrement>false</buildNumberIncrement>
          <serviceBuildNumber>300000</serviceBuildNumber>
					<hbmFileName>src/main/resources/META-INF/module-hbm.xml</hbmFileName>
					<implDirName>src/main/java</implDirName>
					<mergeModelHintsConfigs>src/main/resources/META-INF/portlet-model-hints.xml</mergeModelHintsConfigs>
					<modelHintsFileName>src/main/resources/META-INF/portlet-model-hints.xml</modelHintsFileName>
					<osgiModule>true</osgiModule>
					<propsUtil>mil.army.usaac.ikrome.allfeeds.service.util.ServiceProps</propsUtil>
					<resourcesDirName>src/main/resources</resourcesDirName>
					<springFileName>src/main/resources/META-INF/spring/module-spring.xml</springFileName>
					<sqlDirName>src/main/resources/META-INF/sql</sqlDirName>
					<sqlFileName>tables.sql</sqlFileName>
				</configuration>
			</plugin>
		</plugins>
	</build>
...Stuff Omitted

We can now copy over information from our old service.xml to our new service.xml. Make sure that our new package path is used. The majority of entity definitions can be copied over with no changes.

modules/AllFeeds-Service/AllFeeds-Service-service/service.xml

<?xml version="1.0"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 7.0.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_7_0_0.dtd">

<service-builder package-path="mil.army.usaac.ikrome.allfeeds">
	<namespace>newsfeed</namespace>
	 <entity name="Newsfeed" local-service="true" remote-service="true">
        <column name="NewsfeedKey" type="String" primary="true"></column>
        <column name="NewsfeedName" type="String"></column>
        <column name="NewsfeedValue" type="String" ></column>
    </entity>
    
    
    <entity name="Location" local-service="true" remote-service="true">
    	<column name="locationId" type="long" primary="true"></column>
    	<column name="companyId" type="long"></column>
    	<column name="groupId" type="long"></column>    	
    	<column name="zip" type="String"></column>    	
    	<column name="name" type="String"></column>
    	
    	<finder return-type="Collection" name="GroupId">
    		<finder-column name="groupId"></finder-column>
    	</finder>
    </entity>
</service-builder>

Now we want to run our service builder using maven so that code will be generated in AllFeeds-Service-Api and AllFeeds-Service-service.

modules/AllFeeds-Service/AllFeeds-Service-service/

$ mvn service-builder:build

The generated code in AllFeeds-Service-Api will remain untouched unless you add a configuration element. As shown in Part 1 the cutomizable impl files will be in AllFeeds-Service-service. Copy the code from 6.2 and modify as needed for 7.1. Keep in mind that your import statements will be different and that your IDE may insert the wrong ones as you copy from 6.2 to 7.1. Most likely the only differences in 7.1 will be the import statements.

Now we need to add the bundle activator class. We will need the servlet context and package path from earlier. The servlet context will be your old symbolic name and the package path appended with “.service” will be your new symbolic name. First we need to add dependencies to our pom.xml.

IKROme-Analysis-Tool-Service/IKROme-Analysis-Tool-Service-service/pom.xml

...Stuff omitted
<dependencies>
...Stuff omitted
    <dependency>
      <groupId>org.osgi</groupId>
      <artifactId>org.osgi.core</artifactId>
      <version>6.0.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.osgi</groupId>
      <artifactId>osgi.cmpn</artifactId>
      <version>6.0.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>com.liferay</groupId>
      <artifactId>com.liferay.portal.upgrade.api</artifactId>
      <version>2.0.3</version>
      <scope>provided</scope>
    </dependency>
</dependencies>

The class should be placed in a package that is the package path appended with “.activator”.

AllFeedsServiceBundleActivator.java

package mil.army.usaac.ikrome.allfeeds.activator;

import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle;
import com.liferay.portal.kernel.upgrade.UpgradeException;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.upgrade.release.BaseUpgradeServiceModuleRelease;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;

public AllFeedsServiceBundleActivatorclass  implements BundleActivator{
	
	private ServiceTracker<Object, Object> serviceTracker;
	
	@Override
	public void start(BundleContext bundleContext) throws Exception {
		
		Filter filter = bundleContext.createFilter(
		        StringBundler.concat(
		            "(&(objectClass=", ModuleServiceLifecycle.class.getName(), ")",
		            ModuleServiceLifecycle.DATABASE_INITIALIZED, ")"));
		
		serviceTracker = new ServiceTracker<Object, Object>(bundleContext, filter, null) {
			
			@Override
	         public Object addingService(
	          ServiceReference<Object> serviceReference) {
				 
				 try {
		              BaseUpgradeServiceModuleRelease
		                 upgradeServiceModuleRelease =
		                   new BaseUpgradeServiceModuleRelease() {

		                    @Override
		                    protected String getNamespace() {
		                        return "newsfeed";
		                    }

		                    @Override
		                    protected String getNewBundleSymbolicName() {
		                        return "mil.army.usaac.ikrome.allfeeds.service";
		                    }

		                    @Override
		                    protected String getOldBundleSymbolicName() {
		                        return "allFeeds-portlet";
		                    }
		                                   

		                   };
		                   
		              upgradeServiceModuleRelease.upgrade();

		              return null;
		          }
		          catch (UpgradeException ue) {
		              throw new RuntimeException(ue);
		          }
				 
			}
		};
		serviceTracker.open();
	}
	
	@Override
	public void stop(BundleContext context) throws Exception {
		serviceTracker.close();		
	}
	
}

Once our bundle activator class is complete we need to add a reference to it in our bnd.bnd file. Add an entry for Bundle-Activator.

modules/AllFeeds-Service/AllFeeds-Service-service/bnd.bnd

Bundle-Name: AllFeeds-Service-service
Bundle-SymbolicName: mil.army.usaac.ikrome.allfeeds.service
Bundle-Version: 1.0.0
Bundle-Activator:  mil.army.usaac.ikrome.allfeeds.activator.AllFeedsServiceBundleActivator
Liferay-Require-SchemaVersion: 1.0.0
Liferay-Service: true
-includeresource: META-INF/service.xml=service.xml
-liferay-service-xml: META-INF/service.xml
-plugin.service: com.liferay.ant.bnd.service.ServiceAnalyzerPlugin
-plugin.spring: com.liferay.ant.bnd.spring.SpringDependencyAnalyzerPlugin

Adding the upgrade step class is next. This can be skipped if you don’t have a table with an int or long as a primary key. This class will update the counter service that produces the primary key values. First we will need to find the full package path for our model from the 6.2 service builder project. This is mil.army.hrc.ikrome.feeds.service.model.Location in this project. This code should be placed in the package path appended with “.upgrade”.

AllFeedsUpgradeStep.java

package mil.army.usaac.ikrome.allfeeds.upgrade;

import com.liferay.counter.kernel.model.Counter;
import com.liferay.counter.kernel.service.CounterLocalService;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.upgrade.UpgradeProcess;

import java.util.Optional;

import mil.army.usaac.ikrome.allfeeds.model.Location;


public class AllFeedsUpgradeStep extends UpgradeProcess{
	private static Log LOG = LogFactoryUtil.getLog(AllFeedsUpgradeStep.class);
	
	private String OLD_MODEL_NAME = "mil.army.hrc.ikrome.feeds.service.model.Location";
	
	private volatile CounterLocalService counterLocalService;
	
	public AllFeedsUpgradeStep(CounterLocalService counterLocalService) {		
		this.counterLocalService = Optional.ofNullable(counterLocalService).orElseThrow(IllegalArgumentException::new);
	}
	
	@Override
	protected void doUpgrade() throws Exception {
		//Update package name in counter table	
		try {
			Counter oldCounter =counterLocalService.getCounter(OLD_MODEL_NAME);
			if(oldCounter != null) {
				oldCounter.setName(Location.class.getName());
				oldCounter.persist();
				counterLocalService.deleteCounter(OLD_MODEL_NAME);
			}
		}catch(PortalException | SystemException se) {
			LOG.debug("Error renaming counter.", se);
		}
		
		
	}
}

Next is our upgrade class. This class will go into the same package as the previous class and will use the schema version we acquired earlier. It will also use the previous class. If you didn’t need the previous class you can replace it with the DummyUpgradeStep class.

AllFeedsUpgrade.java

package mil.army.usaac.ikrome.allfeeds.upgrade;

import com.liferay.counter.kernel.service.CounterLocalService;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.upgrade.registry.UpgradeStepRegistrator;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

@Component(immediate = true, service = UpgradeStepRegistrator.class)
public class AllFeedsUpgrade implements UpgradeStepRegistrator{
	
	private static Log LOG = LogFactoryUtil.getLog(AllFeedsUpgrade.class);
	
	@Reference
	private volatile CounterLocalService counterLocalService;
	
	@Override
	public void register(Registry registry) {
		LOG.debug("AllFeedsUpgrade register method called.");
		registry.register(
	             "1", "1.0.0",
	            new AllFeedsUpgradeStep(counterLocalService));		
	}

}

A resource actions file is needed to define permissions on the service. An example is below and needed permissions may vary based on project. Strangely it seems to require a portlet name. You will also need to create a portlet.properties file to point to the file.

modules/AllFeeds-Service/AllFeeds-Service-service/src/main/resources/META-INF/resource-actions/default.xml

<?xml version="1.0"?>
<!DOCTYPE resource-action-mapping PUBLIC "-//Liferay//DTD Resource Action Mapping 7.0.0//EN" "http://www.liferay.com/dtd/liferay-resource-action-mapping_7_0_0.dtd">
<resource-action-mapping>
	
	<model-resource>
		<model-name>mil.army.usaac.ikrome.allfeeds.service.model.Newsfeed</model-name>
		<portlet-ref>
			<portlet-name>allfeeds</portlet-name>
		</portlet-ref>
		<root>true</root>
		<permissions>
			<supports>
				<action-key>UPDATE</action-key>
				<action-key>DELETE</action-key>
				<action-key>VIEW</action-key>
			</supports>
			<site-member-defaults />
			<guest-defaults />
			<guest-unsupported>
				<action-key>UPDATE</action-key>
				<action-key>DELETE</action-key>
				<action-key>VIEW</action-key>
			</guest-unsupported>
		</permissions>
	</model-resource>

	<model-resource>
		<model-name>mil.army.usaac.ikrome.allfeeds.service.model.Location</model-name>
		<portlet-ref>
			<portlet-name>allfeeds</portlet-name>
		</portlet-ref>	
		<root>true</root>	
		<permissions>
			<supports>
				<action-key>UPDATE</action-key>
				<action-key>DELETE</action-key>
				<action-key>VIEW</action-key>
			</supports>
			<site-member-defaults />
			<guest-defaults />
			<guest-unsupported>
				<action-key>UPDATE</action-key>
				<action-key>DELETE</action-key>
				<action-key>VIEW</action-key>
			</guest-unsupported>
		</permissions>
	</model-resource>

</resource-action-mapping>

AllFeeds-Service/AllFeeds-Service-service/src/main/resources/portlet.properties

resource.actions.configs=META-INF/resource-actions/default.xml	

Less Is More ~ Older posts are available in the archive.