Многие разработчики для Liferay Portal 6.0 EE остались недовольны политикой компании Liferay inc., т.к. при выходе версии 6.1, они лишились возможности использования триальных лицензий во время работы над проектами, а ключи от новой версии не являются обратно-совместимыми. Что делать? Если компания, производящая данный продукт не идет на встречу разработчикам и насильно переводит всех на новую версию, а предоставить всем разработчикам реальную лицензию от production-сервера для использования во время разработки — не самая лучшая идея. Выход есть, пусть даже не самый элегантный, но если выбора не оставляют — все равно можно решить данную проблему. Добро пожаловать под кат.
О чем пойдет речь в данной статье
В версии Liferay Portal 6.0 EE используется способ проверки лицензий некоторой сложности (данный раздел не обсуждается в статье), но он имеет очень слабое место (а вот об этом мы как раз и поговорим), которое позволяет, заменив тело одного из методов, всего лишь одной строкой, заставить Liferay принимать файлы лицензий от Liferay Portal 6.1 EE. Но это не является основным преимуществом данного подхода. В результате описанных ниже манипуляций, возможно получить следующий результат: будет выполняться проверка диапазона дат действия лицензии, а также значение количества одновременных сессий и остальных полей, содержащихся в файле лицензии, но не будет выполняться проверка содержимого поля <key/>, что позволяет получить лицензию с неограниченным количеством одновременных сессий, для использования во время работки.
Немного практики
Рассмотрим класс, выполняющий проверку лицензии, который находится в библиотеке «portal-impl.jar». Думаю, люди, понимающие о чем идет речь, знают где его искать. Конечно же в «APP_SERVER/webapps/ROOT/WEB-INF/lib» если используется Tomcat, по аналогии можно найти определить размещение данного файла при использовании другого сервера-приложений.
Итак, приступим к практической части.
В качестве первого шага, при помощи знакомых Java разработчикам инструмента, такого как «JD» или «jad», «натравленного» на «portal-impl.jar», получаем код класса «com.liferay.portal.ee.license.b.d», который нас, собственно, интересует.
Ниже я приведу код, который был получен в результате описанной в предыдущем абзаце операции, дабы не отвлекать читателя от статьи.
package com.liferay.portal.ee.license.b;
import com.liferay.portal.ee.license.a;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.StringUtil;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class d {
private static final String[] a = { "MD5", "SHA-1", "SHA-256", "SHA-512" };
private static final String b = "4a4beb2b97c151cff83cbca7096325086817360a7b8c912b66e1d1dea172033a8c5934cbbacbf7b443496cc119a6a482fc6225d28bcbcb2384f52862e6fd35e49a2625f1458d24a1f62e71235dc16b9de5a971e638af32a9784e566f33dd90234d89e1dde83e8a4a100a70d999b2bb7fa77eeb34fd1be9cdf3645f9478b14c2cd6b8f955";
private static final char[] c = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
private static Log d = LogFactoryUtil.getLog(d.class);
private static d e = new d();
public static Map a(a parama) {
Date localDate1 = parama.p();
Date localDate2 = parama.c();
return a(parama.i(), parama.p(), parama.a(), parama.f(), parama.g(),
parama.m(), parama.n(), parama.l(), parama.k(), parama.j(),
parama.b(), localDate2.getTime() - localDate1.getTime(),
parama.o());
}
public static a b(a parama) {
String str = parama.g();
if (!str.equals("trial"))
return parama;
if (!c(parama))
return parama;
parama.e("developer");
Map localMap = a(parama);
parama.c(e.a(localMap));
return parama;
}
public static boolean c(a a1) {
String s = a1.i();
if (s.equals("1"))
return true;
Map map = a(a1);
String s1 = e.a(map);
if (s1.equals("4a4beb2b97c151cff83cbca7096325086817360a7b8c912b66e1d1dea172033a8c5934cbbacbf7b443496cc119a6a482fc6225d28bcbcb2384f52862e6fd35e49a2625f1458d24a1f62e71235dc16b9de5a971e638af32a9784e566f33dd90234d89e1dde83e8a4a100a70d999b2bb7fa77eeb34fd1be9cdf3645f9478b14c2cd6b8f955"))
return false;
return s1.equals(a1.d());
}
private String a(Map paramMap) {
int i = GetterUtil.getInteger((String) paramMap.get("version"));
try {
if (i == 1)
throw new IllegalArgumentException("Invalid version " + i);
if (i == 2)
return b(paramMap);
} catch (Exception localException) {
d.error(localException, localException);
}
return "";
}
private String b(Map paramMap) {
ArrayList localArrayList = new ArrayList(paramMap.keySet());
Collections.sort(localArrayList);
Object localObject = new ArrayList(paramMap.size());
String str1;
String str2;
for (int i = 0; i < localArrayList.size(); i++) {
str1 = (String) paramMap.get(localArrayList.get(i));
str2 = a[(i % a.length)];
String str3 = a(str1, str2);
((List) localObject).add(str3);
}
localObject = b((List) localObject);
for (int i = 0; i < ((List) localObject).size(); i++) {
str1 = (String) ((List) localObject).get(i);
str2 = a[(i % a.length)];
str1 = a(str1, str2);
((List) localObject).set(i, str1);
}
return (String) a((List) localObject);
}
private String a(String paramString1, String paramString2) {
MessageDigest localMessageDigest = null;
byte[] arrayOfByte = null;
try {
localMessageDigest = MessageDigest.getInstance(paramString2);
localMessageDigest.update(paramString1.getBytes());
arrayOfByte = localMessageDigest.digest();
} catch (NoSuchAlgorithmException e) {
}
StringBuilder localStringBuilder = new StringBuilder(
arrayOfByte.length << 1);
for (int i = 0; i < arrayOfByte.length; i++) {
int j = arrayOfByte[i] & 0xFF;
localStringBuilder.append(c[(j >> 4)]);
localStringBuilder.append(c[(j & 0xF)]);
}
return localStringBuilder.toString();
}
private static Map a(String paramString1, Date paramDate,
String paramString2, String paramString3, String paramString4,
String paramString5, String paramString6, String paramString7,
int paramInt1, int paramInt2, String paramString8, long paramLong,
String[] paramArrayOfString) {
HashMap localHashMap = new HashMap();
localHashMap.put("version", paramString1);
if (!paramString4.equals("trial"))
localHashMap.put("startDate", String.valueOf(paramDate.getTime()));
localHashMap.put("type", paramString4);
localHashMap.put("productVersion", paramString6);
localHashMap.put("owner", paramString7);
localHashMap.put("description", paramString8);
if (paramString4.equals("trial"))
localHashMap.put("lifetime", String.valueOf(paramLong));
else
localHashMap.put("expirationDate",
String.valueOf(paramDate.getTime() + paramLong));
String str1;
if (paramString1.equals("1")) {
if ((paramString4.equals("cluster"))
|| (paramString4.equals("developer-cluster"))) {
for (int i = 0; i < paramArrayOfString.length; i++) {
String str2 = StringUtil.replace(paramArrayOfString[i],
"-", ":");
str2 = str2.trim().toLowerCase();
localHashMap.put("macAddress." + i, str2);
}
} else if (paramString4.equals("production")) {
str1 = paramArrayOfString[0].trim();
localHashMap.put("serverId", str1);
}
} else {
localHashMap.put("accountEntryName", paramString2);
localHashMap.put("licenseEntryName", paramString3);
localHashMap.put("productEntryName", paramString5);
if ((paramString4.equals("cluster"))
|| (paramString4.equals("developer-cluster")))
localHashMap.put("maxServers", String.valueOf(paramInt1));
if ((paramString4.equals("developer"))
|| (paramString4.equals("developer-cluster"))
|| (paramString4.equals("trial")))
localHashMap.put("maxHttpSessions", String.valueOf(paramInt2));
if (paramString4.equals("production")) {
str1 = StringUtil.merge(paramArrayOfString);
str1 = str1.toLowerCase();
str1 = StringUtil.replace(str1, "-", ":");
localHashMap.put("serverIds", str1);
}
}
return localHashMap;
}
private String a(List paramList) {
int i = paramList.size();
int j = 0;
int k = 2147483647;
Iterator localIterator1 = paramList.iterator();
int n;
while (localIterator1.hasNext()) {
Object localObject = (String) localIterator1.next();
n = ((String) localObject).length();
j += n;
if (n >= k)
continue;
k = n;
}
Object localObject = new StringBuilder(j);
for (int m = 0; m < k; m++)
for (n = 0; n < i; n++) {
String str2 = (String) paramList.get(n);
((StringBuilder) localObject).append(str2.charAt(m));
}
Iterator localIterator2 = paramList.iterator();
while (localIterator2.hasNext()) {
String str1 = (String) localIterator2.next();
if (str1.length() <= k)
continue;
((StringBuilder) localObject).append(str1.substring(k));
}
return (String) ((StringBuilder) localObject).toString();
}
private List b(List paramList) {
int i = paramList.size();
int j = i / 4;
if (j * 4 < i)
j++;
ArrayList localArrayList = new ArrayList(4);
StringBuilder localStringBuilder = new StringBuilder();
for (int k = 0; k < i; k++) {
String str = (String) paramList.get(k);
if ((k != 0) && (k % j == 0)) {
localArrayList.add(localStringBuilder.toString());
localStringBuilder.setLength(0);
}
localStringBuilder.append(str);
}
if (localArrayList.size() < 4)
localArrayList.add(localStringBuilder.toString());
return localArrayList;
}
}
Нас интересует единственный метод, который возвращает результат выполнения проверки лицензионного ключа:
public static boolean c(a a1) { ... }
Чтобы всякий лицензионный файл, проходил проверку, необходимо заменить тело данного метода нехитрой конструкцией возврата значния «true».
public static boolean c(a a1) {
return true;
}
Компиляция
Теперь необходимо скомпилировать модифицированный класс и вернуть его на подобающее место. Приступая к компиляции, сначала создадим базовую структуру каталогов.
liferay_license_fix/ src/ classes/ lib/
Сохраним полученный нами исходный код класса, создав в «src/» соответсвующие пакету каталоги. Для выполнения компиляции целевого класса, нам понадобится дополнительный класс «com.liferay.portal.ee.license.a», который содержится все в том же «portal-impl.jar». При включении данной библиотеки в «classpath», целевой класс не скомпилируется, т.к. он уже содержится в «portal-impl.jar». Поэтому нужно распаковать class-файл «com.liferay.portal.ee.license.a» в созданный ранее каталог «classes», а также скопировать «portal-service.jar» из «APP_SERVERlibext» в случае с Tomcat или из другой локации, в зависимости от вашей конфигурации, в каталог «lib» (можно избежать копирования «portal-service.jar», указав его расположение в опции «classpath» компилятора, но в статье будет рассмотрен случай с копированием). Должна получиться следующая картина.
liferay_license_fix/ src/ com/ liferay/ portal/ ee/ license/ b/ d.java classes/ com/ liferay/ portal/ ee/ license/ a.class lib/ portal-service.jar
Выполняем переход в каталог «liferay_license_fix» и компилируем целевой класс следующей коммандой:
javac -cp "libportal-service.jar;classes" -d classes srccomliferayportaleelicensebd.java
Скомпилированный нами класс можно найти здесь:
liferay_license_fix/classes/com/liferay/portal/ee/license/b/d.class
Подготовка патча
Теперь необходимо заменить исходный класс, содержащийся в «portal-impl.jar», на полученный в ходе приведенных выше действий. Это можно сделать как вручную, просто заменив файл внутри «portal-impl.jar» так и при помощи патча и инструмента «patching-tool», входящего в состав Liferay Portal, но нам понадобится инструмент из более новой, чем LR EE 6.0 версии, т.е. содержащийся в версии Liferay Poral 6.1 EE, который можно скачать с официального сайта, и просто заменить текущую версию «patching-tool». После того, как приведенные действия будут выполнены — подготовим патч. Плюс данного подхода в том, что установка и удаление патча выполняется при помощи простых команд и делает данные операции предельно простыми, а также не прийдется заботится о выполнении резервных копий файлов, которые затрагиваются при наложении патча, и в случае необходимости можно легко откатить изменения.
Подготовим структуру каталогов патча:
liferay-license-fix-6012 backup WAR_PATH WEB-INF lib portal-impl.jar com liferay portal ee license b jdk5 WAR_PATH WEB-INF lib portal-impl.jar com liferay portal ee license b jdk6 WAR_PATH WEB-INF lib portal-impl.jar com liferay portal ee license b
Рассмотрим возможный вариант создания структуры каталогов, сначала создадим следующие каталоги «WAR_PATHWEB-INFlibportal-impl.jar» в «liferay-license-fix-6012backup» и скопируем их в «liferay-license-fix-6012jdk5», а также в «liferay-license-fix-6012jdk6». Далее скопируем скомпилированный выше класс, вместе со структурой каталогов пакета «com/liferay/portal/ee/license/b/d.class» в «liferay-license-fix-6012jdk5» и «liferay-license-fix-6012jdk6». Теперь нужно распаковать оригинальный «com.liferay.portal.ee.license.b.d» класс из «portal-impl.jar» в каталог «liferay-license-fix-6012backupWAR_PATHWEB-INFlibportal-impl.jarcomliferayportaleelicenseb».
Далее создаем файл «liferay-license-fix-6012fixpack_documentation.xml» со следующим содержимым:
<?xml version="1.0"?>
<patch>
<id>liferay-license-fix-6012</id>
<name>liferay-license-fix</name>
<patching-tool-version>2</patching-tool-version>
<incremental>false</incremental>
<version>1</version>
<rank>1</rank>
<requirements></requirements>
<component>security-hotfix</component>
<product>6012</product>
<fixed-issues></fixed-issues>
<module-name></module-name>
<module-id></module-id>
</patch>
Последним шагом в подготовке патча является создание zip-архива «liferay-license-fix-6012.zip» со всем содержимым каталога «liferay-license-fix-6012», не включая сам каталог.
Управление патчами
Для установки патча, его нужно скопировать в "/patching-tool/patches" и выполнить инициализацию «patching-tool», если она не была произведена ранее при помощи следующей команды:
patching-tool auto-discovery
После выполнения начальной инициализации можно увидеть файл «default.properties», в котором при необходимости можно откорректировать пути расположения экземпляра сервера с Liferay Portal 6.0 EE SP2, к которому будут применены патчи.
Затем при помощи команды:
patching-tool info
можно получить информацию о текущем состоянии (список патчей, а также их статус). Если в выводе присутствуют следующие строки:
Available patches: [ I] liferay-license-fix-6012 :: Currently not installed; Will be installed.
это означает, что патч может быть установлен.
Команда для выполнения установки патча:
patching-tool install
Если результат выполнения команды является следующим:
The installation was successful. One patch is installed on the system.
это говорит о том, что патч был успешно установлен.
В случае необходимости отмены изменений, полученных в результате установки патча, необходимо выполнить следующую команду:
patching-tool revert
при успешном выполнении вы увидите следующее сообщение:
Revert has been executed successfully. There is no patch installed on the system.
После проделанных манипуляций смело можно пользоваться файлами лицензий от Liferay Portal 6.1 EE или создать файл лицензии со следующим содержимым:
<?xml version="1.0"?>
<license>
<account-name>Liferay Developer</account-name>
<owner>Developer</owner>
<description>Trial license</description>
<product-name>Portal Enterprise</product-name>
<product-version>6.0 SP 2</product-version>
<license-name>Portal Enterprise</license-name>
<license-type>enterprise</license-type>
<license-version>2</license-version>
<start-date>Sunday, January 1, 2012 00:00:00 AM GMT</start-date>
<expiration-date>Thursday, January 1, 2099 00:00:00 AM GMT</expiration-date>
<max-http-sessions>1000</max-http-sessions>
<key></key>
</license>
Скачать архив, содержащий все описанные в статье файлы, а также подготовленный патч и «patching-tool» необходимой версии.
Автор: untergrundbahn