blob: cd7fd66c0d58cc0f94794bff74c32415b23a1a0b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.j2ee.weblogic9.deploy;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.Attributes.Name;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.enterprise.deploy.spi.exceptions.DeploymentManagerCreationException;
import org.netbeans.modules.j2ee.core.api.support.progress.ProgressSupport;
import org.netbeans.modules.j2ee.core.api.support.progress.ProgressSupport.Context;
import org.netbeans.modules.j2ee.deployment.common.api.Version;
import org.netbeans.modules.j2ee.deployment.devmodules.api.Deployment;
import org.netbeans.modules.j2ee.deployment.plugins.api.InstanceProperties;
import org.netbeans.modules.j2ee.weblogic9.WLDeploymentFactory;
import org.netbeans.modules.j2ee.weblogic9.WLPluginProperties;
import org.netbeans.modules.j2ee.weblogic9.WLProductProperties;
import org.netbeans.modules.j2ee.weblogic9.j2ee.WLJ2eePlatformFactory;
import org.netbeans.modules.weblogic.common.api.WebLogicLayout;
import org.netbeans.spi.project.libraries.LibraryImplementation;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
/**
*
* @author Petr Hejl
*/
public final class WLJpa2SwitchSupport {
private static final Version SWITCH_SUPPORTED_VERSION_MIN =
Version.fromJsr277NotationWithFallback("10.3.4"); // NOI18N
private static final String OEPE_CONTRIBUTIONS_JAR = "oepe-contributions.jar"; // NO18N
private static final Pattern JPA_JAR_1_PATTERN = Pattern.compile("^.*javax\\.persistence.*(_2-\\d+(-\\d+)?)\\.jar$"); // NO18N
private static final String JPA_JAR1_FALLBACK = "javax.persistence_1.0.0.0_2-0-0.jar"; // NOI18N
private static final Pattern JPA_JAR_2_PATTERN = Pattern.compile("^.*com\\.oracle\\.jpa2support.*\\.jar$"); // NO18N
private static final String JPA_JAR2_FALLBACK = "com.oracle.jpa2support_1.0.0.0_2-0.jar"; // NOI18N
private static final Logger LOGGER = Logger.getLogger(WLJpa2SwitchSupport.class.getName());
private final File serverRoot;
private final WLDeploymentManager dm;
/** GuardedBy("this")*/
private Version serverVersion;
public WLJpa2SwitchSupport(File serverRoot) {
this.dm = null;
this.serverRoot = serverRoot;
}
public WLJpa2SwitchSupport(WLDeploymentManager dm) {
this.dm = dm;
this.serverRoot = WLPluginProperties.getServerRoot(dm, true);
}
public boolean isSwitchSupported() {
Version version = null;
synchronized (this) {
if (serverVersion != null) {
version = serverVersion;
} else {
if (dm != null) {
version = dm.getServerVersion();
} else {
version = WLPluginProperties.getServerVersion(serverRoot);
}
serverVersion = version;
}
}
return version != null && version.isAboveOrEqual(SWITCH_SUPPORTED_VERSION_MIN)
&& SWITCH_SUPPORTED_VERSION_MIN.getMajor().equals(version.getMajor())
&& SWITCH_SUPPORTED_VERSION_MIN.getMinor().equals(version.getMinor());
}
public void enable() {
List<ProgressSupport.Action> actions = new ArrayList<ProgressSupport.Action>();
actions.add(new ProgressSupport.BackgroundAction() {
@Override
protected void run(Context actionContext) {
actionContext.progress(NbBundle.getMessage(WLJpa2SwitchSupport.class, "MSG_Enabling_JPA2"));
doEnable();
}
});
ProgressSupport.invoke(actions);
}
public void disable() {
List<ProgressSupport.Action> actions = new ArrayList<ProgressSupport.Action>();
actions.add(new ProgressSupport.BackgroundAction() {
@Override
protected void run(Context actionContext) {
actionContext.progress(NbBundle.getMessage(WLJpa2SwitchSupport.class, "MSG_Disabling_JPA2"));
doDisable();
}
});
ProgressSupport.invoke(actions);
}
private void doEnable() {
if (!isSwitchSupported()) {
throw new IllegalStateException("JPA2 switching is not supported for WebLogic " + serverRoot);
}
try {
File libDir = WLPluginProperties.getServerLibDirectory(serverRoot);
if (libDir != null) {
libDir = FileUtil.normalizeFile(libDir);
}
String path = getPathToModules(libDir);
if (path.length() > 0) {
path = path + "/"; // NOI18N
}
final File oepeFile = new File(libDir, OEPE_CONTRIBUTIONS_JAR);
final String relPath = path;
Jpa2Jars jars = getJpa2Jars(libDir, path);
final String contribPath = path + jars.getJpa1Jar() + " " // NOI18N
+ path + jars.getJpa2Jar();
// oepe does not exist
if (!oepeFile.exists()) {
createContributionsJar(oepeFile, contribPath);
// exists so update cp
} else {
JarFile oepeJarFile = new JarFile(oepeFile);
try {
Manifest mf = oepeJarFile.getManifest();
String cp = mf.getMainAttributes().getValue(Name.CLASS_PATH);
if (cp == null) {
cp = ""; // NOI18N
}
if ((!JPA_JAR_1_PATTERN.matcher(cp).matches() || !cp.contains(jars.getJpa1Jar()))
|| (!JPA_JAR_2_PATTERN.matcher(cp).matches() || !cp.contains(jars.getJpa2Jar()))) {
StringBuilder updated = new StringBuilder();
for (String element : cp.split("\\s+")) { // NOI18N
if (!JPA_JAR_1_PATTERN.matcher(element).matches() && !JPA_JAR_2_PATTERN.matcher(element).matches()) {
updated.append(element).append(" "); // NOI18N
}
}
updated.insert(0, " ").insert(0, jars.getJpa2Jar()).insert(0, relPath); // NOI18N
updated.insert(0, " ").insert(0, jars.getJpa1Jar()).insert(0, relPath); // NOI18N
if (cp.length() == 0) {
updated.deleteCharAt(updated.length() - 1);
}
mf.getMainAttributes().put(Name.CLASS_PATH, updated.toString());
replaceManifest(oepeFile, mf);
}
} finally {
oepeJarFile.close();
}
}
// update weblogic.jar
File weblogicFile = WebLogicLayout.getWeblogicJar(serverRoot);
JarFile weblogicJarFile = new JarFile(weblogicFile);
try {
Manifest wlManifest = weblogicJarFile.getManifest();
String cp = wlManifest.getMainAttributes().getValue(Name.CLASS_PATH);
if (cp == null) {
cp = ""; // NOI18N
}
if (!cp.contains(OEPE_CONTRIBUTIONS_JAR)) {
if (cp.length() == 0) {
cp = OEPE_CONTRIBUTIONS_JAR;
} else {
cp = OEPE_CONTRIBUTIONS_JAR + " " + cp; // NOI18N
}
wlManifest.getMainAttributes().put(Name.CLASS_PATH, cp);
replaceManifest(weblogicFile, wlManifest);
}
} finally {
weblogicJarFile.close();
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
} finally {
notifyLibrariesChanged();
}
}
private void doDisable() {
if (!isSwitchSupported()) {
throw new IllegalStateException("JPA2 switching is not supported for WebLogic " + serverRoot);
}
try {
File libDir = WLPluginProperties.getServerLibDirectory(serverRoot);
if (libDir != null) {
libDir = FileUtil.normalizeFile(libDir);
}
File oepeJarFile = new File(libDir, OEPE_CONTRIBUTIONS_JAR);
if (!oepeJarFile.exists() || !oepeJarFile.isFile()) {
return;
}
JarFile file = new JarFile(oepeJarFile);
try {
Manifest mf = file.getManifest();
String cp = mf.getMainAttributes().getValue(Name.CLASS_PATH);
if (cp == null) {
return;
}
StringBuilder builder = new StringBuilder();
for (String element : cp.split("\\s+")) { // NOI18N
if (!JPA_JAR_1_PATTERN.matcher(element).matches() && !JPA_JAR_2_PATTERN.matcher(element).matches()) {
builder.append(element).append(" "); // NOI18N
}
}
if (builder.length() > 0) {
mf.getMainAttributes().put(Name.CLASS_PATH,
builder.substring(0, builder.length() - 1));
} else {
mf.getMainAttributes().remove(Name.CLASS_PATH);
}
replaceManifest(oepeJarFile, mf);
} finally {
file.close();
}
} catch (IOException ex) {
// TODO some exception/message to the user
Exceptions.printStackTrace(ex);
} finally {
notifyLibrariesChanged();
}
}
public boolean isEnabled() {
if (dm != null) {
return dm.getJ2eePlatformImpl().isJpa2Available();
} else {
List<URL> classpath = WLJ2eePlatformFactory.getWLSClassPath(serverRoot,
WLPluginProperties.getMiddlewareHome(serverRoot), null);
for (URL url : classpath) {
URL file = FileUtil.getArchiveFile(url);
if (file != null && JPA_JAR_1_PATTERN.matcher(file.getFile()).matches()) {
return true;
}
}
return false;
}
}
public boolean isEnabledViaSmartUpdate() {
if (dm != null) {
dm.getJ2eePlatformImpl().getLibraries();
for (LibraryImplementation lib : dm.getJ2eePlatformImpl().getLibraries()) {
List<URL> urls = lib.getContent("classpath"); // NOI18N
if (isEnabledViaSmartUpdate(urls)) {
return true;
}
}
return false;
} else {
List<URL> urls = WLJ2eePlatformFactory.getWLSClassPath(serverRoot,
WLPluginProperties.getMiddlewareHome(serverRoot), null);
return isEnabledViaSmartUpdate(urls);
}
}
private boolean isEnabledViaSmartUpdate(List<URL> urls) {
if (urls != null) {
for (URL url : urls) {
URL file = FileUtil.getArchiveFile(url);
if (file != null && file.getFile().endsWith("BUG9923849_WLS103MP4.jar")) { // NOI18N
return true;
}
}
}
return false;
}
/**
* Method sends classpath change event to any instance registered
* in the IDE and sharing the same server root.
*/
private void notifyLibrariesChanged() {
if (dm != null) {
dm.getJ2eePlatformImpl().notifyLibrariesChange();
}
String[] urls = Deployment.getDefault().getInstancesOfServer(WLDeploymentFactory.SERVER_ID);
for (String url : urls) {
if (dm != null && url.equals(dm.getUri())) {
continue;
}
InstanceProperties props = InstanceProperties.getInstanceProperties(url);
if (props != null) {
String serverRootValue = props.getProperty(WLPluginProperties.SERVER_ROOT_ATTR);
File root = FileUtil.normalizeFile(new File(serverRootValue));
if (root.equals(serverRoot)) {
try {
WLDeploymentManager manager = (WLDeploymentManager)
WLDeploymentFactory.getInstance().getDisconnectedDeploymentManager(url);
manager.getJ2eePlatformImpl().notifyLibrariesChange();
} catch (DeploymentManagerCreationException ex) {
LOGGER.log(Level.INFO, null, ex);
}
}
}
}
}
private void replaceManifest(File jarFile, Manifest manifest) throws IOException {
FileObject fo = FileUtil.toFileObject(jarFile);
String tmpName = FileUtil.findFreeFileName(fo.getParent(),
jarFile.getName(), "tmp"); // NOI18N
File tmpJar = new File(jarFile.getParentFile(), tmpName + ".tmp"); // NOI18N
try {
InputStream is = new BufferedInputStream(
new FileInputStream(jarFile));
try {
OutputStream os = new BufferedOutputStream(
new FileOutputStream(tmpJar));
try {
replaceManifest(is, os, manifest);
} finally {
os.close();
}
} finally {
is.close();
}
if (tmpJar.renameTo(jarFile)) {
LOGGER.log(Level.FINE, "Successfully moved {0}", tmpJar);
return;
}
LOGGER.log(Level.FINE, "Byte to byte copy {0}", tmpJar);
copy(tmpJar, jarFile);
} finally {
tmpJar.delete();
}
}
private void replaceManifest(InputStream is, OutputStream os, Manifest manifest) throws IOException {
JarInputStream in = new JarInputStream(is);
try {
JarOutputStream out = new JarOutputStream(os, manifest);
try {
JarEntry entry = null;
byte[] temp = new byte[32768];
while ((entry = in.getNextJarEntry()) != null) {
String name = entry.getName();
if (name.equalsIgnoreCase("META-INF/MANIFEST.MF")) { // NOI18N
continue;
}
out.putNextEntry(entry);
while (in.available() != 0) {
int read = in.read(temp);
if (read != -1) {
out.write(temp, 0, read);
}
}
out.closeEntry();
}
} finally {
out.close();
}
} finally {
in.close();
}
}
private void createContributionsJar(File jarFile, String classpath) throws IOException {
//need to create zip file
OutputStream os = new BufferedOutputStream(new FileOutputStream(jarFile));
try {
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); // NOI18N
manifest.getMainAttributes().put(Name.CLASS_PATH, classpath);
JarOutputStream dest = new JarOutputStream(new BufferedOutputStream(os), manifest);
try {
dest.closeEntry();
dest.finish();
} finally {
dest.close();
}
} finally {
os.close();
}
}
private void copy(File source, File dest) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(source));
try {
OutputStream os = new BufferedOutputStream(new FileOutputStream(dest));
try {
FileUtil.copy(is, os);
} finally {
os.close();
}
} finally {
is.close();
}
}
private String getPathToModules(File from) {
File mwHomeFile = null;
String mwHome = (dm != null)
? dm.getProductProperties().getMiddlewareHome()
: WLProductProperties.getMiddlewareHome(serverRoot);
if (mwHome == null) {
if (serverRoot != null && serverRoot.getParentFile() != null) {
mwHomeFile = serverRoot.getParentFile();
}
} else {
mwHomeFile = new File(mwHome);
}
if (mwHomeFile != null) {
File modules = FileUtil.normalizeFile(new File(mwHomeFile, "modules")); // NOI18N
String relativePath = getRelativePath(from, modules);
if (relativePath == null) {
// FIXME forward slashes
return modules.getAbsolutePath();
}
return relativePath;
}
// just improbable fallback :(
return "../../../modules"; // NOI18N
}
private Jpa2Jars getJpa2Jars(File libDir, String path) {
String jar1 = null;
String jar2 = null;
if (libDir != null) {
File dir = new File(libDir, path);
for (File candidate : dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return JPA_JAR_1_PATTERN.matcher(name).matches() || JPA_JAR_2_PATTERN.matcher(name).matches();
}
})) {
if (jar1 == null && JPA_JAR_1_PATTERN.matcher(candidate.getName()).matches()) {
jar1 = candidate.getName();
} else if (jar2 == null) {
jar2 = candidate.getName();
}
}
}
// just fallback
if (jar1 == null) {
jar1 = JPA_JAR1_FALLBACK;
}
if (jar2 == null) {
jar2 = JPA_JAR2_FALLBACK;
}
return new Jpa2Jars(jar1, jar2);
}
// package for testing only
static String getRelativePath(File from, File to) {
String toPath = to.getAbsolutePath();
String fromPath = from.getAbsolutePath();
if (toPath.startsWith(fromPath)) {
if (toPath.length() == fromPath.length()) {
return "";
}
StringBuilder builder = new StringBuilder();
File currentPath = to;
while (!currentPath.equals(from)) {
builder.insert(0, currentPath.getName());
builder.insert(0, "/"); // NOI18N
currentPath = currentPath.getParentFile();
}
return builder.substring(1);
} else {
File parent = from.getParentFile();
if (parent == null) {
return null;
} else {
return "../" + getRelativePath(parent, to); // NOI18N
}
}
}
private static class Jpa2Jars {
private final String Jpa1Jar;
private final String Jpa2Jar;
public Jpa2Jars(String Jpa1Jar, String Jpa2Jar) {
this.Jpa1Jar = Jpa1Jar;
this.Jpa2Jar = Jpa2Jar;
}
public String getJpa1Jar() {
return Jpa1Jar;
}
public String getJpa2Jar() {
return Jpa2Jar;
}
}
}