/* * #%L * Wisdom-Framework * %% * Copyright (C) 2013 - 2014 Wisdom Framework * %% * Licensed 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. * #L% */ package org.wisdom.framework.jpa; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Instantiate; import org.apache.felix.ipojo.annotations.Provides; import org.apache.felix.ipojo.annotations.Requires; import org.osgi.framework.Bundle; import org.osgi.framework.BundleReference; import org.osgi.framework.Constants; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.hooks.weaving.WeavingHook; import org.osgi.framework.hooks.weaving.WovenClass; import org.osgi.framework.wiring.BundleWiring; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wisdom.framework.osgi.Clauses; import javax.persistence.spi.ClassTransformer; import javax.persistence.spi.PersistenceProvider; import java.io.IOException; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; import java.util.*; import java.util.regex.Pattern; @Component @Provides(specifications = {WeavingHook.class, JPATransformer.class}) @Instantiate public class JPATransformer implements WeavingHook { // // The Transformers hook enables weaving in OSGi // Every persistence unit will register itself with // the transformers hook. Order is important, once // the bundle tracker is called, the transformers // will be registered so do not move this method lower. // private final static Pattern WORD = Pattern.compile("[a-zA-Z0-9]+"); private final static Logger LOGGER = LoggerFactory.getLogger(JPATransformer.class); @Requires(proxy = false) PersistenceProvider persistenceProvider; private final Map<Bundle, List<ClassTransformer>> transformers = new LinkedHashMap<>(); private final List<String> imports; private final ClassTransformer DUMMY_TRANSFORMER = new ClassTransformer() { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { return null; } }; public JPATransformer() throws IOException { this.imports = getImports(); } @Override public void weave(WovenClass clazz) { try { if (transformers.isEmpty()) { return; } BundleWiring wiring = clazz.getBundleWiring(); Bundle b = wiring.getBundle(); ClassTransformer trfs[]; synchronized (transformers) { Collection<ClassTransformer> list = transformers.get(b); if (list == null) { return; } trfs = list.toArray(new ClassTransformer[list.size()]); } LOGGER.info("Transforming {} with {}", clazz.getClassName(), Arrays.toString(trfs)); for (ClassTransformer ctf : trfs) { if (ctf != null) { ctf.transform(wiring.getClassLoader(), clazz.getClassName(), clazz.getDefinedClass(), clazz.getProtectionDomain(), clazz.getBytes()); } } if (!imports.isEmpty()) { clazz.getDynamicImports().addAll(imports); } } catch (Exception e) { LOGGER.error("Error while weaving class {}", clazz.getClassName(), e); } } boolean register(Bundle b, ClassTransformer ctf) { LOGGER.info("register transformer {} on bundle {}", ctf, b); if (ctf == null) { ctf = DUMMY_TRANSFORMER; } synchronized (transformers) { List<ClassTransformer> list = transformers.get(b); if (list == null) { list = new ArrayList<>(); transformers.put(b, list); } list.add(ctf); return true; } } boolean unregister(Bundle b) { LOGGER.info("unregister transformers from bundle {}", b); synchronized (transformers) { transformers.remove(b); return true; } } private List<String> getImports() throws IOException { Bundle bundle; if (persistenceProvider instanceof BundleReference) { bundle = ((BundleReference) persistenceProvider).getBundle(); } else { bundle = FrameworkUtil.getBundle(persistenceProvider.getClass()); } if (bundle != null) { // Get the export clauses of the JPA provider. Clauses clauses = Clauses.parse(bundle.getHeaders().get(Constants.EXPORT_PACKAGE)); if (!clauses.isEmpty()) { List<String> list = new ArrayList<>(); for (Map.Entry<String, Map<String, String>> e : clauses.entrySet()) { // Create a new clause StringBuilder sb = new StringBuilder(); sb.append(e.getKey()); for (Map.Entry<String, String> ee : e.getValue().entrySet()) { if (ee.getKey().endsWith(":")) { continue; } sb.append(";").append(ee.getKey()).append("="); String v = ee.getValue(); if (WORD.matcher(v).matches()) { sb.append(ee.getValue()); } else { sb.append("\"").append(ee.getValue()).append("\""); } } list.add(sb.toString()); } // To retrieve the transaction manager. list.add("org.wisdom.framework.jpa.accessor"); return list; } } return Collections.emptyList(); } }